@generaltranslation/vue-extractor 0.0.0 → 0.1.0-iris.1
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.md +18 -102
- package/dist/detect.d.ts +8 -0
- package/dist/detect.js +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/inspect.d.ts +9 -0
- package/dist/inspect.js +2 -0
- package/dist/integration.d.ts +69 -0
- package/dist/integration.js +162 -0
- package/dist/integration.js.map +1 -0
- package/dist/internal/config/configFiles.d.ts +6 -0
- package/dist/internal/config/configFiles.js +34 -0
- package/dist/internal/config/configFiles.js.map +1 -0
- package/dist/internal/config/resolveVueCompilerOptions.d.ts +6 -0
- package/dist/internal/config/resolveVueCompilerOptions.js +74 -56
- package/dist/internal/config/resolveVueCompilerOptions.js.map +1 -1
- package/dist/internal/extractFromVueSource.d.ts +4 -3
- package/dist/internal/extractFromVueSource.js +255 -12
- package/dist/internal/extractFromVueSource.js.map +1 -1
- package/dist/internal/project/consumerUsage.d.ts +34 -0
- package/dist/internal/project/consumerUsage.js +1220 -0
- package/dist/internal/project/consumerUsage.js.map +1 -0
- package/dist/internal/project/detectVueProject.d.ts +13 -0
- package/dist/internal/project/detectVueProject.js +65 -0
- package/dist/internal/project/detectVueProject.js.map +1 -0
- package/dist/internal/project/extractFromVueProject.d.ts +10 -0
- package/dist/internal/project/extractFromVueProject.js +579 -0
- package/dist/internal/project/extractFromVueProject.js.map +1 -0
- package/dist/internal/project/inspectVueProject.d.ts +48 -0
- package/dist/internal/project/inspectVueProject.js +143 -0
- package/dist/internal/project/inspectVueProject.js.map +1 -0
- package/dist/internal/project/manifest.d.ts +64 -0
- package/dist/internal/project/manifest.js +270 -0
- package/dist/internal/project/manifest.js.map +1 -0
- package/dist/internal/project/mergeVueProjectExtraction.d.ts +18 -0
- package/dist/internal/project/mergeVueProjectExtraction.js +79 -0
- package/dist/internal/project/mergeVueProjectExtraction.js.map +1 -0
- package/dist/internal/project/moduleResolver.d.ts +18 -0
- package/dist/internal/project/moduleResolver.js +325 -0
- package/dist/internal/project/moduleResolver.js.map +1 -0
- package/dist/internal/project/scopes.d.ts +31 -0
- package/dist/internal/project/scopes.js +182 -0
- package/dist/internal/project/scopes.js.map +1 -0
- package/dist/internal/project/sourcePatterns.d.ts +4 -0
- package/dist/internal/project/sourcePatterns.js +32 -0
- package/dist/internal/project/sourcePatterns.js.map +1 -0
- package/dist/internal/project/viteAliases.d.ts +42 -0
- package/dist/internal/project/viteAliases.js +1020 -0
- package/dist/internal/project/viteAliases.js.map +1 -0
- package/dist/internal/project/vueSourceClassification.d.ts +13 -0
- package/dist/internal/project/vueSourceClassification.js +146 -0
- package/dist/internal/project/vueSourceClassification.js.map +1 -0
- package/dist/internal/project/workspaces.d.ts +31 -0
- package/dist/internal/project/workspaces.js +326 -0
- package/dist/internal/project/workspaces.js.map +1 -0
- package/dist/internal/project/wrapperProvenance.d.ts +21 -0
- package/dist/internal/project/wrapperProvenance.js +287 -0
- package/dist/internal/project/wrapperProvenance.js.map +1 -0
- package/dist/internal/script/analyze.js +323 -108
- package/dist/internal/script/analyze.js.map +1 -1
- package/dist/internal/script/jsx.d.ts +7 -4
- package/dist/internal/script/jsx.js +351 -79
- package/dist/internal/script/jsx.js.map +1 -1
- package/dist/internal/script/knownValues.js +4 -0
- package/dist/internal/script/knownValues.js.map +1 -1
- package/dist/internal/script/localModules.d.ts +23 -2
- package/dist/internal/script/localModules.js +535 -20
- package/dist/internal/script/localModules.js.map +1 -1
- package/dist/internal/script/model.d.ts +10 -3
- package/dist/internal/script/parser.js +2 -1
- package/dist/internal/script/parser.js.map +1 -1
- package/dist/internal/script/runtimeModules.d.ts +10 -0
- package/dist/internal/script/runtimeModules.js +27 -0
- package/dist/internal/script/runtimeModules.js.map +1 -0
- package/dist/internal/stringCalls.js +9 -3
- package/dist/internal/stringCalls.js.map +1 -1
- package/dist/internal/template.js +263 -141
- package/dist/internal/template.js.map +1 -1
- package/dist/internal/types.d.ts +5 -2
- package/dist/internal/utils.d.ts +14 -3
- package/dist/internal/utils.js +63 -3
- package/dist/internal/utils.js.map +1 -1
- package/dist/internal/vueCompiler.d.ts +9 -1
- package/dist/internal/vueCompiler.js +37 -14
- package/dist/internal/vueCompiler.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +3 -0
- package/dist/types.d.ts +95 -4
- package/package.json +30 -4
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import { NUXT_CONFIG_FILES, VITE_CONFIG_FILES } from "../config/configFiles.js";
|
|
2
|
+
import { resolveVueCompilerOptions } from "../config/resolveVueCompilerOptions.js";
|
|
3
|
+
import { declaresInstalledJavaScriptDependency, readJavaScriptPackageManifest } from "./manifest.js";
|
|
4
|
+
import { DEFAULT_RESOLUTION_CONDITIONS, createProjectModuleResolver, loadExplicitTypeScriptConfig } from "./moduleResolver.js";
|
|
5
|
+
import { isVueSfcSource } from "./vueSourceClassification.js";
|
|
6
|
+
import { extractFromVueSource } from "../extractFromVueSource.js";
|
|
7
|
+
import { discoverVueProject, findVueSourceScope, readDefaultVueSourcePatterns, resolveProjectDirectory } from "./scopes.js";
|
|
8
|
+
import { readVueProjectInspection } from "./inspectVueProject.js";
|
|
9
|
+
import { resolveVueProjectAliasConfiguration } from "./viteAliases.js";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { createDiagnosticMessage, formatDiagnosticErrorDetails } from "generaltranslation/internal";
|
|
13
|
+
import fg from "fast-glob";
|
|
14
|
+
import { hashSource } from "generaltranslation/id";
|
|
15
|
+
//#region src/internal/project/extractFromVueProject.ts
|
|
16
|
+
const SCRIPT_EXTENSIONS = new Set([
|
|
17
|
+
".js",
|
|
18
|
+
".jsx",
|
|
19
|
+
".mjs",
|
|
20
|
+
".cjs",
|
|
21
|
+
".ts",
|
|
22
|
+
".tsx",
|
|
23
|
+
".mts",
|
|
24
|
+
".cts"
|
|
25
|
+
]);
|
|
26
|
+
const JSX_CONFIG_EXTENSIONS = new Set([".jsx", ".tsx"]);
|
|
27
|
+
/**
|
|
28
|
+
* Extracts, hashes, and deduplicates every gt-vue message owned by a project.
|
|
29
|
+
*
|
|
30
|
+
* This is the package-level orchestration boundary used by host CLIs. It owns
|
|
31
|
+
* workspace discovery, source matching, compiler/config resolution, static
|
|
32
|
+
* module resolution, and catalog hashing so framework hosts do not need Vue
|
|
33
|
+
* implementation details. Configuration failures return no partial updates.
|
|
34
|
+
*/
|
|
35
|
+
async function extractFromVueProject(options = {}) {
|
|
36
|
+
const projectRoot = resolveProjectDirectory(options.cwd ?? options.inspection?.projectRoot ?? process.cwd());
|
|
37
|
+
const discovery = readVueProjectInspection(options.inspection, projectRoot) ?? discoverVueProject(projectRoot);
|
|
38
|
+
const discoveryErrors = options.filePatterns === void 0 ? validateNuxtSourceScopes(discovery.scopes, options.vueCompilerOptions) : [];
|
|
39
|
+
if (discoveryErrors.length > 0) return {
|
|
40
|
+
updates: [],
|
|
41
|
+
errors: discoveryErrors,
|
|
42
|
+
warnings: []
|
|
43
|
+
};
|
|
44
|
+
const patterns = options.filePatterns ?? readDefaultVueSourcePatterns(discovery.scopes);
|
|
45
|
+
if (patterns.length === 0) return {
|
|
46
|
+
updates: [],
|
|
47
|
+
errors: [],
|
|
48
|
+
warnings: []
|
|
49
|
+
};
|
|
50
|
+
const fileMatches = matchProjectFiles(projectRoot, patterns);
|
|
51
|
+
if (fileMatches.errors.length > 0) return {
|
|
52
|
+
updates: [],
|
|
53
|
+
errors: fileMatches.errors,
|
|
54
|
+
warnings: []
|
|
55
|
+
};
|
|
56
|
+
const matchedFiles = fileMatches.files;
|
|
57
|
+
const scopes = addExplicitSourceScopes(projectRoot, discovery.scopes, options.filePatterns === void 0 ? [] : matchedFiles);
|
|
58
|
+
const sourceByFile = /* @__PURE__ */ new Map();
|
|
59
|
+
const standaloneResults = /* @__PURE__ */ new Map();
|
|
60
|
+
const fileScopes = new Map(matchedFiles.map((file) => [file, findVueSourceScope(file, scopes)]));
|
|
61
|
+
const files = await filterVueSourceFiles(matchedFiles.filter((file) => fileScopes.get(file) !== void 0), sourceByFile);
|
|
62
|
+
if (files.length === 0) return {
|
|
63
|
+
updates: [],
|
|
64
|
+
errors: [],
|
|
65
|
+
warnings: []
|
|
66
|
+
};
|
|
67
|
+
const errors = [];
|
|
68
|
+
const warnings = /* @__PURE__ */ new Set();
|
|
69
|
+
const tsconfigPath = resolveExplicitTypeScriptConfigPath(projectRoot, options.tsconfigPath, errors);
|
|
70
|
+
if (errors.length > 0) return {
|
|
71
|
+
updates: [],
|
|
72
|
+
errors,
|
|
73
|
+
warnings: []
|
|
74
|
+
};
|
|
75
|
+
let explicitCompilerResolution = validateExplicitCompilerConfig(projectRoot, options, files.some((file) => path.extname(file).toLowerCase() === ".vue"), errors);
|
|
76
|
+
if (errors.length > 0) return {
|
|
77
|
+
updates: [],
|
|
78
|
+
errors,
|
|
79
|
+
warnings: []
|
|
80
|
+
};
|
|
81
|
+
const moduleResolution = createVueModuleResolver(projectRoot, scopes, fileScopes, options, createProjectModuleResolver(options.conditionNames ?? DEFAULT_RESOLUTION_CONDITIONS, { tsconfigPath }));
|
|
82
|
+
errors.push(...moduleResolution.errors);
|
|
83
|
+
if (errors.length > 0) return {
|
|
84
|
+
updates: [],
|
|
85
|
+
errors,
|
|
86
|
+
warnings: []
|
|
87
|
+
};
|
|
88
|
+
const resolveModuleForFile = moduleResolution.resolveModuleForFile;
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const extension = path.extname(file).toLowerCase();
|
|
91
|
+
if (extension !== ".vue" && !SCRIPT_EXTENSIONS.has(extension)) continue;
|
|
92
|
+
let source = sourceByFile.get(file);
|
|
93
|
+
if (source === void 0) {
|
|
94
|
+
try {
|
|
95
|
+
source = await fs.promises.readFile(file, "utf8");
|
|
96
|
+
} catch (error) {
|
|
97
|
+
errors.push(createReadDiagnostic(projectRoot, file, error));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
sourceByFile.set(file, source);
|
|
101
|
+
}
|
|
102
|
+
if (extension === ".vue") continue;
|
|
103
|
+
standaloneResults.set(file, await extractFromVueSource(source, file, {
|
|
104
|
+
compilerOptions: {},
|
|
105
|
+
includeSourceCodeContext: options.includeSourceCodeContext,
|
|
106
|
+
projectRoot,
|
|
107
|
+
requireGTProvenance: true,
|
|
108
|
+
resolveModule: resolveModuleForFile(file),
|
|
109
|
+
surroundingLineCount: options.surroundingLineCount
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
if (explicitCompilerResolution === void 0 && [...standaloneResults.values()].some(hasExtractionSignal)) explicitCompilerResolution = validateExplicitCompilerConfig(projectRoot, options, true, errors);
|
|
113
|
+
const potentialAliasProvenanceScopes = await probePotentialAliasProvenance(files, sourceByFile, fileScopes, standaloneResults, moduleResolution.resolvePotentialModulesForFile, projectRoot);
|
|
114
|
+
validateIncompleteAliasScopes(files, fileScopes, standaloneResults, moduleResolution.incompleteAliasScopes, potentialAliasProvenanceScopes, errors);
|
|
115
|
+
if (errors.length > 0) return {
|
|
116
|
+
updates: [],
|
|
117
|
+
errors,
|
|
118
|
+
warnings: []
|
|
119
|
+
};
|
|
120
|
+
const compilerOptionsByScope = resolveCompilerOptionsByScope(projectRoot, files.filter((file) => {
|
|
121
|
+
const extension = path.extname(file).toLowerCase();
|
|
122
|
+
if (extension === ".vue") return true;
|
|
123
|
+
const standaloneResult = standaloneResults.get(file);
|
|
124
|
+
return hasExtractionSignal(standaloneResult) && (JSX_CONFIG_EXTENSIONS.has(extension) || standaloneResult?.results.some(({ dataFormat }) => dataFormat === "JSX") === true);
|
|
125
|
+
}), fileScopes, scopes, options, explicitCompilerResolution, errors);
|
|
126
|
+
if (errors.length > 0) return {
|
|
127
|
+
updates: [],
|
|
128
|
+
errors,
|
|
129
|
+
warnings: []
|
|
130
|
+
};
|
|
131
|
+
const results = [];
|
|
132
|
+
for (const file of files) {
|
|
133
|
+
const extension = path.extname(file).toLowerCase();
|
|
134
|
+
if (extension !== ".vue" && !SCRIPT_EXTENSIONS.has(extension)) continue;
|
|
135
|
+
const source = sourceByFile.get(file);
|
|
136
|
+
if (source === void 0) continue;
|
|
137
|
+
const result = extension === ".vue" ? await extractFromVueSource(source, file, {
|
|
138
|
+
compilerOptions: compilerOptionsByScope.get(fileScopes.get(file)?.directory ?? "") ?? {},
|
|
139
|
+
includeSourceCodeContext: options.includeSourceCodeContext,
|
|
140
|
+
projectRoot,
|
|
141
|
+
requireGTProvenance: true,
|
|
142
|
+
resolveModule: resolveModuleForFile(file),
|
|
143
|
+
surroundingLineCount: options.surroundingLineCount
|
|
144
|
+
}) : standaloneResults.get(file);
|
|
145
|
+
if (!result) continue;
|
|
146
|
+
results.push(...result.results);
|
|
147
|
+
errors.push(...result.errors);
|
|
148
|
+
for (const warning of result.warnings) warnings.add(warning);
|
|
149
|
+
}
|
|
150
|
+
const updates = results.map(addCatalogHash);
|
|
151
|
+
deduplicateProjectUpdates(updates);
|
|
152
|
+
return {
|
|
153
|
+
updates: errors.length > 0 ? [] : updates,
|
|
154
|
+
errors,
|
|
155
|
+
warnings: [...warnings]
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/** Excludes script modules whose filename happens to end in `.vue`. */
|
|
159
|
+
async function filterVueSourceFiles(files, sourceByFile) {
|
|
160
|
+
const included = await Promise.all(files.map(async (file) => {
|
|
161
|
+
if (path.extname(file).toLowerCase() !== ".vue") return true;
|
|
162
|
+
try {
|
|
163
|
+
const source = await fs.promises.readFile(file, "utf8");
|
|
164
|
+
sourceByFile.set(file, source);
|
|
165
|
+
return isVueSfcSource(source);
|
|
166
|
+
} catch {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
}));
|
|
170
|
+
return files.filter((_file, index) => included[index]);
|
|
171
|
+
}
|
|
172
|
+
/** Returns whether a standalone file has observable gt-vue extraction work. */
|
|
173
|
+
function hasExtractionSignal(result) {
|
|
174
|
+
return Boolean(result && (result.results.length > 0 || result.errors.length > 0 || result.warnings.length > 0));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Proves whether a statically observed alias candidate can expose gt-vue.
|
|
178
|
+
*
|
|
179
|
+
* Incomplete config candidates are diagnostic evidence only: results from
|
|
180
|
+
* these probes are never published. Reusing the normal provenance analyzer
|
|
181
|
+
* avoids treating ordinary React/local helpers with GT-shaped names as Vue.
|
|
182
|
+
*/
|
|
183
|
+
async function probePotentialAliasProvenance(files, sourceByFile, fileScopes, standaloneResults, resolvePotentialModulesForFile, projectRoot) {
|
|
184
|
+
const provenScopes = /* @__PURE__ */ new Set();
|
|
185
|
+
for (const file of files) {
|
|
186
|
+
if (path.extname(file).toLowerCase() === ".vue") continue;
|
|
187
|
+
const scope = fileScopes.get(file);
|
|
188
|
+
const source = sourceByFile.get(file);
|
|
189
|
+
if (!scope || source === void 0 || provenScopes.has(scope.directory) || hasExtractionSignal(standaloneResults.get(file))) continue;
|
|
190
|
+
for (const resolveModule of resolvePotentialModulesForFile(file)) {
|
|
191
|
+
if (!hasExtractionSignal(await extractFromVueSource(source, file, {
|
|
192
|
+
compilerOptions: {},
|
|
193
|
+
projectRoot,
|
|
194
|
+
requireGTProvenance: true,
|
|
195
|
+
resolveModule
|
|
196
|
+
}))) continue;
|
|
197
|
+
provenScopes.add(scope.directory);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return provenScopes;
|
|
202
|
+
}
|
|
203
|
+
/** Maximum diagnostic-only alias combinations explored for one application. */
|
|
204
|
+
const MAX_POTENTIAL_ALIAS_CONFIGURATIONS = 128;
|
|
205
|
+
/** Builds deterministic candidate maps without trusting any as active config. */
|
|
206
|
+
function createPotentialAliasConfigurations(potentialAliases) {
|
|
207
|
+
if (!potentialAliases || potentialAliases.size === 0) return [];
|
|
208
|
+
let configurations = [/* @__PURE__ */ new Map()];
|
|
209
|
+
for (const [key, replacements] of potentialAliases) {
|
|
210
|
+
if (replacements.size === 0) continue;
|
|
211
|
+
const next = [];
|
|
212
|
+
for (const configuration of configurations) {
|
|
213
|
+
for (const replacement of replacements) {
|
|
214
|
+
next.push(new Map(configuration).set(key, replacement));
|
|
215
|
+
if (next.length >= MAX_POTENTIAL_ALIAS_CONFIGURATIONS) break;
|
|
216
|
+
}
|
|
217
|
+
if (next.length >= MAX_POTENTIAL_ALIAS_CONFIGURATIONS) break;
|
|
218
|
+
}
|
|
219
|
+
configurations = next;
|
|
220
|
+
if (configurations.length === 0) return [];
|
|
221
|
+
}
|
|
222
|
+
return configurations;
|
|
223
|
+
}
|
|
224
|
+
function addCatalogHash(result) {
|
|
225
|
+
const hash = hashSource({
|
|
226
|
+
source: result.source,
|
|
227
|
+
...result.metadata.context && { context: result.metadata.context },
|
|
228
|
+
...result.metadata.maxChars != null && { maxChars: result.metadata.maxChars },
|
|
229
|
+
...result.metadata.requiresReview === true && { requiresReview: true },
|
|
230
|
+
dataFormat: result.dataFormat
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
...result,
|
|
234
|
+
metadata: {
|
|
235
|
+
...result.metadata,
|
|
236
|
+
hash
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** Merges duplicate Vue hashes without repeating source-location metadata. */
|
|
241
|
+
function deduplicateProjectUpdates(updates) {
|
|
242
|
+
const byHash = /* @__PURE__ */ new Map();
|
|
243
|
+
for (const update of updates) {
|
|
244
|
+
const existing = byHash.get(update.metadata.hash);
|
|
245
|
+
if (!existing) {
|
|
246
|
+
byHash.set(update.metadata.hash, update);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
existing.metadata.filePaths = mergeUnique(existing.metadata.filePaths, update.metadata.filePaths);
|
|
250
|
+
existing.metadata.sourceCode = mergeSourceCode(existing.metadata.sourceCode, update.metadata.sourceCode);
|
|
251
|
+
}
|
|
252
|
+
updates.splice(0, updates.length, ...byHash.values());
|
|
253
|
+
}
|
|
254
|
+
function mergeUnique(left, right) {
|
|
255
|
+
const merged = [...new Set([...left ?? [], ...right ?? []])];
|
|
256
|
+
return merged.length > 0 ? merged : void 0;
|
|
257
|
+
}
|
|
258
|
+
function mergeSourceCode(left, right) {
|
|
259
|
+
if (!left && !right) return void 0;
|
|
260
|
+
const merged = {};
|
|
261
|
+
for (const sourceCode of [left, right]) for (const [file, entries] of Object.entries(sourceCode ?? {})) {
|
|
262
|
+
const known = new Set((merged[file] ?? []).map((entry) => JSON.stringify(entry)));
|
|
263
|
+
for (const entry of entries) {
|
|
264
|
+
const key = JSON.stringify(entry);
|
|
265
|
+
if (known.has(key)) continue;
|
|
266
|
+
(merged[file] ??= []).push(entry);
|
|
267
|
+
known.add(key);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return merged;
|
|
271
|
+
}
|
|
272
|
+
/** Matches source patterns without hiding filesystem discovery failures. */
|
|
273
|
+
function matchProjectFiles(cwd, patterns) {
|
|
274
|
+
let matches;
|
|
275
|
+
try {
|
|
276
|
+
matches = fg.sync([...patterns], {
|
|
277
|
+
absolute: true,
|
|
278
|
+
cwd,
|
|
279
|
+
followSymbolicLinks: false,
|
|
280
|
+
ignore: ["**/node_modules/**"],
|
|
281
|
+
onlyFiles: true,
|
|
282
|
+
unique: true
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return {
|
|
286
|
+
files: [],
|
|
287
|
+
errors: [createGlobDiagnostic(patterns, error)]
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
let realRoot;
|
|
291
|
+
try {
|
|
292
|
+
realRoot = fs.realpathSync(cwd);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
return {
|
|
295
|
+
files: [],
|
|
296
|
+
errors: [createRootRealpathDiagnostic(cwd, error)]
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const files = /* @__PURE__ */ new Set();
|
|
300
|
+
const errors = [];
|
|
301
|
+
for (const file of matches) {
|
|
302
|
+
let realFile;
|
|
303
|
+
try {
|
|
304
|
+
realFile = fs.realpathSync(file);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
errors.push(createMatchedFileRealpathDiagnostic(cwd, file, error));
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const relative = path.relative(realRoot, realFile);
|
|
310
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) continue;
|
|
311
|
+
files.add(realFile);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
errors,
|
|
315
|
+
files: [...files].sort((left, right) => left.localeCompare(right))
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Makes custom Nuxt source layouts fail closed before another scope can make
|
|
320
|
+
* an incomplete project look successful. Vite config does not influence
|
|
321
|
+
* source discovery and remains gated on a matched SFC or proven gt-vue JSX.
|
|
322
|
+
*/
|
|
323
|
+
function validateNuxtSourceScopes(scopes, explicitOptions) {
|
|
324
|
+
const errors = [];
|
|
325
|
+
for (const scope of scopes) {
|
|
326
|
+
if (!NUXT_CONFIG_FILES.some((file) => fs.existsSync(path.join(scope.directory, file)))) continue;
|
|
327
|
+
errors.push(...resolveVueCompilerOptions(scope.directory, explicitOptions).errors);
|
|
328
|
+
}
|
|
329
|
+
return errors;
|
|
330
|
+
}
|
|
331
|
+
/** Adds independent package boundaries only for explicit user-selected sources. */
|
|
332
|
+
function addExplicitSourceScopes(projectRoot, discoveredScopes, files) {
|
|
333
|
+
const scopes = [...discoveredScopes];
|
|
334
|
+
const known = new Set(scopes.map(({ directory }) => directory));
|
|
335
|
+
for (const file of files) {
|
|
336
|
+
const directory = findNearestPackageDirectory(file, projectRoot);
|
|
337
|
+
if (known.has(directory)) continue;
|
|
338
|
+
if (!findVueSourceScope(file, scopes)) continue;
|
|
339
|
+
if (!ownsIndependentVueConfiguration(directory)) continue;
|
|
340
|
+
known.add(directory);
|
|
341
|
+
scopes.push({
|
|
342
|
+
directory,
|
|
343
|
+
includeByDefault: false,
|
|
344
|
+
relativeDirectory: toPosixPath(path.relative(projectRoot, directory))
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return scopes;
|
|
348
|
+
}
|
|
349
|
+
/** Returns whether a package must resolve its own Vue build configuration. */
|
|
350
|
+
function ownsIndependentVueConfiguration(directory) {
|
|
351
|
+
if ([...VITE_CONFIG_FILES, ...NUXT_CONFIG_FILES].some((file) => fs.existsSync(path.join(directory, file)))) return true;
|
|
352
|
+
const manifest = readJavaScriptPackageManifest(path.join(directory, "package.json"));
|
|
353
|
+
return Boolean(manifest && declaresInstalledJavaScriptDependency(manifest, "gt-vue"));
|
|
354
|
+
}
|
|
355
|
+
function findNearestPackageDirectory(file, projectRoot) {
|
|
356
|
+
let current = path.dirname(file);
|
|
357
|
+
const root = path.resolve(projectRoot);
|
|
358
|
+
while (current !== root && isWithin(root, current)) {
|
|
359
|
+
if (fs.existsSync(path.join(current, "package.json"))) return current;
|
|
360
|
+
const parent = path.dirname(current);
|
|
361
|
+
if (parent === current) break;
|
|
362
|
+
current = parent;
|
|
363
|
+
}
|
|
364
|
+
return root;
|
|
365
|
+
}
|
|
366
|
+
/** Applies each Vue app's statically resolved Vite/Nuxt aliases before Node resolution. */
|
|
367
|
+
function createVueModuleResolver(projectRoot, scopes, fileScopes, options, resolveModule) {
|
|
368
|
+
const matchedScopes = uniqueSourceScopes(fileScopes.values());
|
|
369
|
+
const ownedMatchedScopes = matchedScopes.filter(({ includeByDefault }) => includeByDefault);
|
|
370
|
+
const candidateExplicitScopes = ownedMatchedScopes.length > 0 ? ownedMatchedScopes : matchedScopes;
|
|
371
|
+
const aliasesByScope = /* @__PURE__ */ new Map();
|
|
372
|
+
const errors = [];
|
|
373
|
+
const incompleteAliasScopes = /* @__PURE__ */ new Set();
|
|
374
|
+
const potentialAliasesByScope = /* @__PURE__ */ new Map();
|
|
375
|
+
if (options.viteConfigPath !== void 0) {
|
|
376
|
+
const explicitConfig = path.resolve(projectRoot, options.viteConfigPath);
|
|
377
|
+
const explicitScope = candidateExplicitScopes.length === 1 ? candidateExplicitScopes[0] : findVueSourceScope(explicitConfig, candidateExplicitScopes);
|
|
378
|
+
if (!explicitScope) errors.push(createExplicitConfigOwnershipDiagnostic(options.viteConfigPath));
|
|
379
|
+
else for (const scope of matchedScopes) addAliasesForScope(aliasesByScope, incompleteAliasScopes, potentialAliasesByScope, scope, scope.directory === explicitScope.directory ? {
|
|
380
|
+
directory: projectRoot,
|
|
381
|
+
viteConfigPath: options.viteConfigPath
|
|
382
|
+
} : { directory: scope.directory });
|
|
383
|
+
} else for (const scope of matchedScopes) addAliasesForScope(aliasesByScope, incompleteAliasScopes, potentialAliasesByScope, scope, { directory: scope.directory });
|
|
384
|
+
return {
|
|
385
|
+
errors,
|
|
386
|
+
incompleteAliasScopes,
|
|
387
|
+
resolveModuleForFile(sourceFile) {
|
|
388
|
+
const sourceScope = findVueSourceScope(sourceFile, scopes);
|
|
389
|
+
return (specifier, importer) => {
|
|
390
|
+
const scope = sourceScope ?? findVueSourceScope(importer, scopes);
|
|
391
|
+
return resolveModule(applyStaticAlias(specifier, scope ? aliasesByScope.get(scope.directory) : void 0), importer);
|
|
392
|
+
};
|
|
393
|
+
},
|
|
394
|
+
resolvePotentialModulesForFile(sourceFile) {
|
|
395
|
+
const sourceScope = findVueSourceScope(sourceFile, scopes);
|
|
396
|
+
if (!sourceScope) return [];
|
|
397
|
+
return createPotentialAliasConfigurations(potentialAliasesByScope.get(sourceScope.directory)).map((aliases) => (specifier, importer) => resolveModule(applyStaticAlias(specifier, aliases), importer));
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function addAliasesForScope(aliasesByScope, incompleteAliasScopes, potentialAliasesByScope, scope, config) {
|
|
402
|
+
const resolution = resolveVueProjectAliasConfiguration(config.directory, { viteConfigPath: config.viteConfigPath });
|
|
403
|
+
if (!resolution.complete) {
|
|
404
|
+
incompleteAliasScopes.add(scope.directory);
|
|
405
|
+
if (resolution.potentialAliases) potentialAliasesByScope.set(scope.directory, resolution.potentialAliases);
|
|
406
|
+
aliasesByScope.set(scope.directory, /* @__PURE__ */ new Map());
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
aliasesByScope.set(scope.directory, resolution.aliases);
|
|
410
|
+
}
|
|
411
|
+
/** Blocks incomplete aliases only after a source proves Vue ownership. */
|
|
412
|
+
function validateIncompleteAliasScopes(files, fileScopes, standaloneResults, incompleteScopes, potentialAliasProvenanceScopes, errors) {
|
|
413
|
+
const diagnosedScopes = /* @__PURE__ */ new Set();
|
|
414
|
+
for (const file of files) {
|
|
415
|
+
const scope = fileScopes.get(file);
|
|
416
|
+
if (!scope || !incompleteScopes.has(scope.directory) || diagnosedScopes.has(scope.directory)) continue;
|
|
417
|
+
const isVueSource = path.extname(file).toLowerCase() === ".vue";
|
|
418
|
+
const hasVueSignal = hasExtractionSignal(standaloneResults.get(file));
|
|
419
|
+
const hasPotentialAliasProvenance = potentialAliasProvenanceScopes.has(scope.directory);
|
|
420
|
+
if (!isVueSource && !hasVueSignal && !hasPotentialAliasProvenance) continue;
|
|
421
|
+
diagnosedScopes.add(scope.directory);
|
|
422
|
+
errors.push(createIncompleteAliasDiagnostic(scope));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function createIncompleteAliasDiagnostic(scope) {
|
|
426
|
+
return createDiagnosticMessage({
|
|
427
|
+
source: "@generaltranslation/vue-extractor",
|
|
428
|
+
severity: "Error",
|
|
429
|
+
whatHappened: "Could not statically resolve the Vue application module aliases",
|
|
430
|
+
why: "Dynamic alias behavior can hide gt-vue imports and produce an incomplete translation catalog",
|
|
431
|
+
fix: "Define Vite or Nuxt aliases with static string values and remove custom alias resolvers",
|
|
432
|
+
details: scope.relativeDirectory || "."
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
function uniqueSourceScopes(scopes) {
|
|
436
|
+
const unique = /* @__PURE__ */ new Map();
|
|
437
|
+
for (const scope of scopes) if (scope) unique.set(scope.directory, scope);
|
|
438
|
+
return [...unique.values()];
|
|
439
|
+
}
|
|
440
|
+
function applyStaticAlias(specifier, aliases) {
|
|
441
|
+
if (!aliases) return specifier;
|
|
442
|
+
for (const [find, replacement] of aliases) {
|
|
443
|
+
const normalized = find.endsWith("/") && replacement.endsWith("/") ? {
|
|
444
|
+
find: find.slice(0, -1),
|
|
445
|
+
replacement: replacement.slice(0, -1)
|
|
446
|
+
} : {
|
|
447
|
+
find,
|
|
448
|
+
replacement
|
|
449
|
+
};
|
|
450
|
+
if (specifier === normalized.find || specifier.startsWith(`${normalized.find}/`)) return specifier.replace(normalized.find, normalized.replacement);
|
|
451
|
+
}
|
|
452
|
+
return specifier;
|
|
453
|
+
}
|
|
454
|
+
/** Resolves every affected scope before parsing any SFC catalog content. */
|
|
455
|
+
function resolveCompilerOptionsByScope(projectRoot, files, fileScopes, scopes, options, explicitCompilerResolution, errors) {
|
|
456
|
+
const optionsByScope = /* @__PURE__ */ new Map();
|
|
457
|
+
const affectedScopes = /* @__PURE__ */ new Map();
|
|
458
|
+
for (const file of files) {
|
|
459
|
+
const scope = fileScopes.get(file);
|
|
460
|
+
if (scope) affectedScopes.set(scope.directory, scope);
|
|
461
|
+
}
|
|
462
|
+
if (affectedScopes.size === 0) return optionsByScope;
|
|
463
|
+
if (options.viteConfigPath !== void 0) {
|
|
464
|
+
const explicitConfig = path.resolve(projectRoot, options.viteConfigPath);
|
|
465
|
+
const explicitResolution = explicitCompilerResolution ?? resolveVueCompilerOptions(projectRoot, options.vueCompilerOptions, {
|
|
466
|
+
viteConfigPath: options.viteConfigPath,
|
|
467
|
+
sourceDiscoveryIsExplicit: options.filePatterns !== void 0
|
|
468
|
+
});
|
|
469
|
+
errors.push(...explicitResolution.errors);
|
|
470
|
+
if (explicitResolution.errors.length > 0) return optionsByScope;
|
|
471
|
+
const explicitScope = affectedScopes.size === 1 ? [...affectedScopes.values()][0] : findVueSourceScope(explicitConfig, scopes);
|
|
472
|
+
if (!explicitScope || !affectedScopes.has(explicitScope.directory)) {
|
|
473
|
+
errors.push(createExplicitConfigOwnershipDiagnostic(options.viteConfigPath));
|
|
474
|
+
return optionsByScope;
|
|
475
|
+
}
|
|
476
|
+
for (const scope of affectedScopes.values()) {
|
|
477
|
+
const resolution = scope.directory === explicitScope.directory ? explicitResolution : resolveVueCompilerOptions(scope.directory, options.vueCompilerOptions, { sourceDiscoveryIsExplicit: options.filePatterns !== void 0 });
|
|
478
|
+
errors.push(...resolution.errors);
|
|
479
|
+
optionsByScope.set(scope.directory, resolution.compilerOptions);
|
|
480
|
+
}
|
|
481
|
+
return optionsByScope;
|
|
482
|
+
}
|
|
483
|
+
for (const scope of affectedScopes.values()) {
|
|
484
|
+
const resolution = resolveVueCompilerOptions(scope.directory, options.vueCompilerOptions, { sourceDiscoveryIsExplicit: options.filePatterns !== void 0 });
|
|
485
|
+
errors.push(...resolution.errors);
|
|
486
|
+
optionsByScope.set(scope.directory, resolution.compilerOptions);
|
|
487
|
+
}
|
|
488
|
+
return optionsByScope;
|
|
489
|
+
}
|
|
490
|
+
/** Validates an explicitly selected config even when every match is STRING. */
|
|
491
|
+
function validateExplicitCompilerConfig(projectRoot, options, hasPotentialVueSource, errors) {
|
|
492
|
+
if (!hasPotentialVueSource || options.viteConfigPath === void 0) return;
|
|
493
|
+
const resolution = resolveVueCompilerOptions(projectRoot, options.vueCompilerOptions, {
|
|
494
|
+
viteConfigPath: options.viteConfigPath,
|
|
495
|
+
sourceDiscoveryIsExplicit: options.filePatterns !== void 0
|
|
496
|
+
});
|
|
497
|
+
errors.push(...resolution.errors);
|
|
498
|
+
return resolution;
|
|
499
|
+
}
|
|
500
|
+
function createExplicitConfigOwnershipDiagnostic(configPath) {
|
|
501
|
+
return createDiagnosticMessage({
|
|
502
|
+
source: "@generaltranslation/vue-extractor",
|
|
503
|
+
severity: "Error",
|
|
504
|
+
whatHappened: "The configured Vite config does not own any matched Vue sources",
|
|
505
|
+
fix: "Run extraction from that application root, include only one centrally configured Vue application, or remove files.gt.parsingFlags.viteConfigPath",
|
|
506
|
+
details: configPath
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
/** Resolves and validates one explicit TypeScript project file atomically. */
|
|
510
|
+
function resolveExplicitTypeScriptConfigPath(projectRoot, configuredPath, errors) {
|
|
511
|
+
if (configuredPath === void 0) return void 0;
|
|
512
|
+
const absolutePath = path.resolve(projectRoot, configuredPath);
|
|
513
|
+
try {
|
|
514
|
+
const configPath = fs.realpathSync(absolutePath);
|
|
515
|
+
if (!fs.statSync(configPath).isFile()) throw new Error("The configured path is not a file");
|
|
516
|
+
const resolution = loadExplicitTypeScriptConfig(configPath);
|
|
517
|
+
if (resolution.resultType === "failed") throw new Error(resolution.message);
|
|
518
|
+
const loadedPath = fs.realpathSync(resolution.configFileAbsolutePath);
|
|
519
|
+
if (loadedPath !== configPath) throw new Error(`The resolver selected a different config file: ${loadedPath}`);
|
|
520
|
+
return configPath;
|
|
521
|
+
} catch (error) {
|
|
522
|
+
errors.push(createDiagnosticMessage({
|
|
523
|
+
source: "@generaltranslation/vue-extractor",
|
|
524
|
+
severity: "Error",
|
|
525
|
+
whatHappened: "Could not load the configured TypeScript project file",
|
|
526
|
+
why: "Unresolved path aliases can hide gt-vue imports and produce an incomplete translation catalog",
|
|
527
|
+
fix: "Set tsconfigPath to a readable tsconfig.json or jsconfig.json file",
|
|
528
|
+
details: [configuredPath, formatDiagnosticErrorDetails(error) ?? "Unknown config error"]
|
|
529
|
+
}));
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function createReadDiagnostic(projectRoot, file, error) {
|
|
534
|
+
return createDiagnosticMessage({
|
|
535
|
+
source: "@generaltranslation/vue-extractor",
|
|
536
|
+
severity: "Error",
|
|
537
|
+
whatHappened: "Could not read a matched Vue source file",
|
|
538
|
+
fix: "Restore read access or exclude the file from the configured source patterns",
|
|
539
|
+
details: [toPosixPath(path.relative(projectRoot, file)), formatDiagnosticErrorDetails(error) ?? "Unknown read error"]
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
function createGlobDiagnostic(patterns, error) {
|
|
543
|
+
return createDiagnosticMessage({
|
|
544
|
+
source: "@generaltranslation/vue-extractor",
|
|
545
|
+
severity: "Error",
|
|
546
|
+
whatHappened: "Could not match the configured Vue source patterns",
|
|
547
|
+
fix: "Correct the source patterns and rerun extraction",
|
|
548
|
+
details: [patterns.join(", ") || "(no patterns)", formatDiagnosticErrorDetails(error) ?? "Unknown glob error"]
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function createRootRealpathDiagnostic(projectRoot, error) {
|
|
552
|
+
return createDiagnosticMessage({
|
|
553
|
+
source: "@generaltranslation/vue-extractor",
|
|
554
|
+
severity: "Error",
|
|
555
|
+
whatHappened: "Could not resolve the Vue project root",
|
|
556
|
+
fix: "Restore access to the project directory and rerun extraction",
|
|
557
|
+
details: [projectRoot, formatDiagnosticErrorDetails(error) ?? "Unknown filesystem error"]
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
function createMatchedFileRealpathDiagnostic(projectRoot, file, error) {
|
|
561
|
+
return createDiagnosticMessage({
|
|
562
|
+
source: "@generaltranslation/vue-extractor",
|
|
563
|
+
severity: "Error",
|
|
564
|
+
whatHappened: "Could not resolve a matched Vue source file",
|
|
565
|
+
fix: "Restore the file or exclude it from the configured source patterns",
|
|
566
|
+
details: [toPosixPath(path.relative(projectRoot, file)), formatDiagnosticErrorDetails(error) ?? "Unknown filesystem error"]
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
function isWithin(root, candidate) {
|
|
570
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
571
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
572
|
+
}
|
|
573
|
+
function toPosixPath(filePath) {
|
|
574
|
+
return filePath.split(path.sep).join(path.posix.sep);
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
577
|
+
export { extractFromVueProject };
|
|
578
|
+
|
|
579
|
+
//# sourceMappingURL=extractFromVueProject.js.map
|