@vizejs/vite-plugin 0.284.0 → 0.286.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.
Files changed (2) hide show
  1. package/dist/index.mjs +107 -9
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -2,11 +2,12 @@ import { createRequire } from "node:module";
2
2
  import { createHash } from "node:crypto";
3
3
  import * as native from "@vizejs/native";
4
4
  import { applyViteDefineReplacements, buildInspectorGraph, chunkVitePrecompileFiles, classifyVitePluginRequest, createViteBareImportBases, createViteBareImportCandidates, createViteVirtualId, detectViteHmrUpdateType, diffVitePrecompileFiles, generateViteHmrCode, hasViteHmrChanges, isViteBareSpecifier, normalizeViteCssModuleFilename, normalizeViteDevMiddlewareUrl, normalizeVitePrecompileBatchSize, normalizeViteRequireBase, normalizeViteResolvedVuePath, resolveViteAliasRequest, resolveViteCssImports, resolveViteRelativeImport, resolveViteVuePath, rewriteViteDynamicTemplateImports, rewriteViteImportMetaGlobBase, rewriteViteStaticAssetUrls, scopeViteCssForPipeline, shouldApplyViteDefineInVirtualModule, splitViteIdQuery, toViteBrowserImportPrefix, transformViteCssVarsForPipeline } from "@vizejs/native";
5
+ import * as vite from "vite";
6
+ import { parseSync } from "vite";
5
7
  import fs from "node:fs";
6
8
  import { glob } from "tinyglobby";
7
9
  import path from "node:path";
8
10
  import { pathToFileURL } from "node:url";
9
- import * as vite from "vite";
10
11
  //#region src/hmr.ts
11
12
  function hasHmrChanges(prev, next) {
12
13
  if (!prev) return true;
@@ -74,6 +75,103 @@ function styleFingerprint(module) {
74
75
  });
75
76
  }
76
77
  //#endregion
78
+ //#region src/utils/module-output.ts
79
+ const OUTPUT_PARSE_ID = "vize-output.tsx";
80
+ const SFC_MAIN_NAME = "_sfc_main";
81
+ function isNode(value) {
82
+ return value != null && typeof value === "object" && typeof value.type === "string";
83
+ }
84
+ function getNodeStart(node) {
85
+ return typeof node?.start === "number" ? node.start : null;
86
+ }
87
+ function getNodeName(node) {
88
+ return isNode(node) && typeof node.name === "string" ? node.name : null;
89
+ }
90
+ function parseProgram(code) {
91
+ try {
92
+ const result = parseSync(OUTPUT_PARSE_ID, code);
93
+ if (result != null && typeof result === "object") {
94
+ const errors = result.errors;
95
+ if (Array.isArray(errors) && errors.length > 0) return null;
96
+ const program = result.program;
97
+ if (isNode(program)) return program;
98
+ }
99
+ return isNode(result) ? result : null;
100
+ } catch {
101
+ return null;
102
+ }
103
+ }
104
+ function getProgramBody(program) {
105
+ if (!program || !Array.isArray(program.body)) return [];
106
+ return program.body.filter(isNode);
107
+ }
108
+ function isIdentifierNamed(node, name) {
109
+ return getNodeName(node) === name;
110
+ }
111
+ function getVariableDeclarationNames(statement) {
112
+ return (Array.isArray(statement.declarations) ? statement.declarations : []).filter(isNode).map((declaration) => isNode(declaration.id) ? getNodeName(declaration.id) : null).filter((name) => name != null);
113
+ }
114
+ function getExportedNames(statement) {
115
+ const declaration = isNode(statement.declaration) ? statement.declaration : null;
116
+ const names = [];
117
+ if (declaration?.type === "FunctionDeclaration" || declaration?.type === "ClassDeclaration") {
118
+ const name = isNode(declaration.id) ? getNodeName(declaration.id) : null;
119
+ if (name) names.push(name);
120
+ }
121
+ if (declaration?.type === "VariableDeclaration") names.push(...getVariableDeclarationNames(declaration));
122
+ const specifiers = Array.isArray(statement.specifiers) ? statement.specifiers : [];
123
+ for (const specifier of specifiers) {
124
+ if (!isNode(specifier)) continue;
125
+ const exported = isNode(specifier.exported) ? specifier.exported : null;
126
+ const local = isNode(specifier.local) ? specifier.local : null;
127
+ const name = getNodeName(exported) ?? getNodeName(local);
128
+ if (name) names.push(name);
129
+ }
130
+ return names;
131
+ }
132
+ function findDefaultExport(program) {
133
+ return getProgramBody(program).find((statement) => statement.type === "ExportDefaultDeclaration") ?? null;
134
+ }
135
+ function getExportDefaultKeywordEnd(code, defaultExport) {
136
+ const exportStart = getNodeStart(defaultExport);
137
+ if (exportStart == null) return null;
138
+ const match = /^export\s+default\b/.exec(code.slice(exportStart));
139
+ return match ? exportStart + match[0].length : null;
140
+ }
141
+ function analyzeModuleOutput(code) {
142
+ const program = parseProgram(code);
143
+ const body = getProgramBody(program);
144
+ const defaultExport = findDefaultExport(program);
145
+ const exportedNames = body.filter((statement) => statement.type === "ExportNamedDeclaration").flatMap(getExportedNames);
146
+ return {
147
+ hasDefaultExport: defaultExport != null,
148
+ hasSfcMainDefined: body.some((statement) => {
149
+ return statement.type === "VariableDeclaration" && getVariableDeclarationNames(statement).includes(SFC_MAIN_NAME);
150
+ }),
151
+ hasNamedRenderExport: exportedNames.includes("render"),
152
+ hasNamedSsrRenderExport: exportedNames.includes("ssrRender")
153
+ };
154
+ }
155
+ function rewriteDefaultExportToSfcMain(code) {
156
+ const defaultExport = findDefaultExport(parseProgram(code));
157
+ const exportStart = getNodeStart(defaultExport);
158
+ const keywordEnd = defaultExport ? getExportDefaultKeywordEnd(code, defaultExport) : null;
159
+ if (exportStart == null || keywordEnd == null) return code;
160
+ return `${code.slice(0, exportStart)}const ${SFC_MAIN_NAME} =${code.slice(keywordEnd)}`;
161
+ }
162
+ function insertBeforeSfcMainDefaultExport(code, insertion, options = {}) {
163
+ const defaultExport = findDefaultExport(parseProgram(code));
164
+ const declaration = isNode(defaultExport?.declaration) ? defaultExport.declaration : null;
165
+ const exportStart = getNodeStart(defaultExport);
166
+ const exportEnd = typeof defaultExport?.end === "number" ? defaultExport.end : null;
167
+ if (!isIdentifierNamed(declaration, SFC_MAIN_NAME) || exportStart == null) return code;
168
+ if (options.normalizeSemicolon && exportEnd != null) {
169
+ const suffixStart = code[exportEnd] === ";" ? exportEnd + 1 : exportEnd;
170
+ return `${code.slice(0, exportStart)}${insertion}\nexport default ${SFC_MAIN_NAME};${code.slice(suffixStart)}`;
171
+ }
172
+ return `${code.slice(0, exportStart)}${insertion}\n${code.slice(exportStart)}`;
173
+ }
174
+ //#endregion
77
175
  //#region src/utils/css.ts
78
176
  function scopeCssForPipeline(css, scopeId) {
79
177
  return scopeViteCssForPipeline(css, scopeId);
@@ -249,17 +347,17 @@ function generateScopeId(filename) {
249
347
  function generateOutput(compiled, options) {
250
348
  const { isProduction, isDev, ssr, hmrUpdateType, extractCss, filePath } = options;
251
349
  let output = compiled.code;
252
- const exportDefaultRegex = /^export default /m;
253
- const hasExportDefault = exportDefaultRegex.test(output);
254
- const hasNamedRenderExport = /^export function render\b/m.test(output);
255
- const hasNamedSsrRenderExport = /^export function ssrRender\b/m.test(output);
256
- const hasSfcMainDefined = /\bconst\s+_sfc_main\s*=/.test(output);
350
+ const moduleInfo = analyzeModuleOutput(output);
351
+ const hasExportDefault = moduleInfo.hasDefaultExport;
352
+ const hasNamedRenderExport = moduleInfo.hasNamedRenderExport;
353
+ const hasNamedSsrRenderExport = moduleInfo.hasNamedSsrRenderExport;
354
+ const hasSfcMainDefined = moduleInfo.hasSfcMainDefined;
257
355
  if (hasExportDefault && !hasSfcMainDefined) {
258
- output = output.replace(exportDefaultRegex, "const _sfc_main = ");
356
+ output = rewriteDefaultExportToSfcMain(output);
259
357
  if (compiled.hasScoped && compiled.scopeId) output += `\n_sfc_main.__scopeId = "data-v-${compiled.scopeId}";`;
260
358
  output += "\nexport default _sfc_main;";
261
359
  } else if (hasExportDefault && hasSfcMainDefined) {
262
- if (compiled.hasScoped && compiled.scopeId) output = output.replace(/^export default _sfc_main/m, `_sfc_main.__scopeId = "data-v-${compiled.scopeId}";\nexport default _sfc_main`);
360
+ if (compiled.hasScoped && compiled.scopeId) output = insertBeforeSfcMainDefaultExport(output, `_sfc_main.__scopeId = "data-v-${compiled.scopeId}";`);
263
361
  } else if (!hasExportDefault && !hasSfcMainDefined && hasNamedRenderExport) {
264
362
  output += "\nconst _sfc_main = {};";
265
363
  if (compiled.hasScoped && compiled.scopeId) output += `\n_sfc_main.__scopeId = "data-v-${compiled.scopeId}";`;
@@ -304,7 +402,7 @@ function generateOutput(compiled, options) {
304
402
  });
305
403
  }
306
404
  const cssModuleSetup = moduleBindings.map((m) => `_sfc_main.__cssModules = _sfc_main.__cssModules || {};\n_sfc_main.__cssModules[${JSON.stringify(m.name)}] = ${m.bindingName};`).join("\n");
307
- output = output.replace(/^export default _sfc_main;?$/m, `${cssModuleSetup}\nexport default _sfc_main;`);
405
+ output = insertBeforeSfcMainDefaultExport(output, cssModuleSetup, { normalizeSemicolon: true });
308
406
  }
309
407
  } else if (!ssr && compiled.css && !(isProduction && extractCss)) output = prependInlineStyleInjection(output, compiled.css, compiled.scopeId);
310
408
  if (!isProduction && isDev && hasExportDefault) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizejs/vite-plugin",
3
- "version": "0.284.0",
3
+ "version": "0.286.0",
4
4
  "description": "High-performance native Vite plugin for Vue SFC compilation powered by Vize",
5
5
  "keywords": [
6
6
  "compiler",
@@ -40,9 +40,9 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@vizejs/native": "0.284.0",
43
+ "@vizejs/native": "0.286.0",
44
44
  "tinyglobby": "0.2.16",
45
- "vize": "0.284.0"
45
+ "vize": "0.286.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "25.9.2",