source: frontend/node_modules/postcss-modules-extract-imports/src/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.8 KB
Line 
1const topologicalSort = require("./topologicalSort");
2
3const matchImports = /^(.+?)\s+from\s+(?:"([^"]+)"|'([^']+)'|(global))$/;
4const icssImport = /^:import\((?:"([^"]+)"|'([^']+)')\)/;
5
6const VISITED_MARKER = 1;
7
8/**
9 * :import('G') {}
10 *
11 * Rule
12 * composes: ... from 'A'
13 * composes: ... from 'B'
14
15 * Rule
16 * composes: ... from 'A'
17 * composes: ... from 'A'
18 * composes: ... from 'C'
19 *
20 * Results in:
21 *
22 * graph: {
23 * G: [],
24 * A: [],
25 * B: ['A'],
26 * C: ['A'],
27 * }
28 */
29function addImportToGraph(importId, parentId, graph, visited) {
30 const siblingsId = parentId + "_" + "siblings";
31 const visitedId = parentId + "_" + importId;
32
33 if (visited[visitedId] !== VISITED_MARKER) {
34 if (!Array.isArray(visited[siblingsId])) {
35 visited[siblingsId] = [];
36 }
37
38 const siblings = visited[siblingsId];
39
40 if (Array.isArray(graph[importId])) {
41 graph[importId] = graph[importId].concat(siblings);
42 } else {
43 graph[importId] = siblings.slice();
44 }
45
46 visited[visitedId] = VISITED_MARKER;
47
48 siblings.push(importId);
49 }
50}
51
52module.exports = (options = {}) => {
53 let importIndex = 0;
54 const createImportedName =
55 typeof options.createImportedName !== "function"
56 ? (importName /*, path*/) =>
57 `i__imported_${importName.replace(/\W/g, "_")}_${importIndex++}`
58 : options.createImportedName;
59 const failOnWrongOrder = options.failOnWrongOrder;
60
61 return {
62 postcssPlugin: "postcss-modules-extract-imports",
63 prepare() {
64 const graph = {};
65 const visited = {};
66 const existingImports = {};
67 const importDecls = {};
68 const imports = {};
69
70 return {
71 Once(root, postcss) {
72 // Check the existing imports order and save refs
73 root.walkRules((rule) => {
74 const matches = icssImport.exec(rule.selector);
75
76 if (matches) {
77 const [, /*match*/ doubleQuotePath, singleQuotePath] = matches;
78 const importPath = doubleQuotePath || singleQuotePath;
79
80 addImportToGraph(importPath, "root", graph, visited);
81
82 existingImports[importPath] = rule;
83 }
84 });
85
86 root.walkDecls(/^composes$/, (declaration) => {
87 const multiple = declaration.value.split(",");
88 const values = [];
89
90 multiple.forEach((value) => {
91 const matches = value.trim().match(matchImports);
92
93 if (!matches) {
94 values.push(value);
95
96 return;
97 }
98
99 let tmpSymbols;
100 let [
101 ,
102 /*match*/ symbols,
103 doubleQuotePath,
104 singleQuotePath,
105 global,
106 ] = matches;
107
108 if (global) {
109 // Composing globals simply means changing these classes to wrap them in global(name)
110 tmpSymbols = symbols.split(/\s+/).map((s) => `global(${s})`);
111 } else {
112 const importPath = doubleQuotePath || singleQuotePath;
113
114 let parent = declaration.parent;
115 let parentIndexes = "";
116
117 while (parent.type !== "root") {
118 parentIndexes =
119 parent.parent.index(parent) + "_" + parentIndexes;
120 parent = parent.parent;
121 }
122
123 const { selector } = declaration.parent;
124 const parentRule = `_${parentIndexes}${selector}`;
125
126 addImportToGraph(importPath, parentRule, graph, visited);
127
128 importDecls[importPath] = declaration;
129 imports[importPath] = imports[importPath] || {};
130
131 tmpSymbols = symbols.split(/\s+/).map((s) => {
132 if (!imports[importPath][s]) {
133 imports[importPath][s] = createImportedName(s, importPath);
134 }
135
136 return imports[importPath][s];
137 });
138 }
139
140 values.push(tmpSymbols.join(" "));
141 });
142
143 declaration.value = values.join(", ");
144 });
145
146 const importsOrder = topologicalSort(graph, failOnWrongOrder);
147
148 if (importsOrder instanceof Error) {
149 const importPath = importsOrder.nodes.find((importPath) =>
150 // eslint-disable-next-line no-prototype-builtins
151 importDecls.hasOwnProperty(importPath)
152 );
153 const decl = importDecls[importPath];
154
155 throw decl.error(
156 "Failed to resolve order of composed modules " +
157 importsOrder.nodes
158 .map((importPath) => "`" + importPath + "`")
159 .join(", ") +
160 ".",
161 {
162 plugin: "postcss-modules-extract-imports",
163 word: "composes",
164 }
165 );
166 }
167
168 let lastImportRule;
169
170 importsOrder.forEach((path) => {
171 const importedSymbols = imports[path];
172 let rule = existingImports[path];
173
174 if (!rule && importedSymbols) {
175 rule = postcss.rule({
176 selector: `:import("${path}")`,
177 raws: { after: "\n" },
178 });
179
180 if (lastImportRule) {
181 root.insertAfter(lastImportRule, rule);
182 } else {
183 root.prepend(rule);
184 }
185 }
186
187 lastImportRule = rule;
188
189 if (!importedSymbols) {
190 return;
191 }
192
193 Object.keys(importedSymbols).forEach((importedSymbol) => {
194 rule.append(
195 postcss.decl({
196 value: importedSymbol,
197 prop: importedSymbols[importedSymbol],
198 raws: { before: "\n " },
199 })
200 );
201 });
202 });
203 },
204 };
205 },
206 };
207};
208
209module.exports.postcss = true;
Note: See TracBrowser for help on using the repository browser.