@menapo/vicss 1.0.0 → 1.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.
- package/README.md +68 -33
- package/dist/cli.js +6 -6
- package/dist/{config-loader-DzP68Snn.js → config-loader-1kWS0cRU.js} +1 -1
- package/dist/config-loader-CG76KA50.js +1 -0
- package/dist/index.d.ts +63 -6
- package/dist/index.js +75 -2
- package/dist/lint.js +0 -0
- package/dist/{pool-BGXMhLfL.js → pool-F3qb30Fy.js} +467 -20
- package/dist/{print-Ds6fkgio.js → print-B_mn7wBu.js} +70 -36
- package/dist/{snapshot-BQb8ZmSV.js → snapshot-DAJ8FLU4.js} +1 -1
- package/dist/{theme-C-0LpsJx.js → theme-zBcr5DHi.js} +25 -2
- package/dist/{types-file-R_knFzME.js → types-file-D6Iu4kzv.js} +7 -7
- package/dist/vite.d.ts +26 -1
- package/dist/vite.js +398 -60
- package/dist/worker.js +3 -3
- package/package.json +4 -4
- package/dist/config-loader-pLzP9par.js +0 -1
|
@@ -1,10 +1,78 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { C as createLocator, _ as evaluate, a as buildRecipe, c as resolveRecipe, h as NOT_HANDLED, i as RecipeValue, m as shadowedByEarlier, n as compileTheme, o as printTable, p as isBaseClass, r as analyzeScopes, s as recipeKey, v as evaluateBinding, y as isDynamic } from "./theme-
|
|
1
|
+
import { $ as isPaletteAtom, A as PROPERTIES, At as readableClass, Et as mergeClassNames, F as MarkerValue, Ft as hash, I as markerId, It as stableStringify, L as markerLabel, Mt as styleLiteral, N as numberKind, Nt as toRecord, Pt as valueLabel, R as markerSites, S as privateTokenError, St as fromRecord, _ as normalizeStyle, _t as VicssError, at as SCALE_NAMESPACES, bt as compareStrings, dt as isPlainObject, et as keepAtom, ft as isScaleName, g as compileKeyframes, gt as suggestTokens, ht as scaleTokens, it as SCALES, j as getProperty, jt as splitClasses, kt as propertyKey, lt as cssVarName, ot as SPACING_STEP, pt as lookupToken, q as placeholder, rt as typedPaletteNames, st as closestNames, ut as didYouMean, wt as makeClassName, x as hasPrivateAccess, yt as classAttribute } from "./print-B_mn7wBu.js";
|
|
2
|
+
import { C as createLocator, _ as evaluate, a as buildRecipe, c as resolveRecipe, h as NOT_HANDLED, i as RecipeValue, m as shadowedByEarlier, n as compileTheme, o as printTable, p as isBaseClass, r as analyzeScopes, s as recipeKey, v as evaluateBinding, y as isDynamic } from "./theme-zBcr5DHi.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { availableParallelism } from "node:os";
|
|
7
7
|
import { Worker } from "node:worker_threads";
|
|
8
|
+
//#region src/core/attributes.ts
|
|
9
|
+
/**
|
|
10
|
+
* A compiled style written as an element's `style=` (PLAN §5.16): what goes
|
|
11
|
+
* inline, what stays a class, and how each framework writes an inline
|
|
12
|
+
* style. The compiler finds the attributes (core/compile.ts); this module
|
|
13
|
+
* holds what does not need the syntax tree.
|
|
14
|
+
*
|
|
15
|
+
* An inline declaration beats every class rule, so a property with a
|
|
16
|
+
* condition (`_hover`, `_md`, `_dark`) cannot be split between the two:
|
|
17
|
+
* its base value inline would win over the class of its hover value. Such
|
|
18
|
+
* a property goes to the classes whole, base value included, and so does
|
|
19
|
+
* every property related to it: a shorthand over a conditional longhand
|
|
20
|
+
* (`padding` inline would beat `_hover: { paddingTop }`), a longhand under a
|
|
21
|
+
* conditional shorthand, and any atom on the same CSS property.
|
|
22
|
+
*/
|
|
23
|
+
/** `a` is `b`, or one lies below the other (`p` and `p-t`, `padding` and `padding-top`). */
|
|
24
|
+
const related = (a, b) => a === b || a.startsWith(`${b}-`) || b.startsWith(`${a}-`);
|
|
25
|
+
/**
|
|
26
|
+
* A compiled style's classes (`CompiledStyle.classes`) split for `style=`.
|
|
27
|
+
* `atomOf` finds a class's atom; a class with none (the app's own names, a
|
|
28
|
+
* theme, a marker, a flattened base) stays a class, and so does a palette
|
|
29
|
+
* atom, whose rule sets custom properties a declaration cannot name.
|
|
30
|
+
*/
|
|
31
|
+
function splitStyle(classes, atomOf) {
|
|
32
|
+
const atoms = [];
|
|
33
|
+
const kept = [];
|
|
34
|
+
for (const cls of classes.split(" ")) {
|
|
35
|
+
if (!cls) continue;
|
|
36
|
+
const atom = atomOf(cls);
|
|
37
|
+
if (atom && !isPaletteAtom(atom)) atoms.push(atom);
|
|
38
|
+
else kept.push(cls);
|
|
39
|
+
}
|
|
40
|
+
const conditional = atoms.filter((atom) => atom.chain.length > 0);
|
|
41
|
+
const inline = [];
|
|
42
|
+
for (const atom of atoms) if (atom.chain.length > 0 || conditional.some((c) => related(c.key, atom.key) || related(c.property, atom.property))) kept.push(atom.className);
|
|
43
|
+
else inline.push(atom);
|
|
44
|
+
inline.sort((a, b) => a.tier - b.tier || (a.property < b.property ? -1 : 1));
|
|
45
|
+
return {
|
|
46
|
+
declarations: inline.map((atom) => [atom.property, atom.value]),
|
|
47
|
+
classes: kept
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** `padding-top` → `paddingTop`, `-webkit-box` → `WebkitBox`, `-ms-grid` → `msGrid`; custom properties stay. */
|
|
51
|
+
function camelProperty(property) {
|
|
52
|
+
if (property.startsWith("--")) return property;
|
|
53
|
+
return (property.startsWith("-ms-") ? property.slice(1) : property).replace(/-([a-z])/g, (_, ch) => ch.toUpperCase());
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The declarations as an inline style's text, `a:b;c:d`. Double quotes in a
|
|
57
|
+
* value (a font family) become single ones, which CSS reads alike, so the
|
|
58
|
+
* text fits a double-quoted attribute in any template unescaped.
|
|
59
|
+
*/
|
|
60
|
+
function inlineText(declarations) {
|
|
61
|
+
return declarations.map(([property, value]) => `${property}:${value.includes("'") ? value : value.replaceAll("\"", "'")}`).join(";");
|
|
62
|
+
}
|
|
63
|
+
/** The declarations as JS: an object in React and Preact's case or Solid's, else a string. */
|
|
64
|
+
function inlineStyle(declarations, shape) {
|
|
65
|
+
if (shape === "string") return JSON.stringify(inlineText(declarations));
|
|
66
|
+
return `{ ${declarations.map(([property, value]) => {
|
|
67
|
+
const key = shape === "camel" ? camelProperty(property) : property;
|
|
68
|
+
return `${/^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key)}: ${JSON.stringify(value)}`;
|
|
69
|
+
}).join(", ")} }`;
|
|
70
|
+
}
|
|
71
|
+
/** Text for a template literal's raw part: what would end it or start a substitution, escaped. */
|
|
72
|
+
function templateText(text) {
|
|
73
|
+
return text.replace(/[`\\]|\$(?=\{)/g, (ch) => `\\${ch}`);
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
8
76
|
//#region src/core/edits.ts
|
|
9
77
|
/**
|
|
10
78
|
* Source edits with a source map (PLAN D20): the few operations the rewrite
|
|
@@ -291,38 +359,55 @@ const RUNTIME_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
291
359
|
const SX_SHAPES = {
|
|
292
360
|
react: {
|
|
293
361
|
attribute: "className",
|
|
294
|
-
dev: "__vxSxDev"
|
|
362
|
+
dev: "__vxSxDev",
|
|
363
|
+
inline: "camel"
|
|
295
364
|
},
|
|
296
365
|
preact: {
|
|
297
366
|
attribute: "className",
|
|
298
|
-
dev: "__vxSxDev"
|
|
367
|
+
dev: "__vxSxDev",
|
|
368
|
+
inline: "camel"
|
|
299
369
|
},
|
|
300
370
|
solid: {
|
|
301
371
|
attribute: "class",
|
|
302
372
|
target: "__vxSxClass",
|
|
303
|
-
dev: "__vxSxDevClass"
|
|
373
|
+
dev: "__vxSxDevClass",
|
|
374
|
+
inline: "kebab"
|
|
304
375
|
},
|
|
305
376
|
vue: {
|
|
306
377
|
attribute: "class",
|
|
307
378
|
target: "__vxSxClass",
|
|
308
|
-
dev: "__vxSxDevClass"
|
|
379
|
+
dev: "__vxSxDevClass",
|
|
380
|
+
inline: "string"
|
|
309
381
|
},
|
|
310
382
|
svelte: {
|
|
311
383
|
attribute: "class",
|
|
312
384
|
target: "__vxSxString",
|
|
313
|
-
dev: "__vxSxDevString"
|
|
385
|
+
dev: "__vxSxDevString",
|
|
386
|
+
inline: "string"
|
|
314
387
|
},
|
|
315
388
|
astro: {
|
|
316
389
|
attribute: "class",
|
|
317
390
|
target: "__vxSxString",
|
|
318
|
-
dev: "__vxSxDevString"
|
|
391
|
+
dev: "__vxSxDevString",
|
|
392
|
+
inline: "string"
|
|
319
393
|
},
|
|
320
394
|
vanilla: {
|
|
321
395
|
attribute: "class",
|
|
322
396
|
target: "__vxSxString",
|
|
323
|
-
dev: "__vxSxDevString"
|
|
397
|
+
dev: "__vxSxDevString",
|
|
398
|
+
inline: "string"
|
|
324
399
|
}
|
|
325
400
|
};
|
|
401
|
+
/** The framework functions a compiled component sets `style` and `class` through. */
|
|
402
|
+
const VUE = /* @__PURE__ */ new Set([
|
|
403
|
+
"vue",
|
|
404
|
+
"vue/server-renderer",
|
|
405
|
+
"@vue/server-renderer"
|
|
406
|
+
]);
|
|
407
|
+
const SVELTE = /* @__PURE__ */ new Set(["svelte/internal/client", "svelte/internal/server"]);
|
|
408
|
+
const ASTRO = /^astro\/(?:runtime\/server(?:\/index\.js)?|compiler-runtime)$/;
|
|
409
|
+
/** Template tags that read `style` as an object of their own (kit's templates). */
|
|
410
|
+
const OBJECT_STYLE_TAGS = /^@menapo\/(?:markup|og)(?:\/|$)/;
|
|
326
411
|
/** D18: a folded `sx()` precomputes 2^n strings for n conditional arguments. */
|
|
327
412
|
const MAX_FOLD_CONDITIONS = 2;
|
|
328
413
|
/** Stands for a runtime value while the normaliser checks the rest of a function style. */
|
|
@@ -579,9 +664,11 @@ var Compiler = class {
|
|
|
579
664
|
/** Rewrites the runtime calls under `node`. */
|
|
580
665
|
rewrite(node, editor) {
|
|
581
666
|
walk(this.root, node, (child) => {
|
|
582
|
-
if (child
|
|
583
|
-
const name = runtimeName(this.root, child.callee);
|
|
667
|
+
if (this.pass.skip.has(child)) return false;
|
|
584
668
|
try {
|
|
669
|
+
if (editor === this.pass.edits) this.rewriteAttributes(child);
|
|
670
|
+
if (child.type !== "CallExpression") return true;
|
|
671
|
+
const name = runtimeName(this.root, child.callee);
|
|
585
672
|
if (name === void 0) return !this.foldRecipeCall(child, editor);
|
|
586
673
|
return !this.rewriteCall(child, name, editor);
|
|
587
674
|
} catch (error) {
|
|
@@ -625,7 +712,7 @@ var Compiler = class {
|
|
|
625
712
|
case "keyframes":
|
|
626
713
|
case "theme": {
|
|
627
714
|
const rule = this.compileRule(root, name, call.callee, call.arguments, evaluateHere);
|
|
628
|
-
const text = name === "theme" ?
|
|
715
|
+
const text = name === "theme" ? this.styled(styleLiteral(rule.id, this.emit)) : JSON.stringify(rule.id);
|
|
629
716
|
this.replace(editor, call.start, call.end, text);
|
|
630
717
|
return true;
|
|
631
718
|
}
|
|
@@ -649,12 +736,361 @@ var Compiler = class {
|
|
|
649
736
|
case "group":
|
|
650
737
|
case "peer": {
|
|
651
738
|
const marker = this.markerOf(root, name, call.callee, call.arguments, evaluateHere);
|
|
652
|
-
this.replace(editor, call.start, call.end,
|
|
739
|
+
this.replace(editor, call.start, call.end, this.styled(styleLiteral(marker.className, this.emit)));
|
|
653
740
|
return true;
|
|
654
741
|
}
|
|
655
742
|
default: return false;
|
|
656
743
|
}
|
|
657
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* A `style=` whose value is a compiled style the compiler can read: its
|
|
747
|
+
* properties without a condition become the inline style, in the shape
|
|
748
|
+
* the element's framework takes, and the rest become classes merged into
|
|
749
|
+
* the element's own (core/attributes.ts says why a conditional property
|
|
750
|
+
* moves whole). Elements are found where each framework's output leaves
|
|
751
|
+
* them: JSX attributes, Vue's vnode props, Svelte's setters, and the
|
|
752
|
+
* templates of Vue's and Svelte's server output, Astro and plain JS.
|
|
753
|
+
*/
|
|
754
|
+
rewriteAttributes(node) {
|
|
755
|
+
switch (node.type) {
|
|
756
|
+
case "JSXOpeningElement":
|
|
757
|
+
this.jsxStyle(node);
|
|
758
|
+
return;
|
|
759
|
+
case "TaggedTemplateExpression": {
|
|
760
|
+
const tag = unwrap(node.tag);
|
|
761
|
+
const binding = tag.type === "Identifier" ? this.root.scopes.bindingOf(tag) : void 0;
|
|
762
|
+
if (binding?.kind === "import" && OBJECT_STYLE_TAGS.test(binding.source ?? "")) this.pass.objectStyles.add(node.quasi);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
case "TemplateLiteral":
|
|
766
|
+
if (!this.pass.objectStyles.has(node)) this.templateStyles(node);
|
|
767
|
+
return;
|
|
768
|
+
case "ObjectExpression":
|
|
769
|
+
if (this.root.vue) this.vueStyle(node);
|
|
770
|
+
return;
|
|
771
|
+
case "CallExpression":
|
|
772
|
+
this.svelteStyle(node);
|
|
773
|
+
return;
|
|
774
|
+
default: return;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
/** The framework function a callee names: `_normalizeStyle`, `$.set_style`, `$$addAttribute`. */
|
|
778
|
+
frameworkFunction(callee) {
|
|
779
|
+
const target = unwrap(callee);
|
|
780
|
+
const { scopes } = this.root;
|
|
781
|
+
if (target.type === "Identifier") {
|
|
782
|
+
const binding = scopes.bindingOf(target);
|
|
783
|
+
return binding?.kind === "import" && binding.source && binding.imported ? {
|
|
784
|
+
source: binding.source,
|
|
785
|
+
name: binding.imported
|
|
786
|
+
} : void 0;
|
|
787
|
+
}
|
|
788
|
+
if (target.type !== "MemberExpression") return void 0;
|
|
789
|
+
const object = unwrap(target.object);
|
|
790
|
+
const key = memberKey(target);
|
|
791
|
+
if (object.type !== "Identifier" || key === void 0) return void 0;
|
|
792
|
+
const binding = scopes.bindingOf(object);
|
|
793
|
+
return binding?.kind === "import" && binding.imported === "*" && binding.source ? {
|
|
794
|
+
source: binding.source,
|
|
795
|
+
name: key
|
|
796
|
+
} : void 0;
|
|
797
|
+
}
|
|
798
|
+
/** The argument of a call of one of `names`, from a source `sources` takes; undefined otherwise. */
|
|
799
|
+
argumentOf(node, sources, names, at = 0) {
|
|
800
|
+
const call = unwrap(node);
|
|
801
|
+
if (call.type !== "CallExpression") return void 0;
|
|
802
|
+
const fn = this.frameworkFunction(call.callee);
|
|
803
|
+
if (!fn || !names.includes(fn.name)) return void 0;
|
|
804
|
+
if (sources instanceof RegExp ? !sources.test(fn.source) : !sources.has(fn.source)) return;
|
|
805
|
+
const arg = call.arguments[at];
|
|
806
|
+
return arg && arg.type !== "SpreadElement" ? arg : void 0;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* The classes of a compiled style written as `style=`: a `css()` call or a
|
|
810
|
+
* binding of one (through Vue's `_unref(x)` and `$setup.x`), undefined for
|
|
811
|
+
* anything else, which the dev check reads at runtime (vite/attributes.ts).
|
|
812
|
+
*/
|
|
813
|
+
styleClasses(node) {
|
|
814
|
+
const inner = unwrap(node);
|
|
815
|
+
const name = inner.type === "CallExpression" ? runtimeName(this.root, inner.callee) : void 0;
|
|
816
|
+
if (!(inner.type === "Identifier" || inner.type === "MemberExpression" || name === "css" || name === "theme" || inner.type === "CallExpression" && isUnref(this.root, inner.callee, inner.arguments))) return void 0;
|
|
817
|
+
const value = attempt(() => evaluate(this.setupValue(inner) ?? inner, this.root.ctx));
|
|
818
|
+
return value?.result instanceof CompiledStyle ? value.result.classes : void 0;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Vue's dev output: the script's value that a separate render function
|
|
822
|
+
* reads as `$setup.x`, which `setup()` returns in `__returned__`
|
|
823
|
+
* (`{ card, … }`). A build inlines the template, where `x` is the binding.
|
|
824
|
+
*/
|
|
825
|
+
setupValue(node) {
|
|
826
|
+
if (!this.root.vue || node.type !== "MemberExpression") return void 0;
|
|
827
|
+
const object = unwrap(node.object);
|
|
828
|
+
const key = memberKey(node);
|
|
829
|
+
if (object.type !== "Identifier" || object.name !== "$setup" && object.name !== "_ctx") return;
|
|
830
|
+
let found;
|
|
831
|
+
walk(this.root, this.root.program, (child) => {
|
|
832
|
+
if (found) return false;
|
|
833
|
+
if (child.type !== "VariableDeclarator" || child.id.type !== "Identifier" || child.id.name !== "__returned__" || child.init?.type !== "ObjectExpression") return true;
|
|
834
|
+
for (const p of child.init.properties) if (p.type === "Property" && !p.computed && p.kind === "init" && patternKey(p) === key) found = p.value;
|
|
835
|
+
return false;
|
|
836
|
+
});
|
|
837
|
+
return found;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Writes a `style=` value `value` as an inline style in `shape` (`empty`
|
|
841
|
+
* where every property moved to classes) and merges the classes into
|
|
842
|
+
* `site`. Returns whether the value was a compiled style.
|
|
843
|
+
*/
|
|
844
|
+
writeStyle(value, shape, empty, site) {
|
|
845
|
+
const classes = this.styleClasses(value);
|
|
846
|
+
if (classes === void 0) return false;
|
|
847
|
+
const split = splitStyle(classes, (cls) => this.atomIndex.get(cls));
|
|
848
|
+
const text = split.declarations.length > 0 ? inlineStyle(split.declarations, shape) : empty;
|
|
849
|
+
this.replace(this.pass.edits, value.start, value.end, text);
|
|
850
|
+
this.pass.skip.add(value);
|
|
851
|
+
if (split.classes.length > 0) this.mergeClasses(site, split.classes.join(" "));
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
/** `classes` (a class list) merged into an element's class, last, as `sx(own, classes)` merges. */
|
|
855
|
+
mergeClasses(site, classes) {
|
|
856
|
+
const { edits } = this.pass;
|
|
857
|
+
const emitted = (list) => {
|
|
858
|
+
for (const cls of list) if (cls.startsWith("!")) this.names.add(cls.slice(1));
|
|
859
|
+
else this.pass.folded.add(cls);
|
|
860
|
+
return list.map(this.emit).join(" ");
|
|
861
|
+
};
|
|
862
|
+
if (site.kind === "none") {
|
|
863
|
+
edits.insert(site.at, site.write(emitted(mergeClassNames([classes]))));
|
|
864
|
+
if (site.close) edits.insert(site.close.at, site.close.text);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (site.kind === "static") {
|
|
868
|
+
const own = splitClasses(site.text).map((cls) => "!" + cls).join(" ");
|
|
869
|
+
const merged = emitted(mergeClassNames([own, classes]));
|
|
870
|
+
this.replace(edits, site.start, site.end, site.write(merged));
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const known = this.known(site.node);
|
|
874
|
+
if (known !== void 0) {
|
|
875
|
+
this.replace(edits, site.node.start, site.node.end, JSON.stringify(emitted(mergeClassNames([known, classes]))));
|
|
876
|
+
this.pass.skip.add(site.node);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
for (const cls of splitClasses(classes)) this.pass.folded.add(cls);
|
|
880
|
+
const record = this.moduleConstant(styleLiteral(toRecord(classes), this.emit));
|
|
881
|
+
edits.insert(site.node.start, `${this.helper("__vxClassList")}(`);
|
|
882
|
+
edits.insert(site.node.end, `, ${record})`);
|
|
883
|
+
}
|
|
884
|
+
/** `text` as a module-level constant after the imports, by its name; inline when there are none. */
|
|
885
|
+
moduleConstant(text) {
|
|
886
|
+
const imports = this.root.program.body.filter((node) => node.type === "ImportDeclaration");
|
|
887
|
+
if (imports.length === 0) return text;
|
|
888
|
+
const local = this.hoistName("__vxClasses");
|
|
889
|
+
this.pass.edits.insert(imports.at(-1).end, this.options.keepLines ? `;const ${local} = ${text};` : `\nconst ${local} = ${text};`);
|
|
890
|
+
return local;
|
|
891
|
+
}
|
|
892
|
+
/** JSX: `style={…}` and the element's `className` or `class` attribute. */
|
|
893
|
+
jsxStyle(element) {
|
|
894
|
+
const named = (...names) => element.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && names.includes(attr.name.name));
|
|
895
|
+
const style = named("style");
|
|
896
|
+
if (style?.value?.type !== "JSXExpressionContainer") return;
|
|
897
|
+
const value = style.value.expression;
|
|
898
|
+
if (value.type === "JSXEmptyExpression") return;
|
|
899
|
+
const own = named("className", "class");
|
|
900
|
+
let site;
|
|
901
|
+
if (!own) {
|
|
902
|
+
const attribute = this.shape.attribute;
|
|
903
|
+
site = {
|
|
904
|
+
kind: "none",
|
|
905
|
+
at: style.end,
|
|
906
|
+
write: (c) => ` ${attribute}={${JSON.stringify(c)}}`
|
|
907
|
+
};
|
|
908
|
+
} else if (own.value?.type === "Literal" && typeof own.value.value === "string") site = {
|
|
909
|
+
kind: "static",
|
|
910
|
+
start: own.value.start,
|
|
911
|
+
end: own.value.end,
|
|
912
|
+
text: own.value.value,
|
|
913
|
+
write: (c) => `{${JSON.stringify(c)}}`
|
|
914
|
+
};
|
|
915
|
+
else if (own.value?.type === "JSXExpressionContainer" && own.value.expression.type !== "JSXEmptyExpression") site = {
|
|
916
|
+
kind: "expr",
|
|
917
|
+
node: own.value.expression
|
|
918
|
+
};
|
|
919
|
+
else return;
|
|
920
|
+
this.writeStyle(value, this.shape.inline, "undefined", site);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Vue's render function: `{ class: …, style: _normalizeStyle(x) }`, the
|
|
924
|
+
* vnode's props, with the class as text or `_normalizeClass(…)`.
|
|
925
|
+
*/
|
|
926
|
+
vueStyle(props) {
|
|
927
|
+
const named = (name) => props.properties.find((p) => p.type === "Property" && !p.computed && (p.key.type === "Identifier" && p.key.name === name || p.key.type === "Literal" && p.key.value === name));
|
|
928
|
+
const style = named("style");
|
|
929
|
+
const value = style && this.argumentOf(style.value, VUE, ["normalizeStyle"]);
|
|
930
|
+
if (!style || !value) return;
|
|
931
|
+
const own = named("class");
|
|
932
|
+
let site;
|
|
933
|
+
if (!own) site = {
|
|
934
|
+
kind: "none",
|
|
935
|
+
at: style.start,
|
|
936
|
+
write: (c) => `class: ${JSON.stringify(c)}, `
|
|
937
|
+
};
|
|
938
|
+
else if (own.value.type === "Literal" && typeof own.value.value === "string") {
|
|
939
|
+
const literal = own.value;
|
|
940
|
+
site = {
|
|
941
|
+
kind: "static",
|
|
942
|
+
start: literal.start,
|
|
943
|
+
end: literal.end,
|
|
944
|
+
text: literal.value,
|
|
945
|
+
write: (c) => JSON.stringify(c)
|
|
946
|
+
};
|
|
947
|
+
} else site = {
|
|
948
|
+
kind: "expr",
|
|
949
|
+
node: this.argumentOf(own.value, VUE, ["normalizeClass"]) ?? own.value
|
|
950
|
+
};
|
|
951
|
+
this.writeStyle(value, "string", "undefined", site);
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Svelte's client output: `$.set_style(node, x)`, with the same node's
|
|
955
|
+
* `$.set_class(node, 1, …)` when the class is dynamic; a static class
|
|
956
|
+
* lives in the template's HTML, which `classList.add` keeps.
|
|
957
|
+
*/
|
|
958
|
+
svelteStyle(call) {
|
|
959
|
+
const value = this.argumentOf(call, SVELTE, ["set_style"], 1);
|
|
960
|
+
const [element] = call.arguments;
|
|
961
|
+
if (!value || element?.type !== "Identifier") return;
|
|
962
|
+
const binding = this.root.scopes.bindingOf(element);
|
|
963
|
+
const own = binding && this.svelteClasses().get(binding);
|
|
964
|
+
const name = element.name;
|
|
965
|
+
const site = own ? {
|
|
966
|
+
kind: "expr",
|
|
967
|
+
node: own
|
|
968
|
+
} : {
|
|
969
|
+
kind: "none",
|
|
970
|
+
at: call.start,
|
|
971
|
+
write: (c) => `(${name}.classList.add(...${JSON.stringify(c)}.split(" ")), `,
|
|
972
|
+
close: {
|
|
973
|
+
at: call.end,
|
|
974
|
+
text: ")"
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
this.writeStyle(value, "string", "undefined", site);
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Each node's class in Svelte's client output, by the node's binding: the
|
|
981
|
+
* value its `$.set_class(node, 1, …)` sets (inside `$.clsx(…)`). Read
|
|
982
|
+
* once per pass, for every `$.set_style` of the module.
|
|
983
|
+
*/
|
|
984
|
+
svelteClasses() {
|
|
985
|
+
if (this.pass.svelteClasses) return this.pass.svelteClasses;
|
|
986
|
+
const { scopes } = this.root;
|
|
987
|
+
const out = /* @__PURE__ */ new Map();
|
|
988
|
+
walk(this.root, this.root.program, (node) => {
|
|
989
|
+
if (node.type !== "CallExpression") return true;
|
|
990
|
+
const target = node.arguments[0];
|
|
991
|
+
const cls = this.argumentOf(node, SVELTE, ["set_class"], 2);
|
|
992
|
+
const binding = target?.type === "Identifier" ? scopes.bindingOf(target) : void 0;
|
|
993
|
+
if (cls && binding && !out.has(binding)) out.set(binding, this.argumentOf(cls, SVELTE, ["clsx"]) ?? cls);
|
|
994
|
+
return true;
|
|
995
|
+
});
|
|
996
|
+
return this.pass.svelteClasses = out;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* A template literal's tags: `style="${x}"` (plain JS, Vue's server
|
|
1000
|
+
* output through `_ssrRenderStyle(x)`), `${$.attr_style(x)}` (Svelte's
|
|
1001
|
+
* server output) and `${$$addAttribute(x, "style")}` (Astro), with the
|
|
1002
|
+
* tag's class written in the same three ways or as text.
|
|
1003
|
+
*/
|
|
1004
|
+
templateStyles(template) {
|
|
1005
|
+
const { quasis, expressions } = template;
|
|
1006
|
+
if (expressions.length === 0) return;
|
|
1007
|
+
const raws = quasis.map((quasi) => quasi.value.raw);
|
|
1008
|
+
const shift = quasis[0].start === template.start ? 1 : 0;
|
|
1009
|
+
const startOf = (k) => quasis[k].start + shift;
|
|
1010
|
+
const ATTRIBUTE = /\s(style|class)=("?)([^"<>]*)$/;
|
|
1011
|
+
/** An expression's attribute, as the text before it or the call around it names it. */
|
|
1012
|
+
const attributeOf = (i) => {
|
|
1013
|
+
const expression = expressions[i];
|
|
1014
|
+
const before = ATTRIBUTE.exec(raws[i]);
|
|
1015
|
+
if (before) {
|
|
1016
|
+
const quoted = before[2] === "\"";
|
|
1017
|
+
if (before[1] === "style" && (before[3] !== "" || quoted && raws[i + 1][0] !== "\"")) return;
|
|
1018
|
+
const name = before[1];
|
|
1019
|
+
return {
|
|
1020
|
+
name,
|
|
1021
|
+
value: this.argumentOf(expression, VUE, name === "style" ? ["ssrRenderStyle"] : ["ssrRenderClass"]) ?? expression,
|
|
1022
|
+
quoted
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
const svelte = this.argumentOf(expression, SVELTE, ["attr_style"]);
|
|
1026
|
+
if (svelte) return {
|
|
1027
|
+
name: "style",
|
|
1028
|
+
value: svelte,
|
|
1029
|
+
quoted: false
|
|
1030
|
+
};
|
|
1031
|
+
const svelteClass = this.argumentOf(expression, SVELTE, ["attr_class"]);
|
|
1032
|
+
if (svelteClass) return {
|
|
1033
|
+
name: "class",
|
|
1034
|
+
value: this.argumentOf(svelteClass, SVELTE, ["clsx"]) ?? svelteClass,
|
|
1035
|
+
quoted: false
|
|
1036
|
+
};
|
|
1037
|
+
const call = unwrap(expression);
|
|
1038
|
+
const astro = this.argumentOf(expression, ASTRO, ["addAttribute"]);
|
|
1039
|
+
const key = call.type === "CallExpression" ? call.arguments[1] : void 0;
|
|
1040
|
+
if (astro && key?.type === "Literal" && (key.value === "style" || key.value === "class")) return {
|
|
1041
|
+
name: key.value,
|
|
1042
|
+
value: astro,
|
|
1043
|
+
quoted: false
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
for (let i = 0; i < expressions.length; i++) {
|
|
1047
|
+
const style = attributeOf(i);
|
|
1048
|
+
if (style?.name !== "style") continue;
|
|
1049
|
+
let first = i;
|
|
1050
|
+
while (first > 0 && !raws[first].includes("<")) first--;
|
|
1051
|
+
let last = i + 1;
|
|
1052
|
+
while (last < raws.length - 1 && !raws[last].includes(">")) last++;
|
|
1053
|
+
const from = raws[first].lastIndexOf("<");
|
|
1054
|
+
const to = raws[last].indexOf(">");
|
|
1055
|
+
let site;
|
|
1056
|
+
for (let j = first; j < last && !site; j++) {
|
|
1057
|
+
const attribute = j === i ? void 0 : attributeOf(j);
|
|
1058
|
+
if (attribute?.name === "class") site = {
|
|
1059
|
+
kind: "expr",
|
|
1060
|
+
node: attribute.value
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
for (let k = first; k <= last && !site; k++) {
|
|
1064
|
+
const text = raws[k];
|
|
1065
|
+
const start = k === first ? from : 0;
|
|
1066
|
+
const end = k === last && to !== -1 ? to : text.length;
|
|
1067
|
+
const match = /\sclass="([^"]*)"/.exec(text.slice(start, end));
|
|
1068
|
+
if (match) {
|
|
1069
|
+
const at = startOf(k) + start + match.index + match[0].indexOf("\"") + 1;
|
|
1070
|
+
site = {
|
|
1071
|
+
kind: "static",
|
|
1072
|
+
start: at,
|
|
1073
|
+
end: at + match[1].length,
|
|
1074
|
+
text: match[1],
|
|
1075
|
+
write: (c) => templateText(c)
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
const at = startOf(i + 1) + (style.quoted ? 1 : 0);
|
|
1080
|
+
site ??= {
|
|
1081
|
+
kind: "none",
|
|
1082
|
+
at,
|
|
1083
|
+
write: (c) => ` class="${templateText(c)}"`
|
|
1084
|
+
};
|
|
1085
|
+
if (site.kind === "static" && site.start === site.end) site = {
|
|
1086
|
+
kind: "none",
|
|
1087
|
+
at: site.start,
|
|
1088
|
+
write: (c) => templateText(c)
|
|
1089
|
+
};
|
|
1090
|
+
const empty = style.value === expressions[i] ? "\"\"" : "undefined";
|
|
1091
|
+
this.writeStyle(style.value, "string", empty, site);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
658
1094
|
/** Whether `offset` lies in a function (or a class field), which may run many times. */
|
|
659
1095
|
inFunction(offset) {
|
|
660
1096
|
const { root } = this;
|
|
@@ -671,12 +1107,12 @@ var Compiler = class {
|
|
|
671
1107
|
}
|
|
672
1108
|
return root.functions.some(([start, end]) => offset > start && offset < end);
|
|
673
1109
|
}
|
|
674
|
-
/** A module-level name for a hoisted recipe, unused in the module. */
|
|
675
|
-
hoistName() {
|
|
1110
|
+
/** A module-level name for a hoisted recipe (or `base`), unused in the module. */
|
|
1111
|
+
hoistName(base = "__vxRecipeTable") {
|
|
676
1112
|
const { code } = this.root;
|
|
677
1113
|
let name;
|
|
678
1114
|
do
|
|
679
|
-
name =
|
|
1115
|
+
name = `${base}${++this.pass.hoisted}`;
|
|
680
1116
|
while (code.includes(name));
|
|
681
1117
|
return name;
|
|
682
1118
|
}
|
|
@@ -727,7 +1163,7 @@ var Compiler = class {
|
|
|
727
1163
|
const classes = resolveRecipe(recipe, props.build(picks), fail);
|
|
728
1164
|
const styles = slots ? Object.values(classes) : [classes];
|
|
729
1165
|
for (const style of styles) for (const cls of Object.values(style)) this.pass.folded.add(cls);
|
|
730
|
-
leaves.push(slots ? Object.fromEntries(slots.map((slot) => [slot, styleLiteral(classes[slot], this.emit)])) : { "": styleLiteral(classes, this.emit) });
|
|
1166
|
+
leaves.push(slots ? Object.fromEntries(slots.map((slot) => [slot, this.styled(styleLiteral(classes[slot], this.emit))])) : { "": this.styled(styleLiteral(classes, this.emit)) });
|
|
731
1167
|
}
|
|
732
1168
|
const tests = props.tests.map((test) => this.testText(test));
|
|
733
1169
|
const choose = (texts, depth = 0, at = 0) => {
|
|
@@ -828,7 +1264,7 @@ var Compiler = class {
|
|
|
828
1264
|
const style = this.compileStyle(this.root, call.callee, call.arguments);
|
|
829
1265
|
const { fn, vars } = style;
|
|
830
1266
|
if (!fn || !vars) {
|
|
831
|
-
this.replace(editor, call.start, call.end,
|
|
1267
|
+
this.replace(editor, call.start, call.end, this.styled(styleLiteral(style.classes, this.emit)));
|
|
832
1268
|
return;
|
|
833
1269
|
}
|
|
834
1270
|
const constant = this.localName("__vxStyle");
|
|
@@ -848,7 +1284,16 @@ var Compiler = class {
|
|
|
848
1284
|
body = `((${picks.join(", ")}) => (${style$}))(${palettes.map((p) => this.paletteClass(p.prefix, p.value)).join(", ")})`;
|
|
849
1285
|
}
|
|
850
1286
|
this.replace(editor, fn.body.start, fn.body.end, body);
|
|
851
|
-
this.replace(editor, fn.end, call.end, `)(${styleLiteral(style.classes, this.emit)})`);
|
|
1287
|
+
this.replace(editor, fn.end, call.end, `)(${this.styled(styleLiteral(style.classes, this.emit))})`);
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* A compiled style literal as the output writes it: on the prototype
|
|
1291
|
+
* every compiled style shares, whose `toString` is its class attribute
|
|
1292
|
+
* (runtime/style.ts), so `class={card}` works. One call per constant, a
|
|
1293
|
+
* few bytes, and pure, so an unused constant still drops.
|
|
1294
|
+
*/
|
|
1295
|
+
styled(literal) {
|
|
1296
|
+
return `/*#__PURE__*/${this.helper("__vxStyled")}(${literal})`;
|
|
852
1297
|
}
|
|
853
1298
|
/** A name for a binding the output adds, unused in the module. */
|
|
854
1299
|
localName(base) {
|
|
@@ -2027,7 +2472,9 @@ function newPass(code, keepLines) {
|
|
|
2027
2472
|
helpers: /* @__PURE__ */ new Map(),
|
|
2028
2473
|
hoisted: 0,
|
|
2029
2474
|
devSx: false,
|
|
2030
|
-
errors: []
|
|
2475
|
+
errors: [],
|
|
2476
|
+
skip: /* @__PURE__ */ new Set(),
|
|
2477
|
+
objectStyles: /* @__PURE__ */ new Set()
|
|
2031
2478
|
};
|
|
2032
2479
|
}
|
|
2033
2480
|
function notSearched(source) {
|