@plumeria/unplugin 18.0.0 → 18.1.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.
Files changed (3) hide show
  1. package/dist/core.js +129 -30
  2. package/dist/core.mjs +130 -31
  3. package/package.json +2 -2
package/dist/core.js CHANGED
@@ -236,6 +236,13 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
236
236
  const actualPath = (0, utils_1.resolveImportPath)(sourcePath, resourcePath);
237
237
  if (actualPath) {
238
238
  node.specifiers.forEach((specifier) => {
239
+ if (specifier.type === 'ImportNamespaceSpecifier') {
240
+ localImports[specifier.local.value] = {
241
+ actualPath,
242
+ importedName: '*',
243
+ };
244
+ return;
245
+ }
239
246
  if (specifier.type === 'ImportSpecifier' ||
240
247
  specifier.type === 'ImportDefaultSpecifier') {
241
248
  const importedName = specifier.type === 'ImportDefaultSpecifier'
@@ -819,6 +826,98 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
819
826
  return scannedTables.createObjectTable[hash];
820
827
  return null;
821
828
  };
829
+ const resolveBracketGroup = (node) => {
830
+ if (!utils_1.t.isMemberExpression(node) ||
831
+ !utils_1.t.isIdentifier(node.object) ||
832
+ node.property.type !== 'Computed' ||
833
+ utils_1.t.isStringLiteral(node.property.expression)) {
834
+ return null;
835
+ }
836
+ const varName = node.object.value;
837
+ const obj = resolveCreateObject(varName);
838
+ return obj
839
+ ? { varName, keyExpr: node.property.expression, obj }
840
+ : null;
841
+ };
842
+ const buildDecision = (node, prefix, off, counter) => {
843
+ if (node.type === 'ParenthesisExpression') {
844
+ return buildDecision(node.expression, prefix, off, counter);
845
+ }
846
+ if (node.type === 'ConditionalExpression') {
847
+ const a = buildDecision(node.consequent, prefix, off, counter);
848
+ if (!a)
849
+ return null;
850
+ const b = buildDecision(node.alternate, prefix, off, counter);
851
+ if (!b)
852
+ return null;
853
+ return {
854
+ keyExpr: `((${getSource(node.test)}) ? ${a.keyExpr} : ${b.keyExpr})`,
855
+ options: [...a.options, ...b.options],
856
+ leaves: a.leaves + b.leaves,
857
+ hasGroup: a.hasGroup || b.hasGroup,
858
+ };
859
+ }
860
+ if (node.type === 'BinaryExpression' && node.operator === '&&') {
861
+ const right = buildDecision(node.right, prefix, off, counter);
862
+ if (!right)
863
+ return null;
864
+ return {
865
+ keyExpr: `((${getSource(node.left)}) ? ${right.keyExpr} : ${JSON.stringify(off)})`,
866
+ options: right.options,
867
+ leaves: right.leaves,
868
+ hasGroup: right.hasGroup,
869
+ };
870
+ }
871
+ const group = resolveBracketGroup(node);
872
+ if (group) {
873
+ return {
874
+ keyExpr: getSource(group.keyExpr),
875
+ options: Object.entries(group.obj).map(([value, style]) => ({
876
+ value,
877
+ style: style,
878
+ })),
879
+ leaves: 1,
880
+ hasGroup: true,
881
+ };
882
+ }
883
+ const style = resolveStyleObject(node);
884
+ if (!style)
885
+ return null;
886
+ const value = `${prefix}${counter.n++}`;
887
+ return {
888
+ keyExpr: JSON.stringify(value),
889
+ options: [{ value, style }],
890
+ leaves: 1,
891
+ hasGroup: false,
892
+ };
893
+ };
894
+ const collectGroupKeys = (node, acc) => {
895
+ if (node.type === 'ParenthesisExpression') {
896
+ collectGroupKeys(node.expression, acc);
897
+ }
898
+ else if (node.type === 'ConditionalExpression') {
899
+ collectGroupKeys(node.consequent, acc);
900
+ collectGroupKeys(node.alternate, acc);
901
+ }
902
+ else if (node.type === 'BinaryExpression' &&
903
+ node.operator === '&&') {
904
+ collectGroupKeys(node.right, acc);
905
+ }
906
+ else {
907
+ const group = resolveBracketGroup(node);
908
+ if (group)
909
+ Object.keys(group.obj).forEach((k) => acc.add(k));
910
+ }
911
+ };
912
+ const resolveDecision = (node) => {
913
+ const groupKeys = new Set();
914
+ collectGroupKeys(node, groupKeys);
915
+ let prefix = '#';
916
+ while ([...groupKeys].some((k) => k.startsWith(prefix)))
917
+ prefix += '#';
918
+ const off = groupKeys.has('') ? `${prefix}off` : '';
919
+ return buildDecision(node, prefix, off, { n: 0 });
920
+ };
822
921
  const collectConditions = (node, currentTestStrings = []) => {
823
922
  const staticStyle = resolveStyleObject(node);
824
923
  if (staticStyle) {
@@ -873,14 +972,6 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
873
972
  else if (node.type === 'ParenthesisExpression') {
874
973
  return collectConditions(node.expression, currentTestStrings);
875
974
  }
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
- }
884
975
  assertResolvable(node);
885
976
  return false;
886
977
  };
@@ -1085,6 +1176,22 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1085
1176
  continue;
1086
1177
  }
1087
1178
  }
1179
+ const decision = resolveDecision(expr);
1180
+ if (decision && (decision.hasGroup || decision.leaves > 2)) {
1181
+ const groupId = ++groupIdCounter;
1182
+ decision.options.forEach(({ value, style }) => conditionals.push({
1183
+ test: expr,
1184
+ testLHS: decision.keyExpr,
1185
+ testString: `${decision.keyExpr} === '${value}'`,
1186
+ truthy: style,
1187
+ falsy: {},
1188
+ groupId,
1189
+ groupName: undefined,
1190
+ valueName: value,
1191
+ varName: undefined,
1192
+ }));
1193
+ continue;
1194
+ }
1088
1195
  const handled = collectConditions(expr);
1089
1196
  if (handled)
1090
1197
  continue;
@@ -1218,7 +1325,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1218
1325
  getSource(options[0].test);
1219
1326
  const lookupMap = {};
1220
1327
  options.forEach((opt) => {
1221
- if (opt.valueName && opt.truthy) {
1328
+ if (opt.valueName !== undefined && opt.truthy) {
1222
1329
  const className = processStyleRecords(opt.truthy)
1223
1330
  .map((r) => r.hash)
1224
1331
  .join(' ');
@@ -1256,6 +1363,15 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1256
1363
  conflictVarGroups[c.groupId].push(c);
1257
1364
  }
1258
1365
  });
1366
+ Object.keys(conflictVarGroups).forEach((id) => {
1367
+ const groupId = Number(id);
1368
+ const opts = conflictVarGroups[groupId];
1369
+ if (opts.some((c) => c.valueName === ''))
1370
+ return;
1371
+ const off = conditionals.find((c) => c.groupId === groupId && c.valueName === '');
1372
+ if (off)
1373
+ opts.push({ ...off, truthy: {}, falsy: {} });
1374
+ });
1259
1375
  Object.entries(conflictVarGroups).forEach(([, opts]) => {
1260
1376
  dimensions.push({
1261
1377
  type: 'var',
@@ -1302,12 +1418,8 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1302
1418
  };
1303
1419
  (0, utils_1.traverse)(ast, {
1304
1420
  JSXOpeningElement({ node }) {
1305
- let tagName = '';
1306
- if (node.name.type === 'Identifier') {
1307
- tagName = node.name.value;
1308
- }
1309
1421
  jsxOpeningElementMap.set(node.span.start, {
1310
- tagName,
1422
+ compKey: (0, utils_1.resolveComponentKey)(node.name, resourcePath, localImports),
1311
1423
  attributes: node.attributes,
1312
1424
  });
1313
1425
  },
@@ -1487,30 +1599,17 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1487
1599
  return;
1488
1600
  const attrName = node.name.value;
1489
1601
  if (attrName !== styleProp) {
1490
- let parentTagName = '';
1602
+ let compKey = null;
1491
1603
  for (const [, val] of jsxOpeningElementMap) {
1492
1604
  const found = val.attributes
1493
1605
  .filter((a) => a.type === 'JSXAttribute')
1494
1606
  .find((a) => a.span.start === node.span.start);
1495
1607
  if (found) {
1496
- parentTagName = val.tagName;
1608
+ compKey = val.compKey;
1497
1609
  break;
1498
1610
  }
1499
1611
  }
1500
- if (parentTagName &&
1501
- parentTagName[0] === parentTagName[0].toUpperCase()) {
1502
- let compKey;
1503
- const imported = localImports[parentTagName];
1504
- if (imported) {
1505
- compKey = `${imported.actualPath}-${imported.importedName}`;
1506
- const resolved = (0, utils_1.resolveExport)(imported.actualPath, imported.importedName);
1507
- if (resolved) {
1508
- compKey = `${resolved.filePath}-${resolved.localName}`;
1509
- }
1510
- }
1511
- else {
1512
- compKey = `${resourcePath}-${parentTagName}`;
1513
- }
1612
+ if (compKey) {
1514
1613
  const list = scannedTables.componentPropsTable?.[compKey]?.[attrName];
1515
1614
  if (list &&
1516
1615
  node.value &&
package/dist/core.mjs CHANGED
@@ -2,7 +2,7 @@ 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, collectReferenceIdentifiers, getStyleRecords, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, extractOndemandStyles, deepMerge, scanAll, resolveImportPath, getLeadingCommentLength, optimizer, getFileDependencies, resolveExport, DEFAULT_STYLE_PROP, } from '@plumeria/utils';
5
+ import { traverse, collectReferenceIdentifiers, getStyleRecords, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, extractOndemandStyles, deepMerge, scanAll, resolveImportPath, getLeadingCommentLength, optimizer, getFileDependencies, resolveExport, resolveComponentKey, 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) => {
@@ -200,6 +200,13 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
200
200
  const actualPath = resolveImportPath(sourcePath, resourcePath);
201
201
  if (actualPath) {
202
202
  node.specifiers.forEach((specifier) => {
203
+ if (specifier.type === 'ImportNamespaceSpecifier') {
204
+ localImports[specifier.local.value] = {
205
+ actualPath,
206
+ importedName: '*',
207
+ };
208
+ return;
209
+ }
203
210
  if (specifier.type === 'ImportSpecifier' ||
204
211
  specifier.type === 'ImportDefaultSpecifier') {
205
212
  const importedName = specifier.type === 'ImportDefaultSpecifier'
@@ -783,6 +790,98 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
783
790
  return scannedTables.createObjectTable[hash];
784
791
  return null;
785
792
  };
793
+ const resolveBracketGroup = (node) => {
794
+ if (!t.isMemberExpression(node) ||
795
+ !t.isIdentifier(node.object) ||
796
+ node.property.type !== 'Computed' ||
797
+ t.isStringLiteral(node.property.expression)) {
798
+ return null;
799
+ }
800
+ const varName = node.object.value;
801
+ const obj = resolveCreateObject(varName);
802
+ return obj
803
+ ? { varName, keyExpr: node.property.expression, obj }
804
+ : null;
805
+ };
806
+ const buildDecision = (node, prefix, off, counter) => {
807
+ if (node.type === 'ParenthesisExpression') {
808
+ return buildDecision(node.expression, prefix, off, counter);
809
+ }
810
+ if (node.type === 'ConditionalExpression') {
811
+ const a = buildDecision(node.consequent, prefix, off, counter);
812
+ if (!a)
813
+ return null;
814
+ const b = buildDecision(node.alternate, prefix, off, counter);
815
+ if (!b)
816
+ return null;
817
+ return {
818
+ keyExpr: `((${getSource(node.test)}) ? ${a.keyExpr} : ${b.keyExpr})`,
819
+ options: [...a.options, ...b.options],
820
+ leaves: a.leaves + b.leaves,
821
+ hasGroup: a.hasGroup || b.hasGroup,
822
+ };
823
+ }
824
+ if (node.type === 'BinaryExpression' && node.operator === '&&') {
825
+ const right = buildDecision(node.right, prefix, off, counter);
826
+ if (!right)
827
+ return null;
828
+ return {
829
+ keyExpr: `((${getSource(node.left)}) ? ${right.keyExpr} : ${JSON.stringify(off)})`,
830
+ options: right.options,
831
+ leaves: right.leaves,
832
+ hasGroup: right.hasGroup,
833
+ };
834
+ }
835
+ const group = resolveBracketGroup(node);
836
+ if (group) {
837
+ return {
838
+ keyExpr: getSource(group.keyExpr),
839
+ options: Object.entries(group.obj).map(([value, style]) => ({
840
+ value,
841
+ style: style,
842
+ })),
843
+ leaves: 1,
844
+ hasGroup: true,
845
+ };
846
+ }
847
+ const style = resolveStyleObject(node);
848
+ if (!style)
849
+ return null;
850
+ const value = `${prefix}${counter.n++}`;
851
+ return {
852
+ keyExpr: JSON.stringify(value),
853
+ options: [{ value, style }],
854
+ leaves: 1,
855
+ hasGroup: false,
856
+ };
857
+ };
858
+ const collectGroupKeys = (node, acc) => {
859
+ if (node.type === 'ParenthesisExpression') {
860
+ collectGroupKeys(node.expression, acc);
861
+ }
862
+ else if (node.type === 'ConditionalExpression') {
863
+ collectGroupKeys(node.consequent, acc);
864
+ collectGroupKeys(node.alternate, acc);
865
+ }
866
+ else if (node.type === 'BinaryExpression' &&
867
+ node.operator === '&&') {
868
+ collectGroupKeys(node.right, acc);
869
+ }
870
+ else {
871
+ const group = resolveBracketGroup(node);
872
+ if (group)
873
+ Object.keys(group.obj).forEach((k) => acc.add(k));
874
+ }
875
+ };
876
+ const resolveDecision = (node) => {
877
+ const groupKeys = new Set();
878
+ collectGroupKeys(node, groupKeys);
879
+ let prefix = '#';
880
+ while ([...groupKeys].some((k) => k.startsWith(prefix)))
881
+ prefix += '#';
882
+ const off = groupKeys.has('') ? `${prefix}off` : '';
883
+ return buildDecision(node, prefix, off, { n: 0 });
884
+ };
786
885
  const collectConditions = (node, currentTestStrings = []) => {
787
886
  const staticStyle = resolveStyleObject(node);
788
887
  if (staticStyle) {
@@ -837,14 +936,6 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
837
936
  else if (node.type === 'ParenthesisExpression') {
838
937
  return collectConditions(node.expression, currentTestStrings);
839
938
  }
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
- }
848
939
  assertResolvable(node);
849
940
  return false;
850
941
  };
@@ -1049,6 +1140,22 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1049
1140
  continue;
1050
1141
  }
1051
1142
  }
1143
+ const decision = resolveDecision(expr);
1144
+ if (decision && (decision.hasGroup || decision.leaves > 2)) {
1145
+ const groupId = ++groupIdCounter;
1146
+ decision.options.forEach(({ value, style }) => conditionals.push({
1147
+ test: expr,
1148
+ testLHS: decision.keyExpr,
1149
+ testString: `${decision.keyExpr} === '${value}'`,
1150
+ truthy: style,
1151
+ falsy: {},
1152
+ groupId,
1153
+ groupName: undefined,
1154
+ valueName: value,
1155
+ varName: undefined,
1156
+ }));
1157
+ continue;
1158
+ }
1052
1159
  const handled = collectConditions(expr);
1053
1160
  if (handled)
1054
1161
  continue;
@@ -1182,7 +1289,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1182
1289
  getSource(options[0].test);
1183
1290
  const lookupMap = {};
1184
1291
  options.forEach((opt) => {
1185
- if (opt.valueName && opt.truthy) {
1292
+ if (opt.valueName !== undefined && opt.truthy) {
1186
1293
  const className = processStyleRecords(opt.truthy)
1187
1294
  .map((r) => r.hash)
1188
1295
  .join(' ');
@@ -1220,6 +1327,15 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1220
1327
  conflictVarGroups[c.groupId].push(c);
1221
1328
  }
1222
1329
  });
1330
+ Object.keys(conflictVarGroups).forEach((id) => {
1331
+ const groupId = Number(id);
1332
+ const opts = conflictVarGroups[groupId];
1333
+ if (opts.some((c) => c.valueName === ''))
1334
+ return;
1335
+ const off = conditionals.find((c) => c.groupId === groupId && c.valueName === '');
1336
+ if (off)
1337
+ opts.push({ ...off, truthy: {}, falsy: {} });
1338
+ });
1223
1339
  Object.entries(conflictVarGroups).forEach(([, opts]) => {
1224
1340
  dimensions.push({
1225
1341
  type: 'var',
@@ -1266,12 +1382,8 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1266
1382
  };
1267
1383
  traverse(ast, {
1268
1384
  JSXOpeningElement({ node }) {
1269
- let tagName = '';
1270
- if (node.name.type === 'Identifier') {
1271
- tagName = node.name.value;
1272
- }
1273
1385
  jsxOpeningElementMap.set(node.span.start, {
1274
- tagName,
1386
+ compKey: resolveComponentKey(node.name, resourcePath, localImports),
1275
1387
  attributes: node.attributes,
1276
1388
  });
1277
1389
  },
@@ -1451,30 +1563,17 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1451
1563
  return;
1452
1564
  const attrName = node.name.value;
1453
1565
  if (attrName !== styleProp) {
1454
- let parentTagName = '';
1566
+ let compKey = null;
1455
1567
  for (const [, val] of jsxOpeningElementMap) {
1456
1568
  const found = val.attributes
1457
1569
  .filter((a) => a.type === 'JSXAttribute')
1458
1570
  .find((a) => a.span.start === node.span.start);
1459
1571
  if (found) {
1460
- parentTagName = val.tagName;
1572
+ compKey = val.compKey;
1461
1573
  break;
1462
1574
  }
1463
1575
  }
1464
- if (parentTagName &&
1465
- parentTagName[0] === parentTagName[0].toUpperCase()) {
1466
- let compKey;
1467
- const imported = localImports[parentTagName];
1468
- if (imported) {
1469
- compKey = `${imported.actualPath}-${imported.importedName}`;
1470
- const resolved = resolveExport(imported.actualPath, imported.importedName);
1471
- if (resolved) {
1472
- compKey = `${resolved.filePath}-${resolved.localName}`;
1473
- }
1474
- }
1475
- else {
1476
- compKey = `${resourcePath}-${parentTagName}`;
1477
- }
1576
+ if (compKey) {
1478
1577
  const list = scannedTables.componentPropsTable?.[compKey]?.[attrName];
1479
1578
  if (list &&
1480
1579
  node.value &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/unplugin",
3
- "version": "18.0.0",
3
+ "version": "18.1.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": "^18.0.0"
92
+ "@plumeria/utils": "^18.1.0"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@swc/core": "1.15.43",