-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathget-custom-properties-from-root.mjs
143 lines (116 loc) · 3.45 KB
/
get-custom-properties-from-root.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import fs from 'node:fs/promises';
import path from 'node:path';
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';
import { resolveId } from './resolve-id.mjs';
// return custom selectors from the css root, conditionally removing them
export default async function getCustomPropertiesFromRoot(root, resolver) {
// initialize custom selectors
let customProperties = {};
// resolve current file directory
let sourceDir = process.cwd();
if (root.source && root.source.input && root.source.input.file) {
sourceDir = path.dirname(root.source.input.file);
}
// recursively add custom properties from @import statements
const importPromises = [];
root.walkAtRules('import', atRule => {
const promise = getImportPromise(atRule, resolver, sourceDir);
if (promise) {
importPromises.push(promise);
}
});
root.walkAtRules('use', atRule => {
const promise = getImportPromise(atRule, resolver, sourceDir);
if (promise) {
importPromises.push(promise);
}
});
(await Promise.all(importPromises)).forEach(propertiesFromImport => {
customProperties = Object.assign(customProperties, propertiesFromImport);
});
// for each custom property declaration
root.walkDecls(decl => {
if (!decl.variable || !decl.prop.startsWith('--')) {
return;
}
// write the parsed value to the custom property
customProperties[decl.prop] = decl.value;
});
// return all custom properties, preferring :root properties over html properties
return customProperties;
}
async function getCustomPropertiesFromCSSFile(from, resolver) {
try {
const css = await fs.readFile(from, 'utf8');
const root = postcss.parse(css, { from });
return await getCustomPropertiesFromRoot(root, resolver);
} catch (e) {
return {};
}
}
function parseImportParams(params) {
const nodes = valueParser(params).nodes;
if (!nodes.length) {
return;
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.type === 'space' || node.type === 'comment') {
continue;
}
if (node.type === 'string') {
return node.value;
}
if (node.type === 'function' && /url/i.test(node.value)) {
for (let j = 0; j < node.nodes.length; j++) {
const urlNode = node.nodes[j];
if (urlNode.type === 'space' || urlNode.type === 'comment') {
continue;
}
if (urlNode.type === 'word') {
return urlNode.value;
}
if (urlNode.type === 'string') {
return urlNode.value;
}
return false;
}
}
return false;
}
return false;
}
function getFileNameFromAlias(fileName, resolver) {
if (!resolver.alias) {
return fileName;
}
const split = fileName.split('/');
if (split.length > 0) {
const resolvedAlias = Object.entries(resolver.alias).find(([key]) => key === split[0]);
if (resolvedAlias) {
const [alias, value] = resolvedAlias;
return fileName.replace(alias, value);
}
}
return fileName;
}
function getImportPromise(atRule, resolver, sourceDir) {
const fileName = parseImportParams(atRule.params);
if (!fileName) {
return;
}
const aliasedFileName = getFileNameFromAlias(fileName, resolver);
if (path.isAbsolute(fileName)) {
return getCustomPropertiesFromCSSFile(aliasedFileName, resolver);
} else {
const promise = resolveId(aliasedFileName, sourceDir, {
paths: resolver.paths,
extensions: resolver.extensions,
moduleDirectories: resolver.moduleDirectories,
})
.then((filePath) => getCustomPropertiesFromCSSFile(filePath, resolver))
.catch(() => {});
return promise;
}
}