@fluixi/compiler 1.0.0-alpha.93 → 1.0.0-alpha.94

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 (90) hide show
  1. package/dist/analyze/intrinsics.cjs +13 -1
  2. package/dist/analyze/intrinsics.d.cts +14 -0
  3. package/dist/analyze/intrinsics.d.ts +14 -0
  4. package/dist/analyze/intrinsics.d.ts.map +1 -1
  5. package/dist/analyze/intrinsics.js +17 -0
  6. package/dist/analyze/intrinsics.mjs +13 -1
  7. package/dist/analyze/static-graph.cjs +1 -1
  8. package/dist/analyze/static-graph.mjs +1 -1
  9. package/dist/{babel-RJHJQVEM.mjs → babel-RJFQVFJW.mjs} +4 -4
  10. package/dist/{chunk-56QSOVVL.mjs → chunk-DP2QDWWH.mjs} +1 -1
  11. package/dist/{chunk-R7PPFBCL.mjs → chunk-E2RWTIYE.mjs} +21 -15
  12. package/dist/{chunk-OD53TMER.mjs → chunk-HCCBEJEZ.mjs} +13 -1
  13. package/dist/{chunk-TJIH7TLX.mjs → chunk-HIZU7WI2.mjs} +1 -1
  14. package/dist/{chunk-U7HCFLZ2.mjs → chunk-ZR2QHTEX.mjs} +356 -24
  15. package/dist/codegen/backends/imperative.cjs +1 -1
  16. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  17. package/dist/codegen/backends/imperative.js +2 -1
  18. package/dist/codegen/backends/imperative.mjs +1 -1
  19. package/dist/frontend/babel/build-ir-lit.cjs +352 -22
  20. package/dist/frontend/babel/build-ir-lit.mjs +352 -22
  21. package/dist/frontend/babel/build-ir.cjs +352 -22
  22. package/dist/frontend/babel/build-ir.mjs +352 -22
  23. package/dist/frontend/babel/index.cjs +353 -23
  24. package/dist/frontend/babel/index.mjs +353 -23
  25. package/dist/frontend/babel/lower-template.cjs +352 -22
  26. package/dist/frontend/babel/lower-template.mjs +352 -22
  27. package/dist/frontend/babel/plugin.cjs +352 -22
  28. package/dist/frontend/babel/plugin.mjs +352 -22
  29. package/dist/index.cjs +321 -22
  30. package/dist/index.mjs +333 -22
  31. package/dist/integrations.cjs +1174 -49
  32. package/dist/integrations.d.ts.map +1 -1
  33. package/dist/integrations.js +44 -2
  34. package/dist/integrations.mjs +205 -19
  35. package/dist/lower/compile-template.cjs +315 -21
  36. package/dist/lower/compile-template.d.cts +13 -0
  37. package/dist/lower/compile-template.d.ts +13 -0
  38. package/dist/lower/compile-template.d.ts.map +1 -1
  39. package/dist/lower/compile-template.js +3 -0
  40. package/dist/lower/compile-template.mjs +327 -21
  41. package/dist/lower/entities.cjs +288 -0
  42. package/dist/lower/entities.d.cts +1 -0
  43. package/dist/lower/entities.d.ts +2 -0
  44. package/dist/lower/entities.d.ts.map +1 -0
  45. package/dist/lower/entities.js +54 -0
  46. package/dist/lower/entities.mjs +267 -0
  47. package/dist/lower/jsx.cjs +104 -47
  48. package/dist/lower/jsx.d.cts +8 -0
  49. package/dist/lower/jsx.d.ts +8 -0
  50. package/dist/lower/jsx.d.ts.map +1 -1
  51. package/dist/lower/jsx.js +92 -2
  52. package/dist/lower/jsx.mjs +104 -47
  53. package/dist/lower/template.cjs +293 -20
  54. package/dist/lower/template.d.ts.map +1 -1
  55. package/dist/lower/template.js +35 -19
  56. package/dist/lower/template.mjs +293 -20
  57. package/dist/resolve/builtins.cjs +1 -1
  58. package/dist/resolve/builtins.mjs +1 -1
  59. package/dist/resolve/component-globals.cjs +6 -1
  60. package/dist/resolve/component-globals.js +2 -2
  61. package/dist/resolve/component-globals.mjs +6 -1
  62. package/dist/resolve/write-globals.cjs +6 -1
  63. package/dist/resolve/write-globals.mjs +6 -1
  64. package/dist/{server-fns-ZFRDIQ2X.mjs → server-fns-JLSTKWPS.mjs} +4 -4
  65. package/dist/{server-functions-CTDXX67X.mjs → server-functions-BGY3RD4R.mjs} +3 -3
  66. package/dist/transform/index.cjs +956 -33
  67. package/dist/transform/index.mjs +974 -47
  68. package/dist/transform/props.mjs +19 -14
  69. package/dist/transform/server-fns.mjs +19 -14
  70. package/dist/transform/styles.cjs +588 -0
  71. package/dist/transform/styles.d.cts +211 -0
  72. package/dist/transform/styles.d.ts +212 -0
  73. package/dist/transform/styles.d.ts.map +1 -0
  74. package/dist/transform/styles.js +714 -0
  75. package/dist/transform/styles.mjs +653 -0
  76. package/dist/transform/templates.cjs +955 -32
  77. package/dist/transform/templates.d.cts +13 -0
  78. package/dist/transform/templates.d.ts +13 -0
  79. package/dist/transform/templates.d.ts.map +1 -1
  80. package/dist/transform/templates.js +49 -12
  81. package/dist/transform/templates.mjs +973 -46
  82. package/dist/{transform-QWWQDCXE.mjs → transform-GBP6Y4UI.mjs} +601 -17
  83. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  84. package/dist/vite-styles.cjs +180 -0
  85. package/dist/vite-styles.d.cts +59 -0
  86. package/dist/vite-styles.d.ts +60 -0
  87. package/dist/vite-styles.d.ts.map +1 -0
  88. package/dist/vite-styles.js +198 -0
  89. package/dist/vite-styles.mjs +159 -0
  90. package/package.json +4 -3
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Component styles in a Vite build: the orchestration around the compiler's style pass.
3
+ *
4
+ * The CSS engine is reached through another plugin's `api.fluixiCss` (@fluixi-css/vite), which
5
+ * owns the project's shared context; the compiler owns ownership and scoping; this file only
6
+ * connects them. Each compiled block becomes a CSS module the component imports, so Vite's own
7
+ * CSS pipeline does the rest: extraction, chunking, dev style tags, hot updates, and the
8
+ * stylesheets @fluixi/start puts in server-rendered pages.
9
+ */
10
+ import { existsSync, readFileSync } from 'node:fs';
11
+ import { basename, dirname, extname, join, relative } from 'node:path';
12
+ // A page stylesheet, or (`&inline`, Vite's own flag) a shadow root's CSS as a string; and a
13
+ // css`` value's CSS for a shadow class's `static styles`.
14
+ const STYLE_ID = /\?fx-style&index=(\d+)(?:&inline)?&lang\.css$/;
15
+ const LITERAL_ID = /\?fx-literal=([\w$]+)&inline&lang\.css$/;
16
+ /** The id a component's n-th style block is served under: a CSS request Vite treats as such. */
17
+ export const styleId = (file, index) => `${file}?fx-style&index=${index}&lang.css`;
18
+ // The same loader @fluixi-ui/tokens ships beside each slice: one <link>, added once.
19
+ const loader = (name) => `const href = new URL(${JSON.stringify(`./${name}`)}, import.meta.url).href;
20
+ if (typeof document !== 'undefined' && !document.querySelector(\`link[data-fx-css="\${href}"]\`)) {
21
+ const link = document.createElement('link');
22
+ link.rel = 'stylesheet';
23
+ link.href = href;
24
+ link.dataset.fxCss = href;
25
+ document.head.appendChild(link);
26
+ }
27
+ `;
28
+ export function createComponentStyles(compile) {
29
+ let api;
30
+ let server;
31
+ let root = process.cwd();
32
+ // A library build (`build.lib`): the directory its entry is in, which its output mirrors.
33
+ let library;
34
+ // What a library build writes: the file under the output directory, and its CSS.
35
+ const emitted = new Map();
36
+ // A module id is a file path, or, for modules a plugin imports by URL (`/src/routes/…`),
37
+ // a path from the project root.
38
+ const fileOf = (id) => (existsSync(id) ? id : join(root, id));
39
+ // The CSS of each module's blocks, with the source it came from, for `load` and for telling
40
+ // what changed.
41
+ const cache = new Map();
42
+ const resolveReference = async (ref, importer, ctx, seen) => {
43
+ const resolved = await ctx.resolve?.(ref.source, importer);
44
+ if (!resolved)
45
+ return null;
46
+ const file = resolved.id.split('?')[0];
47
+ if (seen.has(file))
48
+ return null;
49
+ let code;
50
+ try {
51
+ code = readFileSync(fileOf(file), 'utf8');
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ const styles = api.styles(file);
57
+ let result = compile(code, file, styles);
58
+ // A value that composes values from further away: those first.
59
+ if (result?.styleReferences?.length) {
60
+ const imported = await resolveAll(result.styleReferences, file, ctx, new Set([...seen, file]));
61
+ result = compile(code, file, { ...styles, imported });
62
+ }
63
+ const literal = result?.styleLiterals?.find((l) => l.name === ref.name);
64
+ return literal ? styles.process(literal.css, file) : null;
65
+ };
66
+ const resolveAll = async (refs, importer, ctx, seen) => {
67
+ const imported = new Map();
68
+ for (const ref of refs) {
69
+ const nodes = await resolveReference(ref, importer, ctx, seen);
70
+ if (nodes)
71
+ imported.set(`${ref.source}#${ref.name}`, nodes);
72
+ }
73
+ return imported;
74
+ };
75
+ const run = async (code, id, ctx) => {
76
+ // One name per file, whatever id it was reached by (`/src/…` from a plugin, an absolute
77
+ // path from `load`): the scope id is derived from it, and the elements and the CSS have to
78
+ // agree on it.
79
+ const file = fileOf(id);
80
+ const styles = api.styles(file);
81
+ let result = compile(code, file, styles);
82
+ if (result?.styleReferences?.length) {
83
+ const imported = await resolveAll(result.styleReferences, id, ctx, new Set([file]));
84
+ result = compile(code, file, { ...styles, imported });
85
+ }
86
+ const css = (result?.styles ?? []).map((s) => s.css);
87
+ const maps = (result?.styles ?? []).map((s) => s.map ?? null);
88
+ const literals = new Map((result?.styleLiterals ?? []).map((l) => [l.name, l.css]));
89
+ const before = cache.get(file);
90
+ cache.set(file, { source: code, css, maps, literals });
91
+ // A block whose CSS changed (its own edit, or a value it composes from another module) is
92
+ // reloaded: its module id did not change, so nothing else would tell Vite.
93
+ if (server && before) {
94
+ for (let i = 0; i < css.length; i++) {
95
+ if (before.css[i] === css[i])
96
+ continue;
97
+ const mod = server.moduleGraph.getModuleById(styleId(id, i));
98
+ if (mod)
99
+ void server.reloadModule(mod);
100
+ }
101
+ }
102
+ // Page stylesheets are imported for their effect; a shadow element's blocks already reach
103
+ // its class through the `?inline` imports the compiler wrote.
104
+ const page = (result?.styles ?? []).flatMap((s, i) => (s.shadow ? [] : [i]));
105
+ if (!result || page.length === 0)
106
+ return result;
107
+ if (library) {
108
+ // A package ships a module's CSS as one file next to it, its blocks in source order,
109
+ // imported relatively: any bundler that takes a `.css` import takes it, and nothing in
110
+ // it needs this plugin. The `.css.js` beside it is for a page with no bundler: an import
111
+ // map points the stylesheet's url at it, and it adds the sheet as a <link>.
112
+ const name = `${basename(file, extname(file))}.css`;
113
+ if (existsSync(join(dirname(file), name))) {
114
+ throw new Error(`[fluixi] ${file} has <style> blocks, and ${name} next to it would be overwritten by their CSS. Rename one of them.`);
115
+ }
116
+ const out = join(relative(library, dirname(file)), name);
117
+ emitted.set(out, page.map((i) => css[i]).join('\n'));
118
+ emitted.set(`${out}.js`, loader(name));
119
+ return { ...result, code: `${result.code}\nimport ${JSON.stringify(`./${name}`)};` };
120
+ }
121
+ const imports = page.map((i) => `\nimport ${JSON.stringify(styleId(id, i))};`).join('');
122
+ return { ...result, code: result.code + imports };
123
+ };
124
+ return {
125
+ /** Looks for @fluixi-css/vite among the plugins; without it component styles stay off. */
126
+ connect(plugins, projectRoot) {
127
+ if (projectRoot)
128
+ root = projectRoot;
129
+ api = (plugins ?? [])
130
+ .map((p) => p?.api?.fluixiCss)
131
+ .find((a) => Boolean(a));
132
+ api?.useComponentStyles();
133
+ },
134
+ /** Turns on library output, from the directory of a `build.lib` entry. */
135
+ setLibrary(entryDir) {
136
+ library = entryDir;
137
+ },
138
+ /** A library build's style import: left for the package's consumer to bundle. */
139
+ isLibraryImport(id, importer) {
140
+ if (library === undefined || !importer || !id.startsWith('./') || !id.endsWith('.css'))
141
+ return false;
142
+ return emitted.has(join(relative(library, dirname(fileOf(importer))), id));
143
+ },
144
+ /** The style files a library build writes, by path under the output directory. */
145
+ libraryFiles() {
146
+ return emitted;
147
+ },
148
+ get active() {
149
+ return api !== undefined;
150
+ },
151
+ setServer(dev) {
152
+ server = dev;
153
+ },
154
+ isStyle(id) {
155
+ return STYLE_ID.test(id) || LITERAL_ID.test(id);
156
+ },
157
+ /**
158
+ * A style request as the module id Vite should keep: the file's absolute path and the
159
+ * query. Vite invalidates a module when the file its id names changes, so a root-relative id
160
+ * (`/src/card.tsx?fx-style…`, which is how a request arrives before the component's imports
161
+ * were analysed) was never invalidated, and an edited block kept its old CSS until restart.
162
+ */
163
+ resolveId(id) {
164
+ const at = id.indexOf('?');
165
+ return fileOf(id.slice(0, at)) + id.slice(at);
166
+ },
167
+ compile: run,
168
+ /**
169
+ * A block's CSS and its map back to the module, compiling the module again when the cache
170
+ * does not match the file.
171
+ */
172
+ async load(id, ctx) {
173
+ const match = STYLE_ID.exec(id);
174
+ const literal = LITERAL_ID.exec(id);
175
+ if ((!match && !literal) || !api)
176
+ return null;
177
+ const file = id.slice(0, id.indexOf('?'));
178
+ let source;
179
+ try {
180
+ source = readFileSync(fileOf(file), 'utf8');
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ if (cache.get(fileOf(file))?.source !== source)
186
+ await run(source, file, ctx);
187
+ const entry = cache.get(fileOf(file));
188
+ if (literal) {
189
+ const css = entry?.literals.get(literal[1]);
190
+ if (css === undefined)
191
+ throw new Error(`[fluixi] ${literal[1]} in ${file} is not a css\`\` value, so it cannot be a shadow root's styles.`);
192
+ return { code: css, map: null };
193
+ }
194
+ const index = Number(match[1]);
195
+ return { code: entry?.css[index] ?? '', map: entry?.maps[index] ?? null };
196
+ },
197
+ };
198
+ }
@@ -0,0 +1,159 @@
1
+ // src/vite-styles.ts
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { basename, dirname, extname, join, relative } from "node:path";
4
+ var STYLE_ID = /\?fx-style&index=(\d+)(?:&inline)?&lang\.css$/;
5
+ var LITERAL_ID = /\?fx-literal=([\w$]+)&inline&lang\.css$/;
6
+ var styleId = (file, index) => `${file}?fx-style&index=${index}&lang.css`;
7
+ var loader = (name) => `const href = new URL(${JSON.stringify(`./${name}`)}, import.meta.url).href;
8
+ if (typeof document !== 'undefined' && !document.querySelector(\`link[data-fx-css="\${href}"]\`)) {
9
+ const link = document.createElement('link');
10
+ link.rel = 'stylesheet';
11
+ link.href = href;
12
+ link.dataset.fxCss = href;
13
+ document.head.appendChild(link);
14
+ }
15
+ `;
16
+ function createComponentStyles(compile) {
17
+ let api;
18
+ let server;
19
+ let root = process.cwd();
20
+ let library;
21
+ const emitted = /* @__PURE__ */ new Map();
22
+ const fileOf = (id) => existsSync(id) ? id : join(root, id);
23
+ const cache = /* @__PURE__ */ new Map();
24
+ const resolveReference = async (ref, importer, ctx, seen) => {
25
+ const resolved = await ctx.resolve?.(ref.source, importer);
26
+ if (!resolved) return null;
27
+ const file = resolved.id.split("?")[0];
28
+ if (seen.has(file)) return null;
29
+ let code;
30
+ try {
31
+ code = readFileSync(fileOf(file), "utf8");
32
+ } catch {
33
+ return null;
34
+ }
35
+ const styles = api.styles(file);
36
+ let result = compile(code, file, styles);
37
+ if (result?.styleReferences?.length) {
38
+ const imported = await resolveAll(result.styleReferences, file, ctx, /* @__PURE__ */ new Set([...seen, file]));
39
+ result = compile(code, file, { ...styles, imported });
40
+ }
41
+ const literal = result?.styleLiterals?.find((l) => l.name === ref.name);
42
+ return literal ? styles.process(literal.css, file) : null;
43
+ };
44
+ const resolveAll = async (refs, importer, ctx, seen) => {
45
+ const imported = /* @__PURE__ */ new Map();
46
+ for (const ref of refs) {
47
+ const nodes = await resolveReference(ref, importer, ctx, seen);
48
+ if (nodes) imported.set(`${ref.source}#${ref.name}`, nodes);
49
+ }
50
+ return imported;
51
+ };
52
+ const run = async (code, id, ctx) => {
53
+ const file = fileOf(id);
54
+ const styles = api.styles(file);
55
+ let result = compile(code, file, styles);
56
+ if (result?.styleReferences?.length) {
57
+ const imported = await resolveAll(result.styleReferences, id, ctx, /* @__PURE__ */ new Set([file]));
58
+ result = compile(code, file, { ...styles, imported });
59
+ }
60
+ const css = (result?.styles ?? []).map((s) => s.css);
61
+ const maps = (result?.styles ?? []).map((s) => s.map ?? null);
62
+ const literals = new Map((result?.styleLiterals ?? []).map((l) => [l.name, l.css]));
63
+ const before = cache.get(file);
64
+ cache.set(file, { source: code, css, maps, literals });
65
+ if (server && before) {
66
+ for (let i = 0; i < css.length; i++) {
67
+ if (before.css[i] === css[i]) continue;
68
+ const mod = server.moduleGraph.getModuleById(styleId(id, i));
69
+ if (mod) void server.reloadModule(mod);
70
+ }
71
+ }
72
+ const page = (result?.styles ?? []).flatMap((s, i) => s.shadow ? [] : [i]);
73
+ if (!result || page.length === 0) return result;
74
+ if (library) {
75
+ const name = `${basename(file, extname(file))}.css`;
76
+ if (existsSync(join(dirname(file), name))) {
77
+ throw new Error(`[fluixi] ${file} has <style> blocks, and ${name} next to it would be overwritten by their CSS. Rename one of them.`);
78
+ }
79
+ const out = join(relative(library, dirname(file)), name);
80
+ emitted.set(out, page.map((i) => css[i]).join("\n"));
81
+ emitted.set(`${out}.js`, loader(name));
82
+ return { ...result, code: `${result.code}
83
+ import ${JSON.stringify(`./${name}`)};` };
84
+ }
85
+ const imports = page.map((i) => `
86
+ import ${JSON.stringify(styleId(id, i))};`).join("");
87
+ return { ...result, code: result.code + imports };
88
+ };
89
+ return {
90
+ /** Looks for @fluixi-css/vite among the plugins; without it component styles stay off. */
91
+ connect(plugins, projectRoot) {
92
+ if (projectRoot) root = projectRoot;
93
+ api = (plugins ?? []).map((p) => p?.api?.fluixiCss).find((a) => Boolean(a));
94
+ api?.useComponentStyles();
95
+ },
96
+ /** Turns on library output, from the directory of a `build.lib` entry. */
97
+ setLibrary(entryDir) {
98
+ library = entryDir;
99
+ },
100
+ /** A library build's style import: left for the package's consumer to bundle. */
101
+ isLibraryImport(id, importer) {
102
+ if (library === void 0 || !importer || !id.startsWith("./") || !id.endsWith(".css")) return false;
103
+ return emitted.has(join(relative(library, dirname(fileOf(importer))), id));
104
+ },
105
+ /** The style files a library build writes, by path under the output directory. */
106
+ libraryFiles() {
107
+ return emitted;
108
+ },
109
+ get active() {
110
+ return api !== void 0;
111
+ },
112
+ setServer(dev) {
113
+ server = dev;
114
+ },
115
+ isStyle(id) {
116
+ return STYLE_ID.test(id) || LITERAL_ID.test(id);
117
+ },
118
+ /**
119
+ * A style request as the module id Vite should keep: the file's absolute path and the
120
+ * query. Vite invalidates a module when the file its id names changes, so a root-relative id
121
+ * (`/src/card.tsx?fx-style…`, which is how a request arrives before the component's imports
122
+ * were analysed) was never invalidated, and an edited block kept its old CSS until restart.
123
+ */
124
+ resolveId(id) {
125
+ const at = id.indexOf("?");
126
+ return fileOf(id.slice(0, at)) + id.slice(at);
127
+ },
128
+ compile: run,
129
+ /**
130
+ * A block's CSS and its map back to the module, compiling the module again when the cache
131
+ * does not match the file.
132
+ */
133
+ async load(id, ctx) {
134
+ const match = STYLE_ID.exec(id);
135
+ const literal = LITERAL_ID.exec(id);
136
+ if (!match && !literal || !api) return null;
137
+ const file = id.slice(0, id.indexOf("?"));
138
+ let source;
139
+ try {
140
+ source = readFileSync(fileOf(file), "utf8");
141
+ } catch {
142
+ return null;
143
+ }
144
+ if (cache.get(fileOf(file))?.source !== source) await run(source, file, ctx);
145
+ const entry = cache.get(fileOf(file));
146
+ if (literal) {
147
+ const css = entry?.literals.get(literal[1]);
148
+ if (css === void 0) throw new Error(`[fluixi] ${literal[1]} in ${file} is not a css\`\` value, so it cannot be a shadow root's styles.`);
149
+ return { code: css, map: null };
150
+ }
151
+ const index = Number(match[1]);
152
+ return { code: entry?.css[index] ?? "", map: entry?.maps[index] ?? null };
153
+ }
154
+ };
155
+ }
156
+ export {
157
+ createComponentStyles,
158
+ styleId
159
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluixi/compiler",
3
- "version": "1.0.0-alpha.93",
3
+ "version": "1.0.0-alpha.94",
4
4
  "license": "MIT",
5
5
  "author": "Ibrahima Touré",
6
6
  "repository": {
@@ -90,6 +90,7 @@
90
90
  },
91
91
  "dependencies": {
92
92
  "@babel/parser": "^7.28.5",
93
+ "@jridgewell/sourcemap-codec": "^1.5.5",
93
94
  "magic-string": "^0.30.21",
94
95
  "@fluixi/template-parser": "0.1.0-alpha.4"
95
96
  },
@@ -108,8 +109,8 @@
108
109
  "inquirer": "^14.0.2",
109
110
  "jsdom": "^25.0.0",
110
111
  "vitest": "^1.6.1",
111
- "@fluixi/dom": "1.0.0-alpha.93",
112
- "@fluixi/reactive": "1.0.0-alpha.93"
112
+ "@fluixi/dom": "1.0.0-alpha.94",
113
+ "@fluixi/reactive": "1.0.0-alpha.94"
113
114
  },
114
115
  "peerDependencies": {
115
116
  "@babel/core": "^7.28.5",