source: trip-planner-front/node_modules/postcss-modules-extract-imports/src/index.js@ e29cc2e

Last change on this file since e29cc2e was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 5.5 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 matches = declaration.value.match(matchImports);
88
89 if (!matches) {
90 return;
91 }
92
93 let tmpSymbols;
94 let [
95 ,
96 /*match*/ symbols,
97 doubleQuotePath,
98 singleQuotePath,
99 global,
100 ] = matches;
101
102 if (global) {
103 // Composing globals simply means changing these classes to wrap them in global(name)
104 tmpSymbols = symbols.split(/\s+/).map((s) => `global(${s})`);
105 } else {
106 const importPath = doubleQuotePath || singleQuotePath;
107
108 let parent = declaration.parent;
109 let parentIndexes = "";
110
111 while (parent.type !== "root") {
112 parentIndexes =
113 parent.parent.index(parent) + "_" + parentIndexes;
114 parent = parent.parent;
115 }
116
117 const { selector } = declaration.parent;
118 const parentRule = `_${parentIndexes}${selector}`;
119
120 addImportToGraph(importPath, parentRule, graph, visited);
121
122 importDecls[importPath] = declaration;
123 imports[importPath] = imports[importPath] || {};
124
125 tmpSymbols = symbols.split(/\s+/).map((s) => {
126 if (!imports[importPath][s]) {
127 imports[importPath][s] = createImportedName(s, importPath);
128 }
129
130 return imports[importPath][s];
131 });
132 }
133
134 declaration.value = tmpSymbols.join(" ");
135 });
136
137 const importsOrder = topologicalSort(graph, failOnWrongOrder);
138
139 if (importsOrder instanceof Error) {
140 const importPath = importsOrder.nodes.find((importPath) =>
141 // eslint-disable-next-line no-prototype-builtins
142 importDecls.hasOwnProperty(importPath)
143 );
144 const decl = importDecls[importPath];
145
146 throw decl.error(
147 "Failed to resolve order of composed modules " +
148 importsOrder.nodes
149 .map((importPath) => "`" + importPath + "`")
150 .join(", ") +
151 ".",
152 {
153 plugin: "postcss-modules-extract-imports",
154 word: "composes",
155 }
156 );
157 }
158
159 let lastImportRule;
160
161 importsOrder.forEach((path) => {
162 const importedSymbols = imports[path];
163 let rule = existingImports[path];
164
165 if (!rule && importedSymbols) {
166 rule = postcss.rule({
167 selector: `:import("${path}")`,
168 raws: { after: "\n" },
169 });
170
171 if (lastImportRule) {
172 root.insertAfter(lastImportRule, rule);
173 } else {
174 root.prepend(rule);
175 }
176 }
177
178 lastImportRule = rule;
179
180 if (!importedSymbols) {
181 return;
182 }
183
184 Object.keys(importedSymbols).forEach((importedSymbol) => {
185 rule.append(
186 postcss.decl({
187 value: importedSymbol,
188 prop: importedSymbols[importedSymbol],
189 raws: { before: "\n " },
190 })
191 );
192 });
193 });
194 },
195 };
196 },
197 };
198};
199
200module.exports.postcss = true;
Note: See TracBrowser for help on using the repository browser.