UNPKG

7.14 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3/* eslint-disable no-await-in-loop */
4"use strict";
5
6exports.__esModule = true;
7exports.handler = handler;
8
9var _promises = require("fs/promises");
10
11var _path = _interopRequireDefault(require("path"));
12
13var _processor = _interopRequireDefault(require("@modular-css/processor"));
14
15var _output = _interopRequireDefault(require("@modular-css/processor/lib/output"));
16
17var _globby = _interopRequireDefault(require("globby"));
18
19var _postcssScss = _interopRequireDefault(require("postcss-scss"));
20
21var _resolve = _interopRequireDefault(require("resolve"));
22
23var _yargs = _interopRequireDefault(require("yargs"));
24
25var _config = _interopRequireDefault(require("./config"));
26
27var _createFilename = _interopRequireDefault(require("./utils/createFilename"));
28
29var _loaders = require("./utils/loaders");
30
31function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
32
33/**
34 * use comma seperators for composes classes
35 */
36function composesPlugin(css) {
37 css.walkDecls('composes', decl => {
38 const [, classes, rest = ''] = decl.value.match(/(.+?)(from.*)?$/); // eslint-disable-next-line no-param-reassign
39
40 decl.value = `${classes.split(/,?\s+/).filter(Boolean).join(', ')} ${rest}`;
41 });
42}
43
44composesPlugin.postcssPlugin = 'compat-composes-delimiter';
45
46const 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
81exportPlugin.postcss = true;
82
83const 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
94stripInlineComments.postcss = true;
95
96async 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} // eslint-disable-next-line @typescript-eslint/no-unused-expressions
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