1 | #!/usr/bin/env node
|
2 |
|
3 |
|
4 | "use strict";
|
5 |
|
6 | exports.__esModule = true;
|
7 | exports.handler = handler;
|
8 |
|
9 | var _promises = require("fs/promises");
|
10 |
|
11 | var _path = _interopRequireDefault(require("path"));
|
12 |
|
13 | var _processor = _interopRequireDefault(require("@modular-css/processor"));
|
14 |
|
15 | var _output = _interopRequireDefault(require("@modular-css/processor/lib/output"));
|
16 |
|
17 | var _globby = _interopRequireDefault(require("globby"));
|
18 |
|
19 | var _postcssScss = _interopRequireDefault(require("postcss-scss"));
|
20 |
|
21 | var _resolve = _interopRequireDefault(require("resolve"));
|
22 |
|
23 | var _yargs = _interopRequireDefault(require("yargs"));
|
24 |
|
25 | var _config = _interopRequireDefault(require("./config"));
|
26 |
|
27 | var _createFilename = _interopRequireDefault(require("./utils/createFilename"));
|
28 |
|
29 | var _loaders = require("./utils/loaders");
|
30 |
|
31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | function composesPlugin(css) {
|
37 | css.walkDecls('composes', decl => {
|
38 | const [, classes, rest = ''] = decl.value.match(/(.+?)(from.*)?$/);
|
39 |
|
40 | decl.value = `${classes.split(/,?\s+/).filter(Boolean).join(', ')} ${rest}`;
|
41 | });
|
42 | }
|
43 |
|
44 | composesPlugin.postcssPlugin = 'compat-composes-delimiter';
|
45 |
|
46 | const exportPlugin = () => {
|
47 | function collect(rule, result) {
|
48 | const exported = Object.create(null);
|
49 | rule.walkDecls(decl => {
|
50 | exported[decl.prop] = decl.value;
|
51 | });
|
52 | rule.remove();
|
53 | result.messages.push({
|
54 | type: 'css-module-loader',
|
55 | plugin: 'modular-css-export-icss',
|
56 | exports: exported
|
57 | });
|
58 | }
|
59 |
|
60 | return {
|
61 | postcssPlugin: 'astroturf/icss-export',
|
62 | AtRule: {
|
63 | export: (rule, {
|
64 | result
|
65 | }) => {
|
66 | collect(rule, result);
|
67 | }
|
68 | },
|
69 |
|
70 | Rule(rule, {
|
71 | result
|
72 | }) {
|
73 | if (rule.selector === ':export') {
|
74 | collect(rule, result);
|
75 | }
|
76 | }
|
77 |
|
78 | };
|
79 | };
|
80 |
|
81 | exportPlugin.postcss = true;
|
82 |
|
83 | const stripInlineComments = () => {
|
84 | return {
|
85 | postcssPlugin: 'astroturf/strip-inline-comments',
|
86 |
|
87 | Comment(comment) {
|
88 | if (comment.raws.inline) comment.remove();
|
89 | }
|
90 |
|
91 | };
|
92 | };
|
93 |
|
94 | stripInlineComments.postcss = true;
|
95 |
|
96 | async function handler({
|
97 | files,
|
98 | outFile,
|
99 | configFile
|
100 | }) {
|
101 | const styles = new Map();
|
102 | const outFiles = [];
|
103 | const options = configFile ? await _config.default.load(configFile) : null;
|
104 | const deps = new Map();
|
105 |
|
106 | const dependencyResolver = ({
|
107 | request,
|
108 | identifier
|
109 | }, localStyle) => {
|
110 | const source = _resolve.default.sync(request, {
|
111 | basedir: _path.default.dirname(localStyle.absoluteFilePath)
|
112 | });
|
113 |
|
114 | let styleFile = (0, _createFilename.default)(request, {
|
115 | extension: '.css'
|
116 | }, identifier);
|
117 | styleFile = _path.default.resolve(_path.default.dirname(localStyle.absoluteFilePath), styleFile);
|
118 | deps.set(styleFile, source);
|
119 | return {
|
120 | source: styleFile
|
121 | };
|
122 | };
|
123 |
|
124 | async function runAstroturf(content, file) {
|
125 | const existing = styles.get(file);
|
126 | if (existing) return existing;
|
127 | const fileOptions = options || (await _config.default.search(file));
|
128 | const result = (0, _loaders.collectStyles)(content, file, dependencyResolver, { ...(fileOptions == null ? void 0 : fileOptions.config),
|
129 | writeFiles: false,
|
130 | extension: '.css',
|
131 | generateInterpolations: true,
|
132 | experiments: {
|
133 | modularCssExternals: true
|
134 | }
|
135 | });
|
136 | styles.set(file, result);
|
137 | return result;
|
138 | }
|
139 |
|
140 | const processor = new _processor.default({
|
141 | before: [composesPlugin],
|
142 | processing: [exportPlugin],
|
143 | after: [stripInlineComments],
|
144 | resolvers: [(src, file) => _path.default.resolve(_path.default.dirname(src), file)],
|
145 | postcss: {
|
146 | syntax: _postcssScss.default
|
147 | },
|
148 | loadFile: async src => {
|
149 | if (deps.has(src)) {
|
150 | const hostFile = deps.get(src);
|
151 | const result = await runAstroturf(await (0, _promises.readFile)(hostFile, 'utf-8'), hostFile);
|
152 | const style = result == null ? void 0 : result.styles.find(s => s.absoluteFilePath === src);
|
153 |
|
154 | if (style != null && style.value) {
|
155 | return style.value;
|
156 | }
|
157 | }
|
158 |
|
159 | return (0, _promises.readFile)(src, 'utf-8');
|
160 | }
|
161 | });
|
162 |
|
163 | for (const file of files) {
|
164 | const content = await (0, _promises.readFile)(file, 'utf-8');
|
165 | const astroturf = await runAstroturf(content, file);
|
166 |
|
167 | if (!astroturf || !astroturf.styles.length) {
|
168 | continue;
|
169 | }
|
170 |
|
171 | const changeset = [];
|
172 | const emptyCss = [];
|
173 | await Promise.all(astroturf.styles.map(s => processor.string(s.absoluteFilePath, s.value))).then(processed => {
|
174 | processed.forEach((r, idx) => {
|
175 | const style = astroturf.styles[idx];
|
176 | const isEmpty = !r.details.result.css.trim();
|
177 |
|
178 | if (!outFile) {
|
179 | if (isEmpty) {
|
180 | emptyCss.push(new RegExp(`(import )(.+?from\\s+)["'](${style.requirePath.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g, '\\$&')})["'];?`, 'g'));
|
181 | } else {
|
182 | outFiles.push({
|
183 | absoluteFilePath: style.absoluteFilePath,
|
184 | value: r.details.result.css
|
185 | });
|
186 | }
|
187 | }
|
188 |
|
189 | changeset.push(style);
|
190 |
|
191 | if (style.code) {
|
192 | changeset.push({
|
193 | start: 0,
|
194 | end: 0,
|
195 | code: `\nconst ${style.importIdentifier} = ${JSON.stringify({ ...Object.fromEntries(Object.entries(r.details.values).map(([k, v]) => [`@${k}`, v.value])),
|
196 | ...r.details.exported,
|
197 | ..._output.default.fileCompositions(r.details, processor, {
|
198 | joined: true
|
199 | })
|
200 | }, null, 2)};\n`
|
201 | });
|
202 | }
|
203 | });
|
204 | });
|
205 |
|
206 | for (const change of astroturf.changeset) {
|
207 | if (astroturf.styles.includes(change)) {
|
208 | continue;
|
209 | }
|
210 |
|
211 | if (change.type === 'style-imports') {
|
212 | changeset.push({ ...change,
|
213 | code: !outFile ? emptyCss.reduce((code, exp) => code == null ? void 0 : code.replace(exp, ''), change.code).replace(/(import )(.+?from\s+)(.*)/g, '$1$3') : ''
|
214 | });
|
215 | continue;
|
216 | }
|
217 |
|
218 | changeset.push(change);
|
219 | }
|
220 |
|
221 | await (0, _promises.writeFile)(file, (0, _loaders.replaceStyleTemplates)({}, file, content, changeset).code);
|
222 | }
|
223 |
|
224 | if (outFile) {
|
225 | outFiles.push({
|
226 | absoluteFilePath: _path.default.isAbsolute(outFile) ? outFile : _path.default.resolve(process.cwd(), outFile),
|
227 | value: (await processor.output()).css
|
228 | });
|
229 | }
|
230 |
|
231 | await Promise.all(outFiles.map(style => (0, _promises.writeFile)(style.absoluteFilePath, style.value, 'utf-8')));
|
232 | }
|
233 |
|
234 |
|
235 | _yargs.default.help().alias('h', 'help').command('*', 'Compile astroturf in files to CSS and class hashes', _ => _.option('css-out-file', {
|
236 | alias: 'c',
|
237 | type: 'string',
|
238 | description: 'The CSS output filename when concat-css is set.'
|
239 | }).option('config', {
|
240 | type: 'string',
|
241 | description: 'An astroturf rc file'
|
242 | }), async ({
|
243 | _,
|
244 | 'css-out-file': cssOutFile,
|
245 | config: configFile
|
246 | }) => {
|
247 | const files = await (0, _globby.default)(_);
|
248 | await handler({
|
249 | files,
|
250 | outFile: cssOutFile,
|
251 | configFile
|
252 | });
|
253 | }).argv; |
\ | No newline at end of file |