@plumeria/turbopack-loader 16.4.2 → 17.0.0

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/index.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ export interface LoaderOptions {
2
+ include?: string[];
3
+ exclude?: string[];
4
+ styleProp?: string;
5
+ }
1
6
  interface LoaderContext {
2
7
  resourcePath: string;
3
8
  context: string;
@@ -5,6 +10,8 @@ interface LoaderContext {
5
10
  async: () => (err: Error | null, content?: string) => void;
6
11
  addDependency: (path: string) => void;
7
12
  clearDependencies: () => void;
13
+ getOptions?: () => LoaderOptions | undefined;
14
+ query?: LoaderOptions | string;
8
15
  }
9
16
  export default function loader(this: LoaderContext, source: string): Promise<void>;
10
17
  export {};
package/dist/index.js CHANGED
@@ -39,22 +39,55 @@ const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const zss_engine_1 = require("zss-engine");
41
41
  const utils_1 = require("@plumeria/utils");
42
+ const compiler_1 = require("@plumeria/compiler");
42
43
  const split_css_rules_1 = require("./split-css-rules");
43
44
  const file_lock_1 = require("./file-lock");
44
- let lastValidCss = '';
45
- async function loader(source) {
46
- const callback = this.async();
47
- const resourcePath = this.resourcePath;
48
- const isProduction = process.env.NODE_ENV === 'production';
49
- const VIRTUAL_FILE_PATH = path.resolve(__dirname, '..', 'zero-virtual.css');
50
- let isThemeCSS = false;
51
- if (!lastValidCss && !isProduction) {
45
+ const GENERATED_MARKER = '/* plumeria: generated */';
46
+ const DEFAULT_INCLUDE = ['**/*.{js,jsx,ts,tsx}'];
47
+ const DEFAULT_EXCLUDE = ['**/node_modules/**', '**/dist/**', '**/.next/**'];
48
+ let productionCss = null;
49
+ async function generateProductionCss(virtualFilePath, options) {
50
+ const lockDir = virtualFilePath + '.lock';
51
+ await (0, file_lock_1.acquireLock)(lockDir);
52
+ try {
53
+ let current = '';
52
54
  try {
53
- lastValidCss = fs.readFileSync(VIRTUAL_FILE_PATH, 'utf-8');
55
+ current = fs.readFileSync(virtualFilePath, 'utf-8');
54
56
  }
55
57
  catch (e) {
56
58
  }
59
+ if (current.startsWith(GENERATED_MARKER))
60
+ return;
61
+ const css = (0, compiler_1.compileCSS)({
62
+ include: options.include ?? DEFAULT_INCLUDE,
63
+ exclude: options.exclude ?? DEFAULT_EXCLUDE,
64
+ cwd: process.cwd(),
65
+ styleProp: options.styleProp ?? utils_1.DEFAULT_STYLE_PROP,
66
+ });
67
+ const optimized = await (0, utils_1.optimizer)(css);
68
+ const tempPath = `${virtualFilePath}.${process.pid}.tmp`;
69
+ fs.writeFileSync(tempPath, `${GENERATED_MARKER}\n${optimized}`, 'utf-8');
70
+ fs.renameSync(tempPath, virtualFilePath);
71
+ }
72
+ finally {
73
+ (0, file_lock_1.releaseLockSync)(lockDir);
57
74
  }
75
+ }
76
+ const ensureProductionCss = (virtualFilePath, options) => {
77
+ if (!productionCss) {
78
+ productionCss = generateProductionCss(virtualFilePath, options);
79
+ }
80
+ return productionCss;
81
+ };
82
+ async function loader(source) {
83
+ const callback = this.async();
84
+ const loaderOptions = this.getOptions?.() ??
85
+ (typeof this.query === 'object' ? this.query : undefined) ??
86
+ {};
87
+ const styleProp = loaderOptions.styleProp ?? utils_1.DEFAULT_STYLE_PROP;
88
+ const resourcePath = this.resourcePath;
89
+ const isProduction = process.env.NODE_ENV === 'production';
90
+ const VIRTUAL_FILE_PATH = path.resolve(__dirname, '..', 'zero-virtual.css');
58
91
  if (resourcePath.includes('node_modules') ||
59
92
  !source.includes('@plumeria/core')) {
60
93
  return callback(null, source);
@@ -335,17 +368,7 @@ async function loader(source) {
335
368
  const processedDecls = new Set();
336
369
  const idSpans = new Set();
337
370
  const excludedSpans = new Set();
338
- const checkVariantAssignment = (decl) => {
339
- const init = decl.init;
340
- if (init && utils_1.t.isCallExpression(init) && utils_1.t.isIdentifier(init.callee)) {
341
- const varName = init.callee.value;
342
- if ((localCreateStyles[varName] &&
343
- localCreateStyles[varName].type === 'variant') ||
344
- mergedVariantsTable[varName]) {
345
- throwCompilationError(`Plumeria: Assigning the return value of css.variants() to a variable is not supported.\nPlease pass the variant function directly to styleName or css.use(). Found assignment to: ${utils_1.t.isIdentifier(decl.id) ? decl.id.value : 'unknown'}`, init);
346
- }
347
- }
348
- };
371
+ const referenceIdents = (0, utils_1.collectReferenceIdentifiers)(ast);
349
372
  const registerStyle = (node, declSpan, isExported) => {
350
373
  let propName;
351
374
  const init = node.init;
@@ -449,7 +472,6 @@ async function loader(source) {
449
472
  else if (propName === 'createTheme' &&
450
473
  init.arguments.length >= 2 &&
451
474
  utils_1.t.isObjectExpression(init.arguments[1].expression)) {
452
- isThemeCSS = true;
453
475
  if (utils_1.t.isIdentifier(node.id)) {
454
476
  idSpans.add(node.id.span.start);
455
477
  }
@@ -570,7 +592,6 @@ async function loader(source) {
570
592
  if (utils_1.t.isVariableDeclaration(node.declaration)) {
571
593
  processedDecls.add(node.declaration);
572
594
  node.declaration.declarations.forEach((decl) => {
573
- checkVariantAssignment(decl);
574
595
  registerStyle(decl, node.span, true);
575
596
  checkStyleAliasAssignment(decl);
576
597
  });
@@ -580,7 +601,6 @@ async function loader(source) {
580
601
  if (processedDecls.has(node))
581
602
  return;
582
603
  node.declarations.forEach((decl) => {
583
- checkVariantAssignment(decl);
584
604
  registerStyle(decl, node.span, false);
585
605
  checkStyleAliasAssignment(decl);
586
606
  });
@@ -635,7 +655,6 @@ async function loader(source) {
635
655
  else if (propName === 'createTheme' &&
636
656
  args.length >= 2 &&
637
657
  utils_1.t.isObjectExpression(args[1].expression)) {
638
- isThemeCSS = true;
639
658
  let selector = '';
640
659
  const selectorExpr = args[0].expression;
641
660
  if (utils_1.t.isStringLiteral(selectorExpr)) {
@@ -717,10 +736,17 @@ async function loader(source) {
717
736
  else if (utils_1.t.isMemberExpression(expr) &&
718
737
  utils_1.t.isIdentifier(expr.object) &&
719
738
  (utils_1.t.isIdentifier(expr.property) || expr.property.type === 'Computed')) {
720
- if (expr.property.type === 'Computed')
721
- return null;
722
739
  const varName = expr.object.value;
723
- const propName = expr.property.value;
740
+ let propName;
741
+ if (expr.property.type === 'Computed') {
742
+ const keyExpr = expr.property.expression;
743
+ if (!utils_1.t.isStringLiteral(keyExpr))
744
+ return null;
745
+ propName = keyExpr.value;
746
+ }
747
+ else {
748
+ propName = expr.property.value;
749
+ }
724
750
  const styleInfo = localCreateStyles[varName];
725
751
  if (styleInfo?.obj[propName]) {
726
752
  const style = styleInfo.obj[propName];
@@ -829,6 +855,14 @@ async function loader(source) {
829
855
  else if (node.type === 'ParenthesisExpression') {
830
856
  return collectConditions(node.expression, currentTestStrings);
831
857
  }
858
+ if (currentTestStrings.length > 0 &&
859
+ utils_1.t.isMemberExpression(node) &&
860
+ utils_1.t.isIdentifier(node.object) &&
861
+ node.property.type === 'Computed' &&
862
+ resolveCreateObject(node.object.value)) {
863
+ const varName = node.object.value;
864
+ throwCompilationError(`Plumeria: "${getSource(node)}" cannot be used inside a condition, because its bracket key is not a literal.\nMove the condition into the brackets instead, e.g. ${varName}[cond ? 'a' : 'b'].`, node);
865
+ }
832
866
  assertResolvable(node);
833
867
  return false;
834
868
  };
@@ -1306,17 +1340,6 @@ async function loader(source) {
1306
1340
  return;
1307
1341
  }
1308
1342
  }
1309
- if (localStyle && localStyle.type === 'variant') {
1310
- const variantMap = localStyle.hashMap[propName];
1311
- if (variantMap) {
1312
- replacements.push({
1313
- start: node.span.start - baseByteOffset,
1314
- end: node.span.end - baseByteOffset,
1315
- content: `(${JSON.stringify(variantMap)})`,
1316
- });
1317
- return;
1318
- }
1319
- }
1320
1343
  let hash = scannedTables.createHashTable[uniqueKey];
1321
1344
  if (!hash) {
1322
1345
  hash = mergedCreateTable[varName];
@@ -1377,13 +1400,21 @@ async function loader(source) {
1377
1400
  return;
1378
1401
  if (idSpans.has(node.span.start))
1379
1402
  return;
1380
- const styleInfo = localCreateStyles[node.value];
1381
- if (styleInfo) {
1403
+ if (!referenceIdents.references.has(node.span.start))
1404
+ return;
1405
+ const prefix = referenceIdents.shorthands.has(node.span.start)
1406
+ ? `${node.value}: `
1407
+ : '';
1408
+ const pushReplacement = (content) => {
1382
1409
  replacements.push({
1383
1410
  start: node.span.start - baseByteOffset,
1384
1411
  end: node.span.end - baseByteOffset,
1385
- content: `(${JSON.stringify(styleInfo.hashMap)})`,
1412
+ content: `${prefix}${content}`,
1386
1413
  });
1414
+ };
1415
+ const styleInfo = localCreateStyles[node.value];
1416
+ if (styleInfo) {
1417
+ pushReplacement(`(${JSON.stringify(styleInfo.hashMap)})`);
1387
1418
  return;
1388
1419
  }
1389
1420
  const varName = node.value;
@@ -1402,11 +1433,7 @@ async function loader(source) {
1402
1433
  hashMap[key] = atomicMap[key];
1403
1434
  }
1404
1435
  });
1405
- replacements.push({
1406
- start: node.span.start - baseByteOffset,
1407
- end: node.span.end - baseByteOffset,
1408
- content: `(${JSON.stringify(hashMap)})`,
1409
- });
1436
+ pushReplacement(`(${JSON.stringify(hashMap)})`);
1410
1437
  }
1411
1438
  }
1412
1439
  let themeHash = mergedCreateThemeHashTable[varName];
@@ -1416,11 +1443,7 @@ async function loader(source) {
1416
1443
  if (themeHash) {
1417
1444
  const atomicMap = scannedTables.createAtomicMapTable[themeHash];
1418
1445
  if (atomicMap) {
1419
- replacements.push({
1420
- start: node.span.start - baseByteOffset,
1421
- end: node.span.end - baseByteOffset,
1422
- content: `(${JSON.stringify(atomicMap)})`,
1423
- });
1446
+ pushReplacement(`(${JSON.stringify(atomicMap)})`);
1424
1447
  return;
1425
1448
  }
1426
1449
  }
@@ -1431,11 +1454,7 @@ async function loader(source) {
1431
1454
  if (staticHash) {
1432
1455
  const staticObj = scannedTables.createStaticObjectTable[staticHash];
1433
1456
  if (staticObj) {
1434
- replacements.push({
1435
- start: node.span.start - baseByteOffset,
1436
- end: node.span.end - baseByteOffset,
1437
- content: `(${JSON.stringify(staticObj)})`,
1438
- });
1457
+ pushReplacement(`(${JSON.stringify(staticObj)})`);
1439
1458
  }
1440
1459
  }
1441
1460
  },
@@ -1443,7 +1462,7 @@ async function loader(source) {
1443
1462
  if (node.name.type !== 'Identifier')
1444
1463
  return;
1445
1464
  const attrName = node.name.value;
1446
- if (attrName !== 'styleName') {
1465
+ if (attrName !== styleProp) {
1447
1466
  let parentTagName = '';
1448
1467
  for (const [, val] of jsxOpeningElementMap) {
1449
1468
  const found = val.attributes
@@ -1728,12 +1747,27 @@ async function loader(source) {
1728
1747
  }
1729
1748
  },
1730
1749
  });
1750
+ const buildExportedInit = (info) => {
1751
+ const keys = Object.keys(info.obj);
1752
+ if (isProduction || keys.length === 0) {
1753
+ return JSON.stringify('');
1754
+ }
1755
+ const head = JSON.stringify(`Plumeria: "${info.name}.`);
1756
+ const tail = JSON.stringify(`" was read at runtime. The file that read it was not compiled ` +
1757
+ `because it does not reference "@plumeria/core" — add ` +
1758
+ `import '@plumeria/core'; to it (see the stack below). Defined in ` +
1759
+ `${path.relative(process.cwd(), resourcePath)}.`);
1760
+ return (`(()=>{const k=new Set(${JSON.stringify(keys)});` +
1761
+ `return new Proxy({},{get(t,p){` +
1762
+ `if(typeof p==="string"&&k.has(p))throw new Error(${head}+p+${tail});` +
1763
+ `return t[p];}});})()`);
1764
+ };
1731
1765
  Object.values(localCreateStyles).forEach((info) => {
1732
1766
  if (info.isExported) {
1733
1767
  replacements.push({
1734
1768
  start: info.initSpan.start,
1735
1769
  end: info.initSpan.end,
1736
- content: JSON.stringify(''),
1770
+ content: buildExportedInit(info),
1737
1771
  });
1738
1772
  }
1739
1773
  else {
@@ -1764,16 +1798,16 @@ async function loader(source) {
1764
1798
  if (!relativeImportPath.startsWith('.')) {
1765
1799
  relativeImportPath = './' + relativeImportPath;
1766
1800
  }
1767
- const hash = (0, zss_engine_1.genBase36Hash)(optInCSS, 1, 8);
1768
- const postfix = `\nimport "${relativeImportPath}?v=${hash}";`;
1769
- if (process.env.NODE_ENV === 'production') {
1770
- return callback(null, transformedSource);
1801
+ const postfix = `\nimport "${relativeImportPath}";`;
1802
+ if (isProduction) {
1803
+ await ensureProductionCss(VIRTUAL_FILE_PATH, loaderOptions);
1804
+ return callback(null, transformedSource + postfix);
1771
1805
  }
1772
1806
  if (extractedSheets.length > 0 && process.env.NODE_ENV === 'development') {
1773
1807
  const LOCK_DIR_PATH = VIRTUAL_FILE_PATH + '.lock';
1774
1808
  await (0, file_lock_1.acquireLock)(LOCK_DIR_PATH);
1809
+ let currentCss = '';
1775
1810
  try {
1776
- let currentCss = '';
1777
1811
  try {
1778
1812
  currentCss = fs.readFileSync(VIRTUAL_FILE_PATH, 'utf-8');
1779
1813
  }
@@ -1789,18 +1823,14 @@ async function loader(source) {
1789
1823
  hasNewRule = true;
1790
1824
  }
1791
1825
  }
1792
- if (hasNewRule || isThemeCSS) {
1826
+ if (hasNewRule) {
1793
1827
  const nextCss = (0, utils_1.orderMediaLast)(Array.from(ruleSet)).join('\n\n') + '\n';
1794
1828
  fs.writeFileSync(VIRTUAL_FILE_PATH, nextCss, 'utf-8');
1795
- lastValidCss = nextCss;
1796
- }
1797
- else {
1798
- lastValidCss = currentCss;
1799
1829
  }
1800
1830
  }
1801
1831
  catch (innerError) {
1802
1832
  try {
1803
- fs.writeFileSync(VIRTUAL_FILE_PATH, lastValidCss, 'utf-8');
1833
+ fs.writeFileSync(VIRTUAL_FILE_PATH, currentCss, 'utf-8');
1804
1834
  }
1805
1835
  catch (e) {
1806
1836
  }
@@ -1810,13 +1840,6 @@ async function loader(source) {
1810
1840
  (0, file_lock_1.releaseLockSync)(LOCK_DIR_PATH);
1811
1841
  }
1812
1842
  }
1813
- else if (!isProduction) {
1814
- try {
1815
- lastValidCss = fs.readFileSync(VIRTUAL_FILE_PATH, 'utf-8');
1816
- }
1817
- catch (e) {
1818
- }
1819
- }
1820
1843
  return callback(null, transformedSource + postfix);
1821
1844
  }
1822
1845
  catch (error) {
@@ -7,6 +7,14 @@ function splitCssRules(css) {
7
7
  let depth = 0;
8
8
  let inComment = false;
9
9
  let inString = null;
10
+ let hasContent = false;
11
+ const flush = () => {
12
+ const trimmed = currentRule.trim();
13
+ if (trimmed)
14
+ rules.push(trimmed);
15
+ currentRule = '';
16
+ hasContent = false;
17
+ };
10
18
  for (let i = 0; i < css.length; i++) {
11
19
  const char = css[i];
12
20
  const nextChar = css[i + 1];
@@ -16,6 +24,8 @@ function splitCssRules(css) {
16
24
  currentRule += '/';
17
25
  i++;
18
26
  inComment = false;
27
+ if (depth === 0 && !hasContent)
28
+ flush();
19
29
  }
20
30
  continue;
21
31
  }
@@ -41,26 +51,22 @@ function splitCssRules(css) {
41
51
  if (char === '"' || char === "'") {
42
52
  currentRule += char;
43
53
  inString = char;
54
+ hasContent = true;
44
55
  continue;
45
56
  }
46
57
  currentRule += char;
58
+ if (char.trim())
59
+ hasContent = true;
47
60
  if (char === '{') {
48
61
  depth++;
49
62
  }
50
63
  else if (char === '}') {
51
64
  depth--;
52
- if (depth === 0) {
53
- const trimmed = currentRule.trim();
54
- if (trimmed)
55
- rules.push(trimmed);
56
- currentRule = '';
57
- }
65
+ if (depth === 0)
66
+ flush();
58
67
  }
59
68
  else if (char === ';' && depth === 0) {
60
- const trimmed = currentRule.trim();
61
- if (trimmed)
62
- rules.push(trimmed);
63
- currentRule = '';
69
+ flush();
64
70
  }
65
71
  }
66
72
  const trimmed = currentRule.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/turbopack-loader",
3
- "version": "16.4.2",
3
+ "version": "17.0.0",
4
4
  "description": "Plumeria Turbopack-loader",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -22,7 +22,8 @@
22
22
  "zero-virtual.css"
23
23
  ],
24
24
  "dependencies": {
25
- "@plumeria/utils": "^16.4.2"
25
+ "@plumeria/compiler": "^17.0.0",
26
+ "@plumeria/utils": "^17.0.0"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@swc/core": "1.15.43",