@plumeria/unplugin 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/README.md CHANGED
@@ -146,8 +146,52 @@ plumeria.vite({
146
146
  include: ['**/*.{ts,tsx}'],
147
147
  exclude: ['**/node_modules/**'],
148
148
  devEmitToDisk: false,
149
+ styleProp: 'sx',
149
150
  });
150
151
  ```
152
+
153
+ | Option | Default | Description |
154
+ | :-- | :-- | :-- |
155
+ | `include` | `ts/tsx/js/jsx` | Files to transform. |
156
+ | `exclude` | — | Files to skip. |
157
+ | `devEmitToDisk` | `false` | Write CSS to disk in development so the bundler's watcher drives HMR. |
158
+ | `styleProp` | `'styleName'` | The JSX prop that carries styles. |
159
+
160
+ ### styleProp
161
+
162
+ Renaming the prop takes two steps, and they have to agree. Tell the plugin:
163
+
164
+ ```js
165
+ plumeria.vite({ styleProp: 'sx' });
166
+ ```
167
+
168
+ and declare the same name for TypeScript. `@plumeria/core` ships no prop declaration of its own, so add one file to your project:
169
+
170
+ ```ts
171
+ // plumeria.d.ts
172
+ import type { Style } from '@plumeria/core';
173
+
174
+ declare global {
175
+ namespace React {
176
+ interface HTMLAttributes<T> {
177
+ sx?: Style
178
+ }
179
+ interface SVGAttributes<T> {
180
+ sx?: Style
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ For the default name, reference the declaration that ships with the package instead:
187
+
188
+ ```ts
189
+ // plumeria.d.ts
190
+ /// <reference types="@plumeria/core/style-name" />
191
+ ```
192
+
193
+ If the two disagree, the prop type-checks but is never compiled away. [`@plumeria/eslint-plugin`](https://www.npmjs.com/package/@plumeria/eslint-plugin) reads the same name from `settings.plumeria.styleProp`.
194
+
151
195
  ## Development Mode and HMR (Hot Module Replacement)
152
196
 
153
197
  `@plumeria/unplugin` provides HMR optimized for each bundler in development mode (dev server).
package/dist/core.d.ts CHANGED
@@ -3,6 +3,7 @@ export interface PluginOptions {
3
3
  include?: string | RegExp | Array<string | RegExp>;
4
4
  exclude?: string | RegExp | Array<string | RegExp>;
5
5
  devEmitToDisk?: boolean;
6
+ styleProp?: string;
6
7
  }
7
8
  export declare const TARGET_EXTENSIONS: string[];
8
9
  export declare const EXTENSION_PATTERN: RegExp;
package/dist/core.js CHANGED
@@ -43,6 +43,7 @@ exports.TARGET_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx'];
43
43
  exports.EXTENSION_PATTERN = /\.(ts|tsx|js|jsx)$/;
44
44
  const unpluginFactory = (options = {}, unpluginMeta) => {
45
45
  const filter = (0, pluginutils_1.createFilter)(options.include, options.exclude);
46
+ const styleProp = options.styleProp ?? utils_1.DEFAULT_STYLE_PROP;
46
47
  const cssLookup = new Map();
47
48
  const cssFileLookup = new Map();
48
49
  const targets = [];
@@ -382,17 +383,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
382
383
  const processedDecls = new Set();
383
384
  const idSpans = new Set();
384
385
  const excludedSpans = new Set();
385
- const checkVariantAssignment = (decl) => {
386
- const init = decl.init;
387
- if (init && utils_1.t.isCallExpression(init) && utils_1.t.isIdentifier(init.callee)) {
388
- const varName = init.callee.value;
389
- if ((localCreateStyles[varName] &&
390
- localCreateStyles[varName].type === 'variant') ||
391
- mergedVariantsTable[varName]) {
392
- 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);
393
- }
394
- }
395
- };
386
+ const referenceIdents = (0, utils_1.collectReferenceIdentifiers)(ast);
396
387
  const registerStyle = (node, declSpan, isExported) => {
397
388
  let propName;
398
389
  const init = node.init;
@@ -618,7 +609,6 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
618
609
  if (utils_1.t.isVariableDeclaration(node.declaration)) {
619
610
  processedDecls.add(node.declaration);
620
611
  node.declaration.declarations.forEach((decl) => {
621
- checkVariantAssignment(decl);
622
612
  registerStyle(decl, node.span, true);
623
613
  checkStyleAliasAssignment(decl);
624
614
  });
@@ -628,7 +618,6 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
628
618
  if (processedDecls.has(node))
629
619
  return;
630
620
  node.declarations.forEach((decl) => {
631
- checkVariantAssignment(decl);
632
621
  registerStyle(decl, node.span, false);
633
622
  checkStyleAliasAssignment(decl);
634
623
  });
@@ -764,10 +753,17 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
764
753
  else if (utils_1.t.isMemberExpression(expr) &&
765
754
  utils_1.t.isIdentifier(expr.object) &&
766
755
  (utils_1.t.isIdentifier(expr.property) || expr.property.type === 'Computed')) {
767
- if (expr.property.type === 'Computed')
768
- return null;
769
756
  const varName = expr.object.value;
770
- const propName = expr.property.value;
757
+ let propName;
758
+ if (expr.property.type === 'Computed') {
759
+ const keyExpr = expr.property.expression;
760
+ if (!utils_1.t.isStringLiteral(keyExpr))
761
+ return null;
762
+ propName = keyExpr.value;
763
+ }
764
+ else {
765
+ propName = expr.property.value;
766
+ }
771
767
  const styleInfo = localCreateStyles[varName];
772
768
  if (styleInfo?.obj[propName]) {
773
769
  const style = styleInfo.obj[propName];
@@ -877,6 +873,14 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
877
873
  else if (node.type === 'ParenthesisExpression') {
878
874
  return collectConditions(node.expression, currentTestStrings);
879
875
  }
876
+ if (currentTestStrings.length > 0 &&
877
+ utils_1.t.isMemberExpression(node) &&
878
+ utils_1.t.isIdentifier(node.object) &&
879
+ node.property.type === 'Computed' &&
880
+ resolveCreateObject(node.object.value)) {
881
+ const varName = node.object.value;
882
+ 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);
883
+ }
880
884
  assertResolvable(node);
881
885
  return false;
882
886
  };
@@ -1360,17 +1364,6 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1360
1364
  return;
1361
1365
  }
1362
1366
  }
1363
- if (localStyle && localStyle.type === 'variant') {
1364
- const variantMap = localStyle.hashMap[propName];
1365
- if (variantMap) {
1366
- replacements.push({
1367
- start: node.span.start - baseByteOffset,
1368
- end: node.span.end - baseByteOffset,
1369
- content: `(${JSON.stringify(variantMap)})`,
1370
- });
1371
- return;
1372
- }
1373
- }
1374
1367
  let hash = scannedTables.createHashTable[uniqueKey];
1375
1368
  if (!hash) {
1376
1369
  hash = mergedCreateTable[varName];
@@ -1431,13 +1424,21 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1431
1424
  return;
1432
1425
  if (idSpans.has(node.span.start))
1433
1426
  return;
1434
- const styleInfo = localCreateStyles[node.value];
1435
- if (styleInfo) {
1427
+ if (!referenceIdents.references.has(node.span.start))
1428
+ return;
1429
+ const prefix = referenceIdents.shorthands.has(node.span.start)
1430
+ ? `${node.value}: `
1431
+ : '';
1432
+ const pushReplacement = (content) => {
1436
1433
  replacements.push({
1437
1434
  start: node.span.start - baseByteOffset,
1438
1435
  end: node.span.end - baseByteOffset,
1439
- content: `(${JSON.stringify(styleInfo.hashMap)})`,
1436
+ content: `${prefix}${content}`,
1440
1437
  });
1438
+ };
1439
+ const styleInfo = localCreateStyles[node.value];
1440
+ if (styleInfo) {
1441
+ pushReplacement(`(${JSON.stringify(styleInfo.hashMap)})`);
1441
1442
  return;
1442
1443
  }
1443
1444
  const varName = node.value;
@@ -1456,11 +1457,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1456
1457
  hashMap[key] = atomicMap[key];
1457
1458
  }
1458
1459
  });
1459
- replacements.push({
1460
- start: node.span.start - baseByteOffset,
1461
- end: node.span.end - baseByteOffset,
1462
- content: `(${JSON.stringify(hashMap)})`,
1463
- });
1460
+ pushReplacement(`(${JSON.stringify(hashMap)})`);
1464
1461
  }
1465
1462
  }
1466
1463
  let themeHash = mergedCreateThemeHashTable[varName];
@@ -1470,11 +1467,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1470
1467
  if (themeHash) {
1471
1468
  const atomicMap = scannedTables.createAtomicMapTable[themeHash];
1472
1469
  if (atomicMap) {
1473
- replacements.push({
1474
- start: node.span.start - baseByteOffset,
1475
- end: node.span.end - baseByteOffset,
1476
- content: `(${JSON.stringify(atomicMap)})`,
1477
- });
1470
+ pushReplacement(`(${JSON.stringify(atomicMap)})`);
1478
1471
  return;
1479
1472
  }
1480
1473
  }
@@ -1485,11 +1478,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1485
1478
  if (staticHash) {
1486
1479
  const staticObj = scannedTables.createStaticObjectTable[staticHash];
1487
1480
  if (staticObj) {
1488
- replacements.push({
1489
- start: node.span.start - baseByteOffset,
1490
- end: node.span.end - baseByteOffset,
1491
- content: `(${JSON.stringify(staticObj)})`,
1492
- });
1481
+ pushReplacement(`(${JSON.stringify(staticObj)})`);
1493
1482
  }
1494
1483
  }
1495
1484
  },
@@ -1497,7 +1486,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1497
1486
  if (node.name.type !== 'Identifier')
1498
1487
  return;
1499
1488
  const attrName = node.name.value;
1500
- if (attrName !== 'styleName') {
1489
+ if (attrName !== styleProp) {
1501
1490
  let parentTagName = '';
1502
1491
  for (const [, val] of jsxOpeningElementMap) {
1503
1492
  const found = val.attributes
@@ -1782,12 +1771,27 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1782
1771
  }
1783
1772
  },
1784
1773
  });
1774
+ const buildExportedInit = (info) => {
1775
+ const keys = Object.keys(info.obj);
1776
+ if (!isDev || keys.length === 0) {
1777
+ return JSON.stringify('');
1778
+ }
1779
+ const head = JSON.stringify(`Plumeria: "${info.name}.`);
1780
+ const tail = JSON.stringify(`" was read at runtime. The file that read it was not compiled ` +
1781
+ `because it does not reference "@plumeria/core" — add ` +
1782
+ `import '@plumeria/core'; to it (see the stack below). Defined in ` +
1783
+ `${path.relative(viteRoot, baseId)}.`);
1784
+ return (`(()=>{const k=new Set(${JSON.stringify(keys)});` +
1785
+ `return new Proxy({},{get(t,p){` +
1786
+ `if(typeof p==="string"&&k.has(p))throw new Error(${head}+p+${tail});` +
1787
+ `return t[p];}});})()`);
1788
+ };
1785
1789
  Object.values(localCreateStyles).forEach((info) => {
1786
1790
  if (info.isExported) {
1787
1791
  replacements.push({
1788
1792
  start: info.initSpan.start,
1789
1793
  end: info.initSpan.end,
1790
- content: JSON.stringify(''),
1794
+ content: buildExportedInit(info),
1791
1795
  });
1792
1796
  }
1793
1797
  else {
package/dist/core.mjs CHANGED
@@ -2,11 +2,12 @@ import { createFilter } from '@rollup/pluginutils';
2
2
  import { parseSync } from '@swc/core';
3
3
  import * as path from 'path';
4
4
  import { applyCssValue, genBase36Hash, exceptionCamelCase, camelToKebabCase, isAtRule, } from 'zss-engine';
5
- import { traverse, getStyleRecords, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, extractOndemandStyles, deepMerge, scanAll, resolveImportPath, getLeadingCommentLength, optimizer, getFileDependencies, resolveExport, } from '@plumeria/utils';
5
+ import { traverse, collectReferenceIdentifiers, getStyleRecords, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, extractOndemandStyles, deepMerge, scanAll, resolveImportPath, getLeadingCommentLength, optimizer, getFileDependencies, resolveExport, DEFAULT_STYLE_PROP, } from '@plumeria/utils';
6
6
  export const TARGET_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx'];
7
7
  export const EXTENSION_PATTERN = /\.(ts|tsx|js|jsx)$/;
8
8
  export const unpluginFactory = (options = {}, unpluginMeta) => {
9
9
  const filter = createFilter(options.include, options.exclude);
10
+ const styleProp = options.styleProp ?? DEFAULT_STYLE_PROP;
10
11
  const cssLookup = new Map();
11
12
  const cssFileLookup = new Map();
12
13
  const targets = [];
@@ -346,17 +347,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
346
347
  const processedDecls = new Set();
347
348
  const idSpans = new Set();
348
349
  const excludedSpans = new Set();
349
- const checkVariantAssignment = (decl) => {
350
- const init = decl.init;
351
- if (init && t.isCallExpression(init) && t.isIdentifier(init.callee)) {
352
- const varName = init.callee.value;
353
- if ((localCreateStyles[varName] &&
354
- localCreateStyles[varName].type === 'variant') ||
355
- mergedVariantsTable[varName]) {
356
- 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: ${t.isIdentifier(decl.id) ? decl.id.value : 'unknown'}`, init);
357
- }
358
- }
359
- };
350
+ const referenceIdents = collectReferenceIdentifiers(ast);
360
351
  const registerStyle = (node, declSpan, isExported) => {
361
352
  let propName;
362
353
  const init = node.init;
@@ -582,7 +573,6 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
582
573
  if (t.isVariableDeclaration(node.declaration)) {
583
574
  processedDecls.add(node.declaration);
584
575
  node.declaration.declarations.forEach((decl) => {
585
- checkVariantAssignment(decl);
586
576
  registerStyle(decl, node.span, true);
587
577
  checkStyleAliasAssignment(decl);
588
578
  });
@@ -592,7 +582,6 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
592
582
  if (processedDecls.has(node))
593
583
  return;
594
584
  node.declarations.forEach((decl) => {
595
- checkVariantAssignment(decl);
596
585
  registerStyle(decl, node.span, false);
597
586
  checkStyleAliasAssignment(decl);
598
587
  });
@@ -728,10 +717,17 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
728
717
  else if (t.isMemberExpression(expr) &&
729
718
  t.isIdentifier(expr.object) &&
730
719
  (t.isIdentifier(expr.property) || expr.property.type === 'Computed')) {
731
- if (expr.property.type === 'Computed')
732
- return null;
733
720
  const varName = expr.object.value;
734
- const propName = expr.property.value;
721
+ let propName;
722
+ if (expr.property.type === 'Computed') {
723
+ const keyExpr = expr.property.expression;
724
+ if (!t.isStringLiteral(keyExpr))
725
+ return null;
726
+ propName = keyExpr.value;
727
+ }
728
+ else {
729
+ propName = expr.property.value;
730
+ }
735
731
  const styleInfo = localCreateStyles[varName];
736
732
  if (styleInfo?.obj[propName]) {
737
733
  const style = styleInfo.obj[propName];
@@ -841,6 +837,14 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
841
837
  else if (node.type === 'ParenthesisExpression') {
842
838
  return collectConditions(node.expression, currentTestStrings);
843
839
  }
840
+ if (currentTestStrings.length > 0 &&
841
+ t.isMemberExpression(node) &&
842
+ t.isIdentifier(node.object) &&
843
+ node.property.type === 'Computed' &&
844
+ resolveCreateObject(node.object.value)) {
845
+ const varName = node.object.value;
846
+ 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);
847
+ }
844
848
  assertResolvable(node);
845
849
  return false;
846
850
  };
@@ -1324,17 +1328,6 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1324
1328
  return;
1325
1329
  }
1326
1330
  }
1327
- if (localStyle && localStyle.type === 'variant') {
1328
- const variantMap = localStyle.hashMap[propName];
1329
- if (variantMap) {
1330
- replacements.push({
1331
- start: node.span.start - baseByteOffset,
1332
- end: node.span.end - baseByteOffset,
1333
- content: `(${JSON.stringify(variantMap)})`,
1334
- });
1335
- return;
1336
- }
1337
- }
1338
1331
  let hash = scannedTables.createHashTable[uniqueKey];
1339
1332
  if (!hash) {
1340
1333
  hash = mergedCreateTable[varName];
@@ -1395,13 +1388,21 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1395
1388
  return;
1396
1389
  if (idSpans.has(node.span.start))
1397
1390
  return;
1398
- const styleInfo = localCreateStyles[node.value];
1399
- if (styleInfo) {
1391
+ if (!referenceIdents.references.has(node.span.start))
1392
+ return;
1393
+ const prefix = referenceIdents.shorthands.has(node.span.start)
1394
+ ? `${node.value}: `
1395
+ : '';
1396
+ const pushReplacement = (content) => {
1400
1397
  replacements.push({
1401
1398
  start: node.span.start - baseByteOffset,
1402
1399
  end: node.span.end - baseByteOffset,
1403
- content: `(${JSON.stringify(styleInfo.hashMap)})`,
1400
+ content: `${prefix}${content}`,
1404
1401
  });
1402
+ };
1403
+ const styleInfo = localCreateStyles[node.value];
1404
+ if (styleInfo) {
1405
+ pushReplacement(`(${JSON.stringify(styleInfo.hashMap)})`);
1405
1406
  return;
1406
1407
  }
1407
1408
  const varName = node.value;
@@ -1420,11 +1421,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1420
1421
  hashMap[key] = atomicMap[key];
1421
1422
  }
1422
1423
  });
1423
- replacements.push({
1424
- start: node.span.start - baseByteOffset,
1425
- end: node.span.end - baseByteOffset,
1426
- content: `(${JSON.stringify(hashMap)})`,
1427
- });
1424
+ pushReplacement(`(${JSON.stringify(hashMap)})`);
1428
1425
  }
1429
1426
  }
1430
1427
  let themeHash = mergedCreateThemeHashTable[varName];
@@ -1434,11 +1431,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1434
1431
  if (themeHash) {
1435
1432
  const atomicMap = scannedTables.createAtomicMapTable[themeHash];
1436
1433
  if (atomicMap) {
1437
- replacements.push({
1438
- start: node.span.start - baseByteOffset,
1439
- end: node.span.end - baseByteOffset,
1440
- content: `(${JSON.stringify(atomicMap)})`,
1441
- });
1434
+ pushReplacement(`(${JSON.stringify(atomicMap)})`);
1442
1435
  return;
1443
1436
  }
1444
1437
  }
@@ -1449,11 +1442,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1449
1442
  if (staticHash) {
1450
1443
  const staticObj = scannedTables.createStaticObjectTable[staticHash];
1451
1444
  if (staticObj) {
1452
- replacements.push({
1453
- start: node.span.start - baseByteOffset,
1454
- end: node.span.end - baseByteOffset,
1455
- content: `(${JSON.stringify(staticObj)})`,
1456
- });
1445
+ pushReplacement(`(${JSON.stringify(staticObj)})`);
1457
1446
  }
1458
1447
  }
1459
1448
  },
@@ -1461,7 +1450,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1461
1450
  if (node.name.type !== 'Identifier')
1462
1451
  return;
1463
1452
  const attrName = node.name.value;
1464
- if (attrName !== 'styleName') {
1453
+ if (attrName !== styleProp) {
1465
1454
  let parentTagName = '';
1466
1455
  for (const [, val] of jsxOpeningElementMap) {
1467
1456
  const found = val.attributes
@@ -1746,12 +1735,27 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1746
1735
  }
1747
1736
  },
1748
1737
  });
1738
+ const buildExportedInit = (info) => {
1739
+ const keys = Object.keys(info.obj);
1740
+ if (!isDev || keys.length === 0) {
1741
+ return JSON.stringify('');
1742
+ }
1743
+ const head = JSON.stringify(`Plumeria: "${info.name}.`);
1744
+ const tail = JSON.stringify(`" was read at runtime. The file that read it was not compiled ` +
1745
+ `because it does not reference "@plumeria/core" — add ` +
1746
+ `import '@plumeria/core'; to it (see the stack below). Defined in ` +
1747
+ `${path.relative(viteRoot, baseId)}.`);
1748
+ return (`(()=>{const k=new Set(${JSON.stringify(keys)});` +
1749
+ `return new Proxy({},{get(t,p){` +
1750
+ `if(typeof p==="string"&&k.has(p))throw new Error(${head}+p+${tail});` +
1751
+ `return t[p];}});})()`);
1752
+ };
1749
1753
  Object.values(localCreateStyles).forEach((info) => {
1750
1754
  if (info.isExported) {
1751
1755
  replacements.push({
1752
1756
  start: info.initSpan.start,
1753
1757
  end: info.initSpan.end,
1754
- content: JSON.stringify(''),
1758
+ content: buildExportedInit(info),
1755
1759
  });
1756
1760
  }
1757
1761
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/unplugin",
3
- "version": "16.4.2",
3
+ "version": "17.0.0",
4
4
  "description": "Universal Plumeria plugin for various build tools",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -89,7 +89,7 @@
89
89
  "dependencies": {
90
90
  "@rollup/pluginutils": "^5.4.0",
91
91
  "unplugin": "^3.0.0",
92
- "@plumeria/utils": "^16.4.2"
92
+ "@plumeria/utils": "^17.0.0"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@swc/core": "1.15.43",