@generaltranslation/vue-extractor 0.0.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 (62) hide show
  1. package/LICENSE.md +105 -0
  2. package/dist/config.d.ts +2 -0
  3. package/dist/config.js +2 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +2 -0
  6. package/dist/internal/compilerAst.d.ts +43 -0
  7. package/dist/internal/compilerAst.js +77 -0
  8. package/dist/internal/compilerAst.js.map +1 -0
  9. package/dist/internal/config/resolveVueCompilerOptions.d.ts +23 -0
  10. package/dist/internal/config/resolveVueCompilerOptions.js +987 -0
  11. package/dist/internal/config/resolveVueCompilerOptions.js.map +1 -0
  12. package/dist/internal/extractFromVueSource.d.ts +9 -0
  13. package/dist/internal/extractFromVueSource.js +332 -0
  14. package/dist/internal/extractFromVueSource.js.map +1 -0
  15. package/dist/internal/script/analyze.d.ts +13 -0
  16. package/dist/internal/script/analyze.js +4761 -0
  17. package/dist/internal/script/analyze.js.map +1 -0
  18. package/dist/internal/script/jsx.d.ts +36 -0
  19. package/dist/internal/script/jsx.js +667 -0
  20. package/dist/internal/script/jsx.js.map +1 -0
  21. package/dist/internal/script/knownValues.d.ts +14 -0
  22. package/dist/internal/script/knownValues.js +148 -0
  23. package/dist/internal/script/knownValues.js.map +1 -0
  24. package/dist/internal/script/localModules.d.ts +80 -0
  25. package/dist/internal/script/localModules.js +364 -0
  26. package/dist/internal/script/localModules.js.map +1 -0
  27. package/dist/internal/script/model.d.ts +317 -0
  28. package/dist/internal/script/model.js +1 -0
  29. package/dist/internal/script/parser.d.ts +3 -0
  30. package/dist/internal/script/parser.js +21 -0
  31. package/dist/internal/script/parser.js.map +1 -0
  32. package/dist/internal/script/replay.d.ts +53 -0
  33. package/dist/internal/script/replay.js +1611 -0
  34. package/dist/internal/script/replay.js.map +1 -0
  35. package/dist/internal/script/snapshot.d.ts +25 -0
  36. package/dist/internal/script/snapshot.js +293 -0
  37. package/dist/internal/script/snapshot.js.map +1 -0
  38. package/dist/internal/script/syntax.d.ts +21 -0
  39. package/dist/internal/script/syntax.js +46 -0
  40. package/dist/internal/script/syntax.js.map +1 -0
  41. package/dist/internal/script.d.ts +2 -0
  42. package/dist/internal/script.js +2 -0
  43. package/dist/internal/stringCalls.d.ts +7 -0
  44. package/dist/internal/stringCalls.js +82 -0
  45. package/dist/internal/stringCalls.js.map +1 -0
  46. package/dist/internal/template.d.ts +4 -0
  47. package/dist/internal/template.js +2198 -0
  48. package/dist/internal/template.js.map +1 -0
  49. package/dist/internal/templatePath.d.ts +12 -0
  50. package/dist/internal/templatePath.js +23 -0
  51. package/dist/internal/templatePath.js.map +1 -0
  52. package/dist/internal/types.d.ts +69 -0
  53. package/dist/internal/types.js +1 -0
  54. package/dist/internal/utils.d.ts +19 -0
  55. package/dist/internal/utils.js +149 -0
  56. package/dist/internal/utils.js.map +1 -0
  57. package/dist/internal/vueCompiler.d.ts +35 -0
  58. package/dist/internal/vueCompiler.js +320 -0
  59. package/dist/internal/vueCompiler.js.map +1 -0
  60. package/dist/types.d.ts +80 -0
  61. package/dist/types.js +1 -0
  62. package/package.json +78 -0
@@ -0,0 +1,987 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parse } from "@babel/parser";
4
+ import traverseModule from "@babel/traverse";
5
+ import { createDiagnosticMessage } from "generaltranslation/internal";
6
+ //#region src/internal/config/resolveVueCompilerOptions.ts
7
+ const traverse = traverseModule.default || traverseModule;
8
+ const VITE_CONFIG_FILES = [
9
+ "vite.config.js",
10
+ "vite.config.mjs",
11
+ "vite.config.ts",
12
+ "vite.config.cjs",
13
+ "vite.config.mts",
14
+ "vite.config.cts"
15
+ ];
16
+ const NUXT_CONFIG_FILES = [
17
+ "nuxt.config.js",
18
+ "nuxt.config.ts",
19
+ "nuxt.config.mjs",
20
+ "nuxt.config.cjs",
21
+ "nuxt.config.mts",
22
+ "nuxt.config.cts"
23
+ ];
24
+ const CUSTOM_VITE_CONFIG_EXTENSIONS = new Set([
25
+ ".js",
26
+ ".jsx",
27
+ ".mjs",
28
+ ".cjs",
29
+ ".ts",
30
+ ".tsx",
31
+ ".mts",
32
+ ".cts"
33
+ ]);
34
+ /**
35
+ * Resolves hash-affecting Vue template compiler options without executing
36
+ * project configuration files.
37
+ *
38
+ * Explicit `gt.config.json` values are combined with statically analyzable
39
+ * Vite and Nuxt configuration. Conflicting, dynamic, or unsupported relevant
40
+ * options produce errors so extraction never publishes hashes calculated with
41
+ * compiler behavior that the runtime does not share.
42
+ */
43
+ function resolveVueCompilerOptions(cwd, explicitOptions, resolverOptions = {}) {
44
+ const state = {
45
+ options: {},
46
+ sources: {}
47
+ };
48
+ const errors = [];
49
+ readExplicitOptions(explicitOptions, state, errors);
50
+ if (resolverOptions.viteConfigPath !== void 0) {
51
+ const file = resolveCustomViteConfig(cwd, resolverOptions.viteConfigPath, errors);
52
+ if (file) readViteConfig(file, state, errors);
53
+ return {
54
+ compilerOptions: state.options,
55
+ errors
56
+ };
57
+ }
58
+ const nuxtConfig = findFirstConfig(cwd, NUXT_CONFIG_FILES);
59
+ const viteConfig = findFirstConfig(cwd, VITE_CONFIG_FILES);
60
+ if (nuxtConfig && viteConfig) {
61
+ errors.push(optionError("Found both Nuxt and Vite project config files", "For direct Vite builds, set files.gt.parsingFlags.viteConfigPath to the active Vite config. For Nuxt builds, remove or relocate the inactive Vite config so the compiler configuration is unambiguous"));
62
+ return {
63
+ compilerOptions: state.options,
64
+ errors
65
+ };
66
+ }
67
+ if (nuxtConfig) {
68
+ readNuxtConfig(nuxtConfig, state, errors);
69
+ return {
70
+ compilerOptions: state.options,
71
+ errors
72
+ };
73
+ }
74
+ if (viteConfig) readViteConfig(viteConfig, state, errors);
75
+ return {
76
+ compilerOptions: state.options,
77
+ errors
78
+ };
79
+ }
80
+ /** Selects the first config file in the framework's documented lookup order. */
81
+ function findFirstConfig(cwd, filenames) {
82
+ for (const filename of filenames) {
83
+ const file = path.resolve(cwd, filename);
84
+ if (fs.existsSync(file)) return file;
85
+ }
86
+ }
87
+ /** Validates an explicit Vite config without searching or executing files. */
88
+ function resolveCustomViteConfig(cwd, configuredPath, errors) {
89
+ if (typeof configuredPath !== "string" || !configuredPath.trim()) {
90
+ errors.push(optionError("Found an empty Vite config path", "Set files.gt.parsingFlags.viteConfigPath to one in-project Vite config file"));
91
+ return;
92
+ }
93
+ const root = path.resolve(cwd);
94
+ const candidate = path.resolve(root, configuredPath);
95
+ const relativeCandidate = path.relative(root, candidate);
96
+ if (relativeCandidate.startsWith(`..${path.sep}`) || relativeCandidate === ".." || path.isAbsolute(relativeCandidate) || relativeCandidate.split(path.sep).includes("node_modules")) {
97
+ errors.push(optionError("Found a Vite config path outside the project source boundary", "Choose a Vite config file inside the project root and outside node_modules"));
98
+ return;
99
+ }
100
+ if (!CUSTOM_VITE_CONFIG_EXTENSIONS.has(path.extname(candidate).toLowerCase())) {
101
+ errors.push(optionError("Found an unsupported Vite config file extension", "Use a JavaScript or TypeScript Vite config file"));
102
+ return;
103
+ }
104
+ if (!fs.existsSync(candidate) || !fs.statSync(candidate).isFile()) {
105
+ errors.push(optionError("Could not find the configured Vite config file", "Set files.gt.parsingFlags.viteConfigPath to an existing file inside the project root"));
106
+ return;
107
+ }
108
+ const realRoot = fs.realpathSync(root);
109
+ const realCandidate = fs.realpathSync(candidate);
110
+ const realRelative = path.relative(realRoot, realCandidate);
111
+ if (realRelative.startsWith(`..${path.sep}`) || realRelative === ".." || path.isAbsolute(realRelative) || realRelative.split(path.sep).includes("node_modules")) {
112
+ errors.push(optionError("Found a Vite config path outside the project source boundary", "Choose a Vite config file inside the project root and outside node_modules"));
113
+ return;
114
+ }
115
+ return realCandidate;
116
+ }
117
+ function readExplicitOptions(input, state, errors) {
118
+ if (input === void 0) return;
119
+ if (!isRecord(input)) {
120
+ errors.push(optionError("Found an invalid vueCompilerOptions parsing setting", "Set files.gt.parsingFlags.vueCompilerOptions to an object containing only whitespace and delimiters"));
121
+ return;
122
+ }
123
+ for (const key of Object.keys(input)) if (key !== "whitespace" && key !== "delimiters") errors.push(optionError(`Found unsupported Vue compiler option "${key}" in gt.config.json`, "Configure only whitespace and delimiters in files.gt.parsingFlags.vueCompilerOptions"));
124
+ if (input.whitespace !== void 0) if (input.whitespace !== "condense" && input.whitespace !== "preserve") errors.push(optionError("Found an invalid Vue whitespace compiler option in gt.config.json", "Set whitespace to either \"condense\" or \"preserve\""));
125
+ else mergeOption(state, "whitespace", input.whitespace, { file: "gt.config.json" }, errors);
126
+ if (input.delimiters !== void 0) if (!isDelimiterPair(input.delimiters)) errors.push(optionError("Found invalid Vue interpolation delimiters in gt.config.json", "Set delimiters to exactly two non-empty strings, such as [\"[[\", \"]]\"]"));
127
+ else mergeOption(state, "delimiters", input.delimiters, { file: "gt.config.json" }, errors);
128
+ }
129
+ function readViteConfig(file, state, errors) {
130
+ const ast = parseConfig(file, errors);
131
+ if (!ast) return;
132
+ const pluginBindings = /* @__PURE__ */ new Set();
133
+ const namespaceBindings = /* @__PURE__ */ new Set();
134
+ const jsxPluginBindings = /* @__PURE__ */ new Set();
135
+ const jsxNamespaceBindings = /* @__PURE__ */ new Set();
136
+ const defineConfigBindings = /* @__PURE__ */ new Set();
137
+ traverse(ast, {
138
+ ImportDeclaration(importPath) {
139
+ if (importPath.node.source.value === "vite") {
140
+ for (const specifierPath of importPath.get("specifiers")) {
141
+ const specifier = specifierPath.node;
142
+ if (specifier.type !== "ImportSpecifier" || (specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value) !== "defineConfig") continue;
143
+ const binding = specifierPath.scope.getBinding(specifier.local.name);
144
+ if (binding) defineConfigBindings.add(binding);
145
+ }
146
+ return;
147
+ }
148
+ const moduleSource = importPath.node.source.value;
149
+ if (moduleSource !== "@vitejs/plugin-vue" && moduleSource !== "@vitejs/plugin-vue-jsx") return;
150
+ const targetPluginBindings = moduleSource === "@vitejs/plugin-vue-jsx" ? jsxPluginBindings : pluginBindings;
151
+ const targetNamespaceBindings = moduleSource === "@vitejs/plugin-vue-jsx" ? jsxNamespaceBindings : namespaceBindings;
152
+ for (const specifierPath of importPath.get("specifiers")) {
153
+ const specifier = specifierPath.node;
154
+ const binding = specifierPath.scope.getBinding(specifier.local.name);
155
+ if (!binding) continue;
156
+ if (specifier.type === "ImportNamespaceSpecifier") targetNamespaceBindings.add(binding);
157
+ else if (specifier.type === "ImportDefaultSpecifier" || specifier.type === "ImportSpecifier" && (specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value) === "default") targetPluginBindings.add(binding);
158
+ }
159
+ },
160
+ TSImportEqualsDeclaration(importPath) {
161
+ const reference = importPath.node.moduleReference;
162
+ if (reference.type !== "TSExternalModuleReference" || reference.expression.type !== "StringLiteral" || reference.expression.value !== "@vitejs/plugin-vue" && reference.expression.value !== "@vitejs/plugin-vue-jsx") return;
163
+ const binding = importPath.scope.getBinding(importPath.node.id.name);
164
+ if (!binding) return;
165
+ if (reference.expression.value === "@vitejs/plugin-vue-jsx") jsxNamespaceBindings.add(binding);
166
+ else namespaceBindings.add(binding);
167
+ },
168
+ VariableDeclarator(variablePath) {
169
+ const vueRequireKind = getPluginVueRequireKind(variablePath.node.init, "@vitejs/plugin-vue");
170
+ const jsxRequireKind = getPluginVueRequireKind(variablePath.node.init, "@vitejs/plugin-vue-jsx");
171
+ const requireKind = vueRequireKind ?? jsxRequireKind;
172
+ if (!requireKind) return;
173
+ const targetPluginBindings = jsxRequireKind ? jsxPluginBindings : pluginBindings;
174
+ const targetNamespaceBindings = jsxRequireKind ? jsxNamespaceBindings : namespaceBindings;
175
+ if (variablePath.node.id.type === "Identifier") {
176
+ const binding = variablePath.scope.getBinding(variablePath.node.id.name);
177
+ if (!binding) return;
178
+ if (requireKind === "namespace") targetNamespaceBindings.add(binding);
179
+ else targetPluginBindings.add(binding);
180
+ return;
181
+ }
182
+ if (requireKind !== "namespace" || variablePath.node.id.type !== "ObjectPattern") return;
183
+ for (const property of variablePath.node.id.properties) {
184
+ if (property.type !== "ObjectProperty" || readPropertyKey(property) !== "default") continue;
185
+ const localName = readBindingIdentifier(property.value);
186
+ const binding = localName ? variablePath.scope.getBinding(localName) : void 0;
187
+ if (binding) targetPluginBindings.add(binding);
188
+ }
189
+ }
190
+ });
191
+ const activeExport = findActiveConfigExport(ast, file, errors);
192
+ if (!activeExport) return;
193
+ const functionReturns = collectFunctionReturns(ast);
194
+ const configRoot = resolveActiveConfigRoot(activeExport.node, activeExport.scope, functionReturns, (callee, scope) => resolveConfigHelperCall(callee, scope, defineConfigBindings, void 0), /* @__PURE__ */ new Set());
195
+ if (!configRoot.ok) {
196
+ errors.push(dynamicConfigError(file, configRoot.node));
197
+ return;
198
+ }
199
+ const configObject = resolveStaticObject(configRoot.value.node, configRoot.value.scope, /* @__PURE__ */ new Set());
200
+ if (!configObject.ok) {
201
+ errors.push(dynamicConfigError(file, configObject.node));
202
+ return;
203
+ }
204
+ const plugins = configObject.value.entries.get("plugins");
205
+ if (!plugins) return;
206
+ const pluginContext = {
207
+ moduleSource: "@vitejs/plugin-vue",
208
+ namespaceBindings,
209
+ pluginBindings,
210
+ referencePositions: [...pluginBindings, ...namespaceBindings].flatMap((binding) => binding.referencePaths.map((reference) => reference.node.start).filter((position) => position !== null))
211
+ };
212
+ const calls = [];
213
+ const unresolved = collectActiveVuePluginCalls(plugins.node, plugins.scope, pluginContext, calls, /* @__PURE__ */ new Set());
214
+ if (unresolved) {
215
+ errors.push(dynamicConfigError(file, unresolved));
216
+ return;
217
+ }
218
+ if (calls.length > 1) {
219
+ errors.push(dynamicConfigError(file, calls[1].node));
220
+ return;
221
+ }
222
+ const call = calls[0];
223
+ if (call) {
224
+ const argument = call.node.arguments[0];
225
+ if (argument?.type === "SpreadElement" || argument?.type === "ArgumentPlaceholder") {
226
+ errors.push(dynamicConfigError(file, argument));
227
+ return;
228
+ }
229
+ if (argument) {
230
+ rejectCustomVueCompiler(argument, call.scope, file, errors);
231
+ readNestedCompilerOptions(argument, call.scope, ["template", "compilerOptions"], file, true, state, errors);
232
+ }
233
+ }
234
+ const jsxPluginContext = {
235
+ moduleSource: "@vitejs/plugin-vue-jsx",
236
+ namespaceBindings: jsxNamespaceBindings,
237
+ pluginBindings: jsxPluginBindings,
238
+ referencePositions: [...jsxPluginBindings, ...jsxNamespaceBindings].flatMap((binding) => binding.referencePaths.map((reference) => reference.node.start).filter((position) => position !== null))
239
+ };
240
+ const jsxCalls = [];
241
+ const unresolvedJSX = collectActiveVuePluginCalls(plugins.node, plugins.scope, jsxPluginContext, jsxCalls, /* @__PURE__ */ new Set());
242
+ if (unresolvedJSX) {
243
+ errors.push(dynamicVueJSXConfigError(file, unresolvedJSX));
244
+ return;
245
+ }
246
+ if (jsxCalls.length > 1) {
247
+ errors.push(dynamicVueJSXConfigError(file, jsxCalls[1].node));
248
+ return;
249
+ }
250
+ if (jsxCalls[0]) readVueJSXPluginOptions(jsxCalls[0], file, errors);
251
+ }
252
+ /** Rejects Vite JSX options that can invalidate source-to-VNode parity. */
253
+ function readVueJSXPluginOptions(call, file, errors) {
254
+ const argument = call.node.arguments[0];
255
+ if (!argument) return;
256
+ if (argument.type === "SpreadElement" || argument.type === "ArgumentPlaceholder") {
257
+ errors.push(dynamicVueJSXConfigError(file, argument));
258
+ return;
259
+ }
260
+ const options = resolveStaticObject(argument, call.scope, /* @__PURE__ */ new Set());
261
+ if (!options.ok) {
262
+ errors.push(dynamicVueJSXConfigError(file, options.node));
263
+ return;
264
+ }
265
+ for (const [name, entry] of options.value.entries) {
266
+ let supported = false;
267
+ if (name === "optimize") supported = resolveStaticBoolean(entry.node, entry.scope, /* @__PURE__ */ new Set()) !== void 0;
268
+ else if (name === "transformOn") supported = resolveStaticBoolean(entry.node, entry.scope, /* @__PURE__ */ new Set()) === false;
269
+ else if (name === "enableObjectSlots" || name === "mergeProps") supported = resolveStaticBoolean(entry.node, entry.scope, /* @__PURE__ */ new Set()) === true;
270
+ else if (name === "resolveType") supported = resolveStaticBoolean(entry.node, entry.scope, /* @__PURE__ */ new Set()) === false;
271
+ else if (name === "pragma") supported = resolveStaticString(entry.node, entry.scope, /* @__PURE__ */ new Set()) === "";
272
+ else if (name === "defineComponentName") supported = isDefaultDefineComponentNames(entry.node, entry.scope, /* @__PURE__ */ new Set());
273
+ else if (name === "babelPlugins") supported = isDefinitelyEmptyArray(entry.node, entry.scope, /* @__PURE__ */ new Set());
274
+ else if (name === "tsPluginOptions") {
275
+ const resolved = resolveStaticObject(entry.node, entry.scope, /* @__PURE__ */ new Set());
276
+ supported = resolved.ok && resolved.value.entries.size === 0;
277
+ }
278
+ if (supported) continue;
279
+ errors.push(locatedOptionError(file, entry.node, `Found unsupported @vitejs/plugin-vue-jsx option "${name}"`, "Use default-equivalent Vue JSX options; only a static optimize value may differ in files containing gt-vue translations"));
280
+ }
281
+ }
282
+ /** Resolves an immutable boolean option without coercing another type. */
283
+ function resolveStaticBoolean(input, scope, seen) {
284
+ const node = unwrapExpression(input);
285
+ if (node.type === "BooleanLiteral") return node.value;
286
+ if (node.type !== "Identifier") return void 0;
287
+ const binding = scope.getBinding(node.name);
288
+ if (!binding?.constant || bindingHasMutation(binding) || seen.has(binding)) return;
289
+ const declaration = binding.path.node;
290
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return;
291
+ const nextSeen = new Set(seen);
292
+ nextSeen.add(binding);
293
+ return resolveStaticBoolean(declaration.init, binding.path.scope, nextSeen);
294
+ }
295
+ /** Proves the explicit component-name option equals the plugin default. */
296
+ function isDefaultDefineComponentNames(input, scope, seen) {
297
+ const node = unwrapExpression(input);
298
+ if (node.type === "ArrayExpression") return node.elements.length === 1 && node.elements[0]?.type === "StringLiteral" && node.elements[0].value === "defineComponent";
299
+ if (node.type !== "Identifier") return false;
300
+ const binding = scope.getBinding(node.name);
301
+ if (!binding?.constant || bindingHasMutation(binding) || seen.has(binding)) return false;
302
+ const declaration = binding.path.node;
303
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return false;
304
+ const nextSeen = new Set(seen);
305
+ nextSeen.add(binding);
306
+ return isDefaultDefineComponentNames(declaration.init, binding.path.scope, nextSeen);
307
+ }
308
+ /** Proves an immutable config value is an empty array. */
309
+ function isDefinitelyEmptyArray(input, scope, seen) {
310
+ const node = unwrapExpression(input);
311
+ if (node.type === "ArrayExpression") return node.elements.length === 0;
312
+ if (node.type !== "Identifier") return false;
313
+ const binding = scope.getBinding(node.name);
314
+ if (!binding?.constant || bindingHasMutation(binding) || seen.has(binding)) return false;
315
+ const declaration = binding.path.node;
316
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return false;
317
+ const nextSeen = new Set(seen);
318
+ nextSeen.add(binding);
319
+ return isDefinitelyEmptyArray(declaration.init, binding.path.scope, nextSeen);
320
+ }
321
+ /**
322
+ * Rejects Vite's custom compiler override because source-file resolution can
323
+ * only prove parity with that app's `vue/compiler-sfc` export.
324
+ */
325
+ function rejectCustomVueCompiler(root, scope, file, errors) {
326
+ const rootObject = resolveStaticObject(root, scope, /* @__PURE__ */ new Set());
327
+ if (!rootObject.ok) return;
328
+ const compiler = rootObject.value.entries.get("compiler");
329
+ if (!compiler) return;
330
+ errors.push(locatedOptionError(file, compiler.node, "Found a custom Vue compiler instance in the Vite plugin", "Remove the compiler override so extraction and Vite both use the app's vue/compiler-sfc version"));
331
+ }
332
+ function readNuxtConfig(file, state, errors) {
333
+ const ast = parseConfig(file, errors);
334
+ if (!ast) return;
335
+ const defineNuxtConfigBindings = /* @__PURE__ */ new Set();
336
+ traverse(ast, { ImportDeclaration(importPath) {
337
+ if (importPath.node.source.value !== "nuxt/config" && importPath.node.source.value !== "nuxt" && importPath.node.source.value !== "#imports") return;
338
+ for (const specifierPath of importPath.get("specifiers")) {
339
+ const specifier = specifierPath.node;
340
+ if (specifier.type !== "ImportSpecifier" || (specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value) !== "defineNuxtConfig") continue;
341
+ const binding = specifierPath.scope.getBinding(specifier.local.name);
342
+ if (binding) defineNuxtConfigBindings.add(binding);
343
+ }
344
+ } });
345
+ const activeExport = findActiveConfigExport(ast, file, errors);
346
+ if (!activeExport) return;
347
+ const configRoot = resolveActiveConfigRoot(activeExport.node, activeExport.scope, collectFunctionReturns(ast), (callee, scope) => resolveConfigHelperCall(callee, scope, defineNuxtConfigBindings, "defineNuxtConfig"), /* @__PURE__ */ new Set());
348
+ if (!configRoot.ok) {
349
+ errors.push(dynamicConfigError(file, configRoot.node));
350
+ return;
351
+ }
352
+ const configObject = resolveStaticObject(configRoot.value.node, configRoot.value.scope, /* @__PURE__ */ new Set());
353
+ if (!configObject.ok) {
354
+ errors.push(dynamicConfigError(file, configObject.node));
355
+ return;
356
+ }
357
+ const extendsEntry = configObject.value.entries.get("extends");
358
+ if (extendsEntry && !isDefinitelyEmptyNuxtExtends(extendsEntry.node, extendsEntry.scope)) {
359
+ errors.push(locatedOptionError(file, extendsEntry.node, "Found Nuxt layer inheritance that could change Vue compiler options", "Run extraction from a Nuxt configuration without inherited layers until gt-vue can resolve the complete layer chain safely"));
360
+ return;
361
+ }
362
+ const unsupportedSourceLayout = findUnsupportedNuxtSourceLayout(configObject.value);
363
+ if (unsupportedSourceLayout) {
364
+ errors.push(locatedOptionError(file, unsupportedSourceLayout, "Found custom Nuxt source directories that default Vue discovery cannot scan safely", "Use Nuxt source directories covered by the default app, src, pages, layouts, plugins, and related globs until custom directory discovery is supported"));
365
+ return;
366
+ }
367
+ readNestedCompilerOptions(configRoot.value.node, configRoot.value.scope, ["vue", "compilerOptions"], file, true, state, errors);
368
+ }
369
+ /** Finds a Nuxt source-layout override not covered by default CLI globs. */
370
+ function findUnsupportedNuxtSourceLayout(config) {
371
+ const srcDir = config.entries.get("srcDir");
372
+ if (srcDir) {
373
+ const value = resolveStaticString(srcDir.node, srcDir.scope, /* @__PURE__ */ new Set());
374
+ const normalized = value ? path.posix.normalize(value.replaceAll("\\", "/")).replace(/\/+$/, "") || "." : void 0;
375
+ if (!normalized || ![
376
+ ".",
377
+ "app",
378
+ "src"
379
+ ].includes(normalized)) return srcDir.node;
380
+ }
381
+ const directoryOptions = config.entries.get("dir");
382
+ if (directoryOptions) {
383
+ const resolved = resolveStaticObject(directoryOptions.node, directoryOptions.scope, /* @__PURE__ */ new Set());
384
+ if (!resolved.ok || resolved.value.entries.size > 0) return directoryOptions.node;
385
+ }
386
+ }
387
+ /** Returns true only when a Nuxt extends value provably selects no layers. */
388
+ function isDefinitelyEmptyNuxtExtends(input, scope) {
389
+ const node = unwrapExpression(input);
390
+ if (node.type === "NullLiteral") return true;
391
+ if (node.type === "ArrayExpression") return node.elements.length === 0;
392
+ if (node.type !== "Identifier") return false;
393
+ if (node.name === "undefined" && !scope.getBinding(node.name)) return true;
394
+ const binding = scope.getBinding(node.name);
395
+ const declaration = binding?.path.node;
396
+ return !!(binding?.constant && !bindingHasMutation(binding) && declaration?.type === "VariableDeclarator" && declaration.init && isDefinitelyEmptyNuxtExtends(declaration.init, binding.path.scope));
397
+ }
398
+ /** Returns the one top-level export that the config loader evaluates. */
399
+ function findActiveConfigExport(ast, file, errors) {
400
+ const esmExports = [];
401
+ const commonJsExports = /* @__PURE__ */ new Map();
402
+ traverse(ast, {
403
+ ExportDefaultDeclaration(exportPath) {
404
+ esmExports.push({
405
+ node: exportPath.node.declaration,
406
+ scope: exportPath.scope
407
+ });
408
+ },
409
+ AssignmentExpression(assignmentPath) {
410
+ if (assignmentPath.node.operator !== "=" || assignmentPath.getFunctionParent() || !assignmentPath.parentPath.isExpressionStatement() || !assignmentPath.parentPath.parentPath?.isProgram()) return;
411
+ const kind = readCommonJsConfigExportKind(assignmentPath.node.left, assignmentPath.scope);
412
+ if (!kind) return;
413
+ const entries = commonJsExports.get(kind) ?? [];
414
+ entries.push({
415
+ node: assignmentPath.node.right,
416
+ scope: assignmentPath.scope
417
+ });
418
+ commonJsExports.set(kind, entries);
419
+ }
420
+ });
421
+ const commonJsKinds = [...commonJsExports.keys()];
422
+ if (esmExports.length > 1 || esmExports.length > 0 && commonJsKinds.length > 0 || commonJsKinds.length > 1) {
423
+ const node = esmExports[1]?.node ?? commonJsExports.get(commonJsKinds[1])?.[0]?.node ?? esmExports[0]?.node ?? ast.program;
424
+ errors.push(dynamicConfigError(file, node));
425
+ return;
426
+ }
427
+ if (esmExports[0]) return esmExports[0];
428
+ const commonJs = commonJsExports.get(commonJsKinds[0]);
429
+ return commonJs?.[commonJs.length - 1];
430
+ }
431
+ /** Records return expressions by their immediately containing function. */
432
+ function collectFunctionReturns(ast) {
433
+ const result = /* @__PURE__ */ new Map();
434
+ traverse(ast, { ReturnStatement(returnPath) {
435
+ if (!returnPath.node.argument) return;
436
+ const functionPath = returnPath.getFunctionParent();
437
+ if (!functionPath) return;
438
+ const entries = result.get(functionPath.node) ?? [];
439
+ entries.push({
440
+ node: returnPath.node.argument,
441
+ scope: returnPath.scope
442
+ });
443
+ result.set(functionPath.node, entries);
444
+ } });
445
+ return result;
446
+ }
447
+ /** Resolves an exported object or single-return config function statically. */
448
+ function resolveActiveConfigRoot(input, scope, functionReturns, resolveHelper, seen) {
449
+ const node = unwrapExpression(input);
450
+ if (seen.has(node)) return {
451
+ ok: false,
452
+ node
453
+ };
454
+ const nextSeen = new Set(seen);
455
+ nextSeen.add(node);
456
+ if (node.type === "ObjectExpression") return {
457
+ ok: true,
458
+ value: {
459
+ node,
460
+ scope
461
+ }
462
+ };
463
+ if (node.type === "Identifier") {
464
+ const binding = scope.getBinding(node.name);
465
+ if (!binding?.constant || bindingHasMutation(binding)) return {
466
+ ok: false,
467
+ node
468
+ };
469
+ if (binding.path.isFunctionDeclaration()) return resolveActiveConfigRoot(binding.path.node, binding.path.scope, functionReturns, resolveHelper, nextSeen);
470
+ const declaration = binding.path.node;
471
+ return declaration.type === "VariableDeclarator" && declaration.init ? resolveActiveConfigRoot(declaration.init, binding.path.scope, functionReturns, resolveHelper, nextSeen) : {
472
+ ok: false,
473
+ node
474
+ };
475
+ }
476
+ if (node.type === "CallExpression" || node.type === "OptionalCallExpression") {
477
+ if (resolveHelper(node.callee, scope) !== "static") return {
478
+ ok: false,
479
+ node: node.callee
480
+ };
481
+ const argument = node.arguments[0];
482
+ return argument && argument.type !== "SpreadElement" && argument.type !== "ArgumentPlaceholder" ? resolveActiveConfigRoot(argument, scope, functionReturns, resolveHelper, nextSeen) : {
483
+ ok: false,
484
+ node
485
+ };
486
+ }
487
+ if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
488
+ if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") return resolveActiveConfigRoot(node.body, scope, functionReturns, resolveHelper, nextSeen);
489
+ const returns = functionReturns.get(node) ?? [];
490
+ return returns.length === 1 ? resolveActiveConfigRoot(returns[0].node, returns[0].scope, functionReturns, resolveHelper, nextSeen) : {
491
+ ok: false,
492
+ node
493
+ };
494
+ }
495
+ if (node.type === "ConditionalExpression") {
496
+ const condition = resolveStaticTruthiness(node.test, scope, /* @__PURE__ */ new Set());
497
+ return condition === void 0 ? {
498
+ ok: false,
499
+ node
500
+ } : resolveActiveConfigRoot(condition ? node.consequent : node.alternate, scope, functionReturns, resolveHelper, nextSeen);
501
+ }
502
+ if (node.type === "SequenceExpression") {
503
+ const last = node.expressions[node.expressions.length - 1];
504
+ return last ? resolveActiveConfigRoot(last, scope, functionReturns, resolveHelper, nextSeen) : {
505
+ ok: false,
506
+ node
507
+ };
508
+ }
509
+ if (node.type === "AwaitExpression") return resolveActiveConfigRoot(node.argument, scope, functionReturns, resolveHelper, nextSeen);
510
+ return {
511
+ ok: false,
512
+ node
513
+ };
514
+ }
515
+ /** Resolves imported/global config helpers and their immutable aliases. */
516
+ function resolveConfigHelperCall(input, scope, knownBindings, globalName, seen = /* @__PURE__ */ new Set()) {
517
+ const node = unwrapExpression(input);
518
+ if (node.type !== "Identifier") return void 0;
519
+ const binding = scope.getBinding(node.name);
520
+ if (!binding) return node.name === globalName ? "static" : void 0;
521
+ if (knownBindings.has(binding)) return "static";
522
+ if (seen.has(binding)) return void 0;
523
+ const declaration = binding.path.node;
524
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return;
525
+ const nextSeen = new Set(seen);
526
+ nextSeen.add(binding);
527
+ const initialValue = resolveConfigHelperCall(declaration.init, binding.path.scope, knownBindings, globalName, nextSeen);
528
+ return !binding.constant && initialValue ? "dynamic" : initialValue;
529
+ }
530
+ /** Collects Vue plugin calls reachable through the active `plugins` value. */
531
+ function collectActiveVuePluginCalls(input, scope, context, calls, seen) {
532
+ const node = unwrapExpression(input);
533
+ if (node.type === "Identifier") {
534
+ const value = resolvePluginVueValue(node, scope, context.pluginBindings, context.namespaceBindings, context.moduleSource, /* @__PURE__ */ new Set());
535
+ if (value === "dynamic" || value === "default") return node;
536
+ const binding = scope.getBinding(node.name);
537
+ if (!binding || seen.has(binding)) return mayReferenceVuePlugin(node, scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
538
+ const declaration = binding.path.node;
539
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return mayReferenceVuePlugin(node, scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
540
+ if (bindingHasMutation(binding)) return node;
541
+ if (!binding.constant) return mayReferenceVuePlugin(declaration.init, binding.path.scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
542
+ const nextSeen = new Set(seen);
543
+ nextSeen.add(binding);
544
+ return collectActiveVuePluginCalls(declaration.init, binding.path.scope, context, calls, nextSeen);
545
+ }
546
+ if (node.type === "ArrayExpression") {
547
+ for (const element of node.elements) {
548
+ if (!element) continue;
549
+ const unresolved = collectActiveVuePluginCalls(element.type === "SpreadElement" ? element.argument : element, scope, context, calls, seen);
550
+ if (unresolved) return unresolved;
551
+ }
552
+ return;
553
+ }
554
+ if (node.type === "CallExpression" || node.type === "OptionalCallExpression") {
555
+ const value = resolvePluginVueValue(node.callee, scope, context.pluginBindings, context.namespaceBindings, context.moduleSource, /* @__PURE__ */ new Set());
556
+ if (value === "dynamic") return node.callee;
557
+ if (value === "default") {
558
+ calls.push({
559
+ node,
560
+ scope
561
+ });
562
+ return;
563
+ }
564
+ return mayReferenceVuePlugin(node, scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
565
+ }
566
+ if (node.type === "ConditionalExpression") {
567
+ const condition = resolveStaticTruthiness(node.test, scope, /* @__PURE__ */ new Set());
568
+ if (condition !== void 0) return collectActiveVuePluginCalls(condition ? node.consequent : node.alternate, scope, context, calls, seen);
569
+ return mayReferenceVuePlugin(node, scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
570
+ }
571
+ if (node.type === "LogicalExpression") {
572
+ const left = resolveStaticTruthiness(node.left, scope, /* @__PURE__ */ new Set());
573
+ if (left !== void 0) {
574
+ const selected = node.operator === "&&" ? left ? node.right : node.left : node.operator === "||" ? left ? node.left : node.right : void 0;
575
+ if (selected) return collectActiveVuePluginCalls(selected, scope, context, calls, seen);
576
+ }
577
+ return mayReferenceVuePlugin(node, scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
578
+ }
579
+ if (node.type === "SequenceExpression") {
580
+ const last = node.expressions[node.expressions.length - 1];
581
+ return last ? collectActiveVuePluginCalls(last, scope, context, calls, seen) : void 0;
582
+ }
583
+ return mayReferenceVuePlugin(node, scope, context, /* @__PURE__ */ new Set()) ? node : void 0;
584
+ }
585
+ /** Conservatively follows wrapper bindings that reference the Vue plugin. */
586
+ function mayReferenceVuePlugin(input, scope, context, seen) {
587
+ const node = unwrapExpression(input);
588
+ const start = node.start;
589
+ const end = node.end;
590
+ if (typeof start === "number" && typeof end === "number" && context.referencePositions.some((position) => position >= start && position <= end)) return true;
591
+ const target = node.type === "Identifier" ? node : node.type === "CallExpression" || node.type === "OptionalCallExpression" ? unwrapExpression(node.callee) : void 0;
592
+ if (target?.type !== "Identifier") return false;
593
+ const binding = scope.getBinding(target.name);
594
+ if (!binding) return false;
595
+ if (context.pluginBindings.has(binding) || context.namespaceBindings.has(binding)) return true;
596
+ if (seen.has(binding)) return false;
597
+ const nextSeen = new Set(seen);
598
+ nextSeen.add(binding);
599
+ const declaration = binding.path.node;
600
+ if (declaration.type === "VariableDeclarator" && declaration.init) return mayReferenceVuePlugin(declaration.init, binding.path.scope, context, nextSeen);
601
+ const declarationStart = declaration.start;
602
+ const declarationEnd = declaration.end;
603
+ return typeof declarationStart === "number" && typeof declarationEnd === "number" && context.referencePositions.some((position) => position >= declarationStart && position <= declarationEnd);
604
+ }
605
+ /** Evaluates only primitive truthiness used to choose a static config branch. */
606
+ function resolveStaticTruthiness(input, scope, seen) {
607
+ const node = unwrapExpression(input);
608
+ if (node.type === "BooleanLiteral") return node.value;
609
+ if (node.type === "NullLiteral") return false;
610
+ if (node.type === "NumericLiteral") return !!node.value;
611
+ if (node.type === "BigIntLiteral") return node.value !== "0";
612
+ if (node.type === "StringLiteral") return node.value.length > 0;
613
+ if (node.type === "UnaryExpression" && node.operator === "!") {
614
+ const value = resolveStaticTruthiness(node.argument, scope, seen);
615
+ return value === void 0 ? void 0 : !value;
616
+ }
617
+ if (node.type !== "Identifier") return void 0;
618
+ if (node.name === "undefined" && !scope.getBinding(node.name)) return false;
619
+ const binding = scope.getBinding(node.name);
620
+ if (!binding?.constant || bindingHasMutation(binding) || seen.has(binding)) return;
621
+ const declaration = binding.path.node;
622
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return;
623
+ const nextSeen = new Set(seen);
624
+ nextSeen.add(binding);
625
+ return resolveStaticTruthiness(declaration.init, binding.path.scope, nextSeen);
626
+ }
627
+ function readNestedCompilerOptions(root, scope, keys, file, failOnDynamicRoot, state, errors) {
628
+ const rootObject = resolveStaticObject(root, scope, /* @__PURE__ */ new Set());
629
+ if (!rootObject.ok) {
630
+ if (failOnDynamicRoot) errors.push(dynamicConfigError(file, rootObject.node));
631
+ return;
632
+ }
633
+ const parentEntry = rootObject.value.entries.get(keys[0]);
634
+ if (!parentEntry) return;
635
+ const parentObject = resolveStaticObject(parentEntry.node, parentEntry.scope, /* @__PURE__ */ new Set());
636
+ if (!parentObject.ok) {
637
+ errors.push(dynamicConfigError(file, parentObject.node));
638
+ return;
639
+ }
640
+ const compilerEntry = parentObject.value.entries.get(keys[1]);
641
+ if (!compilerEntry) return;
642
+ const compilerObject = resolveStaticObject(compilerEntry.node, compilerEntry.scope, /* @__PURE__ */ new Set());
643
+ if (!compilerObject.ok) {
644
+ errors.push(dynamicConfigError(file, compilerObject.node));
645
+ return;
646
+ }
647
+ readCompilerOptionsObject(compilerObject.value, file, state, errors);
648
+ }
649
+ function readCompilerOptionsObject(object, file, state, errors) {
650
+ for (const [key, entry] of object.entries) if (key !== "whitespace" && key !== "delimiters") errors.push(locatedOptionError(file, entry.node, `Found unsupported Vue compiler option "${key}"`, "gt-vue extraction currently supports only static whitespace and delimiters compiler options"));
651
+ const whitespaceEntry = object.entries.get("whitespace");
652
+ if (whitespaceEntry) {
653
+ const value = resolveStaticString(whitespaceEntry.node, whitespaceEntry.scope, /* @__PURE__ */ new Set());
654
+ if (value !== "condense" && value !== "preserve") errors.push(dynamicConfigError(file, whitespaceEntry.node));
655
+ else mergeOption(state, "whitespace", value, {
656
+ file,
657
+ node: whitespaceEntry.node
658
+ }, errors);
659
+ }
660
+ const delimitersEntry = object.entries.get("delimiters");
661
+ if (delimitersEntry) {
662
+ const value = resolveStaticDelimiters(delimitersEntry.node, delimitersEntry.scope, /* @__PURE__ */ new Set());
663
+ if (!value) errors.push(dynamicConfigError(file, delimitersEntry.node));
664
+ else mergeOption(state, "delimiters", value, {
665
+ file,
666
+ node: delimitersEntry.node
667
+ }, errors);
668
+ }
669
+ }
670
+ function resolveStaticObject(input, scope, seen) {
671
+ const node = unwrapExpression(input);
672
+ if (seen.has(node)) return {
673
+ ok: false,
674
+ node
675
+ };
676
+ seen.add(node);
677
+ if (node.type === "Identifier") {
678
+ const binding = scope.getBinding(node.name);
679
+ const declaration = binding?.path.node;
680
+ if (!binding?.constant || bindingHasMutation(binding) || declaration?.type !== "VariableDeclarator" || !declaration.init) return {
681
+ ok: false,
682
+ node
683
+ };
684
+ return resolveStaticObject(declaration.init, binding.path.scope, seen);
685
+ }
686
+ if (node.type !== "ObjectExpression") return {
687
+ ok: false,
688
+ node
689
+ };
690
+ const entries = /* @__PURE__ */ new Map();
691
+ for (const property of node.properties) {
692
+ if (property.type === "SpreadElement") {
693
+ const spread = resolveStaticObject(property.argument, scope, seen);
694
+ if (!spread.ok) return {
695
+ ok: false,
696
+ node: spread.node
697
+ };
698
+ for (const [key, entry] of spread.value.entries) entries.set(key, entry);
699
+ continue;
700
+ }
701
+ const key = readPropertyKey(property);
702
+ if (!key) return {
703
+ ok: false,
704
+ node: property
705
+ };
706
+ if (property.type !== "ObjectProperty") {
707
+ entries.set(key, {
708
+ node: property,
709
+ scope
710
+ });
711
+ continue;
712
+ }
713
+ entries.set(key, {
714
+ node: property.value,
715
+ scope
716
+ });
717
+ }
718
+ return {
719
+ ok: true,
720
+ value: { entries }
721
+ };
722
+ }
723
+ function resolveStaticString(input, scope, seen) {
724
+ const node = unwrapExpression(input);
725
+ if (seen.has(node)) return void 0;
726
+ seen.add(node);
727
+ if (node.type === "StringLiteral") return node.value;
728
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join("");
729
+ if (node.type !== "Identifier") return void 0;
730
+ const binding = scope.getBinding(node.name);
731
+ const declaration = binding?.path.node;
732
+ return binding?.constant && !bindingHasMutation(binding) && declaration?.type === "VariableDeclarator" && declaration.init ? resolveStaticString(declaration.init, binding.path.scope, seen) : void 0;
733
+ }
734
+ function resolveStaticDelimiters(input, scope, seen) {
735
+ const node = unwrapExpression(input);
736
+ if (seen.has(node)) return void 0;
737
+ seen.add(node);
738
+ if (node.type === "Identifier") {
739
+ const binding = scope.getBinding(node.name);
740
+ const declaration = binding?.path.node;
741
+ return binding?.constant && !bindingHasMutation(binding) && declaration?.type === "VariableDeclarator" && declaration.init ? resolveStaticDelimiters(declaration.init, binding.path.scope, seen) : void 0;
742
+ }
743
+ if (node.type !== "ArrayExpression" || node.elements.length !== 2) return;
744
+ const values = node.elements.map((element) => element && element.type !== "SpreadElement" ? resolveStaticString(element, scope, /* @__PURE__ */ new Set()) : void 0);
745
+ return isDelimiterPair(values) ? values : void 0;
746
+ }
747
+ function unwrapExpression(input) {
748
+ let node = input;
749
+ while (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression" || node.type === "TypeCastExpression" || node.type === "ParenthesizedExpression") node = node.expression;
750
+ return node;
751
+ }
752
+ function readPropertyKey(property) {
753
+ if (!property.computed && property.key.type === "Identifier") return property.key.name;
754
+ if (property.key.type === "StringLiteral") return property.key.value;
755
+ }
756
+ function getPluginVueRequireKind(input, moduleSource) {
757
+ if (!input) return void 0;
758
+ const node = unwrapExpression(input);
759
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments[0]?.type === "StringLiteral" && node.arguments[0].value === moduleSource) return "namespace";
760
+ if ((node.type === "MemberExpression" || node.type === "OptionalMemberExpression") && isDefaultMember(node)) return getPluginVueRequireKind(node.object, moduleSource) ? "default" : void 0;
761
+ }
762
+ /** Resolves immutable aliases of the Vue Vite plugin without executing config. */
763
+ function resolvePluginVueValue(input, scope, pluginBindings, namespaceBindings, moduleSource, seen) {
764
+ const node = unwrapExpression(input);
765
+ const requireKind = node.type === "CallExpression" || node.type === "MemberExpression" || node.type === "OptionalMemberExpression" ? getPluginVueRequireKind(node, moduleSource) : void 0;
766
+ if (requireKind) return requireKind;
767
+ if (node.type === "Identifier") {
768
+ const binding = scope.getBinding(node.name);
769
+ if (!binding) return void 0;
770
+ if (pluginBindings.has(binding)) return "default";
771
+ if (namespaceBindings.has(binding)) return "namespace";
772
+ if (seen.has(binding)) return void 0;
773
+ const declaration = binding.path.node;
774
+ if (declaration.type !== "VariableDeclarator" || !declaration.init) return;
775
+ const nextSeen = new Set(seen);
776
+ nextSeen.add(binding);
777
+ const initialValue = resolvePluginVueValue(declaration.init, binding.path.scope, pluginBindings, namespaceBindings, moduleSource, nextSeen);
778
+ return !binding.constant && initialValue ? "dynamic" : initialValue;
779
+ }
780
+ if ((node.type === "MemberExpression" || node.type === "OptionalMemberExpression") && isDefaultMember(node)) {
781
+ const objectValue = resolvePluginVueValue(node.object, scope, pluginBindings, namespaceBindings, moduleSource, seen);
782
+ if (objectValue === "dynamic") return "dynamic";
783
+ if (objectValue === "namespace") return "default";
784
+ }
785
+ }
786
+ /** Recognizes every static spelling of a module namespace default export. */
787
+ function isDefaultMember(node) {
788
+ return !node.computed && node.property.type === "Identifier" && node.property.name === "default" || node.computed && node.property.type === "StringLiteral" && node.property.value === "default";
789
+ }
790
+ /** Classifies supported unshadowed CommonJS config exports. */
791
+ function readCommonJsConfigExportKind(node, scope) {
792
+ if (node.type !== "MemberExpression" || node.object.type !== "Identifier" || !(!node.computed && node.property.type === "Identifier" || node.computed && node.property.type === "StringLiteral")) return;
793
+ const property = node.property.type === "Identifier" ? node.property.name : node.property.value;
794
+ if (node.object.name === "module" && property === "exports" && !scope.getBinding("module")) return "module-exports";
795
+ return node.object.name === "exports" && property === "default" && !scope.getBinding("exports") ? "exports-default" : void 0;
796
+ }
797
+ function readBindingIdentifier(node) {
798
+ if (node.type === "Identifier") return node.name;
799
+ return node.type === "AssignmentPattern" && node.left.type === "Identifier" ? node.left.name : void 0;
800
+ }
801
+ const MUTATING_METHODS = new Set([
802
+ "clear",
803
+ "copyWithin",
804
+ "delete",
805
+ "fill",
806
+ "pop",
807
+ "push",
808
+ "reverse",
809
+ "set",
810
+ "shift",
811
+ "sort",
812
+ "splice",
813
+ "unshift"
814
+ ]);
815
+ /** Detects property, collection, and alias mutations Babel does not mark constant. */
816
+ function bindingHasMutation(binding, seen = /* @__PURE__ */ new Set()) {
817
+ if (seen.has(binding)) return false;
818
+ const nextSeen = new Set(seen);
819
+ nextSeen.add(binding);
820
+ for (const reference of binding.referencePaths) {
821
+ let current = reference;
822
+ while (current.parentPath && (current.parentPath.isMemberExpression() || current.parentPath.isOptionalMemberExpression()) && current.parentPath.node.object === current.node) current = current.parentPath;
823
+ const parent = current.parentPath;
824
+ if (parent?.isAssignmentExpression() && parent.node.left === current.node || parent?.isUpdateExpression() && parent.node.argument === current.node || parent?.isUnaryExpression({ operator: "delete" }) && parent.node.argument === current.node) return true;
825
+ if (parent?.isCallExpression() || parent?.isOptionalCallExpression()) {
826
+ if (parent.node.callee === current.node && (current.isMemberExpression() || current.isOptionalMemberExpression()) && MUTATING_METHODS.has(readStaticMemberName(current.node) ?? "")) return true;
827
+ if (parent.node.arguments[0] === current.node && isGlobalMutationCall(parent.node.callee, parent.scope)) return true;
828
+ if (parent.node.arguments.some((argument) => argument === current.node) && !isKnownReadOnlyConfigConsumer(parent.node, current.node, parent.scope)) return true;
829
+ }
830
+ const aliasParent = reference.parentPath;
831
+ if (aliasParent?.isVariableDeclarator() && aliasParent.node.init === reference.node && aliasParent.node.id.type === "Identifier") {
832
+ const alias = aliasParent.scope.getBinding(aliasParent.node.id.name);
833
+ if (alias && bindingHasMutation(alias, nextSeen)) return true;
834
+ }
835
+ if (aliasParent?.isVariableDeclarator() && aliasParent.node.init === reference.node && aliasParent.node.id.type !== "Identifier") for (const name of collectBindingNames(aliasParent.node.id)) {
836
+ const alias = aliasParent.scope.getBinding(name);
837
+ if (alias && bindingHasMutation(alias, nextSeen)) return true;
838
+ }
839
+ if (parent?.isAssignmentExpression({ operator: "=" }) && parent.node.right === current.node) for (const name of collectBindingNames(parent.node.left)) {
840
+ const alias = parent.scope.getBinding(name);
841
+ if (alias && bindingHasMutation(alias, nextSeen)) return true;
842
+ }
843
+ }
844
+ return false;
845
+ }
846
+ function collectBindingNames(input) {
847
+ if (input.type === "Identifier") return [input.name];
848
+ if (input.type === "AssignmentPattern") return collectBindingNames(input.left);
849
+ if (input.type === "RestElement") return collectBindingNames(input.argument);
850
+ if (input.type === "ArrayPattern") return input.elements.flatMap((element) => element ? collectBindingNames(element) : []);
851
+ if (input.type === "ObjectPattern") return input.properties.flatMap((property) => property.type === "RestElement" ? collectBindingNames(property.argument) : collectBindingNames(property.value));
852
+ return [];
853
+ }
854
+ function readStaticMemberName(node) {
855
+ if (!node.computed && node.property.type === "Identifier") return node.property.name;
856
+ return node.computed && node.property.type === "StringLiteral" ? node.property.value : void 0;
857
+ }
858
+ function isGlobalMutationCall(input, scope) {
859
+ const node = unwrapExpression(input);
860
+ if (node.type !== "MemberExpression" && node.type !== "OptionalMemberExpression") return false;
861
+ const method = readStaticMemberName(node);
862
+ if (node.object.type !== "Identifier" || scope.getBinding(node.object.name)) return false;
863
+ return node.object.name === "Object" && (method === "assign" || method === "defineProperty" || method === "defineProperties") || node.object.name === "Reflect" && (method === "set" || method === "deleteProperty");
864
+ }
865
+ function isKnownReadOnlyConfigConsumer(call, argument, scope) {
866
+ if (resolveKnownConfigConsumer(call.callee, scope, /* @__PURE__ */ new Set())) return true;
867
+ const callee = unwrapExpression(call.callee);
868
+ if (callee.type !== "MemberExpression" && callee.type !== "OptionalMemberExpression") return false;
869
+ const method = readStaticMemberName(callee);
870
+ if (callee.object.type !== "Identifier" || scope.getBinding(callee.object.name)) return false;
871
+ const argumentIndex = call.arguments.findIndex((candidate) => candidate === argument);
872
+ if (callee.object.name === "Object") {
873
+ if (method === "assign") return argumentIndex > 0;
874
+ return new Set([
875
+ "entries",
876
+ "freeze",
877
+ "getOwnPropertyDescriptor",
878
+ "getOwnPropertyDescriptors",
879
+ "getOwnPropertyNames",
880
+ "getOwnPropertySymbols",
881
+ "isExtensible",
882
+ "isFrozen",
883
+ "isSealed",
884
+ "keys",
885
+ "preventExtensions",
886
+ "seal",
887
+ "values"
888
+ ]).has(method ?? "");
889
+ }
890
+ return callee.object.name === "Array" && method === "isArray" || callee.object.name === "JSON" && method === "stringify";
891
+ }
892
+ function resolveKnownConfigConsumer(input, scope, seen) {
893
+ const node = unwrapExpression(input);
894
+ if (node.type === "Identifier") {
895
+ const binding = scope.getBinding(node.name);
896
+ if (!binding) return node.name === "defineNuxtConfig";
897
+ if (seen.has(binding)) return false;
898
+ if (binding.path.isImportDefaultSpecifier()) {
899
+ const declaration = binding.path.parentPath;
900
+ return declaration.isImportDeclaration() && (declaration.node.source.value === "@vitejs/plugin-vue" || declaration.node.source.value === "@vitejs/plugin-vue-jsx");
901
+ }
902
+ if (binding.path.isImportSpecifier()) {
903
+ const declaration = binding.path.parentPath;
904
+ if (!declaration.isImportDeclaration()) return false;
905
+ const imported = binding.path.node.imported;
906
+ const name = imported.type === "Identifier" ? imported.name : imported.value;
907
+ const source = declaration.node.source.value;
908
+ return (source === "@vitejs/plugin-vue" || source === "@vitejs/plugin-vue-jsx") && name === "default" || source === "vite" && name === "defineConfig" || (source === "nuxt" || source === "nuxt/config" || source === "#imports") && name === "defineNuxtConfig";
909
+ }
910
+ const declaration = binding.path.node;
911
+ if (binding.constant && declaration.type === "VariableDeclarator" && declaration.init) {
912
+ const nextSeen = new Set(seen);
913
+ nextSeen.add(binding);
914
+ return resolveKnownConfigConsumer(declaration.init, binding.path.scope, nextSeen);
915
+ }
916
+ return false;
917
+ }
918
+ return (node.type === "MemberExpression" || node.type === "OptionalMemberExpression") && isDefaultMember(node) && (getPluginVueRequireKind(node, "@vitejs/plugin-vue") === "default" || getPluginVueRequireKind(node, "@vitejs/plugin-vue-jsx") === "default");
919
+ }
920
+ function parseConfig(file, errors) {
921
+ const source = fs.readFileSync(file, "utf8");
922
+ try {
923
+ return parse(source, {
924
+ sourceType: "unambiguous",
925
+ plugins: parserPlugins(file)
926
+ });
927
+ } catch (error) {
928
+ if (!/compilerOptions|whitespace|delimiters|@vitejs\/plugin-vue-jsx|babelPlugins|pragma/.test(source)) return;
929
+ const syntaxError = error;
930
+ errors.push(withLocation(file, createDiagnosticMessage({
931
+ whatHappened: "Could not parse a project configuration containing Vue compiler options",
932
+ details: syntaxError.message,
933
+ fix: "Use a statically analyzable Vite or Nuxt configuration, or configure files.gt.parsingFlags.vueCompilerOptions explicitly"
934
+ }), syntaxError.loc ? `${syntaxError.loc.line}:${syntaxError.loc.column + 1}` : void 0));
935
+ return;
936
+ }
937
+ }
938
+ function parserPlugins(file) {
939
+ const extension = path.extname(file).toLowerCase();
940
+ const plugins = [];
941
+ if (extension === ".ts" || extension === ".mts" || extension === ".cts" || extension === ".tsx") plugins.push("typescript", "decorators-legacy");
942
+ if (extension === ".jsx" || extension === ".tsx") plugins.push("jsx");
943
+ return plugins;
944
+ }
945
+ function mergeOption(state, key, value, source, errors) {
946
+ const existing = state.options[key];
947
+ if (existing !== void 0 && !sameOption(existing, value)) {
948
+ errors.push(locatedOptionError(source.file, source.node, `Found conflicting Vue ${key} compiler options`, "Use the same Vue compiler options in the project configuration and files.gt.parsingFlags.vueCompilerOptions"));
949
+ return;
950
+ }
951
+ Object.assign(state.options, { [key]: value });
952
+ state.sources[key] ??= source;
953
+ }
954
+ function sameOption(first, second) {
955
+ return Array.isArray(first) && Array.isArray(second) ? first[0] === second[0] && first[1] === second[1] : first === second;
956
+ }
957
+ function isRecord(value) {
958
+ return !!value && typeof value === "object" && !Array.isArray(value);
959
+ }
960
+ function isDelimiterPair(value) {
961
+ return Array.isArray(value) && value.length === 2 && value.every((delimiter) => typeof delimiter === "string" && delimiter.length > 0);
962
+ }
963
+ function dynamicConfigError(file, node) {
964
+ return locatedOptionError(file, node, "Could not statically resolve Vue compiler options", "Use static object and string literals for Vue whitespace and delimiters compiler options so extraction hashes match the compiled app");
965
+ }
966
+ function dynamicVueJSXConfigError(file, node) {
967
+ return locatedOptionError(file, node, "Could not statically resolve @vitejs/plugin-vue-jsx options", "Use one statically configured Vue JSX plugin with the default VNode transform so extraction hashes match the compiled app");
968
+ }
969
+ function locatedOptionError(file, node, whatHappened, fix) {
970
+ return withLocation(file, createDiagnosticMessage({
971
+ whatHappened,
972
+ fix
973
+ }), node?.loc ? `${node.loc.start.line}:${node.loc.start.column + 1}` : void 0);
974
+ }
975
+ function optionError(whatHappened, fix) {
976
+ return createDiagnosticMessage({
977
+ whatHappened,
978
+ fix
979
+ });
980
+ }
981
+ function withLocation(file, message, location) {
982
+ return `${file}${location ? ` (${location})` : ""}: ${message}`;
983
+ }
984
+ //#endregion
985
+ export { resolveVueCompilerOptions };
986
+
987
+ //# sourceMappingURL=resolveVueCompilerOptions.js.map