import { parser } from "../deps.js";import { list } from "../deps.js";import { uniq } from "../utils.js";import { escapeRegexp } from "../utils.js";import { splitSelector } from "../utils.js";
function convert(value) { if ( value && value.length === 2 && value[0] === "span" && parseInt(value[1], 10) > 0 ) { return [false, parseInt(value[1], 10)]; }
if (value && value.length === 1 && parseInt(value[0], 10) > 0) { return [parseInt(value[0], 10), false]; }
return [false, false];}
export { translate };
function translate(values, startIndex, endIndex) { let startValue = values[startIndex]; let endValue = values[endIndex];
if (!startValue) { return [false, false]; }
let [start, spanStart] = convert(startValue); let [end, spanEnd] = convert(endValue);
if (start && !endValue) { return [start, false]; }
if (spanStart && end) { return [end - spanStart, spanStart]; }
if (start && spanEnd) { return [start, spanEnd]; }
if (start && end) { return [start, end - start]; }
return [false, false];}
export { parse };
function parse(decl) { let node = parser(decl.value);
let values = []; let current = 0; values[current] = [];
for (let i of node.nodes) { if (i.type === "div") { current += 1; values[current] = []; } else if (i.type === "word") { values[current].push(i.value); } }
return values;}
export { insertDecl };
function insertDecl(decl, prop, value) { if (value && !decl.parent.some((i) => i.prop === `-ms-${prop}`)) { decl.cloneBefore({ prop: `-ms-${prop}`, value: value.toString(), }); }}
export { prefixTrackProp };
function prefixTrackProp({ prop, prefix }) { return prefix + prop.replace("template-", "");}
function transformRepeat({ nodes }, { gap }) { let { count, size } = nodes.reduce( (result, node) => { if (node.type === "div" && node.value === ",") { result.key = "size"; } else { result[result.key].push(parser.stringify(node)); } return result; }, { key: "count", size: [], count: [], }, );
if (gap) { size = size.filter((i) => i.trim()); let val = []; for (let i = 1; i <= count; i++) { size.forEach((item, index) => { if (index > 0 || i > 1) { val.push(gap); } val.push(item); }); }
return val.join(" "); }
return `(${size.join("")})[${count.join("")}]`;}
export { prefixTrackValue };
function prefixTrackValue({ value, gap }) { let result = parser(value).nodes.reduce((nodes, node) => { if (node.type === "function" && node.value === "repeat") { return nodes.concat({ type: "word", value: transformRepeat(node, { gap }), }); } if (gap && node.type === "space") { return nodes.concat( { type: "space", value: " ", }, { type: "word", value: gap, }, node, ); } return nodes.concat(node); }, []);
return parser.stringify(result);}
let DOTS = /^\.+$/;
function track(start, end) { return { start, end, span: end - start };}
function getColumns(line) { return line.trim().split(/\s+/g);}
export { parseGridAreas };
function parseGridAreas({ rows, gap }) { return rows.reduce((areas, line, rowIndex) => { if (gap.row) rowIndex *= 2;
if (line.trim() === "") return areas;
getColumns(line).forEach((area, columnIndex) => { if (DOTS.test(area)) return;
if (gap.column) columnIndex *= 2;
if (typeof areas[area] === "undefined") { areas[area] = { column: track(columnIndex + 1, columnIndex + 2), row: track(rowIndex + 1, rowIndex + 2), }; } else { let { column, row } = areas[area];
column.start = Math.min(column.start, columnIndex + 1); column.end = Math.max(column.end, columnIndex + 2); column.span = column.end - column.start;
row.start = Math.min(row.start, rowIndex + 1); row.end = Math.max(row.end, rowIndex + 2); row.span = row.end - row.start; } });
return areas; }, {});}
function testTrack(node) { return node.type === "word" && /^\[.+]$/.test(node.value);}
function verifyRowSize(result) { if (result.areas.length > result.rows.length) { result.rows.push("auto"); } return result;}
export { parseTemplate };
function parseTemplate({ decl, gap }) { let gridTemplate = parser(decl.value).nodes.reduce( (result, node) => { let { type, value } = node;
if (testTrack(node) || type === "space") return result;
if (type === "string") { result = verifyRowSize(result); result.areas.push(value); }
if (type === "word" || type === "function") { result[result.key].push(parser.stringify(node)); }
if (type === "div" && value === "/") { result.key = "columns"; result = verifyRowSize(result); }
return result; }, { key: "rows", columns: [], rows: [], areas: [], }, );
return { areas: parseGridAreas({ rows: gridTemplate.areas, gap, }), columns: prefixTrackValue({ value: gridTemplate.columns.join(" "), gap: gap.column, }), rows: prefixTrackValue({ value: gridTemplate.rows.join(" "), gap: gap.row, }), };}
function getMSDecls(area, addRowSpan = false, addColumnSpan = false) { let result = [ { prop: "-ms-grid-row", value: String(area.row.start), }, ]; if (area.row.span > 1 || addRowSpan) { result.push({ prop: "-ms-grid-row-span", value: String(area.row.span), }); } result.push({ prop: "-ms-grid-column", value: String(area.column.start), }); if (area.column.span > 1 || addColumnSpan) { result.push({ prop: "-ms-grid-column-span", value: String(area.column.span), }); } return result;}
function getParentMedia(parent) { if (parent.type === "atrule" && parent.name === "media") { return parent; } if (!parent.parent) { return false; } return getParentMedia(parent.parent);}
function changeDuplicateAreaSelectors(ruleSelectors, templateSelectors) { ruleSelectors = ruleSelectors.map((selector) => { let selectorBySpace = list.space(selector); let selectorByComma = list.comma(selector);
if (selectorBySpace.length > selectorByComma.length) { selector = selectorBySpace.slice(-1).join(""); } return selector; });
return ruleSelectors.map((ruleSelector) => { let newSelector = templateSelectors.map((tplSelector, index) => { let space = index === 0 ? "" : " "; return `${space}${tplSelector} > ${ruleSelector}`; });
return newSelector; });}
function selectorsEqual(ruleA, ruleB) { return ruleA.selectors.some((sel) => { return ruleB.selectors.includes(sel); });}
function parseGridTemplatesData(css) { let parsed = [];
css.walkDecls(/grid-template(-areas)?$/, (d) => { let rule = d.parent; let media = getParentMedia(rule); let gap = getGridGap(d); let inheritedGap = inheritGridGap(d, gap); let { areas } = parseTemplate({ decl: d, gap: inheritedGap || gap }); let areaNames = Object.keys(areas);
if (areaNames.length === 0) { return true; }
let index = parsed.reduce((acc, { allAreas }, idx) => { let hasAreas = allAreas && areaNames.some((area) => allAreas.includes(area)); return hasAreas ? idx : acc; }, null);
if (index !== null) { let { allAreas, rules } = parsed[index];
let hasNoDuplicates = rules.some((r) => { return r.hasDuplicates === false && selectorsEqual(r, rule); });
let duplicatesFound = false;
let duplicateAreaNames = rules.reduce((acc, r) => { if (!r.params && selectorsEqual(r, rule)) { duplicatesFound = true; return r.duplicateAreaNames; } if (!duplicatesFound) { areaNames.forEach((name) => { if (r.areas[name]) { acc.push(name); } }); } return uniq(acc); }, []);
rules.forEach((r) => { areaNames.forEach((name) => { let area = r.areas[name]; if (area && area.row.span !== areas[name].row.span) { areas[name].row.updateSpan = true; }
if (area && area.column.span !== areas[name].column.span) { areas[name].column.updateSpan = true; } }); });
parsed[index].allAreas = uniq([...allAreas, ...areaNames]); parsed[index].rules.push({ hasDuplicates: !hasNoDuplicates, params: media.params, selectors: rule.selectors, node: rule, duplicateAreaNames, areas, }); } else { parsed.push({ allAreas: areaNames, areasCount: 0, rules: [ { hasDuplicates: false, duplicateRules: [], params: media.params, selectors: rule.selectors, node: rule, duplicateAreaNames: [], areas, }, ], }); }
return undefined; });
return parsed;}
export { insertAreas };
function insertAreas(css, isDisabled) { let gridTemplatesData = parseGridTemplatesData(css);
if (gridTemplatesData.length === 0) { return undefined; }
let rulesToInsert = {};
css.walkDecls("grid-area", (gridArea) => { let gridAreaRule = gridArea.parent; let hasPrefixedRow = gridAreaRule.first.prop === "-ms-grid-row"; let gridAreaMedia = getParentMedia(gridAreaRule);
if (isDisabled(gridArea)) { return undefined; }
let gridAreaRuleIndex = css.index(gridAreaMedia || gridAreaRule);
let value = gridArea.value; let data = gridTemplatesData.filter((d) => d.allAreas.includes(value))[0];
if (!data) { return true; }
let lastArea = data.allAreas[data.allAreas.length - 1]; let selectorBySpace = list.space(gridAreaRule.selector); let selectorByComma = list.comma(gridAreaRule.selector); let selectorIsComplex = selectorBySpace.length > 1 && selectorBySpace.length > selectorByComma.length;
if (hasPrefixedRow) { return false; }
if (!rulesToInsert[lastArea]) { rulesToInsert[lastArea] = {}; }
let lastRuleIsSet = false;
for (let rule of data.rules) { let area = rule.areas[value]; let hasDuplicateName = rule.duplicateAreaNames.includes(value);
if (!area) { let lastRule = rulesToInsert[lastArea].lastRule; let lastRuleIndex; if (lastRule) { lastRuleIndex = css.index(lastRule); } else { lastRuleIndex = -1; }
if (gridAreaRuleIndex > lastRuleIndex) { rulesToInsert[lastArea].lastRule = gridAreaMedia || gridAreaRule; } continue; }
if (rule.params && !rulesToInsert[lastArea][rule.params]) { rulesToInsert[lastArea][rule.params] = []; }
if ((!rule.hasDuplicates || !hasDuplicateName) && !rule.params) {
getMSDecls(area, false, false) .reverse() .forEach((i) => gridAreaRule.prepend( Object.assign(i, { raws: { between: gridArea.raws.between, }, }), ) );
rulesToInsert[lastArea].lastRule = gridAreaRule; lastRuleIsSet = true; } else if (rule.hasDuplicates && !rule.params && !selectorIsComplex) { let cloned = gridAreaRule.clone(); cloned.removeAll();
getMSDecls(area, area.row.updateSpan, area.column.updateSpan) .reverse() .forEach((i) => cloned.prepend( Object.assign(i, { raws: { between: gridArea.raws.between, }, }), ) );
cloned.selectors = changeDuplicateAreaSelectors( cloned.selectors, rule.selectors, );
if (rulesToInsert[lastArea].lastRule) { rulesToInsert[lastArea].lastRule.after(cloned); } rulesToInsert[lastArea].lastRule = cloned; lastRuleIsSet = true; } else if ( rule.hasDuplicates && !rule.params && selectorIsComplex && gridAreaRule.selector.includes(rule.selectors[0]) ) { gridAreaRule.walkDecls(/-ms-grid-(row|column)/, (d) => d.remove()); getMSDecls(area, area.row.updateSpan, area.column.updateSpan) .reverse() .forEach((i) => gridAreaRule.prepend( Object.assign(i, { raws: { between: gridArea.raws.between, }, }), ) ); } else if (rule.params) { let cloned = gridAreaRule.clone(); cloned.removeAll();
getMSDecls(area, area.row.updateSpan, area.column.updateSpan) .reverse() .forEach((i) => cloned.prepend( Object.assign(i, { raws: { between: gridArea.raws.between, }, }), ) );
if (rule.hasDuplicates && hasDuplicateName) { cloned.selectors = changeDuplicateAreaSelectors( cloned.selectors, rule.selectors, ); }
cloned.raws = rule.node.raws;
if (css.index(rule.node.parent) > gridAreaRuleIndex) { rule.node.parent.append(cloned); } else { rulesToInsert[lastArea][rule.params].push(cloned); }
if (!lastRuleIsSet) { rulesToInsert[lastArea].lastRule = gridAreaMedia || gridAreaRule; } } }
return undefined; });
Object.keys(rulesToInsert).forEach((area) => { let data = rulesToInsert[area]; let lastRule = data.lastRule; Object.keys(data) .reverse() .filter((p) => p !== "lastRule") .forEach((params) => { if (data[params].length > 0 && lastRule) { lastRule.after({ name: "media", params }); lastRule.next().append(data[params]); } }); });
return undefined;}
export { warnMissedAreas };
function warnMissedAreas(areas, decl, result) { let missed = Object.keys(areas);
decl.root().walkDecls("grid-area", (gridArea) => { missed = missed.filter((e) => e !== gridArea.value); });
if (missed.length > 0) { decl.warn(result, "Can not find grid areas: " + missed.join(", ")); }
return undefined;}
export { warnTemplateSelectorNotFound };
function warnTemplateSelectorNotFound(decl, result) { let rule = decl.parent; let root = decl.root(); let duplicatesFound = false;
let slicedSelectorArr = list .space(rule.selector) .filter((str) => str !== ">") .slice(0, -1);
if (slicedSelectorArr.length > 0) { let gridTemplateFound = false; let foundAreaSelector = null;
root.walkDecls(/grid-template(-areas)?$/, (d) => { let parent = d.parent; let templateSelectors = parent.selectors;
let { areas } = parseTemplate({ decl: d, gap: getGridGap(d) }); let hasArea = areas[decl.value];
for (let tplSelector of templateSelectors) { if (gridTemplateFound) { break; } let tplSelectorArr = list.space(tplSelector).filter((str) => str !== ">" );
gridTemplateFound = tplSelectorArr.every( (item, idx) => item === slicedSelectorArr[idx], ); }
if (gridTemplateFound || !hasArea) { return true; }
if (!foundAreaSelector) { foundAreaSelector = parent.selector; }
if (foundAreaSelector && foundAreaSelector !== parent.selector) { duplicatesFound = true; }
return undefined; });
if (!gridTemplateFound && duplicatesFound) { decl.warn( result, "Autoprefixer cannot find a grid-template " + `containing the duplicate grid-area "${decl.value}" ` + `with full selector matching: ${slicedSelectorArr.join(" ")}`, ); } }}
export { warnIfGridRowColumnExists };
function warnIfGridRowColumnExists(decl, result) { let rule = decl.parent; let decls = []; rule.walkDecls(/^grid-(row|column)/, (d) => { if ( !d.prop.endsWith("-end") && !d.value.startsWith("span") && !d.prop.endsWith("-gap") ) { decls.push(d); } }); if (decls.length > 0) { decls.forEach((d) => { d.warn( result, "You already have a grid-area declaration present in the rule. " + `You should use either grid-area or ${d.prop}, not both`, ); }); }
return undefined;}
export { getGridGap };
function getGridGap(decl) { let gap = {};
let testGap = /^(grid-)?((row|column)-)?gap$/; decl.parent.walkDecls(testGap, ({ prop, value }) => { if (/^(grid-)?gap$/.test(prop)) { let [row, , column] = parser(value).nodes;
gap.row = row && parser.stringify(row); gap.column = column ? parser.stringify(column) : gap.row; } if (/^(grid-)?row-gap$/.test(prop)) gap.row = value; if (/^(grid-)?column-gap$/.test(prop)) gap.column = value; });
return gap;}
function parseMediaParams(params) { if (!params) { return []; } let parsed = parser(params); let prop; let value;
parsed.walk((node) => { if (node.type === "word" && /min|max/g.test(node.value)) { prop = node.value; } else if (node.value.includes("px")) { value = parseInt(node.value.replace(/\D/g, "")); } });
return [prop, value];}
function shouldInheritGap(selA, selB) { let result;
let splitSelectorArrA = splitSelector(selA); let splitSelectorArrB = splitSelector(selB);
if (splitSelectorArrA[0].length < splitSelectorArrB[0].length) { return false; } else if (splitSelectorArrA[0].length > splitSelectorArrB[0].length) {
let idx = splitSelectorArrA[0].reduce((res, [item], index) => { let firstSelectorPart = splitSelectorArrB[0][0][0]; if (item === firstSelectorPart) { return index; } return false; }, false);
if (idx) { result = splitSelectorArrB[0].every((arr, index) => { return arr.every( (part, innerIndex) => splitSelectorArrA[0].slice(idx)[index][innerIndex] === part, ); }); } } else { result = splitSelectorArrB.some((byCommaArr) => { return byCommaArr.every((bySpaceArr, index) => { return bySpaceArr.every( (part, innerIndex) => splitSelectorArrA[0][index][innerIndex] === part, ); }); }); }
return result;}export { inheritGridGap };
function inheritGridGap(decl, gap) { let rule = decl.parent; let mediaRule = getParentMedia(rule); let root = rule.root();
let splitSelectorArr = splitSelector(rule.selector);
if (Object.keys(gap).length > 0) { return false; }
let [prop] = parseMediaParams(mediaRule.params);
let lastBySpace = splitSelectorArr[0];
let escaped = escapeRegexp(lastBySpace[lastBySpace.length - 1][0]);
let regexp = new RegExp(`(${escaped}$)|(${escaped}[,.])`);
let closestRuleGap; root.walkRules(regexp, (r) => { let gridGap;
if (rule.toString() === r.toString()) { return false; }
r.walkDecls("grid-gap", (d) => (gridGap = getGridGap(d)));
if (!gridGap || Object.keys(gridGap).length === 0) { return true; }
if (!shouldInheritGap(rule.selector, r.selector)) { return true; }
let media = getParentMedia(r); if (media) { let propToCompare = parseMediaParams(media.params)[0]; if (propToCompare === prop) { closestRuleGap = gridGap; return true; } } else { closestRuleGap = gridGap; return true; }
return undefined; });
if (closestRuleGap && Object.keys(closestRuleGap).length > 0) { return closestRuleGap; } return false;}
export { warnGridGap };
function warnGridGap({ gap, hasColumns, decl, result }) { let hasBothGaps = gap.row && gap.column; if (!hasColumns && (hasBothGaps || (gap.column && !gap.row))) { delete gap.column; decl.warn( result, "Can not implement grid-gap without grid-template-columns", ); }}
function normalizeRowColumn(str) { let normalized = parser(str).nodes.reduce((result, node) => { if (node.type === "function" && node.value === "repeat") { let key = "count";
let [count, value] = node.nodes.reduce( (acc, n) => { if (n.type === "word" && key === "count") { acc[0] = Math.abs(parseInt(n.value)); return acc; } if (n.type === "div" && n.value === ",") { key = "value"; return acc; } if (key === "value") { acc[1] += parser.stringify(n); } return acc; }, [0, ""], );
if (count) { for (let i = 0; i < count; i++) { result.push(value); } }
return result; } if (node.type === "space") { return result; } return result.concat(parser.stringify(node)); }, []);
return normalized;}
export { autoplaceGridItems };
function autoplaceGridItems(decl, result, gap, autoflowValue = "row") { let { parent } = decl;
let rowDecl = parent.nodes.find((i) => i.prop === "grid-template-rows"); let rows = normalizeRowColumn(rowDecl.value); let columns = normalizeRowColumn(decl.value);
let filledRows = rows.map((_, rowIndex) => { return Array.from( { length: columns.length }, (v, k) => k + rowIndex * columns.length + 1, ).join(" "); });
let areas = parseGridAreas({ rows: filledRows, gap }); let keys = Object.keys(areas); let items = keys.map((i) => areas[i]);
if (autoflowValue.includes("column")) { items = items.sort((a, b) => a.column.start - b.column.start); }
items.reverse().forEach((item, index) => { let { column, row } = item; let nodeSelector = parent.selectors .map((sel) => sel + ` > *:nth-child(${keys.length - index})`) .join(", ");
let node = parent.clone().removeAll();
node.selector = nodeSelector;
node.append({ prop: "-ms-grid-row", value: row.start }); node.append({ prop: "-ms-grid-column", value: column.start });
parent.after(node); });
return undefined;}