1 | "use strict";
|
---|
2 |
|
---|
3 | const ICSSUtils = require("icss-utils");
|
---|
4 |
|
---|
5 | const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/;
|
---|
6 | const matchValueDefinition = /(?:\s+|^)([\w-]+):?(.*?)$/;
|
---|
7 | const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/;
|
---|
8 |
|
---|
9 | module.exports = (options) => {
|
---|
10 | let importIndex = 0;
|
---|
11 | const createImportedName =
|
---|
12 | (options && options.createImportedName) ||
|
---|
13 | ((importName /*, path*/) =>
|
---|
14 | `i__const_${importName.replace(/\W/g, "_")}_${importIndex++}`);
|
---|
15 |
|
---|
16 | return {
|
---|
17 | postcssPlugin: "postcss-modules-values",
|
---|
18 | prepare(result) {
|
---|
19 | const importAliases = [];
|
---|
20 | const definitions = {};
|
---|
21 |
|
---|
22 | return {
|
---|
23 | Once(root, postcss) {
|
---|
24 | root.walkAtRules(/value/i, (atRule) => {
|
---|
25 | const matches = atRule.params.match(matchImports);
|
---|
26 |
|
---|
27 | if (matches) {
|
---|
28 | let [, /*match*/ aliases, path] = matches;
|
---|
29 |
|
---|
30 | // We can use constants for path names
|
---|
31 | if (definitions[path]) {
|
---|
32 | path = definitions[path];
|
---|
33 | }
|
---|
34 |
|
---|
35 | const imports = aliases
|
---|
36 | .replace(/^\(\s*([\s\S]+)\s*\)$/, "$1")
|
---|
37 | .split(/\s*,\s*/)
|
---|
38 | .map((alias) => {
|
---|
39 | const tokens = matchImport.exec(alias);
|
---|
40 |
|
---|
41 | if (tokens) {
|
---|
42 | const [, /*match*/ theirName, myName = theirName] = tokens;
|
---|
43 | const importedName = createImportedName(myName);
|
---|
44 | definitions[myName] = importedName;
|
---|
45 | return { theirName, importedName };
|
---|
46 | } else {
|
---|
47 | throw new Error(`@import statement "${alias}" is invalid!`);
|
---|
48 | }
|
---|
49 | });
|
---|
50 |
|
---|
51 | importAliases.push({ path, imports });
|
---|
52 |
|
---|
53 | atRule.remove();
|
---|
54 |
|
---|
55 | return;
|
---|
56 | }
|
---|
57 |
|
---|
58 | if (atRule.params.indexOf("@value") !== -1) {
|
---|
59 | result.warn("Invalid value definition: " + atRule.params);
|
---|
60 | }
|
---|
61 |
|
---|
62 | let [, key, value] = `${atRule.params}${atRule.raws.between}`.match(
|
---|
63 | matchValueDefinition
|
---|
64 | );
|
---|
65 |
|
---|
66 | const normalizedValue = value.replace(/\/\*((?!\*\/).*?)\*\//g, "");
|
---|
67 |
|
---|
68 | if (normalizedValue.length === 0) {
|
---|
69 | result.warn("Invalid value definition: " + atRule.params);
|
---|
70 | atRule.remove();
|
---|
71 |
|
---|
72 | return;
|
---|
73 | }
|
---|
74 |
|
---|
75 | let isOnlySpace = /^\s+$/.test(normalizedValue);
|
---|
76 |
|
---|
77 | if (!isOnlySpace) {
|
---|
78 | value = value.trim();
|
---|
79 | }
|
---|
80 |
|
---|
81 | // Add to the definitions, knowing that values can refer to each other
|
---|
82 | definitions[key] = ICSSUtils.replaceValueSymbols(
|
---|
83 | value,
|
---|
84 | definitions
|
---|
85 | );
|
---|
86 |
|
---|
87 | atRule.remove();
|
---|
88 | });
|
---|
89 |
|
---|
90 | /* If we have no definitions, don't continue */
|
---|
91 | if (!Object.keys(definitions).length) {
|
---|
92 | return;
|
---|
93 | }
|
---|
94 |
|
---|
95 | /* Perform replacements */
|
---|
96 | ICSSUtils.replaceSymbols(root, definitions);
|
---|
97 |
|
---|
98 | /* 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 */
|
---|
99 | const exportDeclarations = Object.keys(definitions).map((key) =>
|
---|
100 | postcss.decl({
|
---|
101 | value: definitions[key],
|
---|
102 | prop: key,
|
---|
103 | raws: { before: "\n " },
|
---|
104 | })
|
---|
105 | );
|
---|
106 |
|
---|
107 | /* Add export rules if any */
|
---|
108 | if (exportDeclarations.length > 0) {
|
---|
109 | const exportRule = postcss.rule({
|
---|
110 | selector: ":export",
|
---|
111 | raws: { after: "\n" },
|
---|
112 | });
|
---|
113 |
|
---|
114 | exportRule.append(exportDeclarations);
|
---|
115 |
|
---|
116 | root.prepend(exportRule);
|
---|
117 | }
|
---|
118 |
|
---|
119 | /* Add import rules */
|
---|
120 | importAliases.reverse().forEach(({ path, imports }) => {
|
---|
121 | const importRule = postcss.rule({
|
---|
122 | selector: `:import(${path})`,
|
---|
123 | raws: { after: "\n" },
|
---|
124 | });
|
---|
125 |
|
---|
126 | imports.forEach(({ theirName, importedName }) => {
|
---|
127 | importRule.append({
|
---|
128 | value: theirName,
|
---|
129 | prop: importedName,
|
---|
130 | raws: { before: "\n " },
|
---|
131 | });
|
---|
132 | });
|
---|
133 |
|
---|
134 | root.prepend(importRule);
|
---|
135 | });
|
---|
136 | },
|
---|
137 | };
|
---|
138 | },
|
---|
139 | };
|
---|
140 | };
|
---|
141 |
|
---|
142 | module.exports.postcss = true;
|
---|