vize 0.100.0 → 0.103.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/README.md +9 -2
- package/dist/cli.d.mts +1 -18
- package/dist/cli.mjs +2 -1015
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -10
- package/src/cli.ts +2 -1814
- package/dist/cli.d.mts.map +0 -1
- package/src/cli.test.ts +0 -89
package/src/cli.ts
CHANGED
|
@@ -1,1818 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import * as path from "node:path";
|
|
4
1
|
import { createRequire } from "node:module";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { loadConfig } from "./config.js";
|
|
7
2
|
|
|
8
3
|
const require = createRequire(import.meta.url);
|
|
9
|
-
const
|
|
10
|
-
const BUILD_BATCH_SIZE = 128;
|
|
11
|
-
const BUILD_BATCH_MAX_BYTES = 32 * 1024 * 1024;
|
|
12
|
-
const SKIPPED_VUE_FILE_DIRECTORIES = new Set([
|
|
13
|
-
"node_modules",
|
|
14
|
-
"dist",
|
|
15
|
-
".git",
|
|
16
|
-
".nuxt",
|
|
17
|
-
".output",
|
|
18
|
-
".nitro",
|
|
19
|
-
"coverage",
|
|
20
|
-
]);
|
|
4
|
+
const native = require("@vizejs/native") as typeof import("@vizejs/native");
|
|
21
5
|
|
|
22
|
-
|
|
23
|
-
// Native binding loader (oxlint pattern)
|
|
24
|
-
// ============================================================================
|
|
25
|
-
|
|
26
|
-
function isMusl(): boolean {
|
|
27
|
-
const report = process.report?.getReport();
|
|
28
|
-
if (typeof report === "object" && report !== null && "header" in report) {
|
|
29
|
-
const header = (report as { header: { glibcVersionRuntime?: string } }).header;
|
|
30
|
-
return !header.glibcVersionRuntime;
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const lddPath = require("child_process").execSync("which ldd").toString().trim();
|
|
34
|
-
return readFileSync(lddPath, "utf8").includes("musl");
|
|
35
|
-
} catch {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getBindingPackageName(): string {
|
|
41
|
-
const { platform, arch } = process;
|
|
42
|
-
|
|
43
|
-
switch (platform) {
|
|
44
|
-
case "darwin":
|
|
45
|
-
switch (arch) {
|
|
46
|
-
case "x64":
|
|
47
|
-
return "@vizejs/native-darwin-x64";
|
|
48
|
-
case "arm64":
|
|
49
|
-
return "@vizejs/native-darwin-arm64";
|
|
50
|
-
default:
|
|
51
|
-
throw new Error(`Unsupported architecture on macOS: ${arch}`);
|
|
52
|
-
}
|
|
53
|
-
case "win32":
|
|
54
|
-
switch (arch) {
|
|
55
|
-
case "x64":
|
|
56
|
-
return "@vizejs/native-win32-x64-msvc";
|
|
57
|
-
case "arm64":
|
|
58
|
-
return "@vizejs/native-win32-arm64-msvc";
|
|
59
|
-
default:
|
|
60
|
-
throw new Error(`Unsupported architecture on Windows: ${arch}`);
|
|
61
|
-
}
|
|
62
|
-
case "linux":
|
|
63
|
-
switch (arch) {
|
|
64
|
-
case "x64":
|
|
65
|
-
return isMusl() ? "@vizejs/native-linux-x64-musl" : "@vizejs/native-linux-x64-gnu";
|
|
66
|
-
case "arm64":
|
|
67
|
-
return isMusl() ? "@vizejs/native-linux-arm64-musl" : "@vizejs/native-linux-arm64-gnu";
|
|
68
|
-
default:
|
|
69
|
-
throw new Error(`Unsupported architecture on Linux: ${arch}`);
|
|
70
|
-
}
|
|
71
|
-
default:
|
|
72
|
-
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
interface NativeBinding {
|
|
77
|
-
compileSfcBatchWithResults: (
|
|
78
|
-
files: BatchFileInput[],
|
|
79
|
-
options?: NativeBuildOptions,
|
|
80
|
-
) => BatchCompileResult;
|
|
81
|
-
formatSfc: (source: string, options?: NativeFormatOptions) => FormatResult;
|
|
82
|
-
typeCheck: (source: string, options?: NativeTypeCheckOptions) => TypeCheckResult;
|
|
83
|
-
generateDeclaration?: (source: string, options?: NativeDeclarationOptions) => DeclarationResult;
|
|
84
|
-
lint: (
|
|
85
|
-
patterns: string[],
|
|
86
|
-
options?: {
|
|
87
|
-
format?: string;
|
|
88
|
-
max_warnings?: number;
|
|
89
|
-
quiet?: boolean;
|
|
90
|
-
fix?: boolean;
|
|
91
|
-
help_level?: string;
|
|
92
|
-
preset?: string;
|
|
93
|
-
},
|
|
94
|
-
) => LintResult;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
type NativeCommand = "build" | "check" | "fmt" | "lint";
|
|
98
|
-
|
|
99
|
-
const REQUIRED_BINDINGS: Record<NativeCommand, keyof NativeBinding> = {
|
|
100
|
-
build: "compileSfcBatchWithResults",
|
|
101
|
-
check: "typeCheck",
|
|
102
|
-
fmt: "formatSfc",
|
|
103
|
-
lint: "lint",
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
function loadNative(command: NativeCommand): NativeBinding {
|
|
107
|
-
const attemptedPackages = getAttemptedPackages();
|
|
108
|
-
let lastError: unknown = null;
|
|
109
|
-
const requiredBinding = REQUIRED_BINDINGS[command];
|
|
110
|
-
|
|
111
|
-
for (const packageName of attemptedPackages) {
|
|
112
|
-
try {
|
|
113
|
-
const binding = require(packageName) as Partial<NativeBinding>;
|
|
114
|
-
if (typeof binding[requiredBinding] !== "function") {
|
|
115
|
-
throw new Error(`${packageName} does not expose the ${command} binding.`);
|
|
116
|
-
}
|
|
117
|
-
return binding as NativeBinding;
|
|
118
|
-
} catch (error) {
|
|
119
|
-
lastError = error;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
console.error(`Failed to load native binding. Tried: ${attemptedPackages.join(", ")}`);
|
|
124
|
-
console.error("Try reinstalling: npm install vize");
|
|
125
|
-
throw lastError instanceof Error ? lastError : new Error("Failed to load native binding");
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function getAttemptedPackages(): readonly string[] {
|
|
129
|
-
const platformBindingPackage = getBindingPackageName();
|
|
130
|
-
return shouldPreferWorkspaceBinding(resolveWorkspaceBindingPath())
|
|
131
|
-
? [WORKSPACE_BINDING_PATH, platformBindingPackage]
|
|
132
|
-
: [platformBindingPackage, WORKSPACE_BINDING_PATH];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function resolveWorkspaceBindingPath(): string | null {
|
|
136
|
-
try {
|
|
137
|
-
return require.resolve(WORKSPACE_BINDING_PATH);
|
|
138
|
-
} catch {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function shouldPreferWorkspaceBinding(resolvedPath: string | null): boolean {
|
|
144
|
-
const override = process.env.VIZE_PREFER_WORKSPACE_BINDING;
|
|
145
|
-
if (override === "1" || override === "true") {
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
if (override === "0" || override === "false") {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
if (resolvedPath == null) {
|
|
152
|
-
return false;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return resolvedPath.includes(`${path.sep}npm${path.sep}vize-native${path.sep}`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ============================================================================
|
|
159
|
-
// Lint command
|
|
160
|
-
// ============================================================================
|
|
161
|
-
|
|
162
|
-
interface LintOptions {
|
|
163
|
-
format?: string;
|
|
164
|
-
maxWarnings?: number;
|
|
165
|
-
quiet?: boolean;
|
|
166
|
-
fix?: boolean;
|
|
167
|
-
helpLevel?: string;
|
|
168
|
-
preset?: string;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
interface LintResult {
|
|
172
|
-
output: string;
|
|
173
|
-
errorCount: number;
|
|
174
|
-
warningCount: number;
|
|
175
|
-
fileCount: number;
|
|
176
|
-
timeMs: number;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
interface SharedConfigOptions {
|
|
180
|
-
configFile?: string;
|
|
181
|
-
configMode: "root" | "none";
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
interface ParsedLintCommand {
|
|
185
|
-
patterns: string[];
|
|
186
|
-
options: LintOptions;
|
|
187
|
-
sharedConfig: SharedConfigOptions;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function printUsage(): void {
|
|
191
|
-
console.error("Usage: vize <command> [options]");
|
|
192
|
-
console.error("Commands: build, fmt, check, lint, upgrade, ready, musea");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function printBuildUsage(): void {
|
|
196
|
-
console.error("Usage: vize build [options] [files-or-directories]");
|
|
197
|
-
console.error("Options:");
|
|
198
|
-
console.error(" -o, --output <dir> Output directory");
|
|
199
|
-
console.error(" -f, --format <js|json|stats> Output format");
|
|
200
|
-
console.error(" --ssr Enable SSR compilation");
|
|
201
|
-
console.error(" --script-ext <mode> preserve or downcompile");
|
|
202
|
-
console.error(" -j, --threads <number> Worker thread count");
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function printFmtUsage(): void {
|
|
206
|
-
console.error("Usage: vize fmt [options] [files-or-directories]");
|
|
207
|
-
console.error("Options:");
|
|
208
|
-
console.error(" --check Exit with an error if files need formatting");
|
|
209
|
-
console.error(" -w, --write Write formatted output");
|
|
210
|
-
console.error(" --single-quote Use single quotes");
|
|
211
|
-
console.error(" --print-width <number> Maximum line width");
|
|
212
|
-
console.error(" --tab-width <number> Indentation width");
|
|
213
|
-
console.error(" --use-tabs Indent with tabs");
|
|
214
|
-
console.error(" --no-semi Omit semicolons");
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function printCheckUsage(): void {
|
|
218
|
-
console.error("Usage: vize check [options] [files-or-directories]");
|
|
219
|
-
console.error("Options:");
|
|
220
|
-
console.error(" -f, --format <text|json> Output format");
|
|
221
|
-
console.error(" -q, --quiet Show summary only");
|
|
222
|
-
console.error(" --strict Enable strict checks");
|
|
223
|
-
console.error(" --show-virtual-ts Print generated Virtual TS");
|
|
224
|
-
console.error(" --declaration Emit Vue component .d.ts files");
|
|
225
|
-
console.error(" --declaration-dir <dir> Output directory for declarations");
|
|
226
|
-
console.error(" --max-warnings <number> Fail when warnings exceed the limit");
|
|
227
|
-
console.error(" -c, --config <path> Use a specific vize config file");
|
|
228
|
-
console.error(" --no-config Disable config discovery");
|
|
229
|
-
console.error("");
|
|
230
|
-
console.error(
|
|
231
|
-
"Note: npm `vize check` uses the packaged NAPI checker. Install the Rust CLI for project-backed Corsa diagnostics.",
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function printUpgradeUsage(): void {
|
|
236
|
-
console.error("Usage: vize upgrade [options]");
|
|
237
|
-
console.error("Options:");
|
|
238
|
-
console.error(" --package-manager <name> npm, pnpm, yarn, bun, or vp");
|
|
239
|
-
console.error(" -g, --global Upgrade the global installation");
|
|
240
|
-
console.error(" --dry-run Print the command without running it");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function printReadyUsage(): void {
|
|
244
|
-
console.error("Usage: vize ready [options] [files-or-directories]");
|
|
245
|
-
console.error("Runs: fmt --write -> lint -> check -> build");
|
|
246
|
-
console.error("Options:");
|
|
247
|
-
console.error(" -o, --output <dir> Output directory for build");
|
|
248
|
-
console.error(" --ssr Enable SSR compilation for build");
|
|
249
|
-
console.error(" --script-ext <mode> preserve or downcompile");
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function resolvePackageBinaryFromCwd(packageName: string, binName: string = packageName): string {
|
|
253
|
-
const cwdRequire = createRequire(pathToFileURL(path.join(process.cwd(), "package.json")).href);
|
|
254
|
-
const packageJsonPath = cwdRequire.resolve(`${packageName}/package.json`);
|
|
255
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
|
|
256
|
-
bin?: string | Record<string, string>;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const bin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.[binName];
|
|
260
|
-
|
|
261
|
-
if (!bin) {
|
|
262
|
-
throw new Error(`Could not resolve binary '${binName}' from package '${packageName}'`);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return path.resolve(path.dirname(packageJsonPath), bin);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function runMusea(args: string[]): void {
|
|
269
|
-
const isHelp = args.includes("--help") || args.includes("-h");
|
|
270
|
-
if (isHelp) {
|
|
271
|
-
console.error("Usage: vize musea [--build] [...vite options]");
|
|
272
|
-
console.error(" --build Run `vite build` instead of `vite dev`");
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const isBuild = args.includes("--build");
|
|
277
|
-
const viteArgs = args.filter((arg) => arg !== "--build");
|
|
278
|
-
const viteCommand = isBuild ? "build" : "dev";
|
|
279
|
-
const viteBin = resolvePackageBinaryFromCwd("vite");
|
|
280
|
-
const result = spawnSync(process.execPath, [viteBin, viteCommand, ...viteArgs], {
|
|
281
|
-
stdio: "inherit",
|
|
282
|
-
cwd: process.cwd(),
|
|
283
|
-
env: process.env,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
if (result.error) {
|
|
287
|
-
throw result.error;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
process.exit(result.status ?? 1);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function parseLintCommand(args: string[]): ParsedLintCommand {
|
|
294
|
-
const patterns: string[] = [];
|
|
295
|
-
const options: LintOptions = {};
|
|
296
|
-
const sharedConfig: SharedConfigOptions = {
|
|
297
|
-
configMode: "root",
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
for (let i = 0; i < args.length; i++) {
|
|
301
|
-
const arg = args[i];
|
|
302
|
-
if (arg === "--format" || arg === "-f") {
|
|
303
|
-
options.format = args[++i];
|
|
304
|
-
} else if (arg === "--max-warnings") {
|
|
305
|
-
options.maxWarnings = Number.parseInt(args[++i], 10);
|
|
306
|
-
} else if (arg === "--quiet" || arg === "-q") {
|
|
307
|
-
options.quiet = true;
|
|
308
|
-
} else if (arg === "--fix") {
|
|
309
|
-
options.fix = true;
|
|
310
|
-
} else if (arg === "--help-level") {
|
|
311
|
-
options.helpLevel = args[++i];
|
|
312
|
-
} else if (arg === "--preset") {
|
|
313
|
-
options.preset = args[++i];
|
|
314
|
-
} else if (arg === "--config" || arg === "-c") {
|
|
315
|
-
const configFile = args[++i];
|
|
316
|
-
if (!configFile) {
|
|
317
|
-
throw new Error("Missing path after --config");
|
|
318
|
-
}
|
|
319
|
-
sharedConfig.configFile = configFile;
|
|
320
|
-
} else if (arg === "--no-config") {
|
|
321
|
-
sharedConfig.configMode = "none";
|
|
322
|
-
} else if (!arg.startsWith("-")) {
|
|
323
|
-
patterns.push(arg);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return { patterns, options, sharedConfig };
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ============================================================================
|
|
331
|
-
// Build command
|
|
332
|
-
// ============================================================================
|
|
333
|
-
|
|
334
|
-
interface NativeBuildOptions {
|
|
335
|
-
ssr?: boolean;
|
|
336
|
-
vapor?: boolean;
|
|
337
|
-
customRenderer?: boolean;
|
|
338
|
-
custom_renderer?: boolean;
|
|
339
|
-
isTs?: boolean;
|
|
340
|
-
is_ts?: boolean;
|
|
341
|
-
threads?: number;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
interface BatchFileInput {
|
|
345
|
-
path: string;
|
|
346
|
-
source: string;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
interface MacroArtifact {
|
|
350
|
-
kind: string;
|
|
351
|
-
name: string;
|
|
352
|
-
source: string;
|
|
353
|
-
content: string;
|
|
354
|
-
moduleCode?: string;
|
|
355
|
-
start: number;
|
|
356
|
-
end: number;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
interface BatchFileResult {
|
|
360
|
-
path: string;
|
|
361
|
-
code: string;
|
|
362
|
-
css?: string;
|
|
363
|
-
errors: string[];
|
|
364
|
-
warnings: string[];
|
|
365
|
-
scopeId?: string;
|
|
366
|
-
scope_id?: string;
|
|
367
|
-
hasScoped?: boolean;
|
|
368
|
-
has_scoped?: boolean;
|
|
369
|
-
macroArtifacts?: MacroArtifact[];
|
|
370
|
-
macro_artifacts?: MacroArtifact[];
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
interface BatchCompileResult {
|
|
374
|
-
results: BatchFileResult[];
|
|
375
|
-
successCount?: number;
|
|
376
|
-
success_count?: number;
|
|
377
|
-
failedCount?: number;
|
|
378
|
-
failed_count?: number;
|
|
379
|
-
timeMs?: number;
|
|
380
|
-
time_ms?: number;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
interface BuildOptions {
|
|
384
|
-
output: string;
|
|
385
|
-
format: "js" | "json" | "stats";
|
|
386
|
-
ssr?: boolean;
|
|
387
|
-
vapor?: boolean;
|
|
388
|
-
customRenderer?: boolean;
|
|
389
|
-
scriptExt: "preserve" | "downcompile";
|
|
390
|
-
threads?: number;
|
|
391
|
-
help?: boolean;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
interface ParsedBuildCommand {
|
|
395
|
-
patterns: string[];
|
|
396
|
-
options: BuildOptions;
|
|
397
|
-
sharedConfig: SharedConfigOptions;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function parseBuildCommand(args: string[]): ParsedBuildCommand {
|
|
401
|
-
const patterns: string[] = [];
|
|
402
|
-
const options: BuildOptions = {
|
|
403
|
-
output: "./dist",
|
|
404
|
-
format: "js",
|
|
405
|
-
scriptExt: "downcompile",
|
|
406
|
-
};
|
|
407
|
-
const sharedConfig: SharedConfigOptions = {
|
|
408
|
-
configMode: "root",
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
for (let i = 0; i < args.length; i++) {
|
|
412
|
-
const arg = args[i];
|
|
413
|
-
if (arg === "--output" || arg === "-o") {
|
|
414
|
-
options.output = args[++i] ?? options.output;
|
|
415
|
-
} else if (arg === "--format" || arg === "-f") {
|
|
416
|
-
const format = args[++i];
|
|
417
|
-
if (format === "js" || format === "json" || format === "stats") {
|
|
418
|
-
options.format = format;
|
|
419
|
-
}
|
|
420
|
-
} else if (arg === "--ssr") {
|
|
421
|
-
options.ssr = true;
|
|
422
|
-
} else if (arg === "--vapor") {
|
|
423
|
-
options.vapor = true;
|
|
424
|
-
} else if (arg === "--custom-renderer") {
|
|
425
|
-
options.customRenderer = true;
|
|
426
|
-
} else if (arg === "--script-ext") {
|
|
427
|
-
const scriptExt = args[++i];
|
|
428
|
-
if (scriptExt === "preserve" || scriptExt === "downcompile") {
|
|
429
|
-
options.scriptExt = scriptExt;
|
|
430
|
-
}
|
|
431
|
-
} else if (arg === "--threads" || arg === "-j") {
|
|
432
|
-
options.threads = Number.parseInt(args[++i], 10);
|
|
433
|
-
} else if (arg === "--config" || arg === "-c") {
|
|
434
|
-
const configFile = args[++i];
|
|
435
|
-
if (!configFile) {
|
|
436
|
-
throw new Error("Missing path after --config");
|
|
437
|
-
}
|
|
438
|
-
sharedConfig.configFile = configFile;
|
|
439
|
-
} else if (arg === "--no-config") {
|
|
440
|
-
sharedConfig.configMode = "none";
|
|
441
|
-
} else if (arg === "--profile" || arg === "--continue-on-error") {
|
|
442
|
-
// Accepted for command compatibility. The npm build path prints a compact summary.
|
|
443
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
444
|
-
options.help = true;
|
|
445
|
-
} else if (!arg.startsWith("-")) {
|
|
446
|
-
patterns.push(arg);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return { patterns, options, sharedConfig };
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function getScriptLang(source: string): string {
|
|
454
|
-
const match = source.match(/<script\b[^>]*\blang=["']([^"']+)["']/i);
|
|
455
|
-
return match?.[1] ?? "js";
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function getOutputExtension(source: string, scriptExt: BuildOptions["scriptExt"]): string {
|
|
459
|
-
if (scriptExt === "downcompile") {
|
|
460
|
-
return "js";
|
|
461
|
-
}
|
|
462
|
-
const lang = getScriptLang(source);
|
|
463
|
-
return lang === "ts" || lang === "tsx" || lang === "jsx" ? lang : "js";
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function outputFileName(file: string, extension: string): string {
|
|
467
|
-
return path.basename(file).replace(/\.vue$/i, `.${extension}`);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function toNativeBuildOptions(options: BuildOptions): NativeBuildOptions {
|
|
471
|
-
const isTs = options.scriptExt === "preserve";
|
|
472
|
-
return {
|
|
473
|
-
ssr: options.ssr,
|
|
474
|
-
vapor: options.vapor,
|
|
475
|
-
customRenderer: options.customRenderer,
|
|
476
|
-
custom_renderer: options.customRenderer,
|
|
477
|
-
isTs,
|
|
478
|
-
is_ts: isTs,
|
|
479
|
-
threads: options.threads,
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
async function runBuild(args: string[]): Promise<void> {
|
|
484
|
-
const { patterns, options, sharedConfig } = parseBuildCommand(args);
|
|
485
|
-
if (options.help) {
|
|
486
|
-
printBuildUsage();
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const config = await loadConfig(process.cwd(), {
|
|
491
|
-
mode: sharedConfig.configMode,
|
|
492
|
-
configFile: sharedConfig.configFile,
|
|
493
|
-
env: {
|
|
494
|
-
mode: process.env.NODE_ENV ?? "development",
|
|
495
|
-
command: "build",
|
|
496
|
-
},
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
if (sharedConfig.configFile && !config) {
|
|
500
|
-
throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
options.ssr ??= config?.compiler?.ssr;
|
|
504
|
-
options.vapor ??= config?.compiler?.vapor;
|
|
505
|
-
options.customRenderer ??= config?.compiler?.customRenderer;
|
|
506
|
-
if (config?.compiler?.scriptExt === "ts") {
|
|
507
|
-
options.scriptExt = "preserve";
|
|
508
|
-
} else if (config?.compiler?.scriptExt === "js") {
|
|
509
|
-
options.scriptExt = "downcompile";
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const files = collectVueFiles(patterns);
|
|
513
|
-
if (files.length === 0) {
|
|
514
|
-
process.stderr.write(
|
|
515
|
-
`No Vue files found matching inputs: ${sanitizeTerminalText(JSON.stringify(patterns))}\n`,
|
|
516
|
-
);
|
|
517
|
-
process.exit(1);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const native = loadNative("build");
|
|
521
|
-
const startedAt = performance.now();
|
|
522
|
-
const batches = createBoundedFileBatches(files, {
|
|
523
|
-
maxFiles: BUILD_BATCH_SIZE,
|
|
524
|
-
maxBytes: BUILD_BATCH_MAX_BYTES,
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
if (options.format !== "stats") {
|
|
528
|
-
mkdirSync(options.output, { recursive: true });
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
let nativeTimeMs = 0;
|
|
532
|
-
let failed = 0;
|
|
533
|
-
let success = 0;
|
|
534
|
-
|
|
535
|
-
for (const batch of batches) {
|
|
536
|
-
const inputs: { path: string; source: string }[] = [];
|
|
537
|
-
const extensionByPath = new Map<string, string>();
|
|
538
|
-
for (const file of batch) {
|
|
539
|
-
const source = readFileSync(file, "utf8");
|
|
540
|
-
extensionByPath.set(file, getOutputExtension(source, options.scriptExt));
|
|
541
|
-
inputs.push({ path: file, source });
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
const chunkStartedAt = performance.now();
|
|
545
|
-
const result = native.compileSfcBatchWithResults(inputs, toNativeBuildOptions(options));
|
|
546
|
-
inputs.length = 0;
|
|
547
|
-
nativeTimeMs += result.timeMs ?? result.time_ms ?? performance.now() - chunkStartedAt;
|
|
548
|
-
const results = result.results.sort((left, right) => left.path.localeCompare(right.path));
|
|
549
|
-
|
|
550
|
-
for (const fileResult of results) {
|
|
551
|
-
for (const warning of fileResult.warnings) {
|
|
552
|
-
process.stderr.write(
|
|
553
|
-
`warning: ${displayPath(fileResult.path)} ${sanitizeTerminalText(warning)}\n`,
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
for (const error of fileResult.errors) {
|
|
557
|
-
process.stderr.write(
|
|
558
|
-
`error: ${displayPath(fileResult.path)} ${sanitizeTerminalText(error)}\n`,
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (fileResult.errors.length > 0 || options.format === "stats") {
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const extension =
|
|
567
|
-
options.format === "json" ? "json" : (extensionByPath.get(fileResult.path) ?? "js");
|
|
568
|
-
const outputPath = path.join(options.output, outputFileName(fileResult.path, extension));
|
|
569
|
-
const content =
|
|
570
|
-
options.format === "json" ? JSON.stringify(fileResult, null, 2) : fileResult.code;
|
|
571
|
-
writeFileSync(outputPath, content);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const chunkFailed =
|
|
575
|
-
result.failedCount ?? result.failed_count ?? results.filter((r) => r.errors.length).length;
|
|
576
|
-
failed += chunkFailed;
|
|
577
|
-
success += result.successCount ?? result.success_count ?? results.length - chunkFailed;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const timeMs = nativeTimeMs || performance.now() - startedAt;
|
|
581
|
-
process.stderr.write(
|
|
582
|
-
`\x1b[32mOK\x1b[0m Built ${success} Vue file(s) in ${timeMs.toFixed(2)}ms\n`,
|
|
583
|
-
);
|
|
584
|
-
|
|
585
|
-
if (failed > 0) {
|
|
586
|
-
process.stderr.write(`\x1b[31mERR\x1b[0m ${failed} file(s) failed\n`);
|
|
587
|
-
process.exit(1);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// ============================================================================
|
|
592
|
-
// Format command
|
|
593
|
-
// ============================================================================
|
|
594
|
-
|
|
595
|
-
interface NativeFormatOptions {
|
|
596
|
-
printWidth?: number;
|
|
597
|
-
print_width?: number;
|
|
598
|
-
tabWidth?: number;
|
|
599
|
-
tab_width?: number;
|
|
600
|
-
useTabs?: boolean;
|
|
601
|
-
use_tabs?: boolean;
|
|
602
|
-
semi?: boolean;
|
|
603
|
-
singleQuote?: boolean;
|
|
604
|
-
single_quote?: boolean;
|
|
605
|
-
sortAttributes?: boolean;
|
|
606
|
-
sort_attributes?: boolean;
|
|
607
|
-
singleAttributePerLine?: boolean;
|
|
608
|
-
single_attribute_per_line?: boolean;
|
|
609
|
-
maxAttributesPerLine?: number;
|
|
610
|
-
max_attributes_per_line?: number;
|
|
611
|
-
normalizeDirectiveShorthands?: boolean;
|
|
612
|
-
normalize_directive_shorthands?: boolean;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
interface FormatResult {
|
|
616
|
-
code: string;
|
|
617
|
-
changed: boolean;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
interface FmtOptions extends NativeFormatOptions {
|
|
621
|
-
check?: boolean;
|
|
622
|
-
write?: boolean;
|
|
623
|
-
help?: boolean;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
interface ParsedFmtCommand {
|
|
627
|
-
patterns: string[];
|
|
628
|
-
options: FmtOptions;
|
|
629
|
-
sharedConfig: SharedConfigOptions;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
function parseFmtCommand(args: string[]): ParsedFmtCommand {
|
|
633
|
-
const patterns: string[] = [];
|
|
634
|
-
const options: FmtOptions = {};
|
|
635
|
-
const sharedConfig: SharedConfigOptions = {
|
|
636
|
-
configMode: "root",
|
|
637
|
-
};
|
|
638
|
-
|
|
639
|
-
for (let i = 0; i < args.length; i++) {
|
|
640
|
-
const arg = args[i];
|
|
641
|
-
if (arg === "--check") {
|
|
642
|
-
options.check = true;
|
|
643
|
-
} else if (arg === "--write" || arg === "-w") {
|
|
644
|
-
options.write = true;
|
|
645
|
-
} else if (arg === "--single-quote") {
|
|
646
|
-
options.singleQuote = true;
|
|
647
|
-
} else if (arg === "--print-width") {
|
|
648
|
-
options.printWidth = Number.parseInt(args[++i], 10);
|
|
649
|
-
} else if (arg === "--tab-width") {
|
|
650
|
-
options.tabWidth = Number.parseInt(args[++i], 10);
|
|
651
|
-
} else if (arg === "--use-tabs") {
|
|
652
|
-
options.useTabs = true;
|
|
653
|
-
} else if (arg === "--no-semi") {
|
|
654
|
-
options.semi = false;
|
|
655
|
-
} else if (arg === "--sort-attributes") {
|
|
656
|
-
options.sortAttributes = true;
|
|
657
|
-
} else if (arg === "--single-attribute-per-line") {
|
|
658
|
-
options.singleAttributePerLine = true;
|
|
659
|
-
} else if (arg === "--max-attributes-per-line") {
|
|
660
|
-
options.maxAttributesPerLine = Number.parseInt(args[++i], 10);
|
|
661
|
-
} else if (arg === "--normalize-directive-shorthands") {
|
|
662
|
-
options.normalizeDirectiveShorthands = true;
|
|
663
|
-
} else if (arg === "--config" || arg === "-c") {
|
|
664
|
-
const configFile = args[++i];
|
|
665
|
-
if (!configFile) {
|
|
666
|
-
throw new Error("Missing path after --config");
|
|
667
|
-
}
|
|
668
|
-
sharedConfig.configFile = configFile;
|
|
669
|
-
} else if (arg === "--no-config") {
|
|
670
|
-
sharedConfig.configMode = "none";
|
|
671
|
-
} else if (arg === "--profile") {
|
|
672
|
-
// Accepted for command compatibility. The npm fmt path prints a compact summary.
|
|
673
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
674
|
-
options.help = true;
|
|
675
|
-
} else if (!arg.startsWith("-")) {
|
|
676
|
-
patterns.push(arg);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
return { patterns, options, sharedConfig };
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
function toNativeFormatOptions(options: FmtOptions): NativeFormatOptions {
|
|
684
|
-
return {
|
|
685
|
-
printWidth: options.printWidth,
|
|
686
|
-
print_width: options.printWidth,
|
|
687
|
-
tabWidth: options.tabWidth,
|
|
688
|
-
tab_width: options.tabWidth,
|
|
689
|
-
useTabs: options.useTabs,
|
|
690
|
-
use_tabs: options.useTabs,
|
|
691
|
-
semi: options.semi,
|
|
692
|
-
singleQuote: options.singleQuote,
|
|
693
|
-
single_quote: options.singleQuote,
|
|
694
|
-
sortAttributes: options.sortAttributes,
|
|
695
|
-
sort_attributes: options.sortAttributes,
|
|
696
|
-
singleAttributePerLine: options.singleAttributePerLine,
|
|
697
|
-
single_attribute_per_line: options.singleAttributePerLine,
|
|
698
|
-
maxAttributesPerLine: options.maxAttributesPerLine,
|
|
699
|
-
max_attributes_per_line: options.maxAttributesPerLine,
|
|
700
|
-
normalizeDirectiveShorthands: options.normalizeDirectiveShorthands,
|
|
701
|
-
normalize_directive_shorthands: options.normalizeDirectiveShorthands,
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
async function runFmt(args: string[]): Promise<void> {
|
|
706
|
-
const { patterns, options, sharedConfig } = parseFmtCommand(args);
|
|
707
|
-
if (options.help) {
|
|
708
|
-
printFmtUsage();
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
const config = await loadConfig(process.cwd(), {
|
|
713
|
-
mode: sharedConfig.configMode,
|
|
714
|
-
configFile: sharedConfig.configFile,
|
|
715
|
-
env: {
|
|
716
|
-
mode: process.env.NODE_ENV ?? "development",
|
|
717
|
-
command: "fmt",
|
|
718
|
-
},
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
if (sharedConfig.configFile && !config) {
|
|
722
|
-
throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
options.printWidth ??= config?.formatter?.printWidth;
|
|
726
|
-
options.tabWidth ??= config?.formatter?.tabWidth;
|
|
727
|
-
options.useTabs ??= config?.formatter?.useTabs;
|
|
728
|
-
options.semi ??= config?.formatter?.semi;
|
|
729
|
-
options.singleQuote ??= config?.formatter?.singleQuote;
|
|
730
|
-
|
|
731
|
-
const files = collectVueFiles(patterns);
|
|
732
|
-
if (files.length === 0) {
|
|
733
|
-
process.stderr.write(
|
|
734
|
-
`No Vue files found matching inputs: ${sanitizeTerminalText(JSON.stringify(patterns))}\n`,
|
|
735
|
-
);
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
const native = loadNative("fmt");
|
|
740
|
-
let changed = 0;
|
|
741
|
-
let errored = 0;
|
|
742
|
-
|
|
743
|
-
for (const file of files) {
|
|
744
|
-
const source = readFileSync(file, "utf8");
|
|
745
|
-
try {
|
|
746
|
-
const result = native.formatSfc(source, toNativeFormatOptions(options));
|
|
747
|
-
if (!result.changed) {
|
|
748
|
-
continue;
|
|
749
|
-
}
|
|
750
|
-
changed++;
|
|
751
|
-
if (options.check) {
|
|
752
|
-
process.stderr.write(`Would reformat: ${displayPath(file)}\n`);
|
|
753
|
-
} else if (options.write) {
|
|
754
|
-
writeFileSync(file, result.code);
|
|
755
|
-
process.stderr.write(`Reformatted: ${displayPath(file)}\n`);
|
|
756
|
-
} else {
|
|
757
|
-
process.stderr.write(`Would reformat: ${displayPath(file)}\n`);
|
|
758
|
-
}
|
|
759
|
-
} catch (error) {
|
|
760
|
-
errored++;
|
|
761
|
-
process.stderr.write(
|
|
762
|
-
`Error formatting ${displayPath(file)}: ${sanitizeTerminalText(error instanceof Error ? error.message : String(error))}\n`,
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
process.stderr.write(
|
|
768
|
-
`\x1b[32mOK\x1b[0m Formatted ${files.length} Vue file(s), ${changed} changed\n`,
|
|
769
|
-
);
|
|
770
|
-
|
|
771
|
-
if (errored > 0 || (options.check && changed > 0)) {
|
|
772
|
-
process.exit(1);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// ============================================================================
|
|
777
|
-
// Check command
|
|
778
|
-
// ============================================================================
|
|
779
|
-
|
|
780
|
-
interface NativeTypeCheckOptions {
|
|
781
|
-
filename?: string;
|
|
782
|
-
strict?: boolean;
|
|
783
|
-
includeVirtualTs?: boolean;
|
|
784
|
-
include_virtual_ts?: boolean;
|
|
785
|
-
checkProps?: boolean;
|
|
786
|
-
check_props?: boolean;
|
|
787
|
-
checkEmits?: boolean;
|
|
788
|
-
check_emits?: boolean;
|
|
789
|
-
checkTemplateBindings?: boolean;
|
|
790
|
-
check_template_bindings?: boolean;
|
|
791
|
-
checkReactivity?: boolean;
|
|
792
|
-
check_reactivity?: boolean;
|
|
793
|
-
checkSetupContext?: boolean;
|
|
794
|
-
check_setup_context?: boolean;
|
|
795
|
-
checkInvalidExports?: boolean;
|
|
796
|
-
check_invalid_exports?: boolean;
|
|
797
|
-
checkFallthroughAttrs?: boolean;
|
|
798
|
-
check_fallthrough_attrs?: boolean;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
interface NativeDeclarationOptions {
|
|
802
|
-
filename?: string;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
interface DeclarationResult {
|
|
806
|
-
code: string;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
interface TypeDiagnostic {
|
|
810
|
-
severity: string;
|
|
811
|
-
message: string;
|
|
812
|
-
start: number;
|
|
813
|
-
end: number;
|
|
814
|
-
code?: string;
|
|
815
|
-
help?: string;
|
|
816
|
-
related?: Array<{
|
|
817
|
-
message: string;
|
|
818
|
-
start: number;
|
|
819
|
-
end: number;
|
|
820
|
-
filename?: string;
|
|
821
|
-
}>;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
interface TypeCheckResult {
|
|
825
|
-
diagnostics: TypeDiagnostic[];
|
|
826
|
-
virtualTs?: string;
|
|
827
|
-
errorCount: number;
|
|
828
|
-
warningCount: number;
|
|
829
|
-
analysisTimeMs?: number;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
interface CheckOptions {
|
|
833
|
-
format?: string;
|
|
834
|
-
quiet?: boolean;
|
|
835
|
-
strict?: boolean;
|
|
836
|
-
includeVirtualTs?: boolean;
|
|
837
|
-
maxWarnings?: number;
|
|
838
|
-
checkProps?: boolean;
|
|
839
|
-
checkEmits?: boolean;
|
|
840
|
-
checkTemplateBindings?: boolean;
|
|
841
|
-
checkReactivity?: boolean;
|
|
842
|
-
checkSetupContext?: boolean;
|
|
843
|
-
checkInvalidExports?: boolean;
|
|
844
|
-
checkFallthroughAttrs?: boolean;
|
|
845
|
-
declaration?: boolean;
|
|
846
|
-
declarationDir?: string;
|
|
847
|
-
help?: boolean;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
interface ParsedCheckCommand {
|
|
851
|
-
patterns: string[];
|
|
852
|
-
options: CheckOptions;
|
|
853
|
-
sharedConfig: SharedConfigOptions;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
interface EmittedDeclaration {
|
|
857
|
-
file: string;
|
|
858
|
-
path: string;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
function parseCheckCommand(args: string[]): ParsedCheckCommand {
|
|
862
|
-
const patterns: string[] = [];
|
|
863
|
-
const options: CheckOptions = {};
|
|
864
|
-
const sharedConfig: SharedConfigOptions = {
|
|
865
|
-
configMode: "root",
|
|
866
|
-
};
|
|
867
|
-
|
|
868
|
-
for (let i = 0; i < args.length; i++) {
|
|
869
|
-
const arg = args[i];
|
|
870
|
-
if (arg === "--format" || arg === "-f") {
|
|
871
|
-
options.format = args[++i];
|
|
872
|
-
} else if (arg === "--quiet" || arg === "-q") {
|
|
873
|
-
options.quiet = true;
|
|
874
|
-
} else if (arg === "--strict") {
|
|
875
|
-
options.strict = true;
|
|
876
|
-
} else if (arg === "--no-strict") {
|
|
877
|
-
options.strict = false;
|
|
878
|
-
} else if (arg === "--show-virtual-ts" || arg === "--include-virtual-ts") {
|
|
879
|
-
options.includeVirtualTs = true;
|
|
880
|
-
} else if (arg === "--max-warnings") {
|
|
881
|
-
options.maxWarnings = Number.parseInt(args[++i], 10);
|
|
882
|
-
} else if (arg === "--no-check-props") {
|
|
883
|
-
options.checkProps = false;
|
|
884
|
-
} else if (arg === "--no-check-emits") {
|
|
885
|
-
options.checkEmits = false;
|
|
886
|
-
} else if (arg === "--no-check-template-bindings") {
|
|
887
|
-
options.checkTemplateBindings = false;
|
|
888
|
-
} else if (arg === "--no-check-reactivity") {
|
|
889
|
-
options.checkReactivity = false;
|
|
890
|
-
} else if (arg === "--no-check-setup-context") {
|
|
891
|
-
options.checkSetupContext = false;
|
|
892
|
-
} else if (arg === "--no-check-invalid-exports") {
|
|
893
|
-
options.checkInvalidExports = false;
|
|
894
|
-
} else if (arg === "--no-check-fallthrough-attrs") {
|
|
895
|
-
options.checkFallthroughAttrs = false;
|
|
896
|
-
} else if (arg === "--declaration") {
|
|
897
|
-
options.declaration = true;
|
|
898
|
-
} else if (arg === "--declaration-dir") {
|
|
899
|
-
const declarationDir = args[++i];
|
|
900
|
-
if (!declarationDir) {
|
|
901
|
-
throw new Error("Missing path after --declaration-dir");
|
|
902
|
-
}
|
|
903
|
-
options.declarationDir = declarationDir;
|
|
904
|
-
} else if (arg === "--config" || arg === "-c") {
|
|
905
|
-
const configFile = args[++i];
|
|
906
|
-
if (!configFile) {
|
|
907
|
-
throw new Error("Missing path after --config");
|
|
908
|
-
}
|
|
909
|
-
sharedConfig.configFile = configFile;
|
|
910
|
-
} else if (arg === "--no-config") {
|
|
911
|
-
sharedConfig.configMode = "none";
|
|
912
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
913
|
-
options.help = true;
|
|
914
|
-
} else if (arg === "--tsconfig" || arg === "--corsa-path" || arg === "--servers") {
|
|
915
|
-
i++;
|
|
916
|
-
} else if (arg === "--socket" || arg === "-s") {
|
|
917
|
-
i++;
|
|
918
|
-
} else if (arg === "--profile") {
|
|
919
|
-
// Accepted for package-script compatibility with the Rust CLI.
|
|
920
|
-
} else if (!arg.startsWith("-")) {
|
|
921
|
-
patterns.push(arg);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
return { patterns, options, sharedConfig };
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
export function shouldRetainCheckSource(options: {
|
|
929
|
-
declaration?: boolean;
|
|
930
|
-
format?: string;
|
|
931
|
-
quiet?: boolean;
|
|
932
|
-
}): boolean {
|
|
933
|
-
return Boolean(options.declaration || (options.format !== "json" && !options.quiet));
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
function hasGlobSyntax(pattern: string): boolean {
|
|
937
|
-
return pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
function normalizePath(filePath: string): string {
|
|
941
|
-
return filePath.split(path.sep).join("/");
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
export function sanitizeTerminalText(value: unknown): string {
|
|
945
|
-
const text = String(value);
|
|
946
|
-
let sanitized = "";
|
|
947
|
-
|
|
948
|
-
for (let i = 0; i < text.length; i++) {
|
|
949
|
-
const code = text.charCodeAt(i);
|
|
950
|
-
if (code === 0x1b) {
|
|
951
|
-
i = skipTerminalEscapeSequence(text, i);
|
|
952
|
-
continue;
|
|
953
|
-
}
|
|
954
|
-
if (isUnsafeTerminalControl(code)) {
|
|
955
|
-
continue;
|
|
956
|
-
}
|
|
957
|
-
sanitized += text[i];
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
return sanitized;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
function skipTerminalEscapeSequence(text: string, escapeIndex: number): number {
|
|
964
|
-
const introducer = text.charCodeAt(escapeIndex + 1);
|
|
965
|
-
if (introducer === 0x5b) {
|
|
966
|
-
return skipUntilAnsiFinalByte(text, escapeIndex + 2);
|
|
967
|
-
}
|
|
968
|
-
if (introducer === 0x5d || introducer === 0x50 || introducer === 0x5e || introducer === 0x5f) {
|
|
969
|
-
return skipUntilStringTerminator(text, escapeIndex + 2);
|
|
970
|
-
}
|
|
971
|
-
if (Number.isNaN(introducer)) {
|
|
972
|
-
return escapeIndex;
|
|
973
|
-
}
|
|
974
|
-
return escapeIndex + 1;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
function skipUntilAnsiFinalByte(text: string, index: number): number {
|
|
978
|
-
for (let i = index; i < text.length; i++) {
|
|
979
|
-
const code = text.charCodeAt(i);
|
|
980
|
-
if (code >= 0x40 && code <= 0x7e) {
|
|
981
|
-
return i;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
return text.length - 1;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
function skipUntilStringTerminator(text: string, index: number): number {
|
|
988
|
-
for (let i = index; i < text.length; i++) {
|
|
989
|
-
const code = text.charCodeAt(i);
|
|
990
|
-
if (code === 0x07) {
|
|
991
|
-
return i;
|
|
992
|
-
}
|
|
993
|
-
if (code === 0x1b && text.charCodeAt(i + 1) === 0x5c) {
|
|
994
|
-
return i + 1;
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
return text.length - 1;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
function isUnsafeTerminalControl(code: number): boolean {
|
|
1001
|
-
if (code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
1002
|
-
return false;
|
|
1003
|
-
}
|
|
1004
|
-
return (code >= 0x00 && code <= 0x1f) || (code >= 0x7f && code <= 0x9f);
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
export function displayPath(filePath: string): string {
|
|
1008
|
-
const relative = path.relative(process.cwd(), filePath);
|
|
1009
|
-
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
1010
|
-
return sanitizeTerminalText(normalizePath(relative));
|
|
1011
|
-
}
|
|
1012
|
-
return sanitizeTerminalText(normalizePath(filePath));
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
function isVueFile(filePath: string): boolean {
|
|
1016
|
-
return path.extname(filePath) === ".vue";
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
function collectVueFilesFromDirectory(
|
|
1020
|
-
directory: string,
|
|
1021
|
-
recursive: boolean,
|
|
1022
|
-
files: string[] = [],
|
|
1023
|
-
): string[] {
|
|
1024
|
-
const entries = readdirSync(directory, { withFileTypes: true });
|
|
1025
|
-
|
|
1026
|
-
for (const entry of entries) {
|
|
1027
|
-
const entryPath = path.join(directory, entry.name);
|
|
1028
|
-
if (entry.isDirectory()) {
|
|
1029
|
-
if (SKIPPED_VUE_FILE_DIRECTORIES.has(entry.name)) {
|
|
1030
|
-
continue;
|
|
1031
|
-
}
|
|
1032
|
-
if (recursive) {
|
|
1033
|
-
collectVueFilesFromDirectory(entryPath, true, files);
|
|
1034
|
-
}
|
|
1035
|
-
} else if (entry.isFile() && isVueFile(entryPath)) {
|
|
1036
|
-
files.push(entryPath);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
return files;
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
function globBase(pattern: string): string {
|
|
1044
|
-
const normalized = normalizePath(pattern);
|
|
1045
|
-
const globIndex = normalized.search(/[*?[]/);
|
|
1046
|
-
if (globIndex === -1) {
|
|
1047
|
-
return normalized;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
const beforeGlob = normalized.slice(0, globIndex);
|
|
1051
|
-
const slashIndex = beforeGlob.lastIndexOf("/");
|
|
1052
|
-
if (slashIndex === -1) {
|
|
1053
|
-
return ".";
|
|
1054
|
-
}
|
|
1055
|
-
return beforeGlob.slice(0, slashIndex) || "/";
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
function globToRegExp(pattern: string): RegExp {
|
|
1059
|
-
const normalized = normalizePath(pattern);
|
|
1060
|
-
let source = "";
|
|
1061
|
-
|
|
1062
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
1063
|
-
const char = normalized[i];
|
|
1064
|
-
const next = normalized[i + 1];
|
|
1065
|
-
const afterNext = normalized[i + 2];
|
|
1066
|
-
|
|
1067
|
-
if (char === "*" && next === "*" && afterNext === "/") {
|
|
1068
|
-
source += "(?:.*/)?";
|
|
1069
|
-
i += 2;
|
|
1070
|
-
} else if (char === "*" && next === "*") {
|
|
1071
|
-
source += ".*";
|
|
1072
|
-
i++;
|
|
1073
|
-
} else if (char === "*") {
|
|
1074
|
-
source += "[^/]*";
|
|
1075
|
-
} else if (char === "?") {
|
|
1076
|
-
source += "[^/]";
|
|
1077
|
-
} else if ("\\^$+?.()|{}[]".includes(char)) {
|
|
1078
|
-
source += `\\${char}`;
|
|
1079
|
-
} else {
|
|
1080
|
-
source += char;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
return new RegExp(`^${source}$`);
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
function shouldRecurseGlob(pattern: string, base: string): boolean {
|
|
1088
|
-
const normalizedPattern = normalizePath(pattern);
|
|
1089
|
-
const normalizedBase = normalizePath(base);
|
|
1090
|
-
const rest =
|
|
1091
|
-
normalizedBase === "."
|
|
1092
|
-
? normalizedPattern
|
|
1093
|
-
: normalizedPattern.slice(normalizedBase.length).replace(/^\/+/, "");
|
|
1094
|
-
return rest.includes("/");
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
function collectVueFilesFromGlob(pattern: string): string[] {
|
|
1098
|
-
const basePattern = globBase(pattern);
|
|
1099
|
-
const base = path.resolve(process.cwd(), basePattern);
|
|
1100
|
-
if (!existsSync(base)) {
|
|
1101
|
-
return [];
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
const isAbsolutePattern = path.isAbsolute(pattern);
|
|
1105
|
-
const normalizedPattern = normalizePath(isAbsolutePattern ? path.resolve(pattern) : pattern);
|
|
1106
|
-
const regex = globToRegExp(normalizedPattern);
|
|
1107
|
-
const candidates = collectVueFilesFromDirectory(base, shouldRecurseGlob(pattern, basePattern));
|
|
1108
|
-
|
|
1109
|
-
return candidates.filter((file) => {
|
|
1110
|
-
const comparable = isAbsolutePattern
|
|
1111
|
-
? normalizePath(file)
|
|
1112
|
-
: normalizePath(path.relative(process.cwd(), file));
|
|
1113
|
-
return regex.test(comparable);
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
function collectVueFiles(patterns: string[]): string[] {
|
|
1118
|
-
const files = new Set<string>();
|
|
1119
|
-
const inputs = patterns.length === 0 ? ["."] : patterns;
|
|
1120
|
-
|
|
1121
|
-
for (const input of inputs) {
|
|
1122
|
-
if (hasGlobSyntax(input)) {
|
|
1123
|
-
for (const file of collectVueFilesFromGlob(input)) {
|
|
1124
|
-
files.add(path.resolve(file));
|
|
1125
|
-
}
|
|
1126
|
-
continue;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
const resolved = path.resolve(process.cwd(), input);
|
|
1130
|
-
if (!existsSync(resolved)) {
|
|
1131
|
-
continue;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
const stats = statSync(resolved);
|
|
1135
|
-
if (stats.isDirectory()) {
|
|
1136
|
-
for (const file of collectVueFilesFromDirectory(resolved, true)) {
|
|
1137
|
-
files.add(path.resolve(file));
|
|
1138
|
-
}
|
|
1139
|
-
} else if (stats.isFile() && isVueFile(resolved)) {
|
|
1140
|
-
files.add(resolved);
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
return Array.from(files).sort();
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
interface BoundedFileBatchOptions {
|
|
1148
|
-
maxFiles: number;
|
|
1149
|
-
maxBytes: number;
|
|
1150
|
-
sizeOf?: (file: string) => number;
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
function statFileSize(file: string): number {
|
|
1154
|
-
try {
|
|
1155
|
-
return statSync(file).size;
|
|
1156
|
-
} catch {
|
|
1157
|
-
return 0;
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
export function createBoundedFileBatches(
|
|
1162
|
-
files: readonly string[],
|
|
1163
|
-
options: BoundedFileBatchOptions,
|
|
1164
|
-
): string[][] {
|
|
1165
|
-
const maxFiles = Math.max(1, Math.floor(options.maxFiles));
|
|
1166
|
-
const maxBytes = Math.max(1, Math.floor(options.maxBytes));
|
|
1167
|
-
const sizeOf = options.sizeOf ?? statFileSize;
|
|
1168
|
-
const batches: string[][] = [];
|
|
1169
|
-
let current: string[] = [];
|
|
1170
|
-
let currentBytes = 0;
|
|
1171
|
-
|
|
1172
|
-
for (const file of files) {
|
|
1173
|
-
const fileBytes = Math.max(0, sizeOf(file));
|
|
1174
|
-
if (current.length > 0 && (current.length >= maxFiles || currentBytes + fileBytes > maxBytes)) {
|
|
1175
|
-
batches.push(current);
|
|
1176
|
-
current = [];
|
|
1177
|
-
currentBytes = 0;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
current.push(file);
|
|
1181
|
-
currentBytes += fileBytes;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
if (current.length > 0) {
|
|
1185
|
-
batches.push(current);
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
return batches;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
function commonSourceDirectory(files: readonly string[]): string {
|
|
1192
|
-
let common = path.dirname(files[0] ?? process.cwd());
|
|
1193
|
-
|
|
1194
|
-
for (let i = 1; i < files.length; i++) {
|
|
1195
|
-
const directory = path.dirname(files[i]!);
|
|
1196
|
-
while (common !== path.dirname(common)) {
|
|
1197
|
-
const relative = path.relative(common, directory);
|
|
1198
|
-
if (relative !== ".." && !relative.startsWith(`..${path.sep}`)) {
|
|
1199
|
-
break;
|
|
1200
|
-
}
|
|
1201
|
-
common = path.dirname(common);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
return common;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
function emitCheckDeclaration(
|
|
1209
|
-
file: string,
|
|
1210
|
-
source: string,
|
|
1211
|
-
sourceRoot: string,
|
|
1212
|
-
native: NativeBinding,
|
|
1213
|
-
options: CheckOptions,
|
|
1214
|
-
): EmittedDeclaration {
|
|
1215
|
-
const outDir = path.resolve(process.cwd(), options.declarationDir ?? "dist/types");
|
|
1216
|
-
const relative = normalizePath(path.relative(sourceRoot, file));
|
|
1217
|
-
const outputPath = path.join(outDir, `${relative}.d.ts`);
|
|
1218
|
-
mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
1219
|
-
|
|
1220
|
-
const declaration = native.generateDeclaration!(source, { filename: file });
|
|
1221
|
-
writeFileSync(outputPath, declaration.code);
|
|
1222
|
-
|
|
1223
|
-
return {
|
|
1224
|
-
file: displayPath(outputPath),
|
|
1225
|
-
path: outputPath,
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
function lineStarts(source: string): number[] {
|
|
1230
|
-
const starts = [0];
|
|
1231
|
-
for (let i = 0; i < source.length; i++) {
|
|
1232
|
-
if (source.charCodeAt(i) === 10) {
|
|
1233
|
-
starts.push(i + 1);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
return starts;
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
function offsetToLineColumn(starts: number[], offset: number): { line: number; column: number } {
|
|
1240
|
-
let low = 0;
|
|
1241
|
-
let high = starts.length - 1;
|
|
1242
|
-
while (low <= high) {
|
|
1243
|
-
const mid = Math.floor((low + high) / 2);
|
|
1244
|
-
if (starts[mid] <= offset) {
|
|
1245
|
-
low = mid + 1;
|
|
1246
|
-
} else {
|
|
1247
|
-
high = mid - 1;
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
const lineIndex = Math.max(0, high);
|
|
1252
|
-
return {
|
|
1253
|
-
line: lineIndex + 1,
|
|
1254
|
-
column: offset - starts[lineIndex] + 1,
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
function toNativeTypeCheckOptions(file: string, options: CheckOptions): NativeTypeCheckOptions {
|
|
1259
|
-
return {
|
|
1260
|
-
filename: file,
|
|
1261
|
-
strict: options.strict,
|
|
1262
|
-
includeVirtualTs: options.includeVirtualTs,
|
|
1263
|
-
include_virtual_ts: options.includeVirtualTs,
|
|
1264
|
-
checkProps: options.checkProps,
|
|
1265
|
-
check_props: options.checkProps,
|
|
1266
|
-
checkEmits: options.checkEmits,
|
|
1267
|
-
check_emits: options.checkEmits,
|
|
1268
|
-
checkTemplateBindings: options.checkTemplateBindings,
|
|
1269
|
-
check_template_bindings: options.checkTemplateBindings,
|
|
1270
|
-
checkReactivity: options.checkReactivity,
|
|
1271
|
-
check_reactivity: options.checkReactivity,
|
|
1272
|
-
checkSetupContext: options.checkSetupContext,
|
|
1273
|
-
check_setup_context: options.checkSetupContext,
|
|
1274
|
-
checkInvalidExports: options.checkInvalidExports,
|
|
1275
|
-
check_invalid_exports: options.checkInvalidExports,
|
|
1276
|
-
checkFallthroughAttrs: options.checkFallthroughAttrs,
|
|
1277
|
-
check_fallthrough_attrs: options.checkFallthroughAttrs,
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
function renderCheckFileText(
|
|
1282
|
-
file: string,
|
|
1283
|
-
source: string,
|
|
1284
|
-
result: TypeCheckResult,
|
|
1285
|
-
options: CheckOptions,
|
|
1286
|
-
): void {
|
|
1287
|
-
if (options.includeVirtualTs && result.virtualTs) {
|
|
1288
|
-
process.stderr.write(
|
|
1289
|
-
`\n=== ${displayPath(file)} ===\n${sanitizeTerminalText(result.virtualTs)}\n`,
|
|
1290
|
-
);
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
if (options.quiet || result.diagnostics.length === 0) {
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
const starts = lineStarts(source);
|
|
1298
|
-
process.stdout.write(`\n\x1b[4m${displayPath(file)}\x1b[0m\n`);
|
|
1299
|
-
for (const diagnostic of result.diagnostics) {
|
|
1300
|
-
const color = diagnostic.severity === "error" ? "\x1b[31m" : "\x1b[33m";
|
|
1301
|
-
const location = offsetToLineColumn(starts, diagnostic.start);
|
|
1302
|
-
const code = diagnostic.code ? ` [${sanitizeTerminalText(diagnostic.code)}]` : "";
|
|
1303
|
-
process.stdout.write(
|
|
1304
|
-
` ${color}${diagnostic.severity}:${location.line}:${location.column}\x1b[0m${code} ${sanitizeTerminalText(diagnostic.message)}\n`,
|
|
1305
|
-
);
|
|
1306
|
-
if (diagnostic.help) {
|
|
1307
|
-
process.stdout.write(` help: ${sanitizeTerminalText(diagnostic.help)}\n`);
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
function renderCheckSummary(
|
|
1313
|
-
totalErrors: number,
|
|
1314
|
-
totalWarnings: number,
|
|
1315
|
-
fileCount: number,
|
|
1316
|
-
timeMs: number,
|
|
1317
|
-
declarations: readonly EmittedDeclaration[],
|
|
1318
|
-
): void {
|
|
1319
|
-
const status = totalErrors > 0 ? "\x1b[31mERR\x1b[0m" : "\x1b[32mOK\x1b[0m";
|
|
1320
|
-
process.stdout.write(
|
|
1321
|
-
`\n${status} Type checked ${fileCount} Vue files in ${timeMs.toFixed(2)}ms\n`,
|
|
1322
|
-
);
|
|
1323
|
-
if (totalErrors > 0) {
|
|
1324
|
-
process.stdout.write(` \x1b[31m${totalErrors} error(s)\x1b[0m\n`);
|
|
1325
|
-
} else {
|
|
1326
|
-
process.stdout.write(" \x1b[32mNo type errors found!\x1b[0m\n");
|
|
1327
|
-
}
|
|
1328
|
-
if (totalWarnings > 0) {
|
|
1329
|
-
process.stdout.write(` \x1b[33m${totalWarnings} warning(s)\x1b[0m\n`);
|
|
1330
|
-
}
|
|
1331
|
-
if (declarations.length > 0) {
|
|
1332
|
-
process.stdout.write(` \x1b[32mEmitted ${declarations.length} declaration file(s)\x1b[0m\n`);
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
function indentJson(value: unknown, spaces: number): string {
|
|
1337
|
-
const padding = " ".repeat(spaces);
|
|
1338
|
-
return JSON.stringify(value, null, 2)
|
|
1339
|
-
.split("\n")
|
|
1340
|
-
.map((line) => `${padding}${line}`)
|
|
1341
|
-
.join("\n");
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
function writeCheckJsonFile(index: number, file: string, result: TypeCheckResult): void {
|
|
1345
|
-
if (index > 0) {
|
|
1346
|
-
process.stdout.write(",\n");
|
|
1347
|
-
}
|
|
1348
|
-
process.stdout.write(
|
|
1349
|
-
indentJson(
|
|
1350
|
-
{
|
|
1351
|
-
file: displayPath(file),
|
|
1352
|
-
diagnostics: result.diagnostics,
|
|
1353
|
-
virtualTs: result.virtualTs,
|
|
1354
|
-
},
|
|
1355
|
-
4,
|
|
1356
|
-
),
|
|
1357
|
-
);
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
function writeCheckJsonEnd(
|
|
1361
|
-
totalErrors: number,
|
|
1362
|
-
totalWarnings: number,
|
|
1363
|
-
fileCount: number,
|
|
1364
|
-
declarations: readonly EmittedDeclaration[],
|
|
1365
|
-
): void {
|
|
1366
|
-
process.stdout.write("\n ],\n");
|
|
1367
|
-
process.stdout.write(` "errorCount": ${totalErrors},\n`);
|
|
1368
|
-
process.stdout.write(` "warningCount": ${totalWarnings},\n`);
|
|
1369
|
-
process.stdout.write(` "fileCount": ${fileCount},\n`);
|
|
1370
|
-
process.stdout.write(
|
|
1371
|
-
` "declarations": ${indentJson(
|
|
1372
|
-
declarations.map(({ file }) => file),
|
|
1373
|
-
2,
|
|
1374
|
-
).trimStart()}\n`,
|
|
1375
|
-
);
|
|
1376
|
-
process.stdout.write("}\n");
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
async function runCheck(args: string[]): Promise<void> {
|
|
1380
|
-
const { patterns, options, sharedConfig } = parseCheckCommand(args);
|
|
1381
|
-
if (options.help) {
|
|
1382
|
-
printCheckUsage();
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
const config = await loadConfig(process.cwd(), {
|
|
1387
|
-
mode: sharedConfig.configMode,
|
|
1388
|
-
configFile: sharedConfig.configFile,
|
|
1389
|
-
env: {
|
|
1390
|
-
mode: process.env.NODE_ENV ?? "development",
|
|
1391
|
-
command: "check",
|
|
1392
|
-
},
|
|
1393
|
-
});
|
|
1394
|
-
|
|
1395
|
-
if (sharedConfig.configFile && !config) {
|
|
1396
|
-
throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
if (config?.typeChecker?.enabled === false) {
|
|
1400
|
-
process.stderr.write(
|
|
1401
|
-
"[vize] Skipping check because typeChecker.enabled is false in vize.config.\n",
|
|
1402
|
-
);
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
options.strict ??= config?.typeChecker?.strict;
|
|
1407
|
-
options.checkProps ??= config?.typeChecker?.checkProps;
|
|
1408
|
-
options.checkEmits ??= config?.typeChecker?.checkEmits;
|
|
1409
|
-
options.checkTemplateBindings ??= config?.typeChecker?.checkTemplateBindings;
|
|
1410
|
-
|
|
1411
|
-
const files = collectVueFiles(patterns);
|
|
1412
|
-
if (files.length === 0) {
|
|
1413
|
-
process.stderr.write(
|
|
1414
|
-
`No Vue files found matching inputs: ${sanitizeTerminalText(JSON.stringify(patterns))}\n`,
|
|
1415
|
-
);
|
|
1416
|
-
return;
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
const native = loadNative("check");
|
|
1420
|
-
if (options.declaration && typeof native.generateDeclaration !== "function") {
|
|
1421
|
-
throw new Error("The loaded native binding does not support declaration generation.");
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
const sourceRoot = options.declaration ? commonSourceDirectory(files) : "";
|
|
1425
|
-
const checkStartedAt = performance.now();
|
|
1426
|
-
const retainSource = shouldRetainCheckSource(options);
|
|
1427
|
-
const declarations: EmittedDeclaration[] = [];
|
|
1428
|
-
let totalErrors = 0;
|
|
1429
|
-
let totalWarnings = 0;
|
|
1430
|
-
let checkedCount = 0;
|
|
1431
|
-
|
|
1432
|
-
if (options.format === "json") {
|
|
1433
|
-
process.stdout.write('{\n "files": [\n');
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
for (const file of files) {
|
|
1437
|
-
const source = readFileSync(file, "utf8");
|
|
1438
|
-
const result = native.typeCheck(source, toNativeTypeCheckOptions(file, options));
|
|
1439
|
-
totalErrors += result.errorCount;
|
|
1440
|
-
totalWarnings += result.warningCount;
|
|
1441
|
-
checkedCount++;
|
|
1442
|
-
|
|
1443
|
-
if (options.declaration) {
|
|
1444
|
-
declarations.push(emitCheckDeclaration(file, source, sourceRoot, native, options));
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
if (options.format === "json") {
|
|
1448
|
-
writeCheckJsonFile(checkedCount - 1, file, result);
|
|
1449
|
-
} else {
|
|
1450
|
-
renderCheckFileText(file, retainSource ? source : "", result, options);
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
const timeMs = performance.now() - checkStartedAt;
|
|
1455
|
-
|
|
1456
|
-
if (options.format === "json") {
|
|
1457
|
-
writeCheckJsonEnd(totalErrors, totalWarnings, checkedCount, declarations);
|
|
1458
|
-
} else {
|
|
1459
|
-
renderCheckSummary(totalErrors, totalWarnings, checkedCount, timeMs, declarations);
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
if (totalErrors > 0) {
|
|
1463
|
-
process.exit(1);
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
if (options.maxWarnings !== undefined && totalWarnings > options.maxWarnings) {
|
|
1467
|
-
process.stderr.write(`\nToo many warnings (${totalWarnings} > max ${options.maxWarnings})\n`);
|
|
1468
|
-
process.exit(1);
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
async function runLint(args: string[]): Promise<void> {
|
|
1473
|
-
const { patterns, options, sharedConfig } = parseLintCommand(args);
|
|
1474
|
-
const config = await loadConfig(process.cwd(), {
|
|
1475
|
-
mode: sharedConfig.configMode,
|
|
1476
|
-
configFile: sharedConfig.configFile,
|
|
1477
|
-
env: {
|
|
1478
|
-
mode: process.env.NODE_ENV ?? "development",
|
|
1479
|
-
command: "lint",
|
|
1480
|
-
},
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
|
-
if (sharedConfig.configFile && !config) {
|
|
1484
|
-
throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
if (config?.linter?.enabled === false) {
|
|
1488
|
-
process.stderr.write("[vize] Skipping lint because linter.enabled is false in vize.config.\n");
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
options.preset ??= config?.linter?.preset;
|
|
1493
|
-
|
|
1494
|
-
if (patterns.length === 0) {
|
|
1495
|
-
patterns.push(".");
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
const native = loadNative("lint");
|
|
1499
|
-
const result = native.lint(patterns, {
|
|
1500
|
-
format: options.format,
|
|
1501
|
-
max_warnings: options.maxWarnings,
|
|
1502
|
-
quiet: options.quiet,
|
|
1503
|
-
fix: options.fix,
|
|
1504
|
-
help_level: options.helpLevel,
|
|
1505
|
-
preset: options.preset,
|
|
1506
|
-
});
|
|
1507
|
-
|
|
1508
|
-
if (result.output) {
|
|
1509
|
-
process.stdout.write(sanitizeTerminalText(result.output));
|
|
1510
|
-
if (!result.output.endsWith("\n")) {
|
|
1511
|
-
process.stdout.write("\n");
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
if (options.fix) {
|
|
1516
|
-
process.stderr.write("\nNote: --fix is not yet implemented\n");
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
if (result.errorCount > 0) {
|
|
1520
|
-
process.exit(1);
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
if (options.maxWarnings !== undefined && result.warningCount > options.maxWarnings) {
|
|
1524
|
-
process.stderr.write(
|
|
1525
|
-
`\nToo many warnings (${result.warningCount} > max ${options.maxWarnings})\n`,
|
|
1526
|
-
);
|
|
1527
|
-
process.exit(1);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
// ============================================================================
|
|
1532
|
-
// Upgrade command
|
|
1533
|
-
// ============================================================================
|
|
1534
|
-
|
|
1535
|
-
type PackageManager = "bun" | "npm" | "pnpm" | "vp" | "yarn";
|
|
1536
|
-
|
|
1537
|
-
interface UpgradeOptions {
|
|
1538
|
-
packageManager?: PackageManager;
|
|
1539
|
-
global?: boolean;
|
|
1540
|
-
dryRun?: boolean;
|
|
1541
|
-
help?: boolean;
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
function parseUpgradeCommand(args: string[]): UpgradeOptions {
|
|
1545
|
-
const options: UpgradeOptions = {};
|
|
1546
|
-
|
|
1547
|
-
for (let i = 0; i < args.length; i++) {
|
|
1548
|
-
const arg = args[i];
|
|
1549
|
-
if (arg === "--package-manager") {
|
|
1550
|
-
const packageManager = args[++i];
|
|
1551
|
-
if (
|
|
1552
|
-
packageManager === "bun" ||
|
|
1553
|
-
packageManager === "npm" ||
|
|
1554
|
-
packageManager === "pnpm" ||
|
|
1555
|
-
packageManager === "vp" ||
|
|
1556
|
-
packageManager === "yarn"
|
|
1557
|
-
) {
|
|
1558
|
-
options.packageManager = packageManager;
|
|
1559
|
-
}
|
|
1560
|
-
} else if (arg === "--global" || arg === "-g") {
|
|
1561
|
-
options.global = true;
|
|
1562
|
-
} else if (arg === "--dry-run") {
|
|
1563
|
-
options.dryRun = true;
|
|
1564
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
1565
|
-
options.help = true;
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
return options;
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
function readCwdPackageJson(): {
|
|
1573
|
-
packageManager?: string;
|
|
1574
|
-
dependencies?: Record<string, string>;
|
|
1575
|
-
devDependencies?: Record<string, string>;
|
|
1576
|
-
} | null {
|
|
1577
|
-
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
1578
|
-
if (!existsSync(packageJsonPath)) {
|
|
1579
|
-
return null;
|
|
1580
|
-
}
|
|
1581
|
-
return JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
function detectPackageManager(explicit?: PackageManager): PackageManager {
|
|
1585
|
-
if (explicit) {
|
|
1586
|
-
return explicit;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
1590
|
-
if (userAgent.startsWith("pnpm")) {
|
|
1591
|
-
return "pnpm";
|
|
1592
|
-
}
|
|
1593
|
-
if (userAgent.startsWith("yarn")) {
|
|
1594
|
-
return "yarn";
|
|
1595
|
-
}
|
|
1596
|
-
if (userAgent.startsWith("bun")) {
|
|
1597
|
-
return "bun";
|
|
1598
|
-
}
|
|
1599
|
-
if (userAgent.startsWith("npm")) {
|
|
1600
|
-
return "npm";
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
const packageManager = readCwdPackageJson()?.packageManager;
|
|
1604
|
-
if (packageManager?.startsWith("pnpm")) {
|
|
1605
|
-
return "pnpm";
|
|
1606
|
-
}
|
|
1607
|
-
if (packageManager?.startsWith("yarn")) {
|
|
1608
|
-
return "yarn";
|
|
1609
|
-
}
|
|
1610
|
-
if (packageManager?.startsWith("bun")) {
|
|
1611
|
-
return "bun";
|
|
1612
|
-
}
|
|
1613
|
-
return "npm";
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
function buildUpgradeCommand(
|
|
1617
|
-
packageManager: PackageManager,
|
|
1618
|
-
options: UpgradeOptions,
|
|
1619
|
-
): { command: string; args: string[] } {
|
|
1620
|
-
const packageJson = readCwdPackageJson();
|
|
1621
|
-
const saveDev = !packageJson?.dependencies?.vize;
|
|
1622
|
-
const packageSpec = "vize@latest";
|
|
1623
|
-
|
|
1624
|
-
if (packageManager === "vp") {
|
|
1625
|
-
return {
|
|
1626
|
-
command: "vp",
|
|
1627
|
-
args: ["install", ...(options.global ? ["-g"] : saveDev ? ["-D"] : []), packageSpec],
|
|
1628
|
-
};
|
|
1629
|
-
}
|
|
1630
|
-
if (packageManager === "pnpm") {
|
|
1631
|
-
return {
|
|
1632
|
-
command: "pnpm",
|
|
1633
|
-
args: ["add", ...(options.global ? ["-g"] : saveDev ? ["-D"] : []), packageSpec],
|
|
1634
|
-
};
|
|
1635
|
-
}
|
|
1636
|
-
if (packageManager === "yarn") {
|
|
1637
|
-
return {
|
|
1638
|
-
command: "yarn",
|
|
1639
|
-
args: options.global
|
|
1640
|
-
? ["global", "add", packageSpec]
|
|
1641
|
-
: ["add", ...(saveDev ? ["-D"] : []), packageSpec],
|
|
1642
|
-
};
|
|
1643
|
-
}
|
|
1644
|
-
if (packageManager === "bun") {
|
|
1645
|
-
return {
|
|
1646
|
-
command: "bun",
|
|
1647
|
-
args: ["add", ...(options.global ? ["-g"] : saveDev ? ["-d"] : []), packageSpec],
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
return {
|
|
1651
|
-
command: "npm",
|
|
1652
|
-
args: ["install", ...(options.global ? ["-g"] : saveDev ? ["-D"] : []), packageSpec],
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
function runUpgrade(args: string[]): void {
|
|
1657
|
-
const options = parseUpgradeCommand(args);
|
|
1658
|
-
if (options.help) {
|
|
1659
|
-
printUpgradeUsage();
|
|
1660
|
-
return;
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
const packageManager = detectPackageManager(options.packageManager);
|
|
1664
|
-
const command = buildUpgradeCommand(packageManager, options);
|
|
1665
|
-
|
|
1666
|
-
if (options.dryRun) {
|
|
1667
|
-
process.stdout.write(`${command.command} ${command.args.join(" ")}\n`);
|
|
1668
|
-
return;
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
const result = spawnSync(command.command, command.args, {
|
|
1672
|
-
stdio: "inherit",
|
|
1673
|
-
cwd: process.cwd(),
|
|
1674
|
-
env: process.env,
|
|
1675
|
-
});
|
|
1676
|
-
|
|
1677
|
-
if (result.error) {
|
|
1678
|
-
throw result.error;
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
process.exit(result.status ?? 1);
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
// ============================================================================
|
|
1685
|
-
// Ready command
|
|
1686
|
-
// ============================================================================
|
|
1687
|
-
|
|
1688
|
-
interface ReadyOptions {
|
|
1689
|
-
output: string;
|
|
1690
|
-
ssr?: boolean;
|
|
1691
|
-
scriptExt: "preserve" | "downcompile";
|
|
1692
|
-
help?: boolean;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
interface ParsedReadyCommand {
|
|
1696
|
-
patterns: string[];
|
|
1697
|
-
options: ReadyOptions;
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
|
-
function parseReadyCommand(args: string[]): ParsedReadyCommand {
|
|
1701
|
-
const patterns: string[] = [];
|
|
1702
|
-
const options: ReadyOptions = {
|
|
1703
|
-
output: "./dist",
|
|
1704
|
-
scriptExt: "downcompile",
|
|
1705
|
-
};
|
|
1706
|
-
|
|
1707
|
-
for (let i = 0; i < args.length; i++) {
|
|
1708
|
-
const arg = args[i];
|
|
1709
|
-
if (arg === "--output" || arg === "-o") {
|
|
1710
|
-
options.output = args[++i] ?? options.output;
|
|
1711
|
-
} else if (arg === "--ssr") {
|
|
1712
|
-
options.ssr = true;
|
|
1713
|
-
} else if (arg === "--script-ext") {
|
|
1714
|
-
const scriptExt = args[++i];
|
|
1715
|
-
if (scriptExt === "preserve" || scriptExt === "downcompile") {
|
|
1716
|
-
options.scriptExt = scriptExt;
|
|
1717
|
-
}
|
|
1718
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
1719
|
-
options.help = true;
|
|
1720
|
-
} else if (!arg.startsWith("-")) {
|
|
1721
|
-
patterns.push(arg);
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
return { patterns, options };
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
async function runReady(args: string[]): Promise<void> {
|
|
1729
|
-
const { patterns, options } = parseReadyCommand(args);
|
|
1730
|
-
if (options.help) {
|
|
1731
|
-
printReadyUsage();
|
|
1732
|
-
return;
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
process.stderr.write("vize ready: fmt\n");
|
|
1736
|
-
await runFmt(["--write", ...patterns]);
|
|
1737
|
-
|
|
1738
|
-
process.stderr.write("vize ready: lint\n");
|
|
1739
|
-
await runLint(patterns);
|
|
1740
|
-
|
|
1741
|
-
process.stderr.write("vize ready: check\n");
|
|
1742
|
-
await runCheck(patterns);
|
|
1743
|
-
|
|
1744
|
-
process.stderr.write("vize ready: build\n");
|
|
1745
|
-
await runBuild([
|
|
1746
|
-
"--output",
|
|
1747
|
-
options.output,
|
|
1748
|
-
"--script-ext",
|
|
1749
|
-
options.scriptExt,
|
|
1750
|
-
...(options.ssr ? ["--ssr"] : []),
|
|
1751
|
-
...patterns,
|
|
1752
|
-
]);
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
// ============================================================================
|
|
1756
|
-
// Command router
|
|
1757
|
-
// ============================================================================
|
|
1758
|
-
|
|
1759
|
-
const NAPI_COMMANDS = new Set(["build", "check", "fmt", "lint"]);
|
|
1760
|
-
const JS_COMMANDS = new Set(["musea", "ready", "upgrade"]);
|
|
1761
|
-
|
|
1762
|
-
async function main(): Promise<void> {
|
|
1763
|
-
const args = process.argv.slice(2);
|
|
1764
|
-
const command = args[0];
|
|
1765
|
-
|
|
1766
|
-
if (!command || command === "--help" || command === "-h") {
|
|
1767
|
-
printUsage();
|
|
1768
|
-
process.exit(1);
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
if (NAPI_COMMANDS.has(command)) {
|
|
1772
|
-
const commandArgs = args.slice(1);
|
|
1773
|
-
switch (command) {
|
|
1774
|
-
case "build":
|
|
1775
|
-
await runBuild(commandArgs);
|
|
1776
|
-
break;
|
|
1777
|
-
case "check":
|
|
1778
|
-
await runCheck(commandArgs);
|
|
1779
|
-
break;
|
|
1780
|
-
case "fmt":
|
|
1781
|
-
await runFmt(commandArgs);
|
|
1782
|
-
break;
|
|
1783
|
-
case "lint":
|
|
1784
|
-
await runLint(commandArgs);
|
|
1785
|
-
break;
|
|
1786
|
-
}
|
|
1787
|
-
} else if (JS_COMMANDS.has(command)) {
|
|
1788
|
-
const commandArgs = args.slice(1);
|
|
1789
|
-
switch (command) {
|
|
1790
|
-
case "musea":
|
|
1791
|
-
runMusea(commandArgs);
|
|
1792
|
-
break;
|
|
1793
|
-
case "ready":
|
|
1794
|
-
await runReady(commandArgs);
|
|
1795
|
-
break;
|
|
1796
|
-
case "upgrade":
|
|
1797
|
-
runUpgrade(commandArgs);
|
|
1798
|
-
break;
|
|
1799
|
-
}
|
|
1800
|
-
} else {
|
|
1801
|
-
printUsage();
|
|
1802
|
-
console.error(`Unknown command: ${sanitizeTerminalText(command)}`);
|
|
1803
|
-
console.error(
|
|
1804
|
-
"For commands not yet available via NAPI, install from source: cargo install vize",
|
|
1805
|
-
);
|
|
1806
|
-
process.exit(1);
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
const isTestRuntime =
|
|
1811
|
-
Boolean(import.meta.vitest) || process.env.VITEST === "true" || process.env.NODE_ENV === "test";
|
|
1812
|
-
|
|
1813
|
-
if (!isTestRuntime) {
|
|
1814
|
-
void main().catch((error) => {
|
|
1815
|
-
console.error(sanitizeTerminalText(error instanceof Error ? error.message : String(error)));
|
|
1816
|
-
process.exit(1);
|
|
1817
|
-
});
|
|
1818
|
-
}
|
|
6
|
+
native.runCli(process.argv.slice(2));
|