@craft-ts/style 0.7.0-beta.15

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 (73) hide show
  1. package/package.json +41 -0
  2. package/src/index.d.ts +18 -0
  3. package/src/index.d.ts.map +1 -0
  4. package/src/index.js +18 -0
  5. package/src/index.js.map +1 -0
  6. package/src/lib/axes/define.d.ts +115 -0
  7. package/src/lib/axes/define.d.ts.map +1 -0
  8. package/src/lib/axes/define.js +81 -0
  9. package/src/lib/axes/define.js.map +1 -0
  10. package/src/lib/axes/index.d.ts +4 -0
  11. package/src/lib/axes/index.d.ts.map +1 -0
  12. package/src/lib/axes/index.js +4 -0
  13. package/src/lib/axes/index.js.map +1 -0
  14. package/src/lib/axes/standard.d.ts +85 -0
  15. package/src/lib/axes/standard.d.ts.map +1 -0
  16. package/src/lib/axes/standard.js +109 -0
  17. package/src/lib/axes/standard.js.map +1 -0
  18. package/src/lib/axes/types.d.ts +67 -0
  19. package/src/lib/axes/types.d.ts.map +1 -0
  20. package/src/lib/axes/types.js +21 -0
  21. package/src/lib/axes/types.js.map +1 -0
  22. package/src/lib/css-vars.d.ts +100 -0
  23. package/src/lib/css-vars.d.ts.map +1 -0
  24. package/src/lib/css-vars.js +96 -0
  25. package/src/lib/css-vars.js.map +1 -0
  26. package/src/lib/kinds.d.ts +84 -0
  27. package/src/lib/kinds.d.ts.map +1 -0
  28. package/src/lib/kinds.js +44 -0
  29. package/src/lib/kinds.js.map +1 -0
  30. package/src/lib/obligations.d.ts +99 -0
  31. package/src/lib/obligations.d.ts.map +1 -0
  32. package/src/lib/obligations.js +69 -0
  33. package/src/lib/obligations.js.map +1 -0
  34. package/src/lib/props/factory.d.ts +61 -0
  35. package/src/lib/props/factory.d.ts.map +1 -0
  36. package/src/lib/props/factory.js +35 -0
  37. package/src/lib/props/factory.js.map +1 -0
  38. package/src/lib/props/generated.d.ts +974 -0
  39. package/src/lib/props/generated.d.ts.map +1 -0
  40. package/src/lib/props/generated.js +2478 -0
  41. package/src/lib/props/generated.js.map +1 -0
  42. package/src/lib/props/index.d.ts +37 -0
  43. package/src/lib/props/index.d.ts.map +1 -0
  44. package/src/lib/props/index.js +27 -0
  45. package/src/lib/props/index.js.map +1 -0
  46. package/src/lib/styles.d.ts +201 -0
  47. package/src/lib/styles.d.ts.map +1 -0
  48. package/src/lib/styles.js +247 -0
  49. package/src/lib/styles.js.map +1 -0
  50. package/src/lib/tokens/index.d.ts +4 -0
  51. package/src/lib/tokens/index.d.ts.map +1 -0
  52. package/src/lib/tokens/index.js +4 -0
  53. package/src/lib/tokens/index.js.map +1 -0
  54. package/src/lib/tokens/palette.d.ts +97 -0
  55. package/src/lib/tokens/palette.d.ts.map +1 -0
  56. package/src/lib/tokens/palette.js +61 -0
  57. package/src/lib/tokens/palette.js.map +1 -0
  58. package/src/lib/tokens/scales.d.ts +47 -0
  59. package/src/lib/tokens/scales.d.ts.map +1 -0
  60. package/src/lib/tokens/scales.js +51 -0
  61. package/src/lib/tokens/scales.js.map +1 -0
  62. package/src/lib/tokens/units.d.ts +114 -0
  63. package/src/lib/tokens/units.d.ts.map +1 -0
  64. package/src/lib/tokens/units.js +54 -0
  65. package/src/lib/tokens/units.js.map +1 -0
  66. package/src/plugin/emit.d.ts +64 -0
  67. package/src/plugin/emit.d.ts.map +1 -0
  68. package/src/plugin/emit.js +134 -0
  69. package/src/plugin/emit.js.map +1 -0
  70. package/src/plugin/vite.d.ts +66 -0
  71. package/src/plugin/vite.d.ts.map +1 -0
  72. package/src/plugin/vite.js +150 -0
  73. package/src/plugin/vite.js.map +1 -0
@@ -0,0 +1,134 @@
1
+ import { propertyRule } from "../lib/css-vars.js";
2
+ import { prop } from "../lib/props/generated.js";
3
+ import { clipOverflow, containerType, noClipping, scrollPort, } from "../lib/obligations.js";
4
+ /** The layer order is fixed here so that no import order can change it. */
5
+ export const LAYERS = [
6
+ 'reset',
7
+ 'tokens',
8
+ 'components',
9
+ 'variants',
10
+ 'overrides',
11
+ ];
12
+ /**
13
+ * Properties the vocabulary owns.
14
+ *
15
+ * The generated table, plus the ones only an obligation can write. `overflow`
16
+ * is in the second set and not the first on purpose: it is reachable through
17
+ * `provides(scrollPort.block)` and through nothing else.
18
+ */
19
+ export const knownProperties = () => {
20
+ const known = new Set(Object.values(prop));
21
+ const specs = [
22
+ ...Object.values(scrollPort),
23
+ ...Object.values(noClipping),
24
+ ...Object.values(containerType),
25
+ ...Object.values(clipOverflow).map((entry) => entry.spec),
26
+ ];
27
+ for (const spec of specs) {
28
+ for (const declaration of spec.effect)
29
+ known.add(declaration.property);
30
+ }
31
+ return known;
32
+ };
33
+ export class UnknownCssError extends Error {
34
+ property;
35
+ source;
36
+ constructor(property, source) {
37
+ super(`craft-style: '${property}' is not a property of the vocabulary (emitted from ${source}). Nothing in the generated table produces it, so it reached the emitter through an escape hatch. Add it to the table or route it through an obligation.`);
38
+ this.property = property;
39
+ this.source = source;
40
+ }
41
+ }
42
+ /**
43
+ * The last net under the escape hatches.
44
+ *
45
+ * A keyword or property that slipped past the types would otherwise become CSS
46
+ * the browser silently drops — the exact failure the package exists to prevent,
47
+ * arriving at the last possible moment.
48
+ */
49
+ export function validateAtoms(atoms, source = 'the style registry') {
50
+ const known = knownProperties();
51
+ for (const atom of atoms) {
52
+ // A custom property is declared by `cssVars`, which already registered its
53
+ // `@property` block; the table has nothing to say about it.
54
+ if (atom.property.startsWith('--'))
55
+ continue;
56
+ if (!known.has(atom.property)) {
57
+ throw new UnknownCssError(atom.property, source);
58
+ }
59
+ }
60
+ }
61
+ const escapeClass = (className) => `.${className.replace(/([^a-zA-Z0-9_-])/g, '\\$1')}`;
62
+ /** An at-rule condition nests around the rule; a selector fragment joins it. */
63
+ const isAtRule = (point) => point.open.startsWith('@');
64
+ function ruleText(atom) {
65
+ const selector = atom.conditions
66
+ .filter((point) => !isAtRule(point))
67
+ .reduce((current, point) => point.open.replace('&', current), escapeClass(atom.className));
68
+ const body = `${selector}{${atom.property}:${atom.value}}`;
69
+ return atom.conditions
70
+ .filter(isAtRule)
71
+ .reduceRight((inner, point) => `${point.open}{${inner}}`, body);
72
+ }
73
+ const byName = (left, right) => left.className.localeCompare(right.className);
74
+ /**
75
+ * The stylesheet.
76
+ *
77
+ * Unconditional atoms land in `components`, conditional ones in `variants`, so
78
+ * a variant always wins over the base without anyone counting selector
79
+ * specificity. Ordering is by class name rather than by registration order:
80
+ * the output must not depend on which module the bundler happened to load
81
+ * first, or two identical builds would produce two different files.
82
+ */
83
+ export function renderCss(atoms, vars) {
84
+ const base = atoms
85
+ .filter((atom) => atom.conditions.length === 0)
86
+ .sort(byName);
87
+ const variants = atoms
88
+ .filter((atom) => atom.conditions.length > 0)
89
+ .sort(byName);
90
+ const properties = [...vars]
91
+ .sort((left, right) => left.name.localeCompare(right.name))
92
+ .map(propertyRule);
93
+ const sections = [
94
+ `@layer ${LAYERS.join(', ')};`,
95
+ properties.length ? `@layer tokens{${properties.join('')}}` : '',
96
+ base.length ? `@layer components{${base.map(ruleText).join('')}}` : '',
97
+ variants.length
98
+ ? `@layer variants{${variants.map(ruleText).join('')}}`
99
+ : '',
100
+ ];
101
+ return sections.filter(Boolean).join('\n') + '\n';
102
+ }
103
+ /**
104
+ * What the dependency graph consumes.
105
+ *
106
+ * Emitted by the plugin rather than re-derived by an AST pass: there is one
107
+ * graph and two producers, and a second, approximate evaluation of the DSL
108
+ * would disagree with this one sooner or later.
109
+ */
110
+ export function styleDump(classes, atoms, vars) {
111
+ return {
112
+ classes: [...classes]
113
+ .sort((left, right) => left.key.localeCompare(right.key))
114
+ .map((registered) => ({
115
+ key: registered.key,
116
+ className: registered.className,
117
+ axes: registered.axes,
118
+ atoms: registered.rules.map((rule) => rule.className),
119
+ unproven: registered.unproven,
120
+ requires: registered.requires,
121
+ provides: registered.provides,
122
+ violates: registered.violates,
123
+ })),
124
+ atoms: [...atoms].sort(byName).map((atom) => ({
125
+ className: atom.className,
126
+ property: atom.property,
127
+ value: atom.value,
128
+ conditions: atom.conditions.map((point) => `${point.axis}:${point.point}`),
129
+ unproven: atom.unproven,
130
+ })),
131
+ vars: [...vars].sort((left, right) => left.name.localeCompare(right.name)),
132
+ };
133
+ }
134
+ //# sourceMappingURL=emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.js","sourceRoot":"","sources":["../../../../../libs/style/src/plugin/emit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Turning the registry into CSS.\n *\n * This module is pure: registry in, stylesheet out. It never touches the\n * filesystem and never asks the typechecker anything — the types verify, the\n * values emit. Everything that reads files lives in `vite.ts`.\n *\n * Deduplication happened upstream, when the atoms were registered: two\n * components writing `padding: 1rem` under the same condition already share one\n * atom. What is left here is ordering, wrapping, and the last safety net —\n * validating that every property about to be written is one the vocabulary\n * actually owns.\n */\nimport type { AnyAxisPoint } from '../lib/axes/index.ts';\nimport type { AtomicRule, RegisteredClass } from '../lib/styles.ts';\nimport type { CssVarDeclaration } from '../lib/css-vars.ts';\nimport { propertyRule } from '../lib/css-vars.ts';\nimport { prop } from '../lib/props/generated.ts';\nimport {\n clipOverflow,\n containerType,\n noClipping,\n scrollPort,\n} from '../lib/obligations.ts';\n\n/** The layer order is fixed here so that no import order can change it. */\nexport const LAYERS = [\n 'reset',\n 'tokens',\n 'components',\n 'variants',\n 'overrides',\n] as const;\n\n/**\n * Properties the vocabulary owns.\n *\n * The generated table, plus the ones only an obligation can write. `overflow`\n * is in the second set and not the first on purpose: it is reachable through\n * `provides(scrollPort.block)` and through nothing else.\n */\nexport const knownProperties = (): ReadonlySet<string> => {\n const known = new Set<string>(Object.values(prop));\n const specs = [\n ...Object.values(scrollPort),\n ...Object.values(noClipping),\n ...Object.values(containerType),\n ...Object.values(clipOverflow).map((entry) => entry.spec),\n ];\n for (const spec of specs) {\n for (const declaration of spec.effect) known.add(declaration.property);\n }\n return known;\n};\n\nexport class UnknownCssError extends Error {\n readonly property: string;\n readonly source: string;\n\n constructor(property: string, source: string) {\n super(\n `craft-style: '${property}' is not a property of the vocabulary (emitted from ${source}). Nothing in the generated table produces it, so it reached the emitter through an escape hatch. Add it to the table or route it through an obligation.`,\n );\n this.property = property;\n this.source = source;\n }\n}\n\n/**\n * The last net under the escape hatches.\n *\n * A keyword or property that slipped past the types would otherwise become CSS\n * the browser silently drops — the exact failure the package exists to prevent,\n * arriving at the last possible moment.\n */\nexport function validateAtoms(\n atoms: readonly AtomicRule[],\n source = 'the style registry',\n): void {\n const known = knownProperties();\n for (const atom of atoms) {\n // A custom property is declared by `cssVars`, which already registered its\n // `@property` block; the table has nothing to say about it.\n if (atom.property.startsWith('--')) continue;\n if (!known.has(atom.property)) {\n throw new UnknownCssError(atom.property, source);\n }\n }\n}\n\nconst escapeClass = (className: string): string =>\n `.${className.replace(/([^a-zA-Z0-9_-])/g, '\\\\$1')}`;\n\n/** An at-rule condition nests around the rule; a selector fragment joins it. */\nconst isAtRule = (point: AnyAxisPoint): boolean => point.open.startsWith('@');\n\nfunction ruleText(atom: AtomicRule): string {\n const selector = atom.conditions\n .filter((point) => !isAtRule(point))\n .reduce(\n (current, point) => point.open.replace('&', current),\n escapeClass(atom.className),\n );\n const body = `${selector}{${atom.property}:${atom.value}}`;\n return atom.conditions\n .filter(isAtRule)\n .reduceRight((inner, point) => `${point.open}{${inner}}`, body);\n}\n\nconst byName = (left: AtomicRule, right: AtomicRule): number =>\n left.className.localeCompare(right.className);\n\n/**\n * The stylesheet.\n *\n * Unconditional atoms land in `components`, conditional ones in `variants`, so\n * a variant always wins over the base without anyone counting selector\n * specificity. Ordering is by class name rather than by registration order:\n * the output must not depend on which module the bundler happened to load\n * first, or two identical builds would produce two different files.\n */\nexport function renderCss(\n atoms: readonly AtomicRule[],\n vars: readonly CssVarDeclaration[],\n): string {\n const base = atoms\n .filter((atom) => atom.conditions.length === 0)\n .sort(byName);\n const variants = atoms\n .filter((atom) => atom.conditions.length > 0)\n .sort(byName);\n const properties = [...vars]\n .sort((left, right) => left.name.localeCompare(right.name))\n .map(propertyRule);\n\n const sections = [\n `@layer ${LAYERS.join(', ')};`,\n properties.length ? `@layer tokens{${properties.join('')}}` : '',\n base.length ? `@layer components{${base.map(ruleText).join('')}}` : '',\n variants.length\n ? `@layer variants{${variants.map(ruleText).join('')}}`\n : '',\n ];\n\n return sections.filter(Boolean).join('\\n') + '\\n';\n}\n\nexport interface StyleDump {\n readonly classes: readonly {\n readonly key: string;\n readonly className: string;\n readonly axes: Readonly<Record<string, readonly string[]>>;\n readonly atoms: readonly string[];\n readonly unproven: readonly string[];\n readonly requires: readonly string[];\n readonly provides: readonly string[];\n readonly violates: readonly string[];\n }[];\n readonly atoms: readonly {\n readonly className: string;\n readonly property: string;\n readonly value: string;\n readonly conditions: readonly string[];\n readonly unproven: string;\n }[];\n readonly vars: readonly CssVarDeclaration[];\n}\n\n/**\n * What the dependency graph consumes.\n *\n * Emitted by the plugin rather than re-derived by an AST pass: there is one\n * graph and two producers, and a second, approximate evaluation of the DSL\n * would disagree with this one sooner or later.\n */\nexport function styleDump(\n classes: readonly RegisteredClass[],\n atoms: readonly AtomicRule[],\n vars: readonly CssVarDeclaration[],\n): StyleDump {\n return {\n classes: [...classes]\n .sort((left, right) => left.key.localeCompare(right.key))\n .map((registered) => ({\n key: registered.key,\n className: registered.className,\n axes: registered.axes,\n atoms: registered.rules.map((rule) => rule.className),\n unproven: registered.unproven,\n requires: registered.requires,\n provides: registered.provides,\n violates: registered.violates,\n })),\n atoms: [...atoms].sort(byName).map((atom) => ({\n className: atom.className,\n property: atom.property,\n value: atom.value,\n conditions: atom.conditions.map(\n (point) => `${point.axis}:${point.point}`,\n ),\n unproven: atom.unproven,\n })),\n vars: [...vars].sort((left, right) => left.name.localeCompare(right.name)),\n };\n}\n"]}
@@ -0,0 +1,66 @@
1
+ import type { AtomicRule, RegisteredClass } from '../lib/styles.ts';
2
+ import type { CssVarDeclaration } from '../lib/css-vars.ts';
3
+ import { type StyleDump } from './emit.ts';
4
+ /**
5
+ * Re-exported so a consumer can build the graph dump without running a build —
6
+ * a test that already imported the sheets has the registry in hand.
7
+ */
8
+ export { renderCss, styleDump, validateAtoms, type StyleDump } from './emit.ts';
9
+ export declare const VIRTUAL_CSS_ID = "virtual:craft-style.css";
10
+ export interface CraftStyleOptions {
11
+ /** File suffix that marks a style module. */
12
+ readonly suffix?: string;
13
+ /** Directory names never walked into. */
14
+ readonly ignore?: readonly string[];
15
+ /** Where to write the graph dump. Skipped when absent. */
16
+ readonly dumpPath?: string;
17
+ /** Module aliases for the Node evaluation, e.g. a workspace source path. */
18
+ readonly alias?: Readonly<Record<string, string>>;
19
+ }
20
+ interface Registry {
21
+ readonly classes: readonly RegisteredClass[];
22
+ readonly atoms: readonly AtomicRule[];
23
+ readonly vars: readonly CssVarDeclaration[];
24
+ }
25
+ /** Sorted, so that two runs on the same tree evaluate in the same order. */
26
+ export declare function findStyleModules(root: string, suffix?: string, ignore?: readonly string[]): Promise<readonly string[]>;
27
+ /** Bundles the given style modules and reads the registry they filled. */
28
+ export declare function evaluateStyleModules(files: readonly string[], alias?: Readonly<Record<string, string>>): Promise<Registry>;
29
+ export interface EmitResult {
30
+ readonly css: string;
31
+ readonly dump: StyleDump;
32
+ }
33
+ export declare function emitStyles(files: readonly string[], alias?: Readonly<Record<string, string>>): Promise<EmitResult>;
34
+ /**
35
+ * Minimal shape of the Vite plugin object, declared here rather than imported.
36
+ *
37
+ * `vite` is a peer, not a dependency: a consumer building with something else
38
+ * can still call `emitStyles` without pulling Vite's types in.
39
+ */
40
+ export interface CraftStylePlugin {
41
+ readonly name: string;
42
+ readonly enforce?: 'pre' | 'post';
43
+ configResolved?(config: {
44
+ readonly root: string;
45
+ }): void;
46
+ resolveId?(id: string): string | undefined;
47
+ load?(id: string): Promise<string | undefined>;
48
+ handleHotUpdate?(context: {
49
+ readonly file: string;
50
+ readonly server: {
51
+ readonly ws: {
52
+ send(message: {
53
+ readonly type: 'full-reload';
54
+ readonly path: '*';
55
+ }): void | Promise<void>;
56
+ };
57
+ };
58
+ }): void;
59
+ }
60
+ /**
61
+ * `import 'virtual:craft-style.css'` once, at the app entry, and the whole
62
+ * stylesheet arrives — built, not computed. No class is ever assembled at
63
+ * runtime, so what the browser gets is what the emitter proved.
64
+ */
65
+ export declare function craftStyle(options?: CraftStyleOptions): CraftStylePlugin;
66
+ //# sourceMappingURL=vite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../../../../libs/style/src/plugin/vite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * The Vite plugin: evaluate the style modules in Node, emit static CSS.
3
+ *
4
+ * The generation never goes through the typechecker API. The plugin imports the
5
+ * modules and reads the values they registered — which is why a `*.style.ts` is
6
+ * only allowed to import vocabulary (the `style-file-boundary` ESLint rule),
7
+ * and why importing one in Node is safe: there is no application code in it to
8
+ * run.
9
+ *
10
+ * Every style module is bundled into **one** synthetic entry before being
11
+ * imported. Importing them one by one would give each its own copy of the
12
+ * registry, and deduplication across files — the whole point of atomic
13
+ * emission — would quietly stop working.
14
+ *
15
+ * The bundler is Vite's own `build()` in SSR mode rather than esbuild called
16
+ * directly: Vite 8 no longer ships esbuild, and a plugin that reaches for a
17
+ * bundler its host does not have is a plugin that breaks on the next upgrade.
18
+ */
19
+ import { mkdtemp, readdir, rm, writeFile } from 'node:fs/promises';
20
+ import { createRequire } from 'node:module';
21
+ import { tmpdir } from 'node:os';
22
+ import { join } from 'node:path';
23
+ import { pathToFileURL } from 'node:url';
24
+ import { renderCss, styleDump, validateAtoms } from "./emit.js";
25
+ /**
26
+ * Re-exported so a consumer can build the graph dump without running a build —
27
+ * a test that already imported the sheets has the registry in hand.
28
+ */
29
+ export { renderCss, styleDump, validateAtoms } from "./emit.js";
30
+ export const VIRTUAL_CSS_ID = 'virtual:craft-style.css';
31
+ const RESOLVED_CSS_ID = '\0' + VIRTUAL_CSS_ID;
32
+ const DEFAULT_IGNORED = ['node_modules', 'dist', '.git', '.nx', 'tmp'];
33
+ /** Sorted, so that two runs on the same tree evaluate in the same order. */
34
+ export async function findStyleModules(root, suffix = '.style.ts', ignore = DEFAULT_IGNORED) {
35
+ const found = [];
36
+ const walk = async (directory) => {
37
+ const entries = await readdir(directory, { withFileTypes: true });
38
+ for (const entry of entries) {
39
+ const path = join(directory, entry.name);
40
+ if (entry.isDirectory()) {
41
+ if (!ignore.includes(entry.name) && !entry.name.startsWith('.')) {
42
+ await walk(path);
43
+ }
44
+ }
45
+ else if (entry.name.endsWith(suffix)) {
46
+ found.push(path);
47
+ }
48
+ }
49
+ };
50
+ await walk(root);
51
+ return found.sort();
52
+ }
53
+ /** Bundles the given style modules and reads the registry they filled. */
54
+ export async function evaluateStyleModules(files, alias = {}) {
55
+ const { build } = await import('vite');
56
+ const directory = await mkdtemp(join(tmpdir(), 'craft-style-'));
57
+ try {
58
+ const entry = join(directory, 'entry.mjs');
59
+ // Each module is imported as a **namespace** and re-exported, not imported
60
+ // for its side effects. `@craft-ts/style` is marked `sideEffects: false`,
61
+ // so a bare side-effect import lets the bundler drop the very calls the
62
+ // registry is built from — silently, and only for some of them.
63
+ const sorted = [...files].sort();
64
+ const imports = sorted
65
+ .map((file, index) => `import * as module${index} from ${JSON.stringify(pathToFileURL(file).href)};`)
66
+ .join('\n');
67
+ const held = sorted.map((_, index) => `module${index}`).join(', ');
68
+ await writeFile(entry, `${imports}\nexport const evaluated = [${held}];\nexport { registeredAtoms, registeredClasses, registeredVars } from '@craft-ts/style';\n`);
69
+ await build({
70
+ configFile: false,
71
+ logLevel: 'silent',
72
+ resolve: { alias: { ...alias } },
73
+ // Nothing may stay external: an externalised `@craft-ts/style` would be
74
+ // resolved again at import time and hand back a second, empty registry.
75
+ ssr: { noExternal: true },
76
+ build: {
77
+ ssr: entry,
78
+ outDir: directory,
79
+ emptyOutDir: false,
80
+ minify: false,
81
+ write: true,
82
+ rollupOptions: {
83
+ output: { format: 'cjs', entryFileNames: 'bundle.cjs' },
84
+ },
85
+ },
86
+ });
87
+ // Loaded through `require` rather than `import()`: a test runner sitting on
88
+ // top of this module intercepts dynamic imports and resolves them against
89
+ // its own module graph, where a file in a temp directory does not exist.
90
+ // `require` goes to the filesystem, in the runner and in a real build alike.
91
+ const module = createRequire(import.meta.url)(join(directory, 'bundle.cjs'));
92
+ return {
93
+ classes: module.registeredClasses(),
94
+ atoms: module.registeredAtoms(),
95
+ vars: module.registeredVars(),
96
+ };
97
+ }
98
+ finally {
99
+ await rm(directory, { recursive: true, force: true });
100
+ }
101
+ }
102
+ export async function emitStyles(files, alias) {
103
+ const registry = await evaluateStyleModules(files, alias);
104
+ validateAtoms(registry.atoms, files.join(', '));
105
+ return {
106
+ css: renderCss(registry.atoms, registry.vars),
107
+ dump: styleDump(registry.classes, registry.atoms, registry.vars),
108
+ };
109
+ }
110
+ /**
111
+ * `import 'virtual:craft-style.css'` once, at the app entry, and the whole
112
+ * stylesheet arrives — built, not computed. No class is ever assembled at
113
+ * runtime, so what the browser gets is what the emitter proved.
114
+ */
115
+ export function craftStyle(options = {}) {
116
+ let root = process.cwd();
117
+ let cached;
118
+ return {
119
+ name: 'craft-style',
120
+ enforce: 'pre',
121
+ configResolved(config) {
122
+ root = config.root;
123
+ },
124
+ resolveId(id) {
125
+ return id === VIRTUAL_CSS_ID ? RESOLVED_CSS_ID : undefined;
126
+ },
127
+ async load(id) {
128
+ if (id !== RESOLVED_CSS_ID)
129
+ return undefined;
130
+ const files = await findStyleModules(root, options.suffix, options.ignore);
131
+ cached ??= await emitStyles(files, options.alias);
132
+ if (options.dumpPath) {
133
+ await writeFile(options.dumpPath, JSON.stringify(cached.dump, null, 2) + '\n');
134
+ }
135
+ return cached.css;
136
+ },
137
+ handleHotUpdate(context) {
138
+ // A style module changed: the whole sheet is re-derived rather than
139
+ // patched. Atomic output is small and the emission is one bundle away —
140
+ // an incremental path here would be a second source of truth. The
141
+ // stylesheet is a virtual module imported once by the app entry, so
142
+ // invalidating the cache alone would leave the browser on old CSS.
143
+ if (context.file.endsWith(options.suffix ?? '.style.ts')) {
144
+ cached = undefined;
145
+ context.server.ws.send({ type: 'full-reload', path: '*' });
146
+ }
147
+ },
148
+ };
149
+ }
150
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","sourceRoot":"","sources":["../../../../../libs/style/src/plugin/vite.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Vite plugin: evaluate the style modules in Node, emit static CSS.\n *\n * The generation never goes through the typechecker API. The plugin imports the\n * modules and reads the values they registered — which is why a `*.style.ts` is\n * only allowed to import vocabulary (the `style-file-boundary` ESLint rule),\n * and why importing one in Node is safe: there is no application code in it to\n * run.\n *\n * Every style module is bundled into **one** synthetic entry before being\n * imported. Importing them one by one would give each its own copy of the\n * registry, and deduplication across files — the whole point of atomic\n * emission — would quietly stop working.\n *\n * The bundler is Vite's own `build()` in SSR mode rather than esbuild called\n * directly: Vite 8 no longer ships esbuild, and a plugin that reaches for a\n * bundler its host does not have is a plugin that breaks on the next upgrade.\n */\nimport { mkdtemp, readdir, rm, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { AtomicRule, RegisteredClass } from '../lib/styles.ts';\nimport type { CssVarDeclaration } from '../lib/css-vars.ts';\nimport { renderCss, styleDump, validateAtoms, type StyleDump } from './emit.ts';\n\n/**\n * Re-exported so a consumer can build the graph dump without running a build —\n * a test that already imported the sheets has the registry in hand.\n */\nexport { renderCss, styleDump, validateAtoms, type StyleDump } from './emit.ts';\n\nexport const VIRTUAL_CSS_ID = 'virtual:craft-style.css';\nconst RESOLVED_CSS_ID = '\\0' + VIRTUAL_CSS_ID;\n\nconst DEFAULT_IGNORED = ['node_modules', 'dist', '.git', '.nx', 'tmp'];\n\nexport interface CraftStyleOptions {\n /** File suffix that marks a style module. */\n readonly suffix?: string;\n /** Directory names never walked into. */\n readonly ignore?: readonly string[];\n /** Where to write the graph dump. Skipped when absent. */\n readonly dumpPath?: string;\n /** Module aliases for the Node evaluation, e.g. a workspace source path. */\n readonly alias?: Readonly<Record<string, string>>;\n}\n\ninterface Registry {\n readonly classes: readonly RegisteredClass[];\n readonly atoms: readonly AtomicRule[];\n readonly vars: readonly CssVarDeclaration[];\n}\n\n/** Sorted, so that two runs on the same tree evaluate in the same order. */\nexport async function findStyleModules(\n root: string,\n suffix = '.style.ts',\n ignore: readonly string[] = DEFAULT_IGNORED,\n): Promise<readonly string[]> {\n const found: string[] = [];\n const walk = async (directory: string): Promise<void> => {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) {\n if (!ignore.includes(entry.name) && !entry.name.startsWith('.')) {\n await walk(path);\n }\n } else if (entry.name.endsWith(suffix)) {\n found.push(path);\n }\n }\n };\n await walk(root);\n return found.sort();\n}\n\n/** Bundles the given style modules and reads the registry they filled. */\nexport async function evaluateStyleModules(\n files: readonly string[],\n alias: Readonly<Record<string, string>> = {},\n): Promise<Registry> {\n const { build } = await import('vite');\n const directory = await mkdtemp(join(tmpdir(), 'craft-style-'));\n try {\n const entry = join(directory, 'entry.mjs');\n // Each module is imported as a **namespace** and re-exported, not imported\n // for its side effects. `@craft-ts/style` is marked `sideEffects: false`,\n // so a bare side-effect import lets the bundler drop the very calls the\n // registry is built from — silently, and only for some of them.\n const sorted = [...files].sort();\n const imports = sorted\n .map(\n (file, index) =>\n `import * as module${index} from ${JSON.stringify(pathToFileURL(file).href)};`,\n )\n .join('\\n');\n const held = sorted.map((_, index) => `module${index}`).join(', ');\n await writeFile(\n entry,\n `${imports}\\nexport const evaluated = [${held}];\\nexport { registeredAtoms, registeredClasses, registeredVars } from '@craft-ts/style';\\n`,\n );\n\n await build({\n configFile: false,\n logLevel: 'silent',\n resolve: { alias: { ...alias } },\n // Nothing may stay external: an externalised `@craft-ts/style` would be\n // resolved again at import time and hand back a second, empty registry.\n ssr: { noExternal: true },\n build: {\n ssr: entry,\n outDir: directory,\n emptyOutDir: false,\n minify: false,\n write: true,\n rollupOptions: {\n output: { format: 'cjs', entryFileNames: 'bundle.cjs' },\n },\n },\n });\n\n // Loaded through `require` rather than `import()`: a test runner sitting on\n // top of this module intercepts dynamic imports and resolves them against\n // its own module graph, where a file in a temp directory does not exist.\n // `require` goes to the filesystem, in the runner and in a real build alike.\n const module = createRequire(import.meta.url)(\n join(directory, 'bundle.cjs'),\n ) as {\n registeredClasses(): readonly RegisteredClass[];\n registeredAtoms(): readonly AtomicRule[];\n registeredVars(): readonly CssVarDeclaration[];\n };\n return {\n classes: module.registeredClasses(),\n atoms: module.registeredAtoms(),\n vars: module.registeredVars(),\n };\n } finally {\n await rm(directory, { recursive: true, force: true });\n }\n}\n\nexport interface EmitResult {\n readonly css: string;\n readonly dump: StyleDump;\n}\n\nexport async function emitStyles(\n files: readonly string[],\n alias?: Readonly<Record<string, string>>,\n): Promise<EmitResult> {\n const registry = await evaluateStyleModules(files, alias);\n validateAtoms(registry.atoms, files.join(', '));\n return {\n css: renderCss(registry.atoms, registry.vars),\n dump: styleDump(registry.classes, registry.atoms, registry.vars),\n };\n}\n\n/**\n * Minimal shape of the Vite plugin object, declared here rather than imported.\n *\n * `vite` is a peer, not a dependency: a consumer building with something else\n * can still call `emitStyles` without pulling Vite's types in.\n */\nexport interface CraftStylePlugin {\n readonly name: string;\n readonly enforce?: 'pre' | 'post';\n configResolved?(config: { readonly root: string }): void;\n resolveId?(id: string): string | undefined;\n load?(id: string): Promise<string | undefined>;\n handleHotUpdate?(context: {\n readonly file: string;\n readonly server: {\n readonly ws: {\n send(message: {\n readonly type: 'full-reload';\n readonly path: '*';\n }): void | Promise<void>;\n };\n };\n }): void;\n}\n\n/**\n * `import 'virtual:craft-style.css'` once, at the app entry, and the whole\n * stylesheet arrives — built, not computed. No class is ever assembled at\n * runtime, so what the browser gets is what the emitter proved.\n */\nexport function craftStyle(options: CraftStyleOptions = {}): CraftStylePlugin {\n let root = process.cwd();\n let cached: EmitResult | undefined;\n\n return {\n name: 'craft-style',\n enforce: 'pre',\n configResolved(config) {\n root = config.root;\n },\n resolveId(id) {\n return id === VIRTUAL_CSS_ID ? RESOLVED_CSS_ID : undefined;\n },\n async load(id) {\n if (id !== RESOLVED_CSS_ID) return undefined;\n const files = await findStyleModules(\n root,\n options.suffix,\n options.ignore,\n );\n cached ??= await emitStyles(files, options.alias);\n if (options.dumpPath) {\n await writeFile(\n options.dumpPath,\n JSON.stringify(cached.dump, null, 2) + '\\n',\n );\n }\n return cached.css;\n },\n handleHotUpdate(context) {\n // A style module changed: the whole sheet is re-derived rather than\n // patched. Atomic output is small and the emission is one bundle away —\n // an incremental path here would be a second source of truth. The\n // stylesheet is a virtual module imported once by the app entry, so\n // invalidating the cache alone would leave the browser on old CSS.\n if (context.file.endsWith(options.suffix ?? '.style.ts')) {\n cached = undefined;\n context.server.ws.send({ type: 'full-reload', path: '*' });\n }\n },\n };\n}\n"]}