@vizejs/babel-plugin 0.0.0-stage → 0.706.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 +21 -0
- package/README.md +134 -2
- package/dist/index.cjs +405 -0
- package/dist/index.d.cts +41 -0
- package/dist/index.d.mts +41 -0
- package/dist/index.mjs +381 -0
- package/package.json +66 -3
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
|
-
#
|
|
1
|
+
# @vizejs/babel-plugin
|
|
2
2
|
|
|
3
|
-
|
|
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;
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.mts
ADDED
|
@@ -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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.706.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.706.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
|
}
|