@vizejs/babel-plugin 0.0.0-stage → 0.705.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ubugeeei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,135 @@
1
- # Temporary Holding Version
1
+ # @vizejs/babel-plugin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Compile Vue single-file components, JSX, and TSX with the shared Vize compiler inside Babel.
4
+
5
+ ```sh
6
+ pnpm add -D @babel/core @vizejs/babel-plugin
7
+ ```
8
+
9
+ The native Vize compiler is a normal package dependency. Babel is the host peer;
10
+ the application supplies its Vue runtime. The current Vapor preview targets
11
+ Vue 3.6.0-rc.11 and its build containing the Vapor runtime APIs. Select that
12
+ build through the application's normal module resolution configuration and
13
+ use `createVaporApp` or `createVaporSSRApp` for the client entry.
14
+
15
+ The runtime conformance matrix uses Vue 3.5.44 for VDOM. Historical 3.6.0-rc.9
16
+ fixtures remain an unsupported ABI frontier with their failed and reference
17
+ packets retained separately.
18
+
19
+ ```js
20
+ // babel.config.mjs
21
+ export default {
22
+ plugins: [["@vizejs/babel-plugin", { jsxMode: "vdom" }]],
23
+ sourceMaps: true,
24
+ };
25
+ ```
26
+
27
+ Use `@babel/preset-typescript` for type stripping and the usual Babel plugins for
28
+ decorators, language targets, or CommonJS output. The plugin enables JSX parsing
29
+ and TSX parsing for `.tsx` filenames. It leaves plain `.ts` parsing to the host.
30
+
31
+ The same plugin compiles `.vue` files through Babel's parser override. The native
32
+ compiler returns JavaScript before the normal Babel visitor pipeline runs, so module,
33
+ target, and instrumentation plugins continue to work on SFC output. Plain and scoped
34
+ CSS is injected once at runtime by default; server transforms keep the CSS in metadata
35
+ without touching `document`.
36
+
37
+ ## Options
38
+
39
+ | Option | Default | Behavior |
40
+ | -------------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
41
+ | `jsxMode` | `"vdom"` | Default client renderer: `"vdom"` or `"vapor"`. Component mode directives retain their native meaning. |
42
+ | `ssr` | `false` | Generate SSR output for the selected client renderer. |
43
+ | `jsxCompat` | `"native"` | Select native or `"babel"` compatibility semantics. Incompatible combinations surface the compiler diagnostic. |
44
+ | `scopedStyles` | `"error"` | `"metadata"` lets a bundler consume extracted CSS explicitly. |
45
+ | `sfcStyles` | `"inject"` | Handle SFC CSS with runtime `"inject"`, metadata-only `"metadata"`, or `"error"`. |
46
+ | `root` | cwd | Project root used to make SFC scope IDs stable across machines. |
47
+ | `isProduction` | `NODE_ENV` | Enable production SFC output and content-sensitive production scope IDs. |
48
+
49
+ Keep client and server builds on the same renderer setting. Unsupported native
50
+ source shapes surface as compilation errors.
51
+
52
+ ## Component authoring
53
+
54
+ Vapor and setup SSR output use component objects. A block arrow keeps setup
55
+ state inside the component; declare props with a named identifier and an inline
56
+ object type:
57
+
58
+ ```tsx
59
+ import { ref } from "vue";
60
+
61
+ export const Counter = (props: { label: string }) => {
62
+ const count = ref(0);
63
+ const increment = () => {
64
+ count.value++;
65
+ };
66
+ return <button onClick={increment}>{props.label + ":" + count.value}</button>;
67
+ };
68
+ ```
69
+
70
+ The preview rejects destructured props in these component objects. Use their
71
+ bindings as JSX component tags or direct exports; calls, callable aliases,
72
+ introspection, and escaping component values require a separate callable
73
+ representation and produce a diagnostic. Nested JSX that an attribute expression
74
+ cannot lower also produces a diagnostic.
75
+
76
+ VDOM client output supports expression arrows. SSR output for an expression
77
+ functional component is an explicit unsupported role; use a block setup component
78
+ for the paired client/server build.
79
+
80
+ ## Babel composition
81
+
82
+ The compiler runs in a `pre` hook. It serializes the current AST, including edits
83
+ from earlier `pre` hooks, and compiles that file once. Babel's normal visitors then
84
+ process the generated module. Program edits and the host's TypeScript, decorator,
85
+ and module transformations keep their place in that pipeline.
86
+
87
+ Files without JSX return before loading the native addon. JSX-producing visitor
88
+ transforms belong in an earlier Babel pass; introducing new JSX after native
89
+ compilation produces an explicit error.
90
+ The final generation boundary also checks for JSX introduced by later `post`
91
+ hooks. That guard returns control to Babel without claiming code generation;
92
+ other generator overrides keep Babel's normal collision rules.
93
+
94
+ `.vue` files are selected by filename before parsing, so the native compiler is loaded
95
+ for those inputs even though their authored syntax is not JavaScript. External SFC
96
+ blocks, custom blocks, style preprocessors, and CSS `@import` require a bundler adapter
97
+ and fail explicitly in standalone Babel.
98
+
99
+ Use Babel `overrides`, `only`, and `ignore` to select Vue files in mixed projects.
100
+ An explicit foreign JSX runtime or factory is a configuration error.
101
+
102
+ ## Source maps
103
+
104
+ Enable Babel `sourceMaps` normally. For JSX, the plugin composes the native map with
105
+ Babel's current-AST map and any upstream input map, then lets Babel's normal
106
+ generator produce the final map. It does not replace the parser or generator.
107
+ A missing requested native map produces an error.
108
+
109
+ For SFCs, the parser override installs the compiler's structured `.vue` map as Babel's
110
+ input map before ordinary visitors run. Runtime style bootstrap lines are unmapped and
111
+ the authored script/template mappings retain their original `.vue` source.
112
+
113
+ When calling `transformFromAstSync`, pass the original source to retain its
114
+ source content in the map.
115
+
116
+ ## Extracted styles
117
+
118
+ Babel does not emit stylesheets. By default, extracted scoped JSX CSS produces
119
+ an error. A bundler can opt in and emit the returned styles:
120
+
121
+ ```js
122
+ import * as babel from "@babel/core";
123
+ import vize from "@vizejs/babel-plugin";
124
+
125
+ const result = babel.transformSync(source, {
126
+ filename: "Card.tsx",
127
+ plugins: [[vize, { scopedStyles: "metadata" }]],
128
+ });
129
+
130
+ const styles = result.metadata.vize.jsx.scopedStyles;
131
+ // Each entry contains scopeId and already-scoped CSS.
132
+ ```
133
+
134
+ The same metadata contains the configured default mode, SSR flag, native pass
135
+ count, and compiler warnings. Compiler warnings are also emitted as Node warnings.
package/dist/index.cjs ADDED
@@ -0,0 +1,405 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ let node_module = require("node:module");
24
+ let convert_source_map = require("convert-source-map");
25
+ convert_source_map = __toESM(convert_source_map, 1);
26
+ let _jridgewell_remapping = require("@jridgewell/remapping");
27
+ _jridgewell_remapping = __toESM(_jridgewell_remapping, 1);
28
+ //#region src/ast.ts
29
+ function isNode(value) {
30
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
31
+ }
32
+ function hasJsx(ast, keys) {
33
+ const pending = [ast];
34
+ while (pending.length > 0) {
35
+ const node = pending.pop();
36
+ if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
37
+ for (const key of keys[node.type] ?? []) {
38
+ const child = node[key];
39
+ if (Array.isArray(child)) {
40
+ for (const item of child) if (isNode(item)) pending.push(item);
41
+ } else if (isNode(child)) pending.push(child);
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+ function enableSyntax(filename, parser) {
47
+ const plugins = parser.plugins ??= [];
48
+ const has = (name) => plugins.some((p) => (Array.isArray(p) ? p[0] : p) === name);
49
+ if (/\.tsx$/iu.test(filename) && !has("typescript")) plugins.push("typescript");
50
+ if (!/\.(?:ts|mts|cts)$/iu.test(filename) && !has("jsx")) plugins.push("jsx");
51
+ }
52
+ /** Explicit foreign runtime selection is a configuration error, never Vue output. */
53
+ function rejectForeignRuntime(ast, plugins, scope) {
54
+ if (Array.isArray(plugins)) for (const plugin of plugins) {
55
+ if (typeof plugin !== "object" || plugin === null || !("key" in plugin)) continue;
56
+ if (typeof plugin.key === "string" && /^(?:@babel\/plugin-)?transform-react-jsx(?:-development|\/development)?$/u.test(plugin.key)) rejectReact();
57
+ }
58
+ const body = ast.program.body;
59
+ if (Array.isArray(body)) for (const node of body) {
60
+ if (isNode(node) && node.type === "TSImportEqualsDeclaration" && node.importKind !== "type") {
61
+ const owner = node.moduleReference;
62
+ if (isNode(owner) && isNode(owner.expression) && foreignName(owner.expression.value)) rejectReact();
63
+ }
64
+ if (isNode(node) && node.type === "VariableDeclaration" && !scope.getBinding("require")) {
65
+ const declarations = node.declarations;
66
+ if (Array.isArray(declarations)) for (const declaration of declarations) {
67
+ if (!isNode(declaration) || !isNode(declaration.init)) continue;
68
+ const call = declaration.init;
69
+ if (call.type === "CallExpression" && isNode(call.callee) && call.callee.type === "Identifier" && call.callee.name === "require" && Array.isArray(call.arguments) && call.arguments.length === 1 && isNode(call.arguments[0]) && foreignName(call.arguments[0].value)) rejectReact();
70
+ }
71
+ }
72
+ if (!isNode(node) || node.type !== "ImportDeclaration" || node.importKind === "type") continue;
73
+ const specifiers = node.specifiers;
74
+ if (Array.isArray(specifiers) && specifiers.length > 0 && specifiers.every((specifier) => isNode(specifier) && specifier.importKind === "type")) continue;
75
+ const source = node.source;
76
+ if (isNode(source) && typeof source.value === "string" && foreignName(source.value)) rejectReact();
77
+ }
78
+ const comments = ast.comments;
79
+ if (Array.isArray(comments)) for (const comment of comments) {
80
+ if (!isNode(comment) || typeof comment.value !== "string") continue;
81
+ const source = /@jsxImportSource\s+(\S+)/u.exec(comment.value)?.[1];
82
+ if (source && source !== "vue") throw new Error(`Vize cannot compile the explicitly selected JSX import source: ${source}`);
83
+ if (/@jsx\s+(\S+)/u.exec(comment.value)?.[1]) throw new Error("Custom JSX factory pragmas are not supported by the native Babel adapter");
84
+ }
85
+ }
86
+ function foreignName(value) {
87
+ return typeof value === "string" && /^react(?:\/|$)/u.test(value);
88
+ }
89
+ function rejectReact() {
90
+ throw new Error("Vize compiles Vue JSX; scope this plugin to Vue files using Babel overrides");
91
+ }
92
+ //#endregion
93
+ //#region src/native.ts
94
+ let compiler;
95
+ /** Keep addon initialization outside the common no-JSX/non-SFC path. */
96
+ function getCompiler() {
97
+ if (compiler) return compiler;
98
+ const binding = (0, node_module.createRequire)(typeof __filename === "string" ? __filename : require("url").pathToFileURL(__filename).href)("@vizejs/native");
99
+ if (typeof binding !== "object" || binding === null || !("compileJsx" in binding) || typeof binding.compileJsx !== "function" || !("compileSfc" in binding) || typeof binding.compileSfc !== "function" || !("generateSfcScopeId" in binding) || typeof binding.generateSfcScopeId !== "function") throw new Error("The installed Vize native binding does not expose the Babel compiler surface; use matching Vize package versions");
100
+ compiler = binding;
101
+ return compiler;
102
+ }
103
+ //#endregion
104
+ //#region src/options.ts
105
+ function normalizeOptions(value) {
106
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("Vize Babel options must be an object");
107
+ for (const key of Object.keys(value)) if (![
108
+ "jsxMode",
109
+ "jsxCompat",
110
+ "ssr",
111
+ "scopedStyles",
112
+ "sfcStyles",
113
+ "root",
114
+ "isProduction"
115
+ ].includes(key)) throw new TypeError(`Unknown Vize Babel option: ${key}`);
116
+ const { jsxMode = "vdom", jsxCompat = "native", ssr = false, scopedStyles = "error", sfcStyles = "inject", root = process.cwd(), isProduction = process.env.NODE_ENV === "production" } = value;
117
+ if (jsxMode !== "vdom" && jsxMode !== "vapor") throw new TypeError("jsxMode must be \"vdom\" or \"vapor\"");
118
+ if (jsxCompat !== "native" && jsxCompat !== "babel") throw new TypeError("jsxCompat must be \"native\" or \"babel\"");
119
+ if (typeof ssr !== "boolean") throw new TypeError("ssr must be a boolean");
120
+ if (scopedStyles !== "error" && scopedStyles !== "metadata") throw new TypeError("scopedStyles must be \"error\" or \"metadata\"");
121
+ if (sfcStyles !== "inject" && sfcStyles !== "metadata" && sfcStyles !== "error") throw new TypeError("sfcStyles must be \"inject\", \"metadata\", or \"error\"");
122
+ if (typeof root !== "string" || root.length === 0) throw new TypeError("root must be a non-empty string");
123
+ if (typeof isProduction !== "boolean") throw new TypeError("isProduction must be a boolean");
124
+ return {
125
+ jsxMode,
126
+ jsxCompat,
127
+ ssr,
128
+ scopedStyles,
129
+ sfcStyles,
130
+ root,
131
+ isProduction
132
+ };
133
+ }
134
+ //#endregion
135
+ //#region src/scopes.ts
136
+ /** Earlier pre hooks can reserve identifiers before adding their declarations. */
137
+ function captureReservations(file) {
138
+ const names = file.scope.uidsSet ? [...file.scope.uidsSet] : Object.keys(file.scope.uids).filter((name) => file.scope.uids[name]);
139
+ if (names.length === 0) return void 0;
140
+ const authoredBindings = new Set(Object.keys(file.scope.bindings));
141
+ const seen = new Set([file.scope]);
142
+ file.path.traverse({ Scopable({ scope }) {
143
+ if (seen.has(scope)) return;
144
+ seen.add(scope);
145
+ for (const name of Object.keys(scope.bindings)) authoredBindings.add(name);
146
+ } });
147
+ return {
148
+ names,
149
+ authoredBindings
150
+ };
151
+ }
152
+ function restoreReservations(file, reserved) {
153
+ if (!reserved) return;
154
+ const names = new Set(reserved.names);
155
+ for (const name of names) if (file.scope.uidsSet) file.scope.uidsSet.add(name);
156
+ else file.scope.uids[name] = true;
157
+ const seen = /* @__PURE__ */ new Set();
158
+ const restore = (scope) => {
159
+ if (seen.has(scope)) return;
160
+ seen.add(scope);
161
+ for (const name of Object.keys(scope.bindings)) if (names.has(name) && !reserved.authoredBindings.has(name)) scope.rename(name, scope.generateUid(name));
162
+ };
163
+ restore(file.scope);
164
+ file.path.traverse({ Scopable: ({ scope }) => restore(scope) });
165
+ }
166
+ //#endregion
167
+ //#region src/source-map.ts
168
+ function composeInputMap(nativeMap, printedMap) {
169
+ if (!nativeMap || !printedMap) throw new Error("The native JSX compiler did not provide the requested original-source map");
170
+ const parsed = JSON.parse(nativeMap);
171
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed) || parsed.version !== 3 || !("mappings" in parsed) || typeof parsed.mappings !== "string" || !("sources" in parsed) || !Array.isArray(parsed.sources)) throw new Error("The native JSX compiler returned an invalid source map");
172
+ const composed = (0, _jridgewell_remapping.default)([parsed, printedMap], () => null);
173
+ return convert_source_map.default.fromObject(composed);
174
+ }
175
+ //#endregion
176
+ //#region src/sfc.ts
177
+ function isVueSfc(filename) {
178
+ return /\.vue$/iu.test(filename);
179
+ }
180
+ function compileSfcForBabel(source, filename, parserOptions, parse, options) {
181
+ const native = getCompiler();
182
+ const scopeId = native.generateSfcScopeId(filename, options.root, options.isProduction, source);
183
+ const result = native.compileSfc(source, {
184
+ filename,
185
+ mode: "module",
186
+ sourceMap: true,
187
+ ssr: options.ssr,
188
+ vapor: options.jsxMode === "vapor",
189
+ scopeId: `data-v-${scopeId}`,
190
+ isProd: options.isProduction,
191
+ inlineTemplate: options.isProduction && !options.ssr
192
+ });
193
+ assertSupported(result, filename, options.sfcStyles);
194
+ const completed = completeModule(result, scopeId, options.sfcStyles);
195
+ return {
196
+ ast: parse(completed.code, {
197
+ ...parserOptions,
198
+ filename,
199
+ sourceType: "module"
200
+ }),
201
+ code: completed.code,
202
+ map: completed.map,
203
+ metadata: {
204
+ defaultMode: options.jsxMode,
205
+ ssr: options.ssr,
206
+ nativePasses: 1,
207
+ warnings: result.warnings,
208
+ css: result.css ?? null,
209
+ styles: result.styles,
210
+ dependencies: result.dependencies ?? []
211
+ }
212
+ };
213
+ }
214
+ function installSfcFileState(file, compiled) {
215
+ file.code = compiled.code;
216
+ if (file.opts.sourceMaps) {
217
+ if (!compiled.map) throw new Error("The native SFC compiler did not provide the requested original-source map");
218
+ const parsed = JSON.parse(compiled.map);
219
+ if (!isSourceMap(parsed)) throw new Error("The native SFC compiler returned an invalid source map");
220
+ const upstream = file.inputMap?.toObject();
221
+ file.inputMap = upstream ? composeInputMap(compiled.map, upstream) : convert_source_map.default.fromObject(parsed);
222
+ }
223
+ const old = file.metadata.vize;
224
+ file.metadata.vize = {
225
+ ...typeof old === "object" && old !== null ? old : {},
226
+ sfc: compiled.metadata
227
+ };
228
+ for (const warning of compiled.metadata.warnings) process.emitWarning(`[vize] ${warning}`, { type: "VizeSfcWarning" });
229
+ }
230
+ function assertSupported(result, filename, styleMode) {
231
+ if (result.errors.length > 0) throw new Error(result.errors.join("\n"));
232
+ if (result.customBlocks.length > 0 || result.macroArtifacts.length > 0) throw new Error(`${filename}: custom blocks and emitted macro artifacts need a bundler adapter and are not supported by standalone Babel`);
233
+ for (const style of result.styles) {
234
+ if (style.src) throw new Error(`${filename}: <style src> needs a bundler CSS pipeline and is not supported by standalone Babel`);
235
+ if (style.lang && style.lang !== "css") throw new Error(`${filename}: <style lang="${style.lang}"> needs a bundler CSS pipeline and is not supported by standalone Babel`);
236
+ if (style.content.includes("@import")) throw new Error(`${filename}: CSS @import needs a bundler CSS pipeline and is not supported by standalone Babel`);
237
+ }
238
+ if (result.css && styleMode === "error") throw new Error(`${filename}: SFC styles require sfcStyles: "inject" or "metadata" in standalone Babel`);
239
+ }
240
+ function completeModule(result, scopeId, styleMode) {
241
+ let code = result.code;
242
+ if (result.hasScoped) code = attachScope(code, scopeId);
243
+ const prefix = result.css && styleMode === "inject" ? styleInjection(result.css, scopeId) : "";
244
+ return {
245
+ code: prefix + code,
246
+ map: shiftMap(result.map, prefix)
247
+ };
248
+ }
249
+ function attachScope(code, scopeId) {
250
+ if (/^export default _sfc_main/mu.test(code)) return code.replace(/^export default _sfc_main/mu, `_sfc_main.__scopeId = "data-v-${scopeId}";\nexport default _sfc_main`);
251
+ if (/^export default /mu.test(code)) return `${code.replace(/^export default /mu, "const _sfc_main = ")}\n_sfc_main.__scopeId = "data-v-${scopeId}";\nexport default _sfc_main;\n`;
252
+ if (/^export function render\b/mu.test(code)) return `${code}\nconst _sfc_main = { render };\n_sfc_main.__scopeId = "data-v-${scopeId}";\nexport default _sfc_main;\n`;
253
+ return code;
254
+ }
255
+ function styleInjection(css, scopeId) {
256
+ return `export const __vize_css__ = ${JSON.stringify(css)};
257
+ const __vize_css_id__ = ${JSON.stringify(`vize-style-${scopeId}`)};
258
+ (function() {
259
+ if (typeof document !== "undefined") {
260
+ let style = document.getElementById(__vize_css_id__);
261
+ if (!style) {
262
+ style = document.createElement("style");
263
+ style.id = __vize_css_id__;
264
+ document.head.appendChild(style);
265
+ }
266
+ style.textContent = __vize_css__;
267
+ }
268
+ })();
269
+ `;
270
+ }
271
+ function shiftMap(mapJson, prefix) {
272
+ if (!mapJson) return null;
273
+ if (!prefix) return mapJson;
274
+ const parsed = JSON.parse(mapJson);
275
+ if (!isSourceMap(parsed)) return null;
276
+ const lines = prefix.split("\n").length - 1;
277
+ return JSON.stringify({
278
+ ...parsed,
279
+ mappings: ";".repeat(lines) + parsed.mappings
280
+ });
281
+ }
282
+ function isSourceMap(value) {
283
+ return typeof value === "object" && value !== null && "version" in value && value.version === 3 && "sources" in value && Array.isArray(value.sources) && "names" in value && Array.isArray(value.names) && "mappings" in value && typeof value.mappings === "string";
284
+ }
285
+ //#endregion
286
+ //#region src/index.ts
287
+ const compiled = Symbol.for("@vizejs/babel-plugin/compiled");
288
+ /** Babel owns the returned hooks; consumers configure the strongly typed options. */
289
+ function vizeBabelPlugin(apiValue, rawOptions = {}) {
290
+ const api = apiValue;
291
+ api.assertVersion("^7.29.7 || ^8.0.9");
292
+ const options = normalizeOptions(rawOptions);
293
+ const sfcOutputs = /* @__PURE__ */ new WeakMap();
294
+ const assertComplete = (ast) => {
295
+ if (hasJsx(ast, api.types.VISITOR_KEYS)) throw new Error("JSX was introduced after the native phase; run JSX-producing transforms in an earlier Babel pass");
296
+ };
297
+ return {
298
+ name: "vize-vue-jsx",
299
+ manipulateOptions(host, parser) {
300
+ enableSyntax(host.filename ?? "", parser);
301
+ },
302
+ parserOverride(source, parserOptions, parse) {
303
+ const filename = String(parserOptions.filename ?? parserOptions.sourceFilename ?? parserOptions.sourceFileName ?? "");
304
+ if (!filename || !isVueSfc(filename)) return void 0;
305
+ const compiledSfc = compileSfcForBabel(source, filename, parserOptions, parse, options);
306
+ sfcOutputs.set(compiledSfc.ast, compiledSfc);
307
+ return compiledSfc.ast;
308
+ },
309
+ pre(file) {
310
+ const compiledSfc = sfcOutputs.get(file.ast);
311
+ if (compiledSfc) {
312
+ file.set(compiled, true);
313
+ installSfcFileState(file, compiledSfc);
314
+ return;
315
+ }
316
+ if (!hasJsx(file.ast, api.types.VISITOR_KEYS)) return;
317
+ if (file.get(compiled)) throw new Error("JSX was introduced after this file's native compilation pass");
318
+ rejectForeignRuntime(file.ast, file.opts.plugins, file.scope);
319
+ const reservations = captureReservations(file);
320
+ const maps = Boolean(file.opts.sourceMaps);
321
+ const hostGenerator = file.opts.generatorOpts;
322
+ const printed = api.transformFromAstSync(file.ast, file.code, {
323
+ filename: file.opts.filename,
324
+ sourceFileName: file.opts.sourceFileName,
325
+ sourceRoot: file.opts.sourceRoot,
326
+ babelrc: false,
327
+ configFile: false,
328
+ plugins: [],
329
+ presets: [],
330
+ ast: false,
331
+ sourceMaps: maps,
332
+ inputSourceMap: maps ? file.inputMap?.toObject() ?? false : false,
333
+ comments: file.opts.comments,
334
+ generatorOpts: {
335
+ ...typeof hostGenerator === "object" && hostGenerator !== null ? hostGenerator : {},
336
+ sourceMaps: maps
337
+ }
338
+ });
339
+ if (!printed || typeof printed.code !== "string") throw new Error("Babel could not serialize the current JSX AST");
340
+ const parserPlugins = file.opts.parserOpts?.plugins ?? [];
341
+ const typescript = /\.(?:tsx|ts|mts|cts)$/iu.test(file.opts.filename ?? "") || parserPlugins.some((p) => (Array.isArray(p) ? p[0] : p) === "typescript");
342
+ const output = getCompiler().compileJsx(printed.code, {
343
+ filename: file.opts.filename,
344
+ lang: typescript ? "tsx" : "jsx",
345
+ jsxMode: options.jsxMode,
346
+ jsxCompat: options.jsxCompat,
347
+ ssr: options.ssr,
348
+ sourceMap: maps
349
+ });
350
+ file.set(compiled, true);
351
+ if (output.errors.length > 0) throw new Error(output.errors.join("\n"));
352
+ if (output.scopedStyles.length > 0 && options.scopedStyles === "error") throw new Error("Scoped JSX CSS requires a bundler consumer; set scopedStyles: \"metadata\" and consume file.metadata.vize.jsx.scopedStyles");
353
+ const generated = api.parseSync(output.code, {
354
+ filename: file.opts.filename,
355
+ babelrc: false,
356
+ configFile: false,
357
+ plugins: [],
358
+ presets: [],
359
+ sourceType: "module",
360
+ parserOpts: {
361
+ ...file.opts.parserOpts,
362
+ sourceType: "module"
363
+ }
364
+ });
365
+ if (!generated || hasJsx(generated, api.types.VISITOR_KEYS)) throw new Error("The native JSX compiler did not return a complete JSX-free module");
366
+ const inputMap = maps ? composeInputMap(output.map, printed.map) : null;
367
+ file.path.replaceWith(generated.program);
368
+ for (const key of [
369
+ "comments",
370
+ "tokens",
371
+ "errors",
372
+ "start",
373
+ "end",
374
+ "loc"
375
+ ]) if (key in generated) file.ast[key] = generated[key];
376
+ else delete file.ast[key];
377
+ file.scope = file.path.scope;
378
+ file.scope.crawl();
379
+ restoreReservations(file, reservations);
380
+ file.code = output.code;
381
+ file.inputMap = inputMap;
382
+ const old = file.metadata.vize;
383
+ const metadata = {
384
+ defaultMode: options.jsxMode,
385
+ ssr: options.ssr,
386
+ nativePasses: 1,
387
+ warnings: output.warnings,
388
+ scopedStyles: output.scopedStyles
389
+ };
390
+ file.metadata.vize = {
391
+ ...typeof old === "object" && old !== null ? old : {},
392
+ jsx: metadata
393
+ };
394
+ for (const warning of output.warnings) process.emitWarning(`[vize] ${warning}`, { type: "VizeJsxWarning" });
395
+ },
396
+ post(file) {
397
+ assertComplete(file.ast);
398
+ },
399
+ generatorOverride(ast) {
400
+ assertComplete(ast);
401
+ }
402
+ };
403
+ }
404
+ //#endregion
405
+ module.exports = vizeBabelPlugin;
@@ -0,0 +1,41 @@
1
+ import { JsxCompileResultNapi, StyleBlockNapi } from "@vizejs/native";
2
+
3
+ //#region src/types.d.ts
4
+ /** JSX output defaults; component directive prologues can select another mode. */
5
+ interface VizeBabelOptions {
6
+ jsxMode?: "vdom" | "vapor";
7
+ jsxCompat?: "native" | "babel";
8
+ ssr?: boolean;
9
+ /** Explicitly hand extracted CSS to a bundler through Babel file metadata. */
10
+ scopedStyles?: "error" | "metadata";
11
+ /** How standalone Babel transforms handle Vue SFC style blocks. */
12
+ sfcStyles?: "inject" | "metadata" | "error";
13
+ /** Root used to make SFC scope IDs stable across machines. */
14
+ root?: string;
15
+ /** Enables production SFC output and content-sensitive production scope IDs. */
16
+ isProduction?: boolean;
17
+ }
18
+ interface VizeBabelMetadata {
19
+ defaultMode: "vdom" | "vapor";
20
+ ssr: boolean;
21
+ nativePasses: 1;
22
+ warnings: string[];
23
+ scopedStyles: JsxCompileResultNapi["scopedStyles"];
24
+ }
25
+ interface VizeBabelSfcMetadata {
26
+ defaultMode: "vdom" | "vapor";
27
+ ssr: boolean;
28
+ nativePasses: 1;
29
+ warnings: string[];
30
+ css: string | null;
31
+ styles: StyleBlockNapi[];
32
+ dependencies: string[];
33
+ }
34
+ //#endregion
35
+ //#region src/index.d.ts
36
+ /** Babel owns the returned hooks; consumers configure the strongly typed options. */
37
+ declare function vizeBabelPlugin(apiValue: unknown, rawOptions?: VizeBabelOptions): {
38
+ name: string;
39
+ };
40
+ //#endregion
41
+ export { type VizeBabelMetadata, type VizeBabelOptions, type VizeBabelSfcMetadata, vizeBabelPlugin as default };
@@ -0,0 +1,41 @@
1
+ import { JsxCompileResultNapi, StyleBlockNapi } from "@vizejs/native";
2
+
3
+ //#region src/types.d.ts
4
+ /** JSX output defaults; component directive prologues can select another mode. */
5
+ interface VizeBabelOptions {
6
+ jsxMode?: "vdom" | "vapor";
7
+ jsxCompat?: "native" | "babel";
8
+ ssr?: boolean;
9
+ /** Explicitly hand extracted CSS to a bundler through Babel file metadata. */
10
+ scopedStyles?: "error" | "metadata";
11
+ /** How standalone Babel transforms handle Vue SFC style blocks. */
12
+ sfcStyles?: "inject" | "metadata" | "error";
13
+ /** Root used to make SFC scope IDs stable across machines. */
14
+ root?: string;
15
+ /** Enables production SFC output and content-sensitive production scope IDs. */
16
+ isProduction?: boolean;
17
+ }
18
+ interface VizeBabelMetadata {
19
+ defaultMode: "vdom" | "vapor";
20
+ ssr: boolean;
21
+ nativePasses: 1;
22
+ warnings: string[];
23
+ scopedStyles: JsxCompileResultNapi["scopedStyles"];
24
+ }
25
+ interface VizeBabelSfcMetadata {
26
+ defaultMode: "vdom" | "vapor";
27
+ ssr: boolean;
28
+ nativePasses: 1;
29
+ warnings: string[];
30
+ css: string | null;
31
+ styles: StyleBlockNapi[];
32
+ dependencies: string[];
33
+ }
34
+ //#endregion
35
+ //#region src/index.d.ts
36
+ /** Babel owns the returned hooks; consumers configure the strongly typed options. */
37
+ declare function vizeBabelPlugin(apiValue: unknown, rawOptions?: VizeBabelOptions): {
38
+ name: string;
39
+ };
40
+ //#endregion
41
+ export { type VizeBabelMetadata, type VizeBabelOptions, type VizeBabelSfcMetadata, vizeBabelPlugin as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,381 @@
1
+ import { createRequire } from "node:module";
2
+ import convertSourceMap from "convert-source-map";
3
+ import remapping from "@jridgewell/remapping";
4
+ //#region src/ast.ts
5
+ function isNode(value) {
6
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
7
+ }
8
+ function hasJsx(ast, keys) {
9
+ const pending = [ast];
10
+ while (pending.length > 0) {
11
+ const node = pending.pop();
12
+ if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
13
+ for (const key of keys[node.type] ?? []) {
14
+ const child = node[key];
15
+ if (Array.isArray(child)) {
16
+ for (const item of child) if (isNode(item)) pending.push(item);
17
+ } else if (isNode(child)) pending.push(child);
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ function enableSyntax(filename, parser) {
23
+ const plugins = parser.plugins ??= [];
24
+ const has = (name) => plugins.some((p) => (Array.isArray(p) ? p[0] : p) === name);
25
+ if (/\.tsx$/iu.test(filename) && !has("typescript")) plugins.push("typescript");
26
+ if (!/\.(?:ts|mts|cts)$/iu.test(filename) && !has("jsx")) plugins.push("jsx");
27
+ }
28
+ /** Explicit foreign runtime selection is a configuration error, never Vue output. */
29
+ function rejectForeignRuntime(ast, plugins, scope) {
30
+ if (Array.isArray(plugins)) for (const plugin of plugins) {
31
+ if (typeof plugin !== "object" || plugin === null || !("key" in plugin)) continue;
32
+ if (typeof plugin.key === "string" && /^(?:@babel\/plugin-)?transform-react-jsx(?:-development|\/development)?$/u.test(plugin.key)) rejectReact();
33
+ }
34
+ const body = ast.program.body;
35
+ if (Array.isArray(body)) for (const node of body) {
36
+ if (isNode(node) && node.type === "TSImportEqualsDeclaration" && node.importKind !== "type") {
37
+ const owner = node.moduleReference;
38
+ if (isNode(owner) && isNode(owner.expression) && foreignName(owner.expression.value)) rejectReact();
39
+ }
40
+ if (isNode(node) && node.type === "VariableDeclaration" && !scope.getBinding("require")) {
41
+ const declarations = node.declarations;
42
+ if (Array.isArray(declarations)) for (const declaration of declarations) {
43
+ if (!isNode(declaration) || !isNode(declaration.init)) continue;
44
+ const call = declaration.init;
45
+ if (call.type === "CallExpression" && isNode(call.callee) && call.callee.type === "Identifier" && call.callee.name === "require" && Array.isArray(call.arguments) && call.arguments.length === 1 && isNode(call.arguments[0]) && foreignName(call.arguments[0].value)) rejectReact();
46
+ }
47
+ }
48
+ if (!isNode(node) || node.type !== "ImportDeclaration" || node.importKind === "type") continue;
49
+ const specifiers = node.specifiers;
50
+ if (Array.isArray(specifiers) && specifiers.length > 0 && specifiers.every((specifier) => isNode(specifier) && specifier.importKind === "type")) continue;
51
+ const source = node.source;
52
+ if (isNode(source) && typeof source.value === "string" && foreignName(source.value)) rejectReact();
53
+ }
54
+ const comments = ast.comments;
55
+ if (Array.isArray(comments)) for (const comment of comments) {
56
+ if (!isNode(comment) || typeof comment.value !== "string") continue;
57
+ const source = /@jsxImportSource\s+(\S+)/u.exec(comment.value)?.[1];
58
+ if (source && source !== "vue") throw new Error(`Vize cannot compile the explicitly selected JSX import source: ${source}`);
59
+ if (/@jsx\s+(\S+)/u.exec(comment.value)?.[1]) throw new Error("Custom JSX factory pragmas are not supported by the native Babel adapter");
60
+ }
61
+ }
62
+ function foreignName(value) {
63
+ return typeof value === "string" && /^react(?:\/|$)/u.test(value);
64
+ }
65
+ function rejectReact() {
66
+ throw new Error("Vize compiles Vue JSX; scope this plugin to Vue files using Babel overrides");
67
+ }
68
+ //#endregion
69
+ //#region src/native.ts
70
+ let compiler;
71
+ /** Keep addon initialization outside the common no-JSX/non-SFC path. */
72
+ function getCompiler() {
73
+ if (compiler) return compiler;
74
+ const binding = createRequire(typeof __filename === "string" ? __filename : import.meta.url)("@vizejs/native");
75
+ if (typeof binding !== "object" || binding === null || !("compileJsx" in binding) || typeof binding.compileJsx !== "function" || !("compileSfc" in binding) || typeof binding.compileSfc !== "function" || !("generateSfcScopeId" in binding) || typeof binding.generateSfcScopeId !== "function") throw new Error("The installed Vize native binding does not expose the Babel compiler surface; use matching Vize package versions");
76
+ compiler = binding;
77
+ return compiler;
78
+ }
79
+ //#endregion
80
+ //#region src/options.ts
81
+ function normalizeOptions(value) {
82
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("Vize Babel options must be an object");
83
+ for (const key of Object.keys(value)) if (![
84
+ "jsxMode",
85
+ "jsxCompat",
86
+ "ssr",
87
+ "scopedStyles",
88
+ "sfcStyles",
89
+ "root",
90
+ "isProduction"
91
+ ].includes(key)) throw new TypeError(`Unknown Vize Babel option: ${key}`);
92
+ const { jsxMode = "vdom", jsxCompat = "native", ssr = false, scopedStyles = "error", sfcStyles = "inject", root = process.cwd(), isProduction = process.env.NODE_ENV === "production" } = value;
93
+ if (jsxMode !== "vdom" && jsxMode !== "vapor") throw new TypeError("jsxMode must be \"vdom\" or \"vapor\"");
94
+ if (jsxCompat !== "native" && jsxCompat !== "babel") throw new TypeError("jsxCompat must be \"native\" or \"babel\"");
95
+ if (typeof ssr !== "boolean") throw new TypeError("ssr must be a boolean");
96
+ if (scopedStyles !== "error" && scopedStyles !== "metadata") throw new TypeError("scopedStyles must be \"error\" or \"metadata\"");
97
+ if (sfcStyles !== "inject" && sfcStyles !== "metadata" && sfcStyles !== "error") throw new TypeError("sfcStyles must be \"inject\", \"metadata\", or \"error\"");
98
+ if (typeof root !== "string" || root.length === 0) throw new TypeError("root must be a non-empty string");
99
+ if (typeof isProduction !== "boolean") throw new TypeError("isProduction must be a boolean");
100
+ return {
101
+ jsxMode,
102
+ jsxCompat,
103
+ ssr,
104
+ scopedStyles,
105
+ sfcStyles,
106
+ root,
107
+ isProduction
108
+ };
109
+ }
110
+ //#endregion
111
+ //#region src/scopes.ts
112
+ /** Earlier pre hooks can reserve identifiers before adding their declarations. */
113
+ function captureReservations(file) {
114
+ const names = file.scope.uidsSet ? [...file.scope.uidsSet] : Object.keys(file.scope.uids).filter((name) => file.scope.uids[name]);
115
+ if (names.length === 0) return void 0;
116
+ const authoredBindings = new Set(Object.keys(file.scope.bindings));
117
+ const seen = new Set([file.scope]);
118
+ file.path.traverse({ Scopable({ scope }) {
119
+ if (seen.has(scope)) return;
120
+ seen.add(scope);
121
+ for (const name of Object.keys(scope.bindings)) authoredBindings.add(name);
122
+ } });
123
+ return {
124
+ names,
125
+ authoredBindings
126
+ };
127
+ }
128
+ function restoreReservations(file, reserved) {
129
+ if (!reserved) return;
130
+ const names = new Set(reserved.names);
131
+ for (const name of names) if (file.scope.uidsSet) file.scope.uidsSet.add(name);
132
+ else file.scope.uids[name] = true;
133
+ const seen = /* @__PURE__ */ new Set();
134
+ const restore = (scope) => {
135
+ if (seen.has(scope)) return;
136
+ seen.add(scope);
137
+ for (const name of Object.keys(scope.bindings)) if (names.has(name) && !reserved.authoredBindings.has(name)) scope.rename(name, scope.generateUid(name));
138
+ };
139
+ restore(file.scope);
140
+ file.path.traverse({ Scopable: ({ scope }) => restore(scope) });
141
+ }
142
+ //#endregion
143
+ //#region src/source-map.ts
144
+ function composeInputMap(nativeMap, printedMap) {
145
+ if (!nativeMap || !printedMap) throw new Error("The native JSX compiler did not provide the requested original-source map");
146
+ const parsed = JSON.parse(nativeMap);
147
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed) || parsed.version !== 3 || !("mappings" in parsed) || typeof parsed.mappings !== "string" || !("sources" in parsed) || !Array.isArray(parsed.sources)) throw new Error("The native JSX compiler returned an invalid source map");
148
+ const composed = remapping([parsed, printedMap], () => null);
149
+ return convertSourceMap.fromObject(composed);
150
+ }
151
+ //#endregion
152
+ //#region src/sfc.ts
153
+ function isVueSfc(filename) {
154
+ return /\.vue$/iu.test(filename);
155
+ }
156
+ function compileSfcForBabel(source, filename, parserOptions, parse, options) {
157
+ const native = getCompiler();
158
+ const scopeId = native.generateSfcScopeId(filename, options.root, options.isProduction, source);
159
+ const result = native.compileSfc(source, {
160
+ filename,
161
+ mode: "module",
162
+ sourceMap: true,
163
+ ssr: options.ssr,
164
+ vapor: options.jsxMode === "vapor",
165
+ scopeId: `data-v-${scopeId}`,
166
+ isProd: options.isProduction,
167
+ inlineTemplate: options.isProduction && !options.ssr
168
+ });
169
+ assertSupported(result, filename, options.sfcStyles);
170
+ const completed = completeModule(result, scopeId, options.sfcStyles);
171
+ return {
172
+ ast: parse(completed.code, {
173
+ ...parserOptions,
174
+ filename,
175
+ sourceType: "module"
176
+ }),
177
+ code: completed.code,
178
+ map: completed.map,
179
+ metadata: {
180
+ defaultMode: options.jsxMode,
181
+ ssr: options.ssr,
182
+ nativePasses: 1,
183
+ warnings: result.warnings,
184
+ css: result.css ?? null,
185
+ styles: result.styles,
186
+ dependencies: result.dependencies ?? []
187
+ }
188
+ };
189
+ }
190
+ function installSfcFileState(file, compiled) {
191
+ file.code = compiled.code;
192
+ if (file.opts.sourceMaps) {
193
+ if (!compiled.map) throw new Error("The native SFC compiler did not provide the requested original-source map");
194
+ const parsed = JSON.parse(compiled.map);
195
+ if (!isSourceMap(parsed)) throw new Error("The native SFC compiler returned an invalid source map");
196
+ const upstream = file.inputMap?.toObject();
197
+ file.inputMap = upstream ? composeInputMap(compiled.map, upstream) : convertSourceMap.fromObject(parsed);
198
+ }
199
+ const old = file.metadata.vize;
200
+ file.metadata.vize = {
201
+ ...typeof old === "object" && old !== null ? old : {},
202
+ sfc: compiled.metadata
203
+ };
204
+ for (const warning of compiled.metadata.warnings) process.emitWarning(`[vize] ${warning}`, { type: "VizeSfcWarning" });
205
+ }
206
+ function assertSupported(result, filename, styleMode) {
207
+ if (result.errors.length > 0) throw new Error(result.errors.join("\n"));
208
+ if (result.customBlocks.length > 0 || result.macroArtifacts.length > 0) throw new Error(`${filename}: custom blocks and emitted macro artifacts need a bundler adapter and are not supported by standalone Babel`);
209
+ for (const style of result.styles) {
210
+ if (style.src) throw new Error(`${filename}: <style src> needs a bundler CSS pipeline and is not supported by standalone Babel`);
211
+ if (style.lang && style.lang !== "css") throw new Error(`${filename}: <style lang="${style.lang}"> needs a bundler CSS pipeline and is not supported by standalone Babel`);
212
+ if (style.content.includes("@import")) throw new Error(`${filename}: CSS @import needs a bundler CSS pipeline and is not supported by standalone Babel`);
213
+ }
214
+ if (result.css && styleMode === "error") throw new Error(`${filename}: SFC styles require sfcStyles: "inject" or "metadata" in standalone Babel`);
215
+ }
216
+ function completeModule(result, scopeId, styleMode) {
217
+ let code = result.code;
218
+ if (result.hasScoped) code = attachScope(code, scopeId);
219
+ const prefix = result.css && styleMode === "inject" ? styleInjection(result.css, scopeId) : "";
220
+ return {
221
+ code: prefix + code,
222
+ map: shiftMap(result.map, prefix)
223
+ };
224
+ }
225
+ function attachScope(code, scopeId) {
226
+ if (/^export default _sfc_main/mu.test(code)) return code.replace(/^export default _sfc_main/mu, `_sfc_main.__scopeId = "data-v-${scopeId}";\nexport default _sfc_main`);
227
+ if (/^export default /mu.test(code)) return `${code.replace(/^export default /mu, "const _sfc_main = ")}\n_sfc_main.__scopeId = "data-v-${scopeId}";\nexport default _sfc_main;\n`;
228
+ if (/^export function render\b/mu.test(code)) return `${code}\nconst _sfc_main = { render };\n_sfc_main.__scopeId = "data-v-${scopeId}";\nexport default _sfc_main;\n`;
229
+ return code;
230
+ }
231
+ function styleInjection(css, scopeId) {
232
+ return `export const __vize_css__ = ${JSON.stringify(css)};
233
+ const __vize_css_id__ = ${JSON.stringify(`vize-style-${scopeId}`)};
234
+ (function() {
235
+ if (typeof document !== "undefined") {
236
+ let style = document.getElementById(__vize_css_id__);
237
+ if (!style) {
238
+ style = document.createElement("style");
239
+ style.id = __vize_css_id__;
240
+ document.head.appendChild(style);
241
+ }
242
+ style.textContent = __vize_css__;
243
+ }
244
+ })();
245
+ `;
246
+ }
247
+ function shiftMap(mapJson, prefix) {
248
+ if (!mapJson) return null;
249
+ if (!prefix) return mapJson;
250
+ const parsed = JSON.parse(mapJson);
251
+ if (!isSourceMap(parsed)) return null;
252
+ const lines = prefix.split("\n").length - 1;
253
+ return JSON.stringify({
254
+ ...parsed,
255
+ mappings: ";".repeat(lines) + parsed.mappings
256
+ });
257
+ }
258
+ function isSourceMap(value) {
259
+ return typeof value === "object" && value !== null && "version" in value && value.version === 3 && "sources" in value && Array.isArray(value.sources) && "names" in value && Array.isArray(value.names) && "mappings" in value && typeof value.mappings === "string";
260
+ }
261
+ //#endregion
262
+ //#region src/index.ts
263
+ const compiled = Symbol.for("@vizejs/babel-plugin/compiled");
264
+ /** Babel owns the returned hooks; consumers configure the strongly typed options. */
265
+ function vizeBabelPlugin(apiValue, rawOptions = {}) {
266
+ const api = apiValue;
267
+ api.assertVersion("^7.29.7 || ^8.0.9");
268
+ const options = normalizeOptions(rawOptions);
269
+ const sfcOutputs = /* @__PURE__ */ new WeakMap();
270
+ const assertComplete = (ast) => {
271
+ if (hasJsx(ast, api.types.VISITOR_KEYS)) throw new Error("JSX was introduced after the native phase; run JSX-producing transforms in an earlier Babel pass");
272
+ };
273
+ return {
274
+ name: "vize-vue-jsx",
275
+ manipulateOptions(host, parser) {
276
+ enableSyntax(host.filename ?? "", parser);
277
+ },
278
+ parserOverride(source, parserOptions, parse) {
279
+ const filename = String(parserOptions.filename ?? parserOptions.sourceFilename ?? parserOptions.sourceFileName ?? "");
280
+ if (!filename || !isVueSfc(filename)) return void 0;
281
+ const compiledSfc = compileSfcForBabel(source, filename, parserOptions, parse, options);
282
+ sfcOutputs.set(compiledSfc.ast, compiledSfc);
283
+ return compiledSfc.ast;
284
+ },
285
+ pre(file) {
286
+ const compiledSfc = sfcOutputs.get(file.ast);
287
+ if (compiledSfc) {
288
+ file.set(compiled, true);
289
+ installSfcFileState(file, compiledSfc);
290
+ return;
291
+ }
292
+ if (!hasJsx(file.ast, api.types.VISITOR_KEYS)) return;
293
+ if (file.get(compiled)) throw new Error("JSX was introduced after this file's native compilation pass");
294
+ rejectForeignRuntime(file.ast, file.opts.plugins, file.scope);
295
+ const reservations = captureReservations(file);
296
+ const maps = Boolean(file.opts.sourceMaps);
297
+ const hostGenerator = file.opts.generatorOpts;
298
+ const printed = api.transformFromAstSync(file.ast, file.code, {
299
+ filename: file.opts.filename,
300
+ sourceFileName: file.opts.sourceFileName,
301
+ sourceRoot: file.opts.sourceRoot,
302
+ babelrc: false,
303
+ configFile: false,
304
+ plugins: [],
305
+ presets: [],
306
+ ast: false,
307
+ sourceMaps: maps,
308
+ inputSourceMap: maps ? file.inputMap?.toObject() ?? false : false,
309
+ comments: file.opts.comments,
310
+ generatorOpts: {
311
+ ...typeof hostGenerator === "object" && hostGenerator !== null ? hostGenerator : {},
312
+ sourceMaps: maps
313
+ }
314
+ });
315
+ if (!printed || typeof printed.code !== "string") throw new Error("Babel could not serialize the current JSX AST");
316
+ const parserPlugins = file.opts.parserOpts?.plugins ?? [];
317
+ const typescript = /\.(?:tsx|ts|mts|cts)$/iu.test(file.opts.filename ?? "") || parserPlugins.some((p) => (Array.isArray(p) ? p[0] : p) === "typescript");
318
+ const output = getCompiler().compileJsx(printed.code, {
319
+ filename: file.opts.filename,
320
+ lang: typescript ? "tsx" : "jsx",
321
+ jsxMode: options.jsxMode,
322
+ jsxCompat: options.jsxCompat,
323
+ ssr: options.ssr,
324
+ sourceMap: maps
325
+ });
326
+ file.set(compiled, true);
327
+ if (output.errors.length > 0) throw new Error(output.errors.join("\n"));
328
+ if (output.scopedStyles.length > 0 && options.scopedStyles === "error") throw new Error("Scoped JSX CSS requires a bundler consumer; set scopedStyles: \"metadata\" and consume file.metadata.vize.jsx.scopedStyles");
329
+ const generated = api.parseSync(output.code, {
330
+ filename: file.opts.filename,
331
+ babelrc: false,
332
+ configFile: false,
333
+ plugins: [],
334
+ presets: [],
335
+ sourceType: "module",
336
+ parserOpts: {
337
+ ...file.opts.parserOpts,
338
+ sourceType: "module"
339
+ }
340
+ });
341
+ if (!generated || hasJsx(generated, api.types.VISITOR_KEYS)) throw new Error("The native JSX compiler did not return a complete JSX-free module");
342
+ const inputMap = maps ? composeInputMap(output.map, printed.map) : null;
343
+ file.path.replaceWith(generated.program);
344
+ for (const key of [
345
+ "comments",
346
+ "tokens",
347
+ "errors",
348
+ "start",
349
+ "end",
350
+ "loc"
351
+ ]) if (key in generated) file.ast[key] = generated[key];
352
+ else delete file.ast[key];
353
+ file.scope = file.path.scope;
354
+ file.scope.crawl();
355
+ restoreReservations(file, reservations);
356
+ file.code = output.code;
357
+ file.inputMap = inputMap;
358
+ const old = file.metadata.vize;
359
+ const metadata = {
360
+ defaultMode: options.jsxMode,
361
+ ssr: options.ssr,
362
+ nativePasses: 1,
363
+ warnings: output.warnings,
364
+ scopedStyles: output.scopedStyles
365
+ };
366
+ file.metadata.vize = {
367
+ ...typeof old === "object" && old !== null ? old : {},
368
+ jsx: metadata
369
+ };
370
+ for (const warning of output.warnings) process.emitWarning(`[vize] ${warning}`, { type: "VizeJsxWarning" });
371
+ },
372
+ post(file) {
373
+ assertComplete(file.ast);
374
+ },
375
+ generatorOverride(ast) {
376
+ assertComplete(ast);
377
+ }
378
+ };
379
+ }
380
+ //#endregion
381
+ export { vizeBabelPlugin as default };
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@vizejs/babel-plugin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.705.0",
4
+ "description": "Vue JSX and TSX compilation through the shared Vize compiler",
5
+ "homepage": "https://github.com/ubugeeei-prod/vize",
6
+ "bugs": {
7
+ "url": "https://github.com/ubugeeei-prod/vize/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/ubugeeei-prod/vize",
13
+ "directory": "npm/babel"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "type": "module",
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.mjs",
22
+ "types": "./dist/index.d.mts",
23
+ "exports": {
24
+ ".": {
25
+ "import": {
26
+ "types": "./dist/index.d.mts",
27
+ "default": "./dist/index.mjs"
28
+ },
29
+ "require": {
30
+ "types": "./dist/index.d.cts",
31
+ "default": "./dist/index.cjs"
32
+ }
33
+ }
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "dependencies": {
39
+ "@jridgewell/remapping": "2.3.5",
40
+ "@vizejs/native": "0.705.0",
41
+ "convert-source-map": "2.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@babel/core": "8.0.9",
45
+ "@types/convert-source-map": "2.0.3",
46
+ "@types/node": "25.9.2",
47
+ "playwright": "1.62.1",
48
+ "typescript": "6.0.3",
49
+ "vite-plus": "0.1.24",
50
+ "vue": "3.6.0-rc.11"
51
+ },
52
+ "peerDependencies": {
53
+ "@babel/core": "^7.29.7 || ^8.0.9"
54
+ },
55
+ "engines": {
56
+ "node": "^22.18.0 || >=24.11.0"
57
+ },
58
+ "metadata": {
59
+ "vize": {
60
+ "stability": "compatibility-preview",
61
+ "stabilityDocs": "https://github.com/ubugeeei-prod/vize/blob/main/docs/content/stability.md#package-support-tiers"
62
+ }
63
+ },
64
+ "scripts": {
65
+ "build": "vp pack",
66
+ "check": "vp check src vite.config.ts && tsc -p test/tsconfig.json",
67
+ "test": "node --test test/packed-hosts.test.ts test/runtime-renderers.test.ts test/style-guards.test.ts"
68
+ }
6
69
  }