"use strict"; const ICSSUtils = require("icss-utils"); const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/; const matchValueDefinition = /(?:\s+|^)([\w-]+):?(.*?)$/; const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/; module.exports = (options) => { let importIndex = 0; const createImportedName = (options && options.createImportedName) || ((importName /*, path*/) => `i__const_${importName.replace(/\W/g, "_")}_${importIndex++}`); return { postcssPlugin: "postcss-modules-values", prepare(result) { const importAliases = []; const definitions = {}; return { Once(root, postcss) { root.walkAtRules(/value/i, (atRule) => { const matches = atRule.params.match(matchImports); if (matches) { let [, /*match*/ aliases, path] = matches; // We can use constants for path names if (definitions[path]) { path = definitions[path]; } const imports = aliases .replace(/^\(\s*([\s\S]+)\s*\)$/, "$1") .split(/\s*,\s*/) .map((alias) => { const tokens = matchImport.exec(alias); if (tokens) { const [, /*match*/ theirName, myName = theirName] = tokens; const importedName = createImportedName(myName); definitions[myName] = importedName; return { theirName, importedName }; } else { throw new Error(`@import statement "${alias}" is invalid!`); } }); importAliases.push({ path, imports }); atRule.remove(); return; } if (atRule.params.indexOf("@value") !== -1) { result.warn("Invalid value definition: " + atRule.params); } let [, key, value] = `${atRule.params}${atRule.raws.between}`.match( matchValueDefinition ); const normalizedValue = value.replace(/\/\*((?!\*\/).*?)\*\//g, ""); if (normalizedValue.length === 0) { result.warn("Invalid value definition: " + atRule.params); atRule.remove(); return; } let isOnlySpace = /^\s+$/.test(normalizedValue); if (!isOnlySpace) { value = value.trim(); } // Add to the definitions, knowing that values can refer to each other definitions[key] = ICSSUtils.replaceValueSymbols( value, definitions ); atRule.remove(); }); /* If we have no definitions, don't continue */ if (!Object.keys(definitions).length) { return; } /* Perform replacements */ ICSSUtils.replaceSymbols(root, definitions); /* We want to export anything defined by now, but don't add it to the CSS yet or it well get picked up by the replacement stuff */ const exportDeclarations = Object.keys(definitions).map((key) => postcss.decl({ value: definitions[key], prop: key, raws: { before: "\n " }, }) ); /* Add export rules if any */ if (exportDeclarations.length > 0) { const exportRule = postcss.rule({ selector: ":export", raws: { after: "\n" }, }); exportRule.append(exportDeclarations); root.prepend(exportRule); } /* Add import rules */ importAliases.reverse().forEach(({ path, imports }) => { const importRule = postcss.rule({ selector: `:import(${path})`, raws: { after: "\n" }, }); imports.forEach(({ theirName, importedName }) => { importRule.append({ value: theirName, prop: importedName, raws: { before: "\n " }, }); }); root.prepend(importRule); }); }, }; }, }; }; module.exports.postcss = true;