@plumeria/compiler 0.25.0 → 0.25.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extract.js +274 -183
- package/dist/index.js +173 -152
- package/package.json +2 -2
package/dist/extract.js
CHANGED
|
@@ -7,7 +7,8 @@ exports.generatedTsMap = void 0;
|
|
|
7
7
|
exports.extractTSFile = extractTSFile;
|
|
8
8
|
exports.restoreAllOriginals = restoreAllOriginals;
|
|
9
9
|
exports.extractVueAndSvelte = extractVueAndSvelte;
|
|
10
|
-
const
|
|
10
|
+
const core_1 = require("@swc/core");
|
|
11
|
+
const promises_1 = require("fs/promises");
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
12
13
|
const generatedTsMap = new Map();
|
|
13
14
|
exports.generatedTsMap = generatedTsMap;
|
|
@@ -115,177 +116,273 @@ function isInHtmlText(code, position) {
|
|
|
115
116
|
}
|
|
116
117
|
return lastCloseTag > lastOpenTag && nextOpenTag > position;
|
|
117
118
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
const startIndex = match.index + match[0].length;
|
|
129
|
-
let parenCount = 1;
|
|
130
|
-
let currentIndex = startIndex;
|
|
131
|
-
let args = '';
|
|
132
|
-
while (parenCount > 0 && currentIndex < code.length) {
|
|
133
|
-
const char = code[currentIndex];
|
|
134
|
-
if (char === '(') {
|
|
135
|
-
parenCount++;
|
|
136
|
-
}
|
|
137
|
-
else if (char === ')') {
|
|
138
|
-
parenCount--;
|
|
119
|
+
function expressionToString(expr) {
|
|
120
|
+
switch (expr.type) {
|
|
121
|
+
case 'Identifier':
|
|
122
|
+
return expr.value;
|
|
123
|
+
case 'MemberExpression': {
|
|
124
|
+
const obj = expressionToString(expr.object);
|
|
125
|
+
if (obj && expr.property.type === 'Identifier') {
|
|
126
|
+
return `${obj}.${expr.property.value}`;
|
|
139
127
|
}
|
|
140
|
-
|
|
141
|
-
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'CallExpression': {
|
|
131
|
+
if (expr.callee.type !== 'Super' && expr.callee.type !== 'Import') {
|
|
132
|
+
const callee = expressionToString(expr.callee);
|
|
133
|
+
if (callee) {
|
|
134
|
+
const args = expr.arguments
|
|
135
|
+
.map((arg) => expressionToString(arg.expression))
|
|
136
|
+
.join(', ');
|
|
137
|
+
return `${callee}(${args})`;
|
|
138
|
+
}
|
|
142
139
|
}
|
|
143
|
-
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'ObjectExpression': {
|
|
143
|
+
const properties = expr.properties
|
|
144
|
+
.map((prop) => {
|
|
145
|
+
if ('key' in prop && prop.key && prop.key.type === 'Identifier') {
|
|
146
|
+
const key = prop.key.value;
|
|
147
|
+
const value = expressionToString(prop.value);
|
|
148
|
+
return `${key}: ${value}`;
|
|
149
|
+
}
|
|
150
|
+
return '[complex property]';
|
|
151
|
+
})
|
|
152
|
+
.join(', ');
|
|
153
|
+
console.warn(`css.props: Argument unsupported ${expr.type}: { ${properties} } Use css.create instead.`);
|
|
154
|
+
return '';
|
|
144
155
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
case 'StringLiteral':
|
|
157
|
+
return String(expr.value);
|
|
158
|
+
}
|
|
159
|
+
console.warn(`css.props: Argument unsupported ${expr.type}: Use css.create instead.`);
|
|
160
|
+
return '';
|
|
161
|
+
}
|
|
162
|
+
async function extractCssProps(ast) {
|
|
163
|
+
const propsMatches = [];
|
|
164
|
+
try {
|
|
165
|
+
await visit(ast, {
|
|
166
|
+
CallExpression: async (node) => {
|
|
167
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
168
|
+
node.callee.object.type === 'Identifier' &&
|
|
169
|
+
node.callee.object.value === 'css' &&
|
|
170
|
+
node.callee.property.type === 'Identifier' &&
|
|
171
|
+
node.callee.property.value === 'props') {
|
|
172
|
+
const staticArgs = [];
|
|
173
|
+
const conditionalStyleObjects = [];
|
|
174
|
+
for (const arg of node.arguments) {
|
|
175
|
+
if (arg.expression) {
|
|
176
|
+
if (arg.expression.type === 'ConditionalExpression' ||
|
|
177
|
+
(arg.expression.type === 'BinaryExpression' &&
|
|
178
|
+
arg.expression.operator === '&&')) {
|
|
179
|
+
const styles = await extractStyleObjectsFromExpression(arg.expression);
|
|
180
|
+
conditionalStyleObjects.push(...styles);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const argStr = expressionToString(arg.expression);
|
|
184
|
+
if (argStr) {
|
|
185
|
+
staticArgs.push(argStr);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (staticArgs.length > 0) {
|
|
191
|
+
propsMatches.push(`css.props(${staticArgs.join(', ')})`);
|
|
155
192
|
}
|
|
156
|
-
|
|
157
|
-
|
|
193
|
+
for (const styleObj of conditionalStyleObjects) {
|
|
194
|
+
if (styleObj &&
|
|
195
|
+
styleObj !== 'false' &&
|
|
196
|
+
styleObj !== 'null' &&
|
|
197
|
+
styleObj !== 'undefined') {
|
|
198
|
+
propsMatches.push(`css.props(${styleObj})`);
|
|
199
|
+
}
|
|
158
200
|
}
|
|
159
201
|
}
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
console.error(`Failed to parse code to extract css.props: ${e}`);
|
|
207
|
+
}
|
|
208
|
+
return propsMatches;
|
|
209
|
+
}
|
|
210
|
+
function extractStyleObjectsFromExpression(expression) {
|
|
211
|
+
switch (expression.type) {
|
|
212
|
+
case 'BinaryExpression':
|
|
213
|
+
if (expression.operator === '&&') {
|
|
214
|
+
return extractStyleObjectsFromExpression(expression.right);
|
|
160
215
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
216
|
+
break;
|
|
217
|
+
case 'ConditionalExpression':
|
|
218
|
+
return [
|
|
219
|
+
...extractStyleObjectsFromExpression(expression.consequent),
|
|
220
|
+
...extractStyleObjectsFromExpression(expression.alternate),
|
|
221
|
+
];
|
|
222
|
+
case 'BooleanLiteral':
|
|
223
|
+
case 'NullLiteral':
|
|
224
|
+
return [];
|
|
225
|
+
case 'Identifier':
|
|
226
|
+
if (expression.value === 'undefined') {
|
|
227
|
+
return [];
|
|
171
228
|
}
|
|
229
|
+
case 'ObjectExpression': {
|
|
230
|
+
const str = expressionToString(expression);
|
|
231
|
+
return str ? [str] : [];
|
|
172
232
|
}
|
|
173
233
|
}
|
|
174
|
-
|
|
234
|
+
const str = expressionToString(expression);
|
|
235
|
+
if (str) {
|
|
236
|
+
return [str];
|
|
237
|
+
}
|
|
238
|
+
return [];
|
|
175
239
|
}
|
|
176
|
-
function extractStaticStringLiteralVariable(
|
|
240
|
+
async function extractStaticStringLiteralVariable(ast) {
|
|
177
241
|
const matches = [];
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
242
|
+
try {
|
|
243
|
+
for (const node of ast.body) {
|
|
244
|
+
if (node.type === 'VariableDeclaration') {
|
|
245
|
+
const allStringLiterals = node.declarations.length > 0 &&
|
|
246
|
+
node.declarations.every((decl) => decl.init && decl.init.type === 'StringLiteral');
|
|
247
|
+
if (allStringLiterals) {
|
|
248
|
+
const { code: extractedCode } = await (0, core_1.print)({
|
|
249
|
+
type: 'Module',
|
|
250
|
+
body: [node],
|
|
251
|
+
span: { start: 0, end: 0, ctxt: 0 },
|
|
252
|
+
});
|
|
253
|
+
matches.push(extractedCode.trim());
|
|
254
|
+
}
|
|
255
|
+
}
|
|
186
256
|
}
|
|
187
|
-
|
|
257
|
+
}
|
|
258
|
+
catch (e) {
|
|
259
|
+
console.error(`Failed to parse code to extract static string literal variables: ${e}`);
|
|
188
260
|
}
|
|
189
261
|
return matches.join('\n');
|
|
190
262
|
}
|
|
191
|
-
function
|
|
192
|
-
const
|
|
193
|
-
const regex = /
|
|
263
|
+
function extractCssPropsFromTemplate(code) {
|
|
264
|
+
const matches = [];
|
|
265
|
+
const regex = /css\.props\(([^)]*)\)/g;
|
|
194
266
|
let match;
|
|
195
|
-
while ((match = regex.exec(code))) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
267
|
+
while ((match = regex.exec(code)) !== null) {
|
|
268
|
+
const matchStart = match.index;
|
|
269
|
+
if (isInComment(code, matchStart) ||
|
|
270
|
+
isInString(code, matchStart) ||
|
|
271
|
+
isInHtmlText(code, matchStart)) {
|
|
199
272
|
continue;
|
|
200
273
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const regex = /(?:(?:\s*const\s+[a-zA-Z0-9_$]+\s*=\s*css\.keyframes\([\s\S]*?\);\s*))/g;
|
|
208
|
-
let match;
|
|
209
|
-
while ((match = regex.exec(code))) {
|
|
210
|
-
if (isInComment(code, match.index) ||
|
|
211
|
-
isInString(code, match.index) ||
|
|
212
|
-
isInHtmlText(code, match.index)) {
|
|
274
|
+
const scriptStartIndex = code.indexOf('<script');
|
|
275
|
+
const scriptEndIndex = code.indexOf('</script>');
|
|
276
|
+
if (scriptStartIndex !== -1 &&
|
|
277
|
+
scriptEndIndex !== -1 &&
|
|
278
|
+
match.index > scriptStartIndex &&
|
|
279
|
+
match.index < scriptEndIndex) {
|
|
213
280
|
continue;
|
|
214
281
|
}
|
|
215
|
-
|
|
282
|
+
const args = match[1];
|
|
283
|
+
if (args && !args.includes('{') && !args.includes('(')) {
|
|
284
|
+
matches.push(`css.props(${args})`);
|
|
285
|
+
}
|
|
216
286
|
}
|
|
217
|
-
return
|
|
287
|
+
return matches;
|
|
218
288
|
}
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
289
|
+
async function visit(node, visitor) {
|
|
290
|
+
if (!node)
|
|
291
|
+
return;
|
|
292
|
+
const visitorFunc = visitor[node.type];
|
|
293
|
+
if (visitorFunc) {
|
|
294
|
+
await visitorFunc(node);
|
|
295
|
+
}
|
|
296
|
+
for (const key in node) {
|
|
297
|
+
if (typeof node[key] === 'object' && node[key] !== null) {
|
|
298
|
+
if (Array.isArray(node[key])) {
|
|
299
|
+
for (const child of node[key]) {
|
|
300
|
+
await visit(child, visitor);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
await visit(node[key], visitor);
|
|
305
|
+
}
|
|
228
306
|
}
|
|
229
|
-
cssCreateMatches.push(match[0]);
|
|
230
307
|
}
|
|
231
|
-
return cssCreateMatches.join('\n');
|
|
232
308
|
}
|
|
233
|
-
function
|
|
234
|
-
const
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
309
|
+
function importDeclarationToString(node) {
|
|
310
|
+
const source = node.source.value;
|
|
311
|
+
const defaultImport = node.specifiers.find((s) => s.type === 'ImportDefaultSpecifier');
|
|
312
|
+
const namespaceImport = node.specifiers.find((s) => s.type === 'ImportNamespaceSpecifier');
|
|
313
|
+
const namedImports = node.specifiers.filter((s) => s.type === 'ImportSpecifier');
|
|
314
|
+
let importClause = '';
|
|
315
|
+
if (defaultImport) {
|
|
316
|
+
importClause += defaultImport.local.value;
|
|
317
|
+
}
|
|
318
|
+
if (namespaceImport) {
|
|
319
|
+
if (importClause)
|
|
320
|
+
importClause += ', ';
|
|
321
|
+
importClause += `* as ${namespaceImport.local.value}`;
|
|
244
322
|
}
|
|
245
|
-
|
|
323
|
+
if (namedImports.length > 0) {
|
|
324
|
+
if (importClause)
|
|
325
|
+
importClause += ', ';
|
|
326
|
+
const namedParts = namedImports.map((spec) => {
|
|
327
|
+
if (spec.imported && spec.imported.value !== spec.local.value) {
|
|
328
|
+
return `${spec.imported.value} as ${spec.local.value}`;
|
|
329
|
+
}
|
|
330
|
+
return spec.local.value;
|
|
331
|
+
});
|
|
332
|
+
importClause += `{ ${namedParts.join(', ')} }`;
|
|
333
|
+
}
|
|
334
|
+
if (importClause) {
|
|
335
|
+
return `import ${importClause} from '${source}';`;
|
|
336
|
+
}
|
|
337
|
+
return `import '${source}';`;
|
|
246
338
|
}
|
|
247
|
-
function
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
339
|
+
async function extractImportDeclarations(ast) {
|
|
340
|
+
const importDeclarations = [];
|
|
341
|
+
try {
|
|
342
|
+
await visit(ast, {
|
|
343
|
+
ImportDeclaration: (node) => {
|
|
344
|
+
importDeclarations.push(importDeclarationToString(node));
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
console.error(`Failed to parse code to extract import declarations: ${e}`);
|
|
258
350
|
}
|
|
259
|
-
return
|
|
351
|
+
return importDeclarations.join('\n');
|
|
260
352
|
}
|
|
261
|
-
function
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
353
|
+
async function extractCssMethod(ast, methodName) {
|
|
354
|
+
const matches = [];
|
|
355
|
+
try {
|
|
356
|
+
for (const node of ast.body) {
|
|
357
|
+
if (node.type === 'VariableDeclaration') {
|
|
358
|
+
const containsCssMethod = node.declarations.some((decl) => decl.init &&
|
|
359
|
+
decl.init.type === 'CallExpression' &&
|
|
360
|
+
decl.init.callee.type === 'MemberExpression' &&
|
|
361
|
+
decl.init.callee.object.type === 'Identifier' &&
|
|
362
|
+
decl.init.callee.object.value === 'css' &&
|
|
363
|
+
decl.init.callee.property.type === 'Identifier' &&
|
|
364
|
+
decl.init.callee.property.value === methodName);
|
|
365
|
+
if (containsCssMethod && node.span) {
|
|
366
|
+
const { code: extractedCode } = await (0, core_1.print)({
|
|
367
|
+
type: 'Module',
|
|
368
|
+
body: [node],
|
|
369
|
+
span: { start: 0, end: 0, ctxt: 0 },
|
|
370
|
+
});
|
|
371
|
+
matches.push(extractedCode.trim());
|
|
372
|
+
}
|
|
278
373
|
}
|
|
279
374
|
}
|
|
280
|
-
results.push(arg.trim());
|
|
281
375
|
}
|
|
282
|
-
|
|
376
|
+
catch (e) {
|
|
377
|
+
console.error(`Failed to parse code to extract css.${methodName}: ${e}`);
|
|
378
|
+
}
|
|
379
|
+
return matches.join('\n');
|
|
283
380
|
}
|
|
284
381
|
async function extractVueAndSvelte(filePath) {
|
|
285
382
|
const ext = path_1.default.extname(filePath);
|
|
286
383
|
if (!(ext === '.svelte' || ext === '.vue'))
|
|
287
384
|
return filePath;
|
|
288
|
-
const code =
|
|
385
|
+
const code = await (0, promises_1.readFile)(filePath, 'utf8');
|
|
289
386
|
const lines = code.split(/\r?\n/);
|
|
290
387
|
let inScript = false;
|
|
291
388
|
const contentLines = [];
|
|
@@ -304,62 +401,63 @@ async function extractVueAndSvelte(filePath) {
|
|
|
304
401
|
}
|
|
305
402
|
}
|
|
306
403
|
const tsCode = contentLines.join('\n');
|
|
307
|
-
const
|
|
404
|
+
const tsPath = filePath.replace(ext, '.ts');
|
|
405
|
+
if (!tsCode.trim()) {
|
|
406
|
+
generatedTsMap.set(filePath, '');
|
|
407
|
+
return tsPath;
|
|
408
|
+
}
|
|
409
|
+
const ast = await (0, core_1.parse)(tsCode, {
|
|
410
|
+
syntax: 'typescript',
|
|
411
|
+
tsx: true,
|
|
412
|
+
});
|
|
413
|
+
const propsFromScript = await extractCssProps(ast);
|
|
414
|
+
const propsFromTemplate = extractCssPropsFromTemplate(code);
|
|
415
|
+
const propsMatches = [...new Set([...propsFromScript, ...propsFromTemplate])];
|
|
308
416
|
const calls = propsMatches
|
|
309
417
|
.filter(Boolean)
|
|
310
418
|
.map((call) => `${call};`)
|
|
311
419
|
.join('\n');
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const
|
|
319
|
-
const cssDefineConstsSection = extractCssDefineConsts(tsCode);
|
|
320
|
-
const cssDefineTokensSection = extractCssDefineTokens(tsCode);
|
|
420
|
+
const importSection = await extractImportDeclarations(ast);
|
|
421
|
+
const staticVariableSection = await extractStaticStringLiteralVariable(ast);
|
|
422
|
+
const cssCreateSection = await extractCssMethod(ast, 'create');
|
|
423
|
+
const cssKeyframesSection = await extractCssMethod(ast, 'keyframes');
|
|
424
|
+
const cssViewTransitionSection = await extractCssMethod(ast, 'viewTransition');
|
|
425
|
+
const cssDefineConstsSection = await extractCssMethod(ast, 'defineConsts');
|
|
426
|
+
const cssDefineTokensSection = await extractCssMethod(ast, 'defineTokens');
|
|
321
427
|
let finalCode = '';
|
|
322
|
-
if (importSection)
|
|
428
|
+
if (importSection)
|
|
323
429
|
finalCode += importSection + '\n';
|
|
324
|
-
|
|
325
|
-
if (staticVariableSection) {
|
|
430
|
+
if (staticVariableSection)
|
|
326
431
|
finalCode += staticVariableSection + '\n';
|
|
327
|
-
|
|
328
|
-
if (cssKeyframesSection) {
|
|
432
|
+
if (cssKeyframesSection)
|
|
329
433
|
finalCode += cssKeyframesSection + '\n';
|
|
330
|
-
|
|
331
|
-
if (cssViewTransitionSection) {
|
|
434
|
+
if (cssViewTransitionSection)
|
|
332
435
|
finalCode += cssViewTransitionSection + '\n';
|
|
333
|
-
|
|
334
|
-
if (cssDefineConstsSection) {
|
|
436
|
+
if (cssDefineConstsSection)
|
|
335
437
|
finalCode += cssDefineConstsSection + '\n';
|
|
336
|
-
|
|
337
|
-
if (cssDefineTokensSection) {
|
|
438
|
+
if (cssDefineTokensSection)
|
|
338
439
|
finalCode += cssDefineTokensSection + '\n';
|
|
339
|
-
|
|
340
|
-
if (cssCreateSection) {
|
|
440
|
+
if (cssCreateSection)
|
|
341
441
|
finalCode += cssCreateSection + '\n';
|
|
342
|
-
|
|
343
|
-
if (calls) {
|
|
442
|
+
if (calls)
|
|
344
443
|
finalCode += calls + '\n';
|
|
345
|
-
|
|
346
|
-
const tsPath = filePath.replace(ext, '.ts');
|
|
347
|
-
fs_1.default.writeFileSync(tsPath, finalCode, 'utf8');
|
|
348
|
-
generatedTsMap.set(filePath, tsPath);
|
|
444
|
+
generatedTsMap.set(filePath, finalCode);
|
|
349
445
|
return tsPath;
|
|
350
446
|
}
|
|
351
447
|
async function extractTSFile(filePath) {
|
|
352
|
-
const code =
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
const
|
|
448
|
+
const code = await (0, promises_1.readFile)(filePath, 'utf8');
|
|
449
|
+
const ast = await (0, core_1.parse)(code, {
|
|
450
|
+
syntax: 'typescript',
|
|
451
|
+
tsx: true,
|
|
452
|
+
});
|
|
453
|
+
const importSection = await extractImportDeclarations(ast);
|
|
454
|
+
const staticVariableSection = await extractStaticStringLiteralVariable(ast);
|
|
455
|
+
const cssCreateSection = await extractCssMethod(ast, 'create');
|
|
456
|
+
const cssKeyframesSection = await extractCssMethod(ast, 'keyframes');
|
|
457
|
+
const cssViewTransitionSection = await extractCssMethod(ast, 'viewTransition');
|
|
458
|
+
const cssDefineConstsSection = await extractCssMethod(ast, 'defineConsts');
|
|
459
|
+
const cssDefineTokensSection = await extractCssMethod(ast, 'defineTokens');
|
|
460
|
+
const propsMatches = await extractCssProps(ast);
|
|
363
461
|
const calls = propsMatches
|
|
364
462
|
.filter(Boolean)
|
|
365
463
|
.map((call) => `${call};`)
|
|
@@ -380,18 +478,11 @@ async function extractTSFile(filePath) {
|
|
|
380
478
|
if (cssCreateSection)
|
|
381
479
|
finalCode += cssCreateSection + '\n';
|
|
382
480
|
finalCode += calls;
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
fs_1.default.writeFileSync(tempFilePath, finalCode, 'utf8');
|
|
386
|
-
generatedTsMap.set(filePath, tempFilePath);
|
|
481
|
+
const tempFilePath = filePath.replace(path_1.default.extname(filePath), '-temp.ts');
|
|
482
|
+
generatedTsMap.set(filePath, finalCode);
|
|
387
483
|
return tempFilePath;
|
|
388
484
|
}
|
|
389
485
|
async function restoreAllOriginals() {
|
|
390
|
-
for (const [originalPath, genPath] of generatedTsMap.entries()) {
|
|
391
|
-
if (genPath !== originalPath && fs_1.default.existsSync(genPath)) {
|
|
392
|
-
fs_1.default.unlinkSync(genPath);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
486
|
generatedTsMap.clear();
|
|
396
487
|
}
|
|
397
488
|
process.on('uncaughtException', async (error) => {
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const path_1 = __importDefault(require("path"));
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
7
|
const promises_1 = require("fs/promises");
|
|
9
8
|
const postcss_1 = __importDefault(require("postcss"));
|
|
10
9
|
const postcss_combine_media_query_1 = __importDefault(require("postcss-combine-media-query"));
|
|
@@ -14,7 +13,7 @@ const core_1 = require("@swc/core");
|
|
|
14
13
|
const find_up_1 = require("find-up");
|
|
15
14
|
const processors_1 = require("@plumeria/core/processors");
|
|
16
15
|
const extract_1 = require("./extract");
|
|
17
|
-
async function generateStats(buildTime) {
|
|
16
|
+
async function generateStats(buildTime, coreFilePath) {
|
|
18
17
|
const cssCode = await (0, promises_1.readFile)(coreFilePath, 'utf8');
|
|
19
18
|
const cssSize = Buffer.byteLength(cssCode, 'utf8');
|
|
20
19
|
let rules = 0;
|
|
@@ -48,177 +47,199 @@ async function generateStats(buildTime) {
|
|
|
48
47
|
console.log(`Build time: ${buildTime.toFixed(2)}s`);
|
|
49
48
|
console.log('────────────────────────────\n');
|
|
50
49
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
async function main() {
|
|
51
|
+
let projectRoot;
|
|
52
|
+
const workspaceRootFile = await (0, find_up_1.findUp)(async (directory) => {
|
|
53
|
+
const pnpmWsPath = path_1.default.join(directory, 'pnpm-workspace.yaml');
|
|
54
|
+
try {
|
|
55
|
+
await (0, promises_1.access)(pnpmWsPath);
|
|
56
|
+
return pnpmWsPath;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
}
|
|
60
|
+
const pkgJsonPath = path_1.default.join(directory, 'package.json');
|
|
59
61
|
try {
|
|
60
|
-
|
|
62
|
+
await (0, promises_1.access)(pkgJsonPath);
|
|
63
|
+
const pkgJson = JSON.parse(await (0, promises_1.readFile)(pkgJsonPath, 'utf-8'));
|
|
61
64
|
if (pkgJson.workspaces) {
|
|
62
65
|
return pkgJsonPath;
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
|
-
catch
|
|
66
|
-
console.error(err);
|
|
68
|
+
catch {
|
|
67
69
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
projectRoot = path_1.default.dirname(workspaceRootFile);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
const singleProjectRootFile = (0, find_up_1.findUpSync)('package.json');
|
|
76
|
-
if (singleProjectRootFile) {
|
|
77
|
-
projectRoot = path_1.default.dirname(singleProjectRootFile);
|
|
70
|
+
return undefined;
|
|
71
|
+
});
|
|
72
|
+
if (workspaceRootFile) {
|
|
73
|
+
projectRoot = path_1.default.dirname(workspaceRootFile);
|
|
78
74
|
}
|
|
79
75
|
else {
|
|
80
|
-
|
|
76
|
+
const singleProjectRootFile = await (0, find_up_1.findUp)('package.json');
|
|
77
|
+
if (singleProjectRootFile) {
|
|
78
|
+
projectRoot = path_1.default.dirname(singleProjectRootFile);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
projectRoot = process.cwd();
|
|
82
|
+
}
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
let coreFilePath;
|
|
85
|
+
const coreSourcePackageJsonPath = path_1.default.join(process.cwd(), 'package.json');
|
|
86
|
+
const coreSourcePackageJson = JSON.parse(await (0, promises_1.readFile)(coreSourcePackageJsonPath, 'utf-8'));
|
|
87
|
+
const dependencies = {
|
|
88
|
+
...coreSourcePackageJson.dependencies,
|
|
89
|
+
...coreSourcePackageJson.devDependencies,
|
|
90
|
+
};
|
|
91
|
+
const coreVersion = dependencies['@plumeria/core'];
|
|
92
|
+
const resolvedCorePackageJsonPath = require.resolve('@plumeria/core/package.json', {
|
|
93
|
+
paths: [projectRoot, process.cwd()],
|
|
94
|
+
});
|
|
95
|
+
if (workspaceRootFile) {
|
|
96
|
+
if (coreVersion.includes('workspace')) {
|
|
97
|
+
coreFilePath = path_1.default.join(path_1.default.dirname(resolvedCorePackageJsonPath), 'stylesheet.css');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const corePackageJson = JSON.parse(await (0, promises_1.readFile)(resolvedCorePackageJsonPath, 'utf-8'));
|
|
101
|
+
const exactCoreVersion = corePackageJson.version;
|
|
102
|
+
coreFilePath = path_1.default.join(projectRoot, 'node_modules', '.pnpm', `@plumeria+core@${exactCoreVersion}`, 'node_modules', '@plumeria', 'core', 'stylesheet.css');
|
|
103
|
+
}
|
|
97
104
|
}
|
|
98
105
|
else {
|
|
99
|
-
|
|
100
|
-
const exactCoreVersion = corePackageJson.version;
|
|
101
|
-
coreFilePath = path_1.default.join(projectRoot, 'node_modules', '.pnpm', `@plumeria+core@${exactCoreVersion}`, 'node_modules', '@plumeria', 'core', 'stylesheet.css');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
coreFilePath = path_1.default.join(path_1.default.dirname(resolvedCorePackageJsonPath), 'stylesheet.css');
|
|
106
|
-
}
|
|
107
|
-
const cleanUp = async () => {
|
|
108
|
-
if (process.env.CI && (0, fs_1.existsSync)(coreFilePath)) {
|
|
109
|
-
(0, fs_1.unlinkSync)(coreFilePath);
|
|
110
|
-
console.log('File deleted successfully');
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
await (0, promises_1.writeFile)(coreFilePath, '', 'utf-8');
|
|
114
|
-
}
|
|
115
|
-
catch (err) {
|
|
116
|
-
console.error('An error occurred:', err);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
function isCSS(filePath) {
|
|
120
|
-
if ((0, fs_1.statSync)(filePath).isDirectory()) {
|
|
121
|
-
return false;
|
|
106
|
+
coreFilePath = path_1.default.join(path_1.default.dirname(resolvedCorePackageJsonPath), 'stylesheet.css');
|
|
122
107
|
}
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (node.object?.type === 'Identifier' && node.object.value === 'css') {
|
|
134
|
-
if (node.property.value === 'props') {
|
|
135
|
-
found = true;
|
|
108
|
+
const cleanUp = async () => {
|
|
109
|
+
if (process.env.CI) {
|
|
110
|
+
try {
|
|
111
|
+
await (0, promises_1.access)(coreFilePath);
|
|
112
|
+
await (0, promises_1.unlink)(coreFilePath);
|
|
113
|
+
console.log('File deleted successfully');
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error.code !== 'ENOENT') {
|
|
117
|
+
console.error(`Error deleting ${coreFilePath}:`, error);
|
|
136
118
|
}
|
|
137
119
|
}
|
|
138
120
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
121
|
+
try {
|
|
122
|
+
await (0, promises_1.writeFile)(coreFilePath, '', 'utf-8');
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
console.error('An error occurred:', err);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
async function isCSS(code, filePath) {
|
|
129
|
+
if (!code.includes('css.props')) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const ast = await (0, core_1.parse)(code, {
|
|
133
|
+
syntax: 'typescript',
|
|
134
|
+
tsx: filePath.endsWith('.tsx'),
|
|
135
|
+
decorators: false,
|
|
136
|
+
dynamicImport: true,
|
|
137
|
+
});
|
|
138
|
+
let found = false;
|
|
139
|
+
function visit(node) {
|
|
140
|
+
if (node.type === 'MemberExpression' && node.property?.value) {
|
|
141
|
+
if (node.object?.type === 'Identifier' && node.object.value === 'css') {
|
|
142
|
+
if (node.property.value === 'props') {
|
|
143
|
+
found = true;
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
}
|
|
147
|
+
for (const key in node) {
|
|
148
|
+
const value = node[key];
|
|
149
|
+
if (value && typeof value === 'object') {
|
|
150
|
+
if (Array.isArray(value)) {
|
|
151
|
+
for (const item of value) {
|
|
152
|
+
visit(item);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
visit(value);
|
|
157
|
+
}
|
|
149
158
|
}
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
filesSupportExtensions.push(tsFile);
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
const tempFile = await (0, extract_1.extractTSFile)(file);
|
|
194
|
-
filesSupportExtensions.push(tempFile);
|
|
161
|
+
visit(ast);
|
|
162
|
+
return found;
|
|
163
|
+
}
|
|
164
|
+
async function optimizeCSS() {
|
|
165
|
+
const cssCode = await (0, promises_1.readFile)(coreFilePath, 'utf8');
|
|
166
|
+
const merged = (0, postcss_1.default)([(0, postcss_combine_media_query_1.default)()]).process(cssCode, {
|
|
167
|
+
from: coreFilePath,
|
|
168
|
+
to: coreFilePath,
|
|
169
|
+
});
|
|
170
|
+
const light = (0, lightningcss_1.transform)({
|
|
171
|
+
filename: coreFilePath,
|
|
172
|
+
code: Buffer.from(merged.css),
|
|
173
|
+
minify: process.env.NODE_ENV === 'production',
|
|
174
|
+
targets: {
|
|
175
|
+
safari: 16,
|
|
176
|
+
edge: 110,
|
|
177
|
+
firefox: 110,
|
|
178
|
+
chrome: 110,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
const optimizedCss = Buffer.from(light.code).toString('utf-8');
|
|
182
|
+
await (0, promises_1.writeFile)(coreFilePath, optimizedCss, 'utf-8');
|
|
183
|
+
}
|
|
184
|
+
(async () => {
|
|
185
|
+
const startTime = performance.now();
|
|
186
|
+
await cleanUp();
|
|
187
|
+
const scanRoot = process.cwd();
|
|
188
|
+
const files = [];
|
|
189
|
+
for await (const entry of (0, promises_1.glob)('**/*.{js,jsx,ts,tsx,vue,svelte}', {
|
|
190
|
+
cwd: scanRoot,
|
|
191
|
+
exclude: [
|
|
192
|
+
'**/node_modules/**',
|
|
193
|
+
'**/dist/**',
|
|
194
|
+
'**/build/**',
|
|
195
|
+
'**/.next/**',
|
|
196
|
+
],
|
|
197
|
+
})) {
|
|
198
|
+
files.push(entry);
|
|
195
199
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
const projectName = path_1.default.basename(projectRoot);
|
|
201
|
+
const tempToOriginalMap = new Map();
|
|
202
|
+
const filesSupportExtensions = await Promise.all(files.map(async (file) => {
|
|
203
|
+
const ext = path_1.default.extname(file);
|
|
204
|
+
let tempFile;
|
|
205
|
+
if (ext === '.vue' || ext === '.svelte') {
|
|
206
|
+
tempFile = await (0, extract_1.extractVueAndSvelte)(file);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
tempFile = await (0, extract_1.extractTSFile)(file);
|
|
210
|
+
}
|
|
211
|
+
tempToOriginalMap.set(tempFile, file);
|
|
212
|
+
return tempFile;
|
|
213
|
+
}));
|
|
214
|
+
const styleFiles = await Promise.all(filesSupportExtensions.map(async (file) => {
|
|
215
|
+
const originalFile = tempToOriginalMap.get(file);
|
|
216
|
+
const code = extract_1.generatedTsMap.get(originalFile);
|
|
217
|
+
const isCssFile = code ? await isCSS(code, file) : false;
|
|
218
|
+
return isCssFile ? file : null;
|
|
219
|
+
}))
|
|
220
|
+
.then((results) => results.filter(Boolean))
|
|
221
|
+
.then((results) => results.sort());
|
|
222
|
+
for (const file of styleFiles) {
|
|
223
|
+
const originalFile = tempToOriginalMap.get(file);
|
|
224
|
+
const code = extract_1.generatedTsMap.get(originalFile);
|
|
225
|
+
if (code) {
|
|
226
|
+
await (0, promises_1.writeFile)(file, code, 'utf8');
|
|
227
|
+
await (0, execute_1.execute)(path_1.default.resolve(file));
|
|
228
|
+
if (process.argv.includes('--paths')) {
|
|
229
|
+
console.log(`✅: ${projectName}/${path_1.default.relative(projectRoot, originalFile)}`);
|
|
230
|
+
}
|
|
231
|
+
await (0, promises_1.unlink)(file);
|
|
232
|
+
}
|
|
209
233
|
}
|
|
210
|
-
}
|
|
211
|
-
for (let i = 0; i < styleFiles.length; i++) {
|
|
212
234
|
await (0, processors_1.buildGlobal)(coreFilePath);
|
|
213
|
-
}
|
|
214
|
-
for (let i = 0; i < styleFiles.length; i++) {
|
|
215
235
|
await (0, processors_1.buildProps)(coreFilePath);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
}
|
|
236
|
+
await optimizeCSS();
|
|
237
|
+
await (0, extract_1.restoreAllOriginals)();
|
|
238
|
+
if (process.argv.includes('--stats')) {
|
|
239
|
+
const endTime = performance.now();
|
|
240
|
+
const buildTime = (endTime - startTime) / 1000;
|
|
241
|
+
await generateStats(buildTime, coreFilePath);
|
|
242
|
+
}
|
|
243
|
+
})();
|
|
244
|
+
}
|
|
245
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumeria/compiler",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.2",
|
|
4
4
|
"description": "Plumeria Rust-based compiler",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"lightningcss": "^1.30.2",
|
|
22
22
|
"postcss": "^8.5.6",
|
|
23
23
|
"postcss-combine-media-query": "^2.1.0",
|
|
24
|
-
"rscute": "^1.0.
|
|
24
|
+
"rscute": "^1.0.2"
|
|
25
25
|
},
|
|
26
26
|
"publishConfig": {
|
|
27
27
|
"access": "public"
|