@vizejs/vite-plugin 0.0.1-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # @vizejs/vite-plugin
2
+
3
+ High-performance native Vite plugin for Vue SFC compilation powered by [Vize](https://github.com/ubugeeei/vize).
4
+
5
+ ## Features
6
+
7
+ - **Native Performance**: Uses Rust-based compiler via Node.js native bindings (NAPI)
8
+ - **Pre-compilation**: All `.vue` files are compiled at server startup for instant module resolution
9
+ - **Virtual Modules**: Compiled code is served from memory as virtual modules
10
+ - **HMR Support**: Hot Module Replacement with automatic re-compilation on file changes
11
+ - **Vapor Mode**: Optional support for Vue Vapor mode compilation
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ # npm
17
+ npm install @vizejs/vite-plugin
18
+
19
+ # pnpm
20
+ pnpm add @vizejs/vite-plugin
21
+
22
+ # yarn
23
+ yarn add @vizejs/vite-plugin
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Vite
29
+
30
+ ```ts
31
+ // vite.config.ts
32
+ import { defineConfig } from 'vite'
33
+ import { vizeNative } from '@vizejs/vite-plugin'
34
+
35
+ export default defineConfig({
36
+ plugins: [
37
+ vizeNative({
38
+ // options
39
+ })
40
+ ]
41
+ })
42
+ ```
43
+
44
+ ### Nuxt
45
+
46
+ For Nuxt 3, add the plugin to your `nuxt.config.ts`:
47
+
48
+ ```ts
49
+ // nuxt.config.ts
50
+ import { vizeNative } from '@vizejs/vite-plugin'
51
+
52
+ export default defineNuxtConfig({
53
+ vite: {
54
+ plugins: [
55
+ vizeNative({
56
+ // Exclude Nuxt's internal .vue files if needed
57
+ exclude: [/node_modules/, /#/, /\.nuxt/]
58
+ })
59
+ ]
60
+ },
61
+
62
+ // Disable the default Vue plugin
63
+ vue: {
64
+ propsDestructure: false
65
+ }
66
+ })
67
+ ```
68
+
69
+ **Note**: When using with Nuxt, you may need to disable Nuxt's built-in Vue plugin to avoid conflicts:
70
+
71
+ ```ts
72
+ // nuxt.config.ts
73
+ export default defineNuxtConfig({
74
+ hooks: {
75
+ 'vite:extendConfig': (config) => {
76
+ // Remove @vitejs/plugin-vue from plugins
77
+ config.plugins = config.plugins?.filter(
78
+ (p) => p && (Array.isArray(p) ? p[0] : p).name !== 'vite:vue'
79
+ )
80
+ }
81
+ },
82
+ vite: {
83
+ plugins: [
84
+ vizeNative()
85
+ ]
86
+ }
87
+ })
88
+ ```
89
+
90
+ ## Options
91
+
92
+ ```ts
93
+ interface VizeNativeOptions {
94
+ /**
95
+ * Files to include in compilation
96
+ * @default /\.vue$/
97
+ */
98
+ include?: string | RegExp | (string | RegExp)[]
99
+
100
+ /**
101
+ * Files to exclude from compilation
102
+ * @default /node_modules/
103
+ */
104
+ exclude?: string | RegExp | (string | RegExp)[]
105
+
106
+ /**
107
+ * Force production mode
108
+ * @default auto-detected from Vite config
109
+ */
110
+ isProduction?: boolean
111
+
112
+ /**
113
+ * Enable SSR mode
114
+ * @default false
115
+ */
116
+ ssr?: boolean
117
+
118
+ /**
119
+ * Enable source map generation
120
+ * @default true in development, false in production
121
+ */
122
+ sourceMap?: boolean
123
+
124
+ /**
125
+ * Enable Vapor mode compilation
126
+ * @default false
127
+ */
128
+ vapor?: boolean
129
+
130
+ /**
131
+ * Root directory to scan for .vue files
132
+ * @default Vite's root
133
+ */
134
+ root?: string
135
+
136
+ /**
137
+ * Glob patterns to scan for .vue files during pre-compilation
138
+ * @default ['**\/*.vue']
139
+ */
140
+ scanPatterns?: string[]
141
+
142
+ /**
143
+ * Glob patterns to ignore during pre-compilation
144
+ * @default ['node_modules/**', 'dist/**', '.git/**']
145
+ */
146
+ ignorePatterns?: string[]
147
+ }
148
+ ```
149
+
150
+ ## How It Works
151
+
152
+ ### Pre-compilation at Startup
153
+
154
+ When the Vite dev server starts (or build begins), the plugin:
155
+
156
+ 1. Scans the project root for all `.vue` files matching the configured patterns
157
+ 2. Compiles each file using the native Vize compiler
158
+ 3. Stores the compiled JavaScript and CSS in an in-memory cache
159
+
160
+ This approach leverages Vize's exceptional performance - compiling 15,000 SFC files in under 500ms with multi-threading.
161
+
162
+ ### Virtual Module Resolution
163
+
164
+ When Vite requests a `.vue` file:
165
+
166
+ 1. The plugin intercepts the module resolution
167
+ 2. Returns the pre-compiled code from cache (or compiles on-demand if not cached)
168
+ 3. CSS is injected inline with deduplication support
169
+
170
+ ### HMR (Hot Module Replacement)
171
+
172
+ When a `.vue` file changes:
173
+
174
+ 1. The plugin detects the change via `handleHotUpdate`
175
+ 2. Re-compiles only the changed file
176
+ 3. Updates the cache
177
+ 4. Vite handles the rest of the HMR flow
178
+
179
+ ## Performance
180
+
181
+ Vize's native compiler is significantly faster than the official Vue compiler:
182
+
183
+ | Benchmark (15,000 SFCs) | @vue/compiler-sfc | Vize | Speedup |
184
+ |-------------------------|-------------------|------|---------|
185
+ | Single-threaded | 16.21s | 6.65s | **2.4x** |
186
+ | Multi-threaded | 4.13s | 498ms | **8.3x** |
187
+
188
+ ## Comparison with vite-plugin-vize
189
+
190
+ | Feature | vite-plugin-vize | vite-plugin-vize |
191
+ |---------|------------------|-------------------------|
192
+ | Compiler | WASM | Native (NAPI) |
193
+ | Pre-compilation | No | Yes |
194
+ | Module Loading | Transform | Virtual Module (Load) |
195
+ | Performance | Fast | Fastest |
196
+ | Platform | Any | Node.js only |
197
+
198
+ Use `vite-plugin-vize` (WASM-based) when you need:
199
+ - Browser compatibility (e.g., StackBlitz, WebContainers)
200
+ - Platform-independent deployment
201
+
202
+ Use `vite-plugin-vize` when you need:
203
+ - Maximum performance
204
+ - Server-side only (standard Node.js environment)
205
+
206
+ ## Requirements
207
+
208
+ - Node.js 18+
209
+ - Vite 5.0+ / 6.0+ / 7.0+
210
+ - Vue 3.x
211
+
212
+ ## License
213
+
214
+ MIT
@@ -0,0 +1,6 @@
1
+ import type { CompileSfcFn, CompiledModule } from './types.js';
2
+ export declare function loadNative(): CompileSfcFn;
3
+ export declare function compileFile(filePath: string, cache: Map<string, CompiledModule>, options: {
4
+ sourceMap: boolean;
5
+ ssr: boolean;
6
+ }, source?: string): CompiledModule;
@@ -0,0 +1,46 @@
1
+ import fs from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { generateScopeId } from './utils.js';
4
+ const require = createRequire(import.meta.url);
5
+ let compileSfc = null;
6
+ export function loadNative() {
7
+ if (compileSfc)
8
+ return compileSfc;
9
+ try {
10
+ const native = require('@vizejs/native');
11
+ compileSfc = native.compileSfc;
12
+ return compileSfc;
13
+ }
14
+ catch (e) {
15
+ throw new Error(`Failed to load @vizejs/native. Make sure it's installed and built:\n${e}`);
16
+ }
17
+ }
18
+ export function compileFile(filePath, cache, options, source) {
19
+ const compile = loadNative();
20
+ const content = source ?? fs.readFileSync(filePath, 'utf-8');
21
+ const scopeId = generateScopeId(filePath);
22
+ const hasScoped = /<style[^>]*\bscoped\b/.test(content);
23
+ const result = compile(content, {
24
+ filename: filePath,
25
+ sourceMap: options.sourceMap,
26
+ ssr: options.ssr,
27
+ scopeId: hasScoped ? `data-v-${scopeId}` : undefined,
28
+ });
29
+ if (result.errors.length > 0) {
30
+ const errorMsg = result.errors.join('\n');
31
+ console.error(`[vize] Compilation error in ${filePath}:\n${errorMsg}`);
32
+ }
33
+ if (result.warnings.length > 0) {
34
+ result.warnings.forEach((warning) => {
35
+ console.warn(`[vize] Warning in ${filePath}: ${warning}`);
36
+ });
37
+ }
38
+ const compiled = {
39
+ code: result.code,
40
+ css: result.css,
41
+ scopeId,
42
+ hasScoped,
43
+ };
44
+ cache.set(filePath, compiled);
45
+ return compiled;
46
+ }
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { VizeOptions, CompiledModule } from './types.js';
3
+ export type { VizeOptions, CompiledModule };
4
+ export declare function vize(options?: VizeOptions): Plugin;
5
+ export default vize;
package/dist/index.js ADDED
@@ -0,0 +1,141 @@
1
+ import path from 'node:path';
2
+ import { glob } from 'tinyglobby';
3
+ import { compileFile } from './compiler.js';
4
+ import { createFilter, generateOutput } from './utils.js';
5
+ const VIRTUAL_PREFIX = '\0vize:';
6
+ export function vize(options = {}) {
7
+ const filter = createFilter(options.include, options.exclude);
8
+ const cache = new Map();
9
+ // Map from virtual ID to real file path
10
+ const virtualToReal = new Map();
11
+ let isProduction;
12
+ let root;
13
+ let server = null;
14
+ const scanPatterns = options.scanPatterns ?? ['**/*.vue'];
15
+ const ignorePatterns = options.ignorePatterns ?? [
16
+ 'node_modules/**',
17
+ 'dist/**',
18
+ '.git/**',
19
+ ];
20
+ async function compileAll() {
21
+ const startTime = performance.now();
22
+ const files = await glob(scanPatterns, {
23
+ cwd: root,
24
+ ignore: ignorePatterns,
25
+ absolute: true,
26
+ });
27
+ console.log(`[vize] Pre-compiling ${files.length} Vue files...`);
28
+ let successCount = 0;
29
+ let errorCount = 0;
30
+ for (const file of files) {
31
+ try {
32
+ compileFile(file, cache, {
33
+ sourceMap: options.sourceMap ?? !isProduction,
34
+ ssr: options.ssr ?? false,
35
+ });
36
+ successCount++;
37
+ }
38
+ catch (e) {
39
+ errorCount++;
40
+ console.error(`[vize] Failed to compile ${file}:`, e);
41
+ }
42
+ }
43
+ const elapsed = (performance.now() - startTime).toFixed(2);
44
+ console.log(`[vize] Pre-compilation complete: ${successCount} succeeded, ${errorCount} failed (${elapsed}ms)`);
45
+ }
46
+ function resolveVuePath(id, importer) {
47
+ let resolved;
48
+ if (path.isAbsolute(id)) {
49
+ resolved = id;
50
+ }
51
+ else if (importer) {
52
+ // Remove virtual prefix from importer if present
53
+ const realImporter = importer.startsWith(VIRTUAL_PREFIX)
54
+ ? virtualToReal.get(importer) ?? importer.slice(VIRTUAL_PREFIX.length)
55
+ : importer;
56
+ resolved = path.resolve(path.dirname(realImporter), id);
57
+ }
58
+ else {
59
+ resolved = path.resolve(root, id);
60
+ }
61
+ return path.normalize(resolved);
62
+ }
63
+ return {
64
+ name: 'vite-plugin-vize',
65
+ enforce: 'pre',
66
+ configResolved(resolvedConfig) {
67
+ isProduction = options.isProduction ?? resolvedConfig.isProduction;
68
+ root = options.root ?? resolvedConfig.root;
69
+ },
70
+ configureServer(devServer) {
71
+ server = devServer;
72
+ },
73
+ async buildStart() {
74
+ await compileAll();
75
+ },
76
+ resolveId(id, importer) {
77
+ if (id.includes('?vue&type=style')) {
78
+ return id;
79
+ }
80
+ if (id.endsWith('.vue')) {
81
+ const resolved = resolveVuePath(id, importer);
82
+ // Return virtual module ID if cached
83
+ if (cache.has(resolved)) {
84
+ const virtualId = VIRTUAL_PREFIX + resolved;
85
+ virtualToReal.set(virtualId, resolved);
86
+ return virtualId;
87
+ }
88
+ }
89
+ return null;
90
+ },
91
+ load(id) {
92
+ if (id.includes('?vue&type=style')) {
93
+ const [filename] = id.split('?');
94
+ const realPath = filename.startsWith(VIRTUAL_PREFIX)
95
+ ? virtualToReal.get(filename) ?? filename.slice(VIRTUAL_PREFIX.length)
96
+ : filename;
97
+ const compiled = cache.get(realPath);
98
+ if (compiled?.css) {
99
+ return compiled.css;
100
+ }
101
+ return '';
102
+ }
103
+ // Handle virtual module
104
+ if (id.startsWith(VIRTUAL_PREFIX)) {
105
+ const realPath = virtualToReal.get(id) ?? id.slice(VIRTUAL_PREFIX.length);
106
+ const compiled = cache.get(realPath);
107
+ if (compiled) {
108
+ return {
109
+ code: generateOutput(compiled, isProduction, server !== null),
110
+ map: null,
111
+ };
112
+ }
113
+ }
114
+ return null;
115
+ },
116
+ async handleHotUpdate(ctx) {
117
+ const { file, server, read } = ctx;
118
+ if (file.endsWith('.vue') && filter(file)) {
119
+ try {
120
+ const source = await read();
121
+ compileFile(file, cache, {
122
+ sourceMap: options.sourceMap ?? !isProduction,
123
+ ssr: options.ssr ?? false,
124
+ }, source);
125
+ console.log(`[vize] Re-compiled: ${path.relative(root, file)}`);
126
+ }
127
+ catch (e) {
128
+ console.error(`[vize] Re-compilation failed for ${file}:`, e);
129
+ }
130
+ // Find the virtual module for this file
131
+ const virtualId = VIRTUAL_PREFIX + file;
132
+ const modules = server.moduleGraph.getModulesByFile(virtualId)
133
+ ?? server.moduleGraph.getModulesByFile(file);
134
+ if (modules) {
135
+ return [...modules];
136
+ }
137
+ }
138
+ },
139
+ };
140
+ }
141
+ export default vize;
@@ -0,0 +1,66 @@
1
+ export interface SfcCompileOptionsNapi {
2
+ filename?: string;
3
+ sourceMap?: boolean;
4
+ ssr?: boolean;
5
+ scopeId?: string;
6
+ }
7
+ export interface SfcCompileResultNapi {
8
+ code: string;
9
+ css?: string;
10
+ errors: string[];
11
+ warnings: string[];
12
+ }
13
+ export type CompileSfcFn = (source: string, options?: SfcCompileOptionsNapi) => SfcCompileResultNapi;
14
+ export interface VizeOptions {
15
+ /**
16
+ * Files to include in compilation
17
+ * @default /\.vue$/
18
+ */
19
+ include?: string | RegExp | (string | RegExp)[];
20
+ /**
21
+ * Files to exclude from compilation
22
+ * @default /node_modules/
23
+ */
24
+ exclude?: string | RegExp | (string | RegExp)[];
25
+ /**
26
+ * Force production mode
27
+ * @default auto-detected from Vite config
28
+ */
29
+ isProduction?: boolean;
30
+ /**
31
+ * Enable SSR mode
32
+ * @default false
33
+ */
34
+ ssr?: boolean;
35
+ /**
36
+ * Enable source map generation
37
+ * @default true in development, false in production
38
+ */
39
+ sourceMap?: boolean;
40
+ /**
41
+ * Enable Vapor mode compilation
42
+ * @default false
43
+ */
44
+ vapor?: boolean;
45
+ /**
46
+ * Root directory to scan for .vue files
47
+ * @default Vite's root
48
+ */
49
+ root?: string;
50
+ /**
51
+ * Glob patterns to scan for .vue files during pre-compilation
52
+ * @default ['**\/*.vue']
53
+ */
54
+ scanPatterns?: string[];
55
+ /**
56
+ * Glob patterns to ignore during pre-compilation
57
+ * @default ['node_modules/**', 'dist/**', '.git/**']
58
+ */
59
+ ignorePatterns?: string[];
60
+ }
61
+ export interface CompiledModule {
62
+ code: string;
63
+ css?: string;
64
+ scopeId: string;
65
+ hasScoped: boolean;
66
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { CompiledModule } from './types.js';
2
+ export declare function generateScopeId(filename: string): string;
3
+ export declare function createFilter(include?: string | RegExp | (string | RegExp)[], exclude?: string | RegExp | (string | RegExp)[]): (id: string) => boolean;
4
+ export declare function generateOutput(compiled: CompiledModule, isProduction: boolean, isDev: boolean): string;
package/dist/utils.js ADDED
@@ -0,0 +1,71 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function generateScopeId(filename) {
3
+ const hash = createHash('sha256').update(filename).digest('hex');
4
+ return hash.slice(0, 8);
5
+ }
6
+ export function createFilter(include, exclude) {
7
+ const includePatterns = include
8
+ ? Array.isArray(include)
9
+ ? include
10
+ : [include]
11
+ : [/\.vue$/];
12
+ const excludePatterns = exclude
13
+ ? Array.isArray(exclude)
14
+ ? exclude
15
+ : [exclude]
16
+ : [/node_modules/];
17
+ return (id) => {
18
+ const matchInclude = includePatterns.some((pattern) => typeof pattern === 'string' ? id.includes(pattern) : pattern.test(id));
19
+ const matchExclude = excludePatterns.some((pattern) => typeof pattern === 'string' ? id.includes(pattern) : pattern.test(id));
20
+ return matchInclude && !matchExclude;
21
+ };
22
+ }
23
+ export function generateOutput(compiled, isProduction, isDev) {
24
+ let output = compiled.code;
25
+ // Rewrite "export default" to named variable for HMR
26
+ const hasExportDefault = output.includes('export default');
27
+ if (hasExportDefault) {
28
+ output = output.replace('export default', 'const _sfc_main =');
29
+ output += '\nexport default _sfc_main;';
30
+ }
31
+ // Inject CSS
32
+ if (compiled.css) {
33
+ const cssCode = JSON.stringify(compiled.css);
34
+ const cssId = JSON.stringify(`vize-style-${compiled.scopeId}`);
35
+ output = `
36
+ const __vize_css__ = ${cssCode};
37
+ const __vize_css_id__ = ${cssId};
38
+ (function() {
39
+ if (typeof document !== 'undefined') {
40
+ let style = document.getElementById(__vize_css_id__);
41
+ if (!style) {
42
+ style = document.createElement('style');
43
+ style.id = __vize_css_id__;
44
+ style.textContent = __vize_css__;
45
+ document.head.appendChild(style);
46
+ } else {
47
+ style.textContent = __vize_css__;
48
+ }
49
+ }
50
+ })();
51
+ ${output}`;
52
+ }
53
+ // Add HMR support in development
54
+ if (!isProduction && isDev && hasExportDefault) {
55
+ output += `
56
+ if (import.meta.hot) {
57
+ _sfc_main.__hmrId = ${JSON.stringify(compiled.scopeId)};
58
+ import.meta.hot.accept((mod) => {
59
+ if (!mod) return;
60
+ const { default: updated } = mod;
61
+ if (typeof __VUE_HMR_RUNTIME__ !== 'undefined') {
62
+ __VUE_HMR_RUNTIME__.reload(_sfc_main.__hmrId, updated);
63
+ }
64
+ });
65
+ if (typeof __VUE_HMR_RUNTIME__ !== 'undefined') {
66
+ __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main);
67
+ }
68
+ }`;
69
+ }
70
+ return output;
71
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@vizejs/vite-plugin",
3
+ "version": "0.0.1-alpha.8",
4
+ "description": "High-performance native Vite plugin for Vue SFC compilation powered by Vize",
5
+ "publishConfig": {
6
+ "provenance": false,
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "types": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsc --watch"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "keywords": [
26
+ "vite",
27
+ "vue",
28
+ "plugin",
29
+ "sfc",
30
+ "compiler",
31
+ "native",
32
+ "fast"
33
+ ],
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "@vizejs/native": "workspace:*",
37
+ "tinyglobby": "^0.2.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "typescript": "~5.6.0",
42
+ "vite": "^7.3.1"
43
+ },
44
+ "peerDependencies": {
45
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
46
+ }
47
+ }