@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.
- package/LICENSE.md +105 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/internal/compilerAst.d.ts +43 -0
- package/dist/internal/compilerAst.js +77 -0
- package/dist/internal/compilerAst.js.map +1 -0
- package/dist/internal/config/resolveVueCompilerOptions.d.ts +23 -0
- package/dist/internal/config/resolveVueCompilerOptions.js +987 -0
- package/dist/internal/config/resolveVueCompilerOptions.js.map +1 -0
- package/dist/internal/extractFromVueSource.d.ts +9 -0
- package/dist/internal/extractFromVueSource.js +332 -0
- package/dist/internal/extractFromVueSource.js.map +1 -0
- package/dist/internal/script/analyze.d.ts +13 -0
- package/dist/internal/script/analyze.js +4761 -0
- package/dist/internal/script/analyze.js.map +1 -0
- package/dist/internal/script/jsx.d.ts +36 -0
- package/dist/internal/script/jsx.js +667 -0
- package/dist/internal/script/jsx.js.map +1 -0
- package/dist/internal/script/knownValues.d.ts +14 -0
- package/dist/internal/script/knownValues.js +148 -0
- package/dist/internal/script/knownValues.js.map +1 -0
- package/dist/internal/script/localModules.d.ts +80 -0
- package/dist/internal/script/localModules.js +364 -0
- package/dist/internal/script/localModules.js.map +1 -0
- package/dist/internal/script/model.d.ts +317 -0
- package/dist/internal/script/model.js +1 -0
- package/dist/internal/script/parser.d.ts +3 -0
- package/dist/internal/script/parser.js +21 -0
- package/dist/internal/script/parser.js.map +1 -0
- package/dist/internal/script/replay.d.ts +53 -0
- package/dist/internal/script/replay.js +1611 -0
- package/dist/internal/script/replay.js.map +1 -0
- package/dist/internal/script/snapshot.d.ts +25 -0
- package/dist/internal/script/snapshot.js +293 -0
- package/dist/internal/script/snapshot.js.map +1 -0
- package/dist/internal/script/syntax.d.ts +21 -0
- package/dist/internal/script/syntax.js +46 -0
- package/dist/internal/script/syntax.js.map +1 -0
- package/dist/internal/script.d.ts +2 -0
- package/dist/internal/script.js +2 -0
- package/dist/internal/stringCalls.d.ts +7 -0
- package/dist/internal/stringCalls.js +82 -0
- package/dist/internal/stringCalls.js.map +1 -0
- package/dist/internal/template.d.ts +4 -0
- package/dist/internal/template.js +2198 -0
- package/dist/internal/template.js.map +1 -0
- package/dist/internal/templatePath.d.ts +12 -0
- package/dist/internal/templatePath.js +23 -0
- package/dist/internal/templatePath.js.map +1 -0
- package/dist/internal/types.d.ts +69 -0
- package/dist/internal/types.js +1 -0
- package/dist/internal/utils.d.ts +19 -0
- package/dist/internal/utils.js +149 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/internal/vueCompiler.d.ts +35 -0
- package/dist/internal/vueCompiler.js +320 -0
- package/dist/internal/vueCompiler.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.js +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { VueExtractionResult } from '../types.js';
|
|
2
|
+
import type { ImplicitSlotWhitespace } from './vueCompiler.js';
|
|
3
|
+
export type GTComponentName = 'T' | 'Var' | 'Num' | 'DateTime' | 'Currency' | 'Plural' | 'Branch';
|
|
4
|
+
/** Vue builtins whose runtime identity changes rich-content traversal. */
|
|
5
|
+
export type VueBuiltinName = 'Fragment' | 'Suspense';
|
|
6
|
+
export type StringFunctionKind = 'gt' | 'messages' | 'msg';
|
|
7
|
+
export type TemplateContainerKind = 'array' | 'object';
|
|
8
|
+
export type TemplateBindings = {
|
|
9
|
+
/** Lengths of statically known, non-mutated array paths. */
|
|
10
|
+
arrayLengths: Map<string, number>;
|
|
11
|
+
/** Callable bindings that may return a GT or Vue component identity. */
|
|
12
|
+
componentFactories: Set<string>;
|
|
13
|
+
components: Map<string, GTComponentName>;
|
|
14
|
+
/** Statically known container paths available to template expressions. */
|
|
15
|
+
containerKinds: Map<string, TemplateContainerKind>;
|
|
16
|
+
/** Container paths whose immediate runtime value may be a GT component. */
|
|
17
|
+
possibleGTContainers: Set<string>;
|
|
18
|
+
/** Callable paths whose returned container may directly contain `<T>`. */
|
|
19
|
+
gtContainerFactories: Set<string>;
|
|
20
|
+
/** Program bindings whose identity takes precedence over registrations. */
|
|
21
|
+
directBindings: Set<string>;
|
|
22
|
+
/** GT identities registered through an Options API `components` object. */
|
|
23
|
+
registeredComponents: Map<string, GTComponentName>;
|
|
24
|
+
/** Vue builtin identities registered through an Options API object. */
|
|
25
|
+
registeredVueBuiltins: Map<string, VueBuiltinName>;
|
|
26
|
+
staticValues: Map<string, string | number | bigint | boolean | null>;
|
|
27
|
+
/** Vue identity helpers such as `markRaw`. */
|
|
28
|
+
identityFunctions: Set<string>;
|
|
29
|
+
/** Statically visible string alternatives for non-singleton expressions. */
|
|
30
|
+
possibleStaticStrings: Map<string, Set<string>>;
|
|
31
|
+
stringFunctions: Map<string, StringFunctionKind>;
|
|
32
|
+
/** Callable aliases that may resolve to a gt-vue string function. */
|
|
33
|
+
uncertainStringFunctions: Set<string>;
|
|
34
|
+
/** Component aliases whose runtime identity escaped static analysis. */
|
|
35
|
+
uncertainComponents: Set<string>;
|
|
36
|
+
/** Direct aliases that can specifically resolve to a GT component. */
|
|
37
|
+
uncertainGTComponents: Set<string>;
|
|
38
|
+
/** Callable bindings that may specifically return a GT component. */
|
|
39
|
+
gtComponentFactories: Set<string>;
|
|
40
|
+
/** Uncertain identities originating in an Options API registration. */
|
|
41
|
+
uncertainRegisteredComponents: Set<string>;
|
|
42
|
+
/** Uncertain GT identities originating in an Options API registration. */
|
|
43
|
+
uncertainRegisteredGTComponents: Set<string>;
|
|
44
|
+
/** Statically proven local aliases of Vue builtins. */
|
|
45
|
+
vueBuiltins: Map<string, VueBuiltinName>;
|
|
46
|
+
};
|
|
47
|
+
export type ExtractionPosition = {
|
|
48
|
+
line: number;
|
|
49
|
+
column: number;
|
|
50
|
+
offset: number;
|
|
51
|
+
};
|
|
52
|
+
export type ExtractionLocation = {
|
|
53
|
+
start: ExtractionPosition;
|
|
54
|
+
end: ExtractionPosition;
|
|
55
|
+
};
|
|
56
|
+
export type VueExtractionContext = {
|
|
57
|
+
errors: string[];
|
|
58
|
+
file: string;
|
|
59
|
+
/** Whitespace predicate used by the resolved consumer Vue compiler. */
|
|
60
|
+
implicitSlotWhitespace: ImplicitSlotWhitespace;
|
|
61
|
+
includeSourceCodeContext: boolean;
|
|
62
|
+
relativeFile: string;
|
|
63
|
+
results: VueExtractionResult[];
|
|
64
|
+
source: string;
|
|
65
|
+
surroundingLineCount: number;
|
|
66
|
+
/** Variable component nodes whose public shape was already validated. */
|
|
67
|
+
validatedVariableComponents: WeakSet<object>;
|
|
68
|
+
warnings: Set<string>;
|
|
69
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as t from '@babel/types';
|
|
2
|
+
import type { VueExtractionResult } from '../types.js';
|
|
3
|
+
import type { ExtractionLocation, VueExtractionContext } from './types.js';
|
|
4
|
+
export declare function createVueExtractionContext(file: string, source: string, projectRoot: string, includeSourceCodeContext: boolean, surroundingLineCount: number, results: VueExtractionResult[], errors: string[], warnings: Set<string>): VueExtractionContext;
|
|
5
|
+
export declare function addVueError(context: VueExtractionContext, location: ExtractionLocation | undefined, whatHappened: string, fix?: string): void;
|
|
6
|
+
export declare function babelLocation(location: t.SourceLocation | null | undefined): ExtractionLocation | undefined;
|
|
7
|
+
export declare function createInlineMetadata(context: VueExtractionContext, location: ExtractionLocation | undefined, translationContext?: string): VueExtractionResult['metadata'];
|
|
8
|
+
export type StaticPrimitive = string | number | bigint | boolean | null;
|
|
9
|
+
export type StaticPrimitiveResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
value: StaticPrimitive;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
};
|
|
15
|
+
export type StaticIdentifierResolver = (identifier: t.Identifier) => StaticPrimitiveResult;
|
|
16
|
+
/** Removes syntax-only TypeScript, Flow, and parenthesis wrappers. */
|
|
17
|
+
export declare function unwrapExpression(input: t.Node | null | undefined): t.Node | undefined;
|
|
18
|
+
/** Evaluates the side-effect-free primitive subset supported by extraction. */
|
|
19
|
+
export declare function readStaticPrimitive(input: t.Node | null | undefined, resolveIdentifier?: StaticIdentifierResolver): StaticPrimitiveResult;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { createDiagnosticMessage } from "generaltranslation/internal";
|
|
3
|
+
//#region src/internal/utils.ts
|
|
4
|
+
function createVueExtractionContext(file, source, projectRoot, includeSourceCodeContext, surroundingLineCount, results, errors, warnings) {
|
|
5
|
+
return {
|
|
6
|
+
errors,
|
|
7
|
+
file,
|
|
8
|
+
implicitSlotWhitespace: "ecmascript",
|
|
9
|
+
includeSourceCodeContext,
|
|
10
|
+
relativeFile: path.relative(projectRoot, file),
|
|
11
|
+
results,
|
|
12
|
+
source,
|
|
13
|
+
surroundingLineCount,
|
|
14
|
+
validatedVariableComponents: /* @__PURE__ */ new WeakSet(),
|
|
15
|
+
warnings
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function addVueError(context, location, whatHappened, fix) {
|
|
19
|
+
const position = location ? ` (${location.start.line}:${location.start.column})` : "";
|
|
20
|
+
context.errors.push(`${context.file}${position}: ${createDiagnosticMessage({
|
|
21
|
+
whatHappened,
|
|
22
|
+
fix
|
|
23
|
+
})}`);
|
|
24
|
+
}
|
|
25
|
+
function babelLocation(location) {
|
|
26
|
+
if (!location) return void 0;
|
|
27
|
+
return {
|
|
28
|
+
start: {
|
|
29
|
+
line: location.start.line,
|
|
30
|
+
column: location.start.column + 1,
|
|
31
|
+
offset: location.start.index ?? 0
|
|
32
|
+
},
|
|
33
|
+
end: {
|
|
34
|
+
line: location.end.line,
|
|
35
|
+
column: location.end.column + 1,
|
|
36
|
+
offset: location.end.index ?? 0
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createInlineMetadata(context, location, translationContext) {
|
|
41
|
+
const metadata = {
|
|
42
|
+
...translationContext !== void 0 && { context: translationContext },
|
|
43
|
+
...context.relativeFile && { filePaths: [context.relativeFile] }
|
|
44
|
+
};
|
|
45
|
+
if (context.includeSourceCodeContext && context.relativeFile && location?.start.line && location.end.line) {
|
|
46
|
+
const sourceCode = extractSourceCode(context.source, location.start.line, location.end.line, context.surroundingLineCount);
|
|
47
|
+
if (sourceCode) metadata.sourceCode = { [context.relativeFile]: [sourceCode] };
|
|
48
|
+
}
|
|
49
|
+
return metadata;
|
|
50
|
+
}
|
|
51
|
+
/** Extracts source lines around a 1-based line range. */
|
|
52
|
+
function extractSourceCode(source, startLine, endLine, surroundingLineCount) {
|
|
53
|
+
const lines = source.split("\n");
|
|
54
|
+
if (lines.length === 0) return void 0;
|
|
55
|
+
const targetStart = Math.max(0, startLine - 1);
|
|
56
|
+
const targetEnd = Math.min(lines.length - 1, endLine - 1);
|
|
57
|
+
if (targetStart > targetEnd) return void 0;
|
|
58
|
+
return {
|
|
59
|
+
before: lines.slice(Math.max(0, targetStart - surroundingLineCount), targetStart).join("\n"),
|
|
60
|
+
target: lines.slice(targetStart, targetEnd + 1).join("\n"),
|
|
61
|
+
after: lines.slice(targetEnd + 1, targetEnd + 1 + surroundingLineCount).join("\n")
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Removes syntax-only TypeScript, Flow, and parenthesis wrappers. */
|
|
65
|
+
function unwrapExpression(input) {
|
|
66
|
+
let current = input ?? void 0;
|
|
67
|
+
while (current && (current.type === "TSAsExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSInstantiationExpression" || current.type === "TypeCastExpression" || current.type === "ParenthesizedExpression")) current = current.expression;
|
|
68
|
+
return current;
|
|
69
|
+
}
|
|
70
|
+
/** Evaluates the side-effect-free primitive subset supported by extraction. */
|
|
71
|
+
function readStaticPrimitive(input, resolveIdentifier) {
|
|
72
|
+
const expression = unwrapExpression(input);
|
|
73
|
+
if (!expression) return { ok: false };
|
|
74
|
+
if (expression.type === "Identifier" && resolveIdentifier) return resolveIdentifier(expression);
|
|
75
|
+
if (expression.type === "StringLiteral") return {
|
|
76
|
+
ok: true,
|
|
77
|
+
value: expression.value
|
|
78
|
+
};
|
|
79
|
+
if (expression.type === "NumericLiteral") return {
|
|
80
|
+
ok: true,
|
|
81
|
+
value: expression.value
|
|
82
|
+
};
|
|
83
|
+
if (expression.type === "BigIntLiteral") try {
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
value: BigInt(expression.value)
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
return { ok: false };
|
|
90
|
+
}
|
|
91
|
+
if (expression.type === "BooleanLiteral") return {
|
|
92
|
+
ok: true,
|
|
93
|
+
value: expression.value
|
|
94
|
+
};
|
|
95
|
+
if (expression.type === "NullLiteral") return {
|
|
96
|
+
ok: true,
|
|
97
|
+
value: null
|
|
98
|
+
};
|
|
99
|
+
if (expression.type === "TemplateLiteral") {
|
|
100
|
+
const firstQuasi = expression.quasis[0]?.value.cooked;
|
|
101
|
+
if (firstQuasi == null) return { ok: false };
|
|
102
|
+
let value = firstQuasi;
|
|
103
|
+
for (let index = 0; index < expression.expressions.length; index += 1) {
|
|
104
|
+
const interpolation = readStaticPrimitive(expression.expressions[index], resolveIdentifier);
|
|
105
|
+
if (!interpolation.ok) return { ok: false };
|
|
106
|
+
value += String(interpolation.value);
|
|
107
|
+
const quasi = expression.quasis[index + 1]?.value.cooked;
|
|
108
|
+
if (quasi == null) return { ok: false };
|
|
109
|
+
value += quasi;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
value
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (expression.type === "BinaryExpression" && expression.operator === "+") {
|
|
117
|
+
const left = readStaticPrimitive(expression.left, resolveIdentifier);
|
|
118
|
+
const right = readStaticPrimitive(expression.right, resolveIdentifier);
|
|
119
|
+
if (!left.ok || !right.ok) return { ok: false };
|
|
120
|
+
if (typeof left.value === "string" || typeof right.value === "string") return {
|
|
121
|
+
ok: true,
|
|
122
|
+
value: String(left.value) + String(right.value)
|
|
123
|
+
};
|
|
124
|
+
if (typeof left.value === "bigint" || typeof right.value === "bigint") return typeof left.value === "bigint" && typeof right.value === "bigint" ? {
|
|
125
|
+
ok: true,
|
|
126
|
+
value: left.value + right.value
|
|
127
|
+
} : { ok: false };
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
value: Number(left.value) + Number(right.value)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (expression.type === "UnaryExpression" && (expression.operator === "+" || expression.operator === "-")) {
|
|
134
|
+
const argument = readStaticPrimitive(expression.argument, resolveIdentifier);
|
|
135
|
+
if (argument.ok && typeof argument.value === "number") return {
|
|
136
|
+
ok: true,
|
|
137
|
+
value: expression.operator === "-" ? -argument.value : argument.value
|
|
138
|
+
};
|
|
139
|
+
if (argument.ok && typeof argument.value === "bigint") return expression.operator === "-" ? {
|
|
140
|
+
ok: true,
|
|
141
|
+
value: -argument.value
|
|
142
|
+
} : { ok: false };
|
|
143
|
+
}
|
|
144
|
+
return { ok: false };
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { addVueError, babelLocation, createInlineMetadata, createVueExtractionContext, readStaticPrimitive, unwrapExpression };
|
|
148
|
+
|
|
149
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../src/internal/utils.ts"],"sourcesContent":["import path from 'node:path';\nimport type * as t from '@babel/types';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\nimport type { VueExtractionResult, VueSourceCode } from '../types.js';\nimport type { ExtractionLocation, VueExtractionContext } from './types.js';\n\nexport function createVueExtractionContext(\n file: string,\n source: string,\n projectRoot: string,\n includeSourceCodeContext: boolean,\n surroundingLineCount: number,\n results: VueExtractionResult[],\n errors: string[],\n warnings: Set<string>\n): VueExtractionContext {\n return {\n errors,\n file,\n // Script-only extraction never reads this field. Vue SFC extraction\n // replaces it with the resolved consumer compiler's behavior before\n // traversing the template.\n implicitSlotWhitespace: 'ecmascript',\n includeSourceCodeContext,\n relativeFile: path.relative(projectRoot, file),\n results,\n source,\n surroundingLineCount,\n validatedVariableComponents: new WeakSet(),\n warnings,\n };\n}\n\nexport function addVueError(\n context: VueExtractionContext,\n location: ExtractionLocation | undefined,\n whatHappened: string,\n fix?: string\n): void {\n const position = location\n ? ` (${location.start.line}:${location.start.column})`\n : '';\n context.errors.push(\n `${context.file}${position}: ${createDiagnosticMessage({ whatHappened, fix })}`\n );\n}\n\nexport function babelLocation(\n location: t.SourceLocation | null | undefined\n): ExtractionLocation | undefined {\n if (!location) return undefined;\n return {\n start: {\n line: location.start.line,\n column: location.start.column + 1,\n offset: location.start.index ?? 0,\n },\n end: {\n line: location.end.line,\n column: location.end.column + 1,\n offset: location.end.index ?? 0,\n },\n };\n}\n\nexport function createInlineMetadata(\n context: VueExtractionContext,\n location: ExtractionLocation | undefined,\n translationContext?: string\n): VueExtractionResult['metadata'] {\n const metadata: VueExtractionResult['metadata'] = {\n ...(translationContext !== undefined && { context: translationContext }),\n ...(context.relativeFile && { filePaths: [context.relativeFile] }),\n };\n\n if (\n context.includeSourceCodeContext &&\n context.relativeFile &&\n location?.start.line &&\n location.end.line\n ) {\n const sourceCode = extractSourceCode(\n context.source,\n location.start.line,\n location.end.line,\n context.surroundingLineCount\n );\n if (sourceCode) {\n metadata.sourceCode = { [context.relativeFile]: [sourceCode] };\n }\n }\n\n return metadata;\n}\n\n/** Extracts source lines around a 1-based line range. */\nfunction extractSourceCode(\n source: string,\n startLine: number,\n endLine: number,\n surroundingLineCount: number\n): VueSourceCode | undefined {\n const lines = source.split('\\n');\n if (lines.length === 0) return undefined;\n\n const targetStart = Math.max(0, startLine - 1);\n const targetEnd = Math.min(lines.length - 1, endLine - 1);\n if (targetStart > targetEnd) return undefined;\n\n return {\n before: lines\n .slice(Math.max(0, targetStart - surroundingLineCount), targetStart)\n .join('\\n'),\n target: lines.slice(targetStart, targetEnd + 1).join('\\n'),\n after: lines\n .slice(targetEnd + 1, targetEnd + 1 + surroundingLineCount)\n .join('\\n'),\n };\n}\n\nexport type StaticPrimitive = string | number | bigint | boolean | null;\nexport type StaticPrimitiveResult =\n | { ok: true; value: StaticPrimitive }\n | { ok: false };\nexport type StaticIdentifierResolver = (\n identifier: t.Identifier\n) => StaticPrimitiveResult;\n\n/** Removes syntax-only TypeScript, Flow, and parenthesis wrappers. */\nexport function unwrapExpression(\n input: t.Node | null | undefined\n): t.Node | undefined {\n let current = input ?? undefined;\n while (\n current &&\n (current.type === 'TSAsExpression' ||\n current.type === 'TSTypeAssertion' ||\n current.type === 'TSNonNullExpression' ||\n current.type === 'TSSatisfiesExpression' ||\n current.type === 'TSInstantiationExpression' ||\n current.type === 'TypeCastExpression' ||\n current.type === 'ParenthesizedExpression')\n ) {\n current = current.expression;\n }\n return current;\n}\n\n/** Evaluates the side-effect-free primitive subset supported by extraction. */\nexport function readStaticPrimitive(\n input: t.Node | null | undefined,\n resolveIdentifier?: StaticIdentifierResolver\n): StaticPrimitiveResult {\n const expression = unwrapExpression(input);\n if (!expression) return { ok: false };\n if (expression.type === 'Identifier' && resolveIdentifier) {\n return resolveIdentifier(expression);\n }\n if (expression.type === 'StringLiteral') {\n return { ok: true, value: expression.value };\n }\n if (expression.type === 'NumericLiteral') {\n return { ok: true, value: expression.value };\n }\n if (expression.type === 'BigIntLiteral') {\n try {\n return { ok: true, value: BigInt(expression.value) };\n } catch {\n return { ok: false };\n }\n }\n if (expression.type === 'BooleanLiteral') {\n return { ok: true, value: expression.value };\n }\n if (expression.type === 'NullLiteral') {\n return { ok: true, value: null };\n }\n if (expression.type === 'TemplateLiteral') {\n const firstQuasi = expression.quasis[0]?.value.cooked;\n // Executable, untagged template literals always have cooked values.\n // Missing cooked data belongs to malformed or tagged-template ASTs and\n // must fail closed instead of being interpreted with different raw-text\n // semantics.\n if (firstQuasi == null) return { ok: false };\n let value = firstQuasi;\n for (let index = 0; index < expression.expressions.length; index += 1) {\n const interpolation = readStaticPrimitive(\n expression.expressions[index],\n resolveIdentifier\n );\n if (!interpolation.ok) return { ok: false };\n value += String(interpolation.value);\n const quasi = expression.quasis[index + 1]?.value.cooked;\n if (quasi == null) return { ok: false };\n value += quasi;\n }\n return { ok: true, value };\n }\n if (expression.type === 'BinaryExpression' && expression.operator === '+') {\n const left = readStaticPrimitive(expression.left, resolveIdentifier);\n const right = readStaticPrimitive(expression.right, resolveIdentifier);\n if (!left.ok || !right.ok) return { ok: false };\n if (typeof left.value === 'string' || typeof right.value === 'string') {\n return { ok: true, value: String(left.value) + String(right.value) };\n }\n if (typeof left.value === 'bigint' || typeof right.value === 'bigint') {\n return typeof left.value === 'bigint' && typeof right.value === 'bigint'\n ? { ok: true, value: left.value + right.value }\n : { ok: false };\n }\n return { ok: true, value: Number(left.value) + Number(right.value) };\n }\n if (\n expression.type === 'UnaryExpression' &&\n (expression.operator === '+' || expression.operator === '-')\n ) {\n const argument = readStaticPrimitive(\n expression.argument,\n resolveIdentifier\n );\n if (argument.ok && typeof argument.value === 'number') {\n return {\n ok: true,\n value: expression.operator === '-' ? -argument.value : argument.value,\n };\n }\n if (argument.ok && typeof argument.value === 'bigint') {\n return expression.operator === '-'\n ? { ok: true, value: -argument.value }\n : { ok: false };\n }\n }\n return { ok: false };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { parse as parseVueTemplate } from '@vue/compiler-dom';
|
|
2
|
+
import type * as VueCompilerModule from '@vue/compiler-sfc';
|
|
3
|
+
import type { TemplateCompiler } from '@vue/compiler-sfc';
|
|
4
|
+
import type { VueCompiler } from '../types.js';
|
|
5
|
+
/** Whitespace test used by Vue when it constructs an implicit default slot. */
|
|
6
|
+
export type ImplicitSlotWhitespace = 'ecmascript' | 'html';
|
|
7
|
+
export type ResolvedVueCompiler = {
|
|
8
|
+
compiler: ExactVueCompiler;
|
|
9
|
+
implicitSlotWhitespace: ImplicitSlotWhitespace;
|
|
10
|
+
parseTemplate?: typeof parseVueTemplate;
|
|
11
|
+
templateCompiler?: TemplateCompiler;
|
|
12
|
+
templateParseOptionsSupported: boolean;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
type CompilerResolution = {
|
|
16
|
+
ok: true;
|
|
17
|
+
value: ResolvedVueCompiler;
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
details: string;
|
|
21
|
+
};
|
|
22
|
+
type ExactVueCompiler = Pick<typeof VueCompilerModule, 'compileTemplate' | 'parse' | 'version'> & {
|
|
23
|
+
parseTemplate?: typeof parseVueTemplate;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the exact Vue compiler selected by the consuming source file.
|
|
27
|
+
*
|
|
28
|
+
* The `vue/compiler-sfc` export is version-locked to that installation's Vue
|
|
29
|
+
* runtime. Resolving from the SFC first is important in workspaces where apps
|
|
30
|
+
* intentionally use different Vue versions.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveVueCompiler(file: string, projectRoot: string): CompilerResolution;
|
|
33
|
+
/** Validates an explicitly supplied compiler before extraction uses it. */
|
|
34
|
+
export declare function inspectVueCompiler(compiler: VueCompiler): CompilerResolution;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/internal/vueCompiler.ts
|
|
5
|
+
const resolvedCompilers = /* @__PURE__ */ new Map();
|
|
6
|
+
const inspectedCompilers = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the exact Vue compiler selected by the consuming source file.
|
|
9
|
+
*
|
|
10
|
+
* The `vue/compiler-sfc` export is version-locked to that installation's Vue
|
|
11
|
+
* runtime. Resolving from the SFC first is important in workspaces where apps
|
|
12
|
+
* intentionally use different Vue versions.
|
|
13
|
+
*/
|
|
14
|
+
function resolveVueCompiler(file, projectRoot) {
|
|
15
|
+
const absoluteFile = path.isAbsolute(file) ? file : path.resolve(projectRoot, file);
|
|
16
|
+
const anchors = [absoluteFile, path.join(projectRoot, "package.json")];
|
|
17
|
+
for (const anchor of anchors) {
|
|
18
|
+
const requireFromConsumer = createRequire(anchor);
|
|
19
|
+
let vueManifestPath;
|
|
20
|
+
try {
|
|
21
|
+
vueManifestPath = requireFromConsumer.resolve("vue/package.json");
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (isMissingModule(error)) continue;
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
details: formatResolutionError(error)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
let compilerPath;
|
|
30
|
+
try {
|
|
31
|
+
compilerPath = requireFromConsumer.resolve("vue/compiler-sfc");
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
details: `Resolved the app's Vue package at ${vueManifestPath}, but its compiler-sfc export could not be loaded: ${formatResolutionError(error)}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const cached = resolvedCompilers.get(compilerPath);
|
|
39
|
+
if (cached) return cached;
|
|
40
|
+
const resolution = loadCompiler(requireFromConsumer, compilerPath, vueManifestPath, readVueVersion(requireFromConsumer, vueManifestPath));
|
|
41
|
+
resolvedCompilers.set(compilerPath, resolution);
|
|
42
|
+
return resolution;
|
|
43
|
+
}
|
|
44
|
+
const declaredVersion = findDeclaredVueVersion(absoluteFile, projectRoot);
|
|
45
|
+
if (declaredVersion) return {
|
|
46
|
+
ok: false,
|
|
47
|
+
details: `The project declares Vue ${JSON.stringify(declaredVersion)} but vue/compiler-sfc could not be resolved from ${absoluteFile}.`
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
details: `Could not resolve vue/compiler-sfc from ${absoluteFile}.`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Validates an explicitly supplied compiler before extraction uses it. */
|
|
55
|
+
function inspectVueCompiler(compiler) {
|
|
56
|
+
if (!compiler || typeof compiler !== "object") return {
|
|
57
|
+
ok: false,
|
|
58
|
+
details: "The supplied Vue compiler is not an object."
|
|
59
|
+
};
|
|
60
|
+
const cached = inspectedCompilers.get(compiler);
|
|
61
|
+
if (cached) return cached;
|
|
62
|
+
const exactCompiler = compiler;
|
|
63
|
+
const resolution = isVueCompiler(compiler) ? inspectCompiler(exactCompiler, exactCompiler.parseTemplate) : {
|
|
64
|
+
ok: false,
|
|
65
|
+
details: "The supplied object does not expose the Vue compiler-sfc API."
|
|
66
|
+
};
|
|
67
|
+
inspectedCompilers.set(compiler, resolution);
|
|
68
|
+
return resolution;
|
|
69
|
+
}
|
|
70
|
+
function loadCompiler(requireFromConsumer, compilerPath, vueManifestPath, vueVersion) {
|
|
71
|
+
try {
|
|
72
|
+
const loaded = requireFromConsumer(compilerPath);
|
|
73
|
+
const defaultExport = readDefaultExport(loaded);
|
|
74
|
+
const compiler = isVueCompiler(loaded) ? loaded : isVueCompiler(defaultExport) ? defaultExport : void 0;
|
|
75
|
+
if (!compiler) return {
|
|
76
|
+
ok: false,
|
|
77
|
+
details: `The module at ${compilerPath} does not expose the Vue compiler-sfc API.`
|
|
78
|
+
};
|
|
79
|
+
if (vueVersion && compiler.version !== vueVersion) return {
|
|
80
|
+
ok: false,
|
|
81
|
+
details: `The app resolves Vue ${vueVersion} but vue/compiler-sfc ${compiler.version}. Install one matching Vue package version.`
|
|
82
|
+
};
|
|
83
|
+
if (!isSupportedVueVersion(compiler.version)) return {
|
|
84
|
+
ok: false,
|
|
85
|
+
details: `Resolved Vue compiler version ${JSON.stringify(compiler.version)}; gt-vue supports Vue 3.3 through Vue 3.x.`
|
|
86
|
+
};
|
|
87
|
+
const requireFromCompiler = createRequire(compilerPath);
|
|
88
|
+
const compilerDomPath = requireFromCompiler.resolve("@vue/compiler-dom");
|
|
89
|
+
const compilerDom = requireFromCompiler(compilerDomPath);
|
|
90
|
+
const compilerDomVersion = readVueVersion(requireFromCompiler, requireFromCompiler.resolve("@vue/compiler-dom/package.json"));
|
|
91
|
+
if (compilerDomVersion !== compiler.version) return {
|
|
92
|
+
ok: false,
|
|
93
|
+
details: `vue/compiler-sfc ${compiler.version} resolves @vue/compiler-dom ${String(compilerDomVersion)}. Install matching Vue compiler packages.`
|
|
94
|
+
};
|
|
95
|
+
if (typeof compilerDom.parse !== "function") return {
|
|
96
|
+
ok: false,
|
|
97
|
+
details: `The template compiler at ${compilerDomPath} does not expose parse().`
|
|
98
|
+
};
|
|
99
|
+
if (typeof compilerDom.compile !== "function") return {
|
|
100
|
+
ok: false,
|
|
101
|
+
details: `The template compiler at ${compilerDomPath} does not expose compile().`
|
|
102
|
+
};
|
|
103
|
+
return inspectCompiler(compiler, compilerDom.parse, {
|
|
104
|
+
compile: compilerDom.compile,
|
|
105
|
+
parse: compilerDom.parse
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const bunResolution = loadBunCompiler(requireFromConsumer, vueManifestPath, vueVersion);
|
|
109
|
+
if (bunResolution) return bunResolution;
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
details: formatResolutionError(error)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Loads exact, self-contained Vue browser compilers in a compiled Bun CLI. */
|
|
117
|
+
function loadBunCompiler(requireFromConsumer, vueManifestPath, vueVersion) {
|
|
118
|
+
if (typeof process.versions.bun !== "string") return void 0;
|
|
119
|
+
try {
|
|
120
|
+
const compilerSfcRoot = findInstalledPackageRoot(vueManifestPath, "@vue/compiler-sfc");
|
|
121
|
+
const compilerDomRoot = findInstalledPackageRoot(vueManifestPath, "@vue/compiler-dom");
|
|
122
|
+
if (!compilerSfcRoot || !compilerDomRoot) return {
|
|
123
|
+
ok: false,
|
|
124
|
+
details: "The Vue installation does not contain its compiler-sfc and compiler-dom dependencies."
|
|
125
|
+
};
|
|
126
|
+
const compilerPath = path.join(compilerSfcRoot, "dist/compiler-sfc.esm-browser.js");
|
|
127
|
+
const compilerDomPath = path.join(compilerDomRoot, "dist/compiler-dom.esm-browser.js");
|
|
128
|
+
const loaded = requireFromConsumer(compilerPath);
|
|
129
|
+
const defaultExport = readDefaultExport(loaded);
|
|
130
|
+
const compiler = isVueCompiler(loaded) ? loaded : isVueCompiler(defaultExport) ? defaultExport : void 0;
|
|
131
|
+
if (!compiler) return {
|
|
132
|
+
ok: false,
|
|
133
|
+
details: `The module at ${compilerPath} does not expose the Vue compiler-sfc API.`
|
|
134
|
+
};
|
|
135
|
+
if (vueVersion && compiler.version !== vueVersion) return {
|
|
136
|
+
ok: false,
|
|
137
|
+
details: `The app resolves Vue ${vueVersion} but vue/compiler-sfc ${compiler.version}. Install one matching Vue package version.`
|
|
138
|
+
};
|
|
139
|
+
if (!isSupportedVueVersion(compiler.version)) return {
|
|
140
|
+
ok: false,
|
|
141
|
+
details: `Resolved Vue compiler version ${JSON.stringify(compiler.version)}; gt-vue supports Vue 3.3 through Vue 3.x.`
|
|
142
|
+
};
|
|
143
|
+
const compilerDom = requireFromConsumer(compilerDomPath);
|
|
144
|
+
const compilerDomVersion = readPackageVersion(compilerDomRoot);
|
|
145
|
+
if (compilerDomVersion !== compiler.version) return {
|
|
146
|
+
ok: false,
|
|
147
|
+
details: `vue/compiler-sfc ${compiler.version} resolves @vue/compiler-dom ${String(compilerDomVersion)}. Install matching Vue compiler packages.`
|
|
148
|
+
};
|
|
149
|
+
if (typeof compilerDom.parse !== "function") return {
|
|
150
|
+
ok: false,
|
|
151
|
+
details: `The template compiler at ${compilerDomPath} does not expose parse().`
|
|
152
|
+
};
|
|
153
|
+
if (typeof compilerDom.compile !== "function") return {
|
|
154
|
+
ok: false,
|
|
155
|
+
details: `The template compiler at ${compilerDomPath} does not expose compile().`
|
|
156
|
+
};
|
|
157
|
+
return inspectCompiler(compiler, compilerDom.parse, {
|
|
158
|
+
compile: compilerDom.compile,
|
|
159
|
+
parse: compilerDom.parse
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
details: formatResolutionError(error)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Finds one dependency relative to the physical Vue installation on disk. */
|
|
169
|
+
function findInstalledPackageRoot(vueManifestPath, packageName) {
|
|
170
|
+
const packageSegments = packageName.split("/");
|
|
171
|
+
let directory = path.dirname(vueManifestPath);
|
|
172
|
+
while (true) {
|
|
173
|
+
const candidate = path.basename(directory) === "node_modules" ? path.join(directory, ...packageSegments) : path.join(directory, "node_modules", ...packageSegments);
|
|
174
|
+
if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
|
|
175
|
+
const parent = path.dirname(directory);
|
|
176
|
+
if (parent === directory) return void 0;
|
|
177
|
+
directory = parent;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function readPackageVersion(packageRoot) {
|
|
181
|
+
try {
|
|
182
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
183
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
184
|
+
} catch {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function readVueVersion(requireFromConsumer, manifestPath) {
|
|
189
|
+
try {
|
|
190
|
+
const manifest = requireFromConsumer(manifestPath);
|
|
191
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
192
|
+
} catch {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** Finds an installed-but-missing declaration that bundled fallback could hide. */
|
|
197
|
+
function findDeclaredVueVersion(absoluteFile, projectRoot) {
|
|
198
|
+
const stop = path.resolve(projectRoot);
|
|
199
|
+
let directory = path.dirname(absoluteFile);
|
|
200
|
+
while (true) {
|
|
201
|
+
const version = readDeclaredVueVersion(path.join(directory, "package.json"));
|
|
202
|
+
if (version) return version;
|
|
203
|
+
if (directory === stop || directory === path.dirname(directory)) break;
|
|
204
|
+
directory = path.dirname(directory);
|
|
205
|
+
}
|
|
206
|
+
return readDeclaredVueVersion(path.join(stop, "package.json"));
|
|
207
|
+
}
|
|
208
|
+
function readDeclaredVueVersion(manifestPath) {
|
|
209
|
+
try {
|
|
210
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
211
|
+
for (const section of [
|
|
212
|
+
"dependencies",
|
|
213
|
+
"devDependencies",
|
|
214
|
+
"peerDependencies",
|
|
215
|
+
"optionalDependencies"
|
|
216
|
+
]) {
|
|
217
|
+
const dependencies = manifest[section];
|
|
218
|
+
if (dependencies && typeof dependencies === "object" && "vue" in dependencies) {
|
|
219
|
+
const version = dependencies.vue;
|
|
220
|
+
return typeof version === "string" ? version : "an unknown version";
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function inspectCompiler(compiler, parseTemplate, templateCompiler) {
|
|
228
|
+
const version = compiler.version;
|
|
229
|
+
if (!isSupportedVueVersion(version)) return {
|
|
230
|
+
ok: false,
|
|
231
|
+
details: `Resolved Vue compiler version ${JSON.stringify(version)}; gt-vue supports Vue 3.3 through Vue 3.x.`
|
|
232
|
+
};
|
|
233
|
+
try {
|
|
234
|
+
const templateParseOptionsSupported = supportsTemplateParseOptions(compiler);
|
|
235
|
+
if (!parseTemplate && !templateParseOptionsSupported) return {
|
|
236
|
+
ok: false,
|
|
237
|
+
details: "The supplied compiler does not expose exact template parser options. Let the extractor resolve vue/compiler-sfc from the consuming app instead."
|
|
238
|
+
};
|
|
239
|
+
return {
|
|
240
|
+
ok: true,
|
|
241
|
+
value: {
|
|
242
|
+
compiler,
|
|
243
|
+
implicitSlotWhitespace: detectImplicitSlotWhitespace(compiler),
|
|
244
|
+
parseTemplate,
|
|
245
|
+
templateCompiler,
|
|
246
|
+
templateParseOptionsSupported,
|
|
247
|
+
version
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
details: formatResolutionError(error)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Detects the SFC parser option added after Vue 3.4. */
|
|
258
|
+
function supportsTemplateParseOptions(compiler) {
|
|
259
|
+
const source = "<template><Probe>First\n second</Probe></template>";
|
|
260
|
+
const parseText = (whitespace) => {
|
|
261
|
+
return collectText(compiler.parse(source, { templateParseOptions: { whitespace } }).descriptor.template?.ast);
|
|
262
|
+
};
|
|
263
|
+
return parseText("condense") !== parseText("preserve");
|
|
264
|
+
}
|
|
265
|
+
function collectText(value) {
|
|
266
|
+
if (!value || typeof value !== "object") return "";
|
|
267
|
+
let output = "";
|
|
268
|
+
if ("type" in value && value.type === 2 && "content" in value) output += String(value.content);
|
|
269
|
+
if ("children" in value && Array.isArray(value.children)) for (const child of value.children) output += collectText(child);
|
|
270
|
+
return output;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Detects the compiler's slot-whitespace semantics instead of inferring them
|
|
274
|
+
* from a semver range. Vue 3.3/3.4 use ECMAScript `trim()`, while Vue 3.5 uses
|
|
275
|
+
* the HTML parser's ASCII whitespace set. A behavior probe remains exact if a
|
|
276
|
+
* later Vue 3.x release backports or revises that implementation.
|
|
277
|
+
*/
|
|
278
|
+
function detectImplicitSlotWhitespace(compiler) {
|
|
279
|
+
const result = compiler.compileTemplate({
|
|
280
|
+
filename: "gt-vue-compiler-probe.vue",
|
|
281
|
+
id: "gt-vue-compiler-probe",
|
|
282
|
+
source: "<Probe><template #named>x</template> </Probe>"
|
|
283
|
+
});
|
|
284
|
+
if (result.errors.length > 0) throw new Error(`The Vue compiler compatibility probe failed: ${result.errors.map(String).join("; ")}`);
|
|
285
|
+
const component = result.ast?.children.find((node) => typeof node === "object" && node !== null && "tag" in node && node.tag === "Probe");
|
|
286
|
+
const codegen = component && "codegenNode" in component ? component.codegenNode : void 0;
|
|
287
|
+
const children = codegen && typeof codegen === "object" && "children" in codegen && codegen.children && typeof codegen.children === "object" ? codegen.children : void 0;
|
|
288
|
+
const properties = children && "properties" in children && Array.isArray(children.properties) ? children.properties : void 0;
|
|
289
|
+
if (!properties) throw new Error("The Vue compiler compatibility probe returned an unknown slot AST.");
|
|
290
|
+
return properties.some((property) => {
|
|
291
|
+
if (!property || typeof property !== "object" || !("key" in property)) return false;
|
|
292
|
+
const key = property.key;
|
|
293
|
+
return key != null && typeof key === "object" && "content" in key && key.content === "default";
|
|
294
|
+
}) ? "html" : "ecmascript";
|
|
295
|
+
}
|
|
296
|
+
function isVueCompiler(value) {
|
|
297
|
+
if (!value || typeof value !== "object") return false;
|
|
298
|
+
return "compileTemplate" in value && typeof value.compileTemplate === "function" && "parse" in value && typeof value.parse === "function" && "version" in value && typeof value.version === "string";
|
|
299
|
+
}
|
|
300
|
+
/** Reads a possible CommonJS-interoperability default export. */
|
|
301
|
+
function readDefaultExport(value) {
|
|
302
|
+
return value && typeof value === "object" && "default" in value ? value.default : void 0;
|
|
303
|
+
}
|
|
304
|
+
function isSupportedVueVersion(version) {
|
|
305
|
+
const match = /^(\d+)\.(\d+)(?:\.|$)/.exec(version);
|
|
306
|
+
if (!match) return false;
|
|
307
|
+
const major = Number(match[1]);
|
|
308
|
+
const minor = Number(match[2]);
|
|
309
|
+
return major === 3 && minor >= 3;
|
|
310
|
+
}
|
|
311
|
+
function isMissingModule(error) {
|
|
312
|
+
return !!error && typeof error === "object" && "code" in error && error.code === "MODULE_NOT_FOUND";
|
|
313
|
+
}
|
|
314
|
+
function formatResolutionError(error) {
|
|
315
|
+
return error instanceof Error ? error.message : String(error);
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
export { inspectVueCompiler, resolveVueCompiler };
|
|
319
|
+
|
|
320
|
+
//# sourceMappingURL=vueCompiler.js.map
|