vize 0.64.0 → 0.66.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/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { loadConfig } from "./config.mjs";
2
2
  import { createRequire } from "node:module";
3
- import { readFileSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
4
  import * as path from "node:path";
5
5
  import { spawnSync } from "node:child_process";
6
6
  import { pathToFileURL } from "node:url";
@@ -37,12 +37,19 @@ function getBindingPackageName() {
37
37
  default: throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
38
38
  }
39
39
  }
40
- function loadNative() {
40
+ const REQUIRED_BINDINGS = {
41
+ build: "compileSfcBatchWithResults",
42
+ check: "typeCheck",
43
+ fmt: "formatSfc",
44
+ lint: "lint"
45
+ };
46
+ function loadNative(command) {
41
47
  const attemptedPackages = getAttemptedPackages();
42
48
  let lastError = null;
49
+ const requiredBinding = REQUIRED_BINDINGS[command];
43
50
  for (const packageName of attemptedPackages) try {
44
51
  const binding = require(packageName);
45
- if (typeof binding.lint !== "function") throw new Error(`${packageName} does not expose the lint binding.`);
52
+ if (typeof binding[requiredBinding] !== "function") throw new Error(`${packageName} does not expose the ${command} binding.`);
46
53
  return binding;
47
54
  } catch (error) {
48
55
  lastError = error;
@@ -71,7 +78,55 @@ function shouldPreferWorkspaceBinding(resolvedPath) {
71
78
  }
72
79
  function printUsage() {
73
80
  console.error("Usage: vize <command> [options]");
74
- console.error("Commands: lint, musea");
81
+ console.error("Commands: build, fmt, check, lint, upgrade, ready, musea");
82
+ }
83
+ function printBuildUsage() {
84
+ console.error("Usage: vize build [options] [files-or-directories]");
85
+ console.error("Options:");
86
+ console.error(" -o, --output <dir> Output directory");
87
+ console.error(" -f, --format <js|json|stats> Output format");
88
+ console.error(" --ssr Enable SSR compilation");
89
+ console.error(" --script-ext <mode> preserve or downcompile");
90
+ console.error(" -j, --threads <number> Worker thread count");
91
+ }
92
+ function printFmtUsage() {
93
+ console.error("Usage: vize fmt [options] [files-or-directories]");
94
+ console.error("Options:");
95
+ console.error(" --check Exit with an error if files need formatting");
96
+ console.error(" -w, --write Write formatted output");
97
+ console.error(" --single-quote Use single quotes");
98
+ console.error(" --print-width <number> Maximum line width");
99
+ console.error(" --tab-width <number> Indentation width");
100
+ console.error(" --use-tabs Indent with tabs");
101
+ console.error(" --no-semi Omit semicolons");
102
+ }
103
+ function printCheckUsage() {
104
+ console.error("Usage: vize check [options] [files-or-directories]");
105
+ console.error("Options:");
106
+ console.error(" -f, --format <text|json> Output format");
107
+ console.error(" -q, --quiet Show summary only");
108
+ console.error(" --strict Enable strict checks");
109
+ console.error(" --show-virtual-ts Print generated Virtual TS");
110
+ console.error(" --max-warnings <number> Fail when warnings exceed the limit");
111
+ console.error(" -c, --config <path> Use a specific vize config file");
112
+ console.error(" --no-config Disable config discovery");
113
+ console.error("");
114
+ console.error("Note: npm `vize check` uses the packaged NAPI checker. Install the Rust CLI for project-backed Corsa diagnostics.");
115
+ }
116
+ function printUpgradeUsage() {
117
+ console.error("Usage: vize upgrade [options]");
118
+ console.error("Options:");
119
+ console.error(" --package-manager <name> npm, pnpm, yarn, bun, or vp");
120
+ console.error(" -g, --global Upgrade the global installation");
121
+ console.error(" --dry-run Print the command without running it");
122
+ }
123
+ function printReadyUsage() {
124
+ console.error("Usage: vize ready [options] [files-or-directories]");
125
+ console.error("Runs: fmt --write -> lint -> check -> build");
126
+ console.error("Options:");
127
+ console.error(" -o, --output <dir> Output directory for build");
128
+ console.error(" --ssr Enable SSR compilation for build");
129
+ console.error(" --script-ext <mode> preserve or downcompile");
75
130
  }
76
131
  function resolvePackageBinaryFromCwd(packageName, binName = packageName) {
77
132
  const packageJsonPath = createRequire(pathToFileURL(path.join(process.cwd(), "package.json")).href).resolve(`${packageName}/package.json`);
@@ -127,6 +182,459 @@ function parseLintCommand(args) {
127
182
  sharedConfig
128
183
  };
129
184
  }
185
+ function parseBuildCommand(args) {
186
+ const patterns = [];
187
+ const options = {
188
+ output: "./dist",
189
+ format: "js",
190
+ scriptExt: "downcompile"
191
+ };
192
+ const sharedConfig = { configMode: "root" };
193
+ for (let i = 0; i < args.length; i++) {
194
+ const arg = args[i];
195
+ if (arg === "--output" || arg === "-o") options.output = args[++i] ?? options.output;
196
+ else if (arg === "--format" || arg === "-f") {
197
+ const format = args[++i];
198
+ if (format === "js" || format === "json" || format === "stats") options.format = format;
199
+ } else if (arg === "--ssr") options.ssr = true;
200
+ else if (arg === "--vapor") options.vapor = true;
201
+ else if (arg === "--custom-renderer") options.customRenderer = true;
202
+ else if (arg === "--script-ext") {
203
+ const scriptExt = args[++i];
204
+ if (scriptExt === "preserve" || scriptExt === "downcompile") options.scriptExt = scriptExt;
205
+ } else if (arg === "--threads" || arg === "-j") options.threads = Number.parseInt(args[++i], 10);
206
+ else if (arg === "--config" || arg === "-c") {
207
+ const configFile = args[++i];
208
+ if (!configFile) throw new Error("Missing path after --config");
209
+ sharedConfig.configFile = configFile;
210
+ } else if (arg === "--no-config") sharedConfig.configMode = "none";
211
+ else if (arg === "--profile" || arg === "--continue-on-error") {} else if (arg === "--help" || arg === "-h") options.help = true;
212
+ else if (!arg.startsWith("-")) patterns.push(arg);
213
+ }
214
+ return {
215
+ patterns,
216
+ options,
217
+ sharedConfig
218
+ };
219
+ }
220
+ function getScriptLang(source) {
221
+ return source.match(/<script\b[^>]*\blang=["']([^"']+)["']/i)?.[1] ?? "js";
222
+ }
223
+ function getOutputExtension(source, scriptExt) {
224
+ if (scriptExt === "downcompile") return "js";
225
+ const lang = getScriptLang(source);
226
+ return lang === "ts" || lang === "tsx" || lang === "jsx" ? lang : "js";
227
+ }
228
+ function outputFileName(file, extension) {
229
+ return path.basename(file).replace(/\.vue$/i, `.${extension}`);
230
+ }
231
+ function toNativeBuildOptions(options) {
232
+ const isTs = options.scriptExt === "preserve";
233
+ return {
234
+ ssr: options.ssr,
235
+ vapor: options.vapor,
236
+ customRenderer: options.customRenderer,
237
+ custom_renderer: options.customRenderer,
238
+ isTs,
239
+ is_ts: isTs,
240
+ threads: options.threads
241
+ };
242
+ }
243
+ async function runBuild(args) {
244
+ const { patterns, options, sharedConfig } = parseBuildCommand(args);
245
+ if (options.help) {
246
+ printBuildUsage();
247
+ return;
248
+ }
249
+ const config = await loadConfig(process.cwd(), {
250
+ mode: sharedConfig.configMode,
251
+ configFile: sharedConfig.configFile,
252
+ env: {
253
+ mode: process.env.NODE_ENV ?? "development",
254
+ command: "build"
255
+ }
256
+ });
257
+ if (sharedConfig.configFile && !config) throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
258
+ options.ssr ??= config?.compiler?.ssr;
259
+ options.vapor ??= config?.compiler?.vapor;
260
+ options.customRenderer ??= config?.compiler?.customRenderer;
261
+ if (config?.compiler?.scriptExt === "ts") options.scriptExt = "preserve";
262
+ else if (config?.compiler?.scriptExt === "js") options.scriptExt = "downcompile";
263
+ const files = collectVueFiles(patterns);
264
+ if (files.length === 0) {
265
+ process.stderr.write(`No Vue files found matching inputs: ${JSON.stringify(patterns)}\n`);
266
+ process.exit(1);
267
+ }
268
+ const inputs = files.map((file) => ({
269
+ path: file,
270
+ source: readFileSync(file, "utf8")
271
+ }));
272
+ const native = loadNative("build");
273
+ const startedAt = performance.now();
274
+ const result = native.compileSfcBatchWithResults(inputs, toNativeBuildOptions(options));
275
+ const timeMs = result.timeMs ?? result.time_ms ?? performance.now() - startedAt;
276
+ const results = [...result.results].sort((left, right) => left.path.localeCompare(right.path));
277
+ if (options.format !== "stats") mkdirSync(options.output, { recursive: true });
278
+ for (const fileResult of results) {
279
+ const source = inputs.find((input) => input.path === fileResult.path)?.source ?? "";
280
+ for (const warning of fileResult.warnings) process.stderr.write(`warning: ${displayPath(fileResult.path)} ${warning}\n`);
281
+ for (const error of fileResult.errors) process.stderr.write(`error: ${displayPath(fileResult.path)} ${error}\n`);
282
+ if (fileResult.errors.length > 0 || options.format === "stats") continue;
283
+ const extension = options.format === "json" ? "json" : getOutputExtension(source, options.scriptExt);
284
+ writeFileSync(path.join(options.output, outputFileName(fileResult.path, extension)), options.format === "json" ? JSON.stringify(fileResult, null, 2) : fileResult.code);
285
+ }
286
+ const failed = result.failedCount ?? result.failed_count ?? results.filter((r) => r.errors.length).length;
287
+ const success = result.successCount ?? result.success_count ?? results.length - failed;
288
+ process.stderr.write(`\x1b[32mOK\x1b[0m Built ${success} Vue file(s) in ${timeMs.toFixed(2)}ms\n`);
289
+ if (failed > 0) {
290
+ process.stderr.write(`\x1b[31mERR\x1b[0m ${failed} file(s) failed\n`);
291
+ process.exit(1);
292
+ }
293
+ }
294
+ function parseFmtCommand(args) {
295
+ const patterns = [];
296
+ const options = {};
297
+ const sharedConfig = { configMode: "root" };
298
+ for (let i = 0; i < args.length; i++) {
299
+ const arg = args[i];
300
+ if (arg === "--check") options.check = true;
301
+ else if (arg === "--write" || arg === "-w") options.write = true;
302
+ else if (arg === "--single-quote") options.singleQuote = true;
303
+ else if (arg === "--print-width") options.printWidth = Number.parseInt(args[++i], 10);
304
+ else if (arg === "--tab-width") options.tabWidth = Number.parseInt(args[++i], 10);
305
+ else if (arg === "--use-tabs") options.useTabs = true;
306
+ else if (arg === "--no-semi") options.semi = false;
307
+ else if (arg === "--sort-attributes") options.sortAttributes = true;
308
+ else if (arg === "--single-attribute-per-line") options.singleAttributePerLine = true;
309
+ else if (arg === "--max-attributes-per-line") options.maxAttributesPerLine = Number.parseInt(args[++i], 10);
310
+ else if (arg === "--normalize-directive-shorthands") options.normalizeDirectiveShorthands = true;
311
+ else if (arg === "--config" || arg === "-c") {
312
+ const configFile = args[++i];
313
+ if (!configFile) throw new Error("Missing path after --config");
314
+ sharedConfig.configFile = configFile;
315
+ } else if (arg === "--no-config") sharedConfig.configMode = "none";
316
+ else if (arg === "--profile") {} else if (arg === "--help" || arg === "-h") options.help = true;
317
+ else if (!arg.startsWith("-")) patterns.push(arg);
318
+ }
319
+ return {
320
+ patterns,
321
+ options,
322
+ sharedConfig
323
+ };
324
+ }
325
+ function toNativeFormatOptions(options) {
326
+ return {
327
+ printWidth: options.printWidth,
328
+ print_width: options.printWidth,
329
+ tabWidth: options.tabWidth,
330
+ tab_width: options.tabWidth,
331
+ useTabs: options.useTabs,
332
+ use_tabs: options.useTabs,
333
+ semi: options.semi,
334
+ singleQuote: options.singleQuote,
335
+ single_quote: options.singleQuote,
336
+ sortAttributes: options.sortAttributes,
337
+ sort_attributes: options.sortAttributes,
338
+ singleAttributePerLine: options.singleAttributePerLine,
339
+ single_attribute_per_line: options.singleAttributePerLine,
340
+ maxAttributesPerLine: options.maxAttributesPerLine,
341
+ max_attributes_per_line: options.maxAttributesPerLine,
342
+ normalizeDirectiveShorthands: options.normalizeDirectiveShorthands,
343
+ normalize_directive_shorthands: options.normalizeDirectiveShorthands
344
+ };
345
+ }
346
+ async function runFmt(args) {
347
+ const { patterns, options, sharedConfig } = parseFmtCommand(args);
348
+ if (options.help) {
349
+ printFmtUsage();
350
+ return;
351
+ }
352
+ const config = await loadConfig(process.cwd(), {
353
+ mode: sharedConfig.configMode,
354
+ configFile: sharedConfig.configFile,
355
+ env: {
356
+ mode: process.env.NODE_ENV ?? "development",
357
+ command: "fmt"
358
+ }
359
+ });
360
+ if (sharedConfig.configFile && !config) throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
361
+ options.printWidth ??= config?.formatter?.printWidth;
362
+ options.tabWidth ??= config?.formatter?.tabWidth;
363
+ options.useTabs ??= config?.formatter?.useTabs;
364
+ options.semi ??= config?.formatter?.semi;
365
+ options.singleQuote ??= config?.formatter?.singleQuote;
366
+ const files = collectVueFiles(patterns);
367
+ if (files.length === 0) {
368
+ process.stderr.write(`No Vue files found matching inputs: ${JSON.stringify(patterns)}\n`);
369
+ return;
370
+ }
371
+ const native = loadNative("fmt");
372
+ let changed = 0;
373
+ let errored = 0;
374
+ for (const file of files) {
375
+ const source = readFileSync(file, "utf8");
376
+ try {
377
+ const result = native.formatSfc(source, toNativeFormatOptions(options));
378
+ if (!result.changed) continue;
379
+ changed++;
380
+ if (options.check) process.stderr.write(`Would reformat: ${displayPath(file)}\n`);
381
+ else if (options.write) {
382
+ writeFileSync(file, result.code);
383
+ process.stderr.write(`Reformatted: ${displayPath(file)}\n`);
384
+ } else process.stderr.write(`Would reformat: ${displayPath(file)}\n`);
385
+ } catch (error) {
386
+ errored++;
387
+ process.stderr.write(`Error formatting ${displayPath(file)}: ${error instanceof Error ? error.message : String(error)}\n`);
388
+ }
389
+ }
390
+ process.stderr.write(`\x1b[32mOK\x1b[0m Formatted ${files.length} Vue file(s), ${changed} changed\n`);
391
+ if (errored > 0 || options.check && changed > 0) process.exit(1);
392
+ }
393
+ function parseCheckCommand(args) {
394
+ const patterns = [];
395
+ const options = {};
396
+ const sharedConfig = { configMode: "root" };
397
+ for (let i = 0; i < args.length; i++) {
398
+ const arg = args[i];
399
+ if (arg === "--format" || arg === "-f") options.format = args[++i];
400
+ else if (arg === "--quiet" || arg === "-q") options.quiet = true;
401
+ else if (arg === "--strict") options.strict = true;
402
+ else if (arg === "--no-strict") options.strict = false;
403
+ else if (arg === "--show-virtual-ts" || arg === "--include-virtual-ts") options.includeVirtualTs = true;
404
+ else if (arg === "--max-warnings") options.maxWarnings = Number.parseInt(args[++i], 10);
405
+ else if (arg === "--no-check-props") options.checkProps = false;
406
+ else if (arg === "--no-check-emits") options.checkEmits = false;
407
+ else if (arg === "--no-check-template-bindings") options.checkTemplateBindings = false;
408
+ else if (arg === "--no-check-reactivity") options.checkReactivity = false;
409
+ else if (arg === "--no-check-setup-context") options.checkSetupContext = false;
410
+ else if (arg === "--no-check-invalid-exports") options.checkInvalidExports = false;
411
+ else if (arg === "--no-check-fallthrough-attrs") options.checkFallthroughAttrs = false;
412
+ else if (arg === "--config" || arg === "-c") {
413
+ const configFile = args[++i];
414
+ if (!configFile) throw new Error("Missing path after --config");
415
+ sharedConfig.configFile = configFile;
416
+ } else if (arg === "--no-config") sharedConfig.configMode = "none";
417
+ else if (arg === "--help" || arg === "-h") options.help = true;
418
+ else if (arg === "--tsconfig" || arg === "--corsa-path" || arg === "--servers") i++;
419
+ else if (arg === "--socket" || arg === "-s" || arg === "--declaration-dir") i++;
420
+ else if (arg === "--profile" || arg === "--declaration") {} else if (!arg.startsWith("-")) patterns.push(arg);
421
+ }
422
+ return {
423
+ patterns,
424
+ options,
425
+ sharedConfig
426
+ };
427
+ }
428
+ function hasGlobSyntax(pattern) {
429
+ return pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
430
+ }
431
+ function normalizePath(filePath) {
432
+ return filePath.split(path.sep).join("/");
433
+ }
434
+ function displayPath(filePath) {
435
+ const relative = path.relative(process.cwd(), filePath);
436
+ if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) return normalizePath(relative);
437
+ return normalizePath(filePath);
438
+ }
439
+ function isVueFile(filePath) {
440
+ return path.extname(filePath) === ".vue";
441
+ }
442
+ function collectVueFilesFromDirectory(directory, recursive) {
443
+ const files = [];
444
+ const entries = readdirSync(directory, { withFileTypes: true });
445
+ for (const entry of entries) {
446
+ const entryPath = path.join(directory, entry.name);
447
+ if (entry.isDirectory()) {
448
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
449
+ if (recursive) files.push(...collectVueFilesFromDirectory(entryPath, true));
450
+ } else if (entry.isFile() && isVueFile(entryPath)) files.push(entryPath);
451
+ }
452
+ return files;
453
+ }
454
+ function globBase(pattern) {
455
+ const normalized = normalizePath(pattern);
456
+ const globIndex = normalized.search(/[*?[]/);
457
+ if (globIndex === -1) return normalized;
458
+ const beforeGlob = normalized.slice(0, globIndex);
459
+ const slashIndex = beforeGlob.lastIndexOf("/");
460
+ if (slashIndex === -1) return ".";
461
+ return beforeGlob.slice(0, slashIndex) || "/";
462
+ }
463
+ function globToRegExp(pattern) {
464
+ const normalized = normalizePath(pattern);
465
+ let source = "";
466
+ for (let i = 0; i < normalized.length; i++) {
467
+ const char = normalized[i];
468
+ const next = normalized[i + 1];
469
+ const afterNext = normalized[i + 2];
470
+ if (char === "*" && next === "*" && afterNext === "/") {
471
+ source += "(?:.*/)?";
472
+ i += 2;
473
+ } else if (char === "*" && next === "*") {
474
+ source += ".*";
475
+ i++;
476
+ } else if (char === "*") source += "[^/]*";
477
+ else if (char === "?") source += "[^/]";
478
+ else if ("\\^$+?.()|{}[]".includes(char)) source += `\\${char}`;
479
+ else source += char;
480
+ }
481
+ return new RegExp(`^${source}$`);
482
+ }
483
+ function shouldRecurseGlob(pattern, base) {
484
+ const normalizedPattern = normalizePath(pattern);
485
+ const normalizedBase = normalizePath(base);
486
+ return (normalizedBase === "." ? normalizedPattern : normalizedPattern.slice(normalizedBase.length).replace(/^\/+/, "")).includes("/");
487
+ }
488
+ function collectVueFilesFromGlob(pattern) {
489
+ const basePattern = globBase(pattern);
490
+ const base = path.resolve(process.cwd(), basePattern);
491
+ if (!existsSync(base)) return [];
492
+ const isAbsolutePattern = path.isAbsolute(pattern);
493
+ const regex = globToRegExp(normalizePath(isAbsolutePattern ? path.resolve(pattern) : pattern));
494
+ return collectVueFilesFromDirectory(base, shouldRecurseGlob(pattern, basePattern)).filter((file) => {
495
+ const comparable = isAbsolutePattern ? normalizePath(file) : normalizePath(path.relative(process.cwd(), file));
496
+ return regex.test(comparable);
497
+ });
498
+ }
499
+ function collectVueFiles(patterns) {
500
+ const files = /* @__PURE__ */ new Set();
501
+ const inputs = patterns.length === 0 ? ["."] : patterns;
502
+ for (const input of inputs) {
503
+ if (hasGlobSyntax(input)) {
504
+ for (const file of collectVueFilesFromGlob(input)) files.add(path.resolve(file));
505
+ continue;
506
+ }
507
+ const resolved = path.resolve(process.cwd(), input);
508
+ if (!existsSync(resolved)) continue;
509
+ const stats = statSync(resolved);
510
+ if (stats.isDirectory()) for (const file of collectVueFilesFromDirectory(resolved, true)) files.add(path.resolve(file));
511
+ else if (stats.isFile() && isVueFile(resolved)) files.add(resolved);
512
+ }
513
+ return Array.from(files).sort();
514
+ }
515
+ function lineStarts(source) {
516
+ const starts = [0];
517
+ for (let i = 0; i < source.length; i++) if (source.charCodeAt(i) === 10) starts.push(i + 1);
518
+ return starts;
519
+ }
520
+ function offsetToLineColumn(starts, offset) {
521
+ let low = 0;
522
+ let high = starts.length - 1;
523
+ while (low <= high) {
524
+ const mid = Math.floor((low + high) / 2);
525
+ if (starts[mid] <= offset) low = mid + 1;
526
+ else high = mid - 1;
527
+ }
528
+ const lineIndex = Math.max(0, high);
529
+ return {
530
+ line: lineIndex + 1,
531
+ column: offset - starts[lineIndex] + 1
532
+ };
533
+ }
534
+ function toNativeTypeCheckOptions(file, options) {
535
+ return {
536
+ filename: file,
537
+ strict: options.strict,
538
+ includeVirtualTs: options.includeVirtualTs,
539
+ include_virtual_ts: options.includeVirtualTs,
540
+ checkProps: options.checkProps,
541
+ check_props: options.checkProps,
542
+ checkEmits: options.checkEmits,
543
+ check_emits: options.checkEmits,
544
+ checkTemplateBindings: options.checkTemplateBindings,
545
+ check_template_bindings: options.checkTemplateBindings,
546
+ checkReactivity: options.checkReactivity,
547
+ check_reactivity: options.checkReactivity,
548
+ checkSetupContext: options.checkSetupContext,
549
+ check_setup_context: options.checkSetupContext,
550
+ checkInvalidExports: options.checkInvalidExports,
551
+ check_invalid_exports: options.checkInvalidExports,
552
+ checkFallthroughAttrs: options.checkFallthroughAttrs,
553
+ check_fallthrough_attrs: options.checkFallthroughAttrs
554
+ };
555
+ }
556
+ function renderCheckText(results, options, timeMs) {
557
+ let totalErrors = 0;
558
+ let totalWarnings = 0;
559
+ for (const { file, source, result } of results) {
560
+ totalErrors += result.errorCount;
561
+ totalWarnings += result.warningCount;
562
+ if (options.includeVirtualTs && result.virtualTs) process.stderr.write(`\n=== ${displayPath(file)} ===\n${result.virtualTs}\n`);
563
+ if (options.quiet || result.diagnostics.length === 0) continue;
564
+ const starts = lineStarts(source);
565
+ process.stdout.write(`\n\x1b[4m${displayPath(file)}\x1b[0m\n`);
566
+ for (const diagnostic of result.diagnostics) {
567
+ const color = diagnostic.severity === "error" ? "\x1B[31m" : "\x1B[33m";
568
+ const location = offsetToLineColumn(starts, diagnostic.start);
569
+ const code = diagnostic.code ? ` [${diagnostic.code}]` : "";
570
+ process.stdout.write(` ${color}${diagnostic.severity}:${location.line}:${location.column}\x1b[0m${code} ${diagnostic.message}\n`);
571
+ if (diagnostic.help) process.stdout.write(` help: ${diagnostic.help}\n`);
572
+ }
573
+ }
574
+ const status = totalErrors > 0 ? "\x1B[31mERR\x1B[0m" : "\x1B[32mOK\x1B[0m";
575
+ process.stdout.write(`\n${status} Type checked ${results.length} Vue files in ${timeMs.toFixed(2)}ms\n`);
576
+ if (totalErrors > 0) process.stdout.write(` \x1b[31m${totalErrors} error(s)\x1b[0m\n`);
577
+ else process.stdout.write(" \x1B[32mNo type errors found!\x1B[0m\n");
578
+ if (totalWarnings > 0) process.stdout.write(` \x1b[33m${totalWarnings} warning(s)\x1b[0m\n`);
579
+ }
580
+ async function runCheck(args) {
581
+ const { patterns, options, sharedConfig } = parseCheckCommand(args);
582
+ if (options.help) {
583
+ printCheckUsage();
584
+ return;
585
+ }
586
+ const config = await loadConfig(process.cwd(), {
587
+ mode: sharedConfig.configMode,
588
+ configFile: sharedConfig.configFile,
589
+ env: {
590
+ mode: process.env.NODE_ENV ?? "development",
591
+ command: "check"
592
+ }
593
+ });
594
+ if (sharedConfig.configFile && !config) throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
595
+ if (config?.typeChecker?.enabled === false) {
596
+ process.stderr.write("[vize] Skipping check because typeChecker.enabled is false in vize.config.\n");
597
+ return;
598
+ }
599
+ options.strict ??= config?.typeChecker?.strict;
600
+ options.checkProps ??= config?.typeChecker?.checkProps;
601
+ options.checkEmits ??= config?.typeChecker?.checkEmits;
602
+ options.checkTemplateBindings ??= config?.typeChecker?.checkTemplateBindings;
603
+ const files = collectVueFiles(patterns);
604
+ if (files.length === 0) {
605
+ process.stderr.write(`No Vue files found matching inputs: ${JSON.stringify(patterns)}\n`);
606
+ return;
607
+ }
608
+ const native = loadNative("check");
609
+ const start = performance.now();
610
+ const results = files.map((file) => {
611
+ const source = readFileSync(file, "utf8");
612
+ return {
613
+ file,
614
+ source,
615
+ result: native.typeCheck(source, toNativeTypeCheckOptions(file, options))
616
+ };
617
+ });
618
+ const timeMs = performance.now() - start;
619
+ const totalErrors = results.reduce((sum, { result }) => sum + result.errorCount, 0);
620
+ const totalWarnings = results.reduce((sum, { result }) => sum + result.warningCount, 0);
621
+ if (options.format === "json") process.stdout.write(`${JSON.stringify({
622
+ files: results.map(({ file, result }) => ({
623
+ file: displayPath(file),
624
+ diagnostics: result.diagnostics,
625
+ virtualTs: result.virtualTs
626
+ })),
627
+ errorCount: totalErrors,
628
+ warningCount: totalWarnings,
629
+ fileCount: results.length
630
+ }, null, 2)}\n`);
631
+ else renderCheckText(results, options, timeMs);
632
+ if (totalErrors > 0) process.exit(1);
633
+ if (options.maxWarnings !== void 0 && totalWarnings > options.maxWarnings) {
634
+ process.stderr.write(`\nToo many warnings (${totalWarnings} > max ${options.maxWarnings})\n`);
635
+ process.exit(1);
636
+ }
637
+ }
130
638
  async function runLint(args) {
131
639
  const { patterns, options, sharedConfig } = parseLintCommand(args);
132
640
  const config = await loadConfig(process.cwd(), {
@@ -144,7 +652,7 @@ async function runLint(args) {
144
652
  }
145
653
  options.preset ??= config?.linter?.preset;
146
654
  if (patterns.length === 0) patterns.push(".");
147
- const result = loadNative().lint(patterns, {
655
+ const result = loadNative("lint").lint(patterns, {
148
656
  format: options.format,
149
657
  max_warnings: options.maxWarnings,
150
658
  quiet: options.quiet,
@@ -163,8 +671,158 @@ async function runLint(args) {
163
671
  process.exit(1);
164
672
  }
165
673
  }
166
- const NAPI_COMMANDS = new Set(["lint"]);
167
- const JS_COMMANDS = new Set(["musea"]);
674
+ function parseUpgradeCommand(args) {
675
+ const options = {};
676
+ for (let i = 0; i < args.length; i++) {
677
+ const arg = args[i];
678
+ if (arg === "--package-manager") {
679
+ const packageManager = args[++i];
680
+ if (packageManager === "bun" || packageManager === "npm" || packageManager === "pnpm" || packageManager === "vp" || packageManager === "yarn") options.packageManager = packageManager;
681
+ } else if (arg === "--global" || arg === "-g") options.global = true;
682
+ else if (arg === "--dry-run") options.dryRun = true;
683
+ else if (arg === "--help" || arg === "-h") options.help = true;
684
+ }
685
+ return options;
686
+ }
687
+ function readCwdPackageJson() {
688
+ const packageJsonPath = path.join(process.cwd(), "package.json");
689
+ if (!existsSync(packageJsonPath)) return null;
690
+ return JSON.parse(readFileSync(packageJsonPath, "utf8"));
691
+ }
692
+ function detectPackageManager(explicit) {
693
+ if (explicit) return explicit;
694
+ const userAgent = process.env.npm_config_user_agent ?? "";
695
+ if (userAgent.startsWith("pnpm")) return "pnpm";
696
+ if (userAgent.startsWith("yarn")) return "yarn";
697
+ if (userAgent.startsWith("bun")) return "bun";
698
+ if (userAgent.startsWith("npm")) return "npm";
699
+ const packageManager = readCwdPackageJson()?.packageManager;
700
+ if (packageManager?.startsWith("pnpm")) return "pnpm";
701
+ if (packageManager?.startsWith("yarn")) return "yarn";
702
+ if (packageManager?.startsWith("bun")) return "bun";
703
+ return "npm";
704
+ }
705
+ function buildUpgradeCommand(packageManager, options) {
706
+ const saveDev = !readCwdPackageJson()?.dependencies?.vize;
707
+ const packageSpec = "vize@latest";
708
+ if (packageManager === "vp") return {
709
+ command: "vp",
710
+ args: [
711
+ "install",
712
+ ...options.global ? ["-g"] : saveDev ? ["-D"] : [],
713
+ packageSpec
714
+ ]
715
+ };
716
+ if (packageManager === "pnpm") return {
717
+ command: "pnpm",
718
+ args: [
719
+ "add",
720
+ ...options.global ? ["-g"] : saveDev ? ["-D"] : [],
721
+ packageSpec
722
+ ]
723
+ };
724
+ if (packageManager === "yarn") return {
725
+ command: "yarn",
726
+ args: options.global ? [
727
+ "global",
728
+ "add",
729
+ packageSpec
730
+ ] : [
731
+ "add",
732
+ ...saveDev ? ["-D"] : [],
733
+ packageSpec
734
+ ]
735
+ };
736
+ if (packageManager === "bun") return {
737
+ command: "bun",
738
+ args: [
739
+ "add",
740
+ ...options.global ? ["-g"] : saveDev ? ["-d"] : [],
741
+ packageSpec
742
+ ]
743
+ };
744
+ return {
745
+ command: "npm",
746
+ args: [
747
+ "install",
748
+ ...options.global ? ["-g"] : saveDev ? ["-D"] : [],
749
+ packageSpec
750
+ ]
751
+ };
752
+ }
753
+ function runUpgrade(args) {
754
+ const options = parseUpgradeCommand(args);
755
+ if (options.help) {
756
+ printUpgradeUsage();
757
+ return;
758
+ }
759
+ const command = buildUpgradeCommand(detectPackageManager(options.packageManager), options);
760
+ if (options.dryRun) {
761
+ process.stdout.write(`${command.command} ${command.args.join(" ")}\n`);
762
+ return;
763
+ }
764
+ const result = spawnSync(command.command, command.args, {
765
+ stdio: "inherit",
766
+ cwd: process.cwd(),
767
+ env: process.env
768
+ });
769
+ if (result.error) throw result.error;
770
+ process.exit(result.status ?? 1);
771
+ }
772
+ function parseReadyCommand(args) {
773
+ const patterns = [];
774
+ const options = {
775
+ output: "./dist",
776
+ scriptExt: "downcompile"
777
+ };
778
+ for (let i = 0; i < args.length; i++) {
779
+ const arg = args[i];
780
+ if (arg === "--output" || arg === "-o") options.output = args[++i] ?? options.output;
781
+ else if (arg === "--ssr") options.ssr = true;
782
+ else if (arg === "--script-ext") {
783
+ const scriptExt = args[++i];
784
+ if (scriptExt === "preserve" || scriptExt === "downcompile") options.scriptExt = scriptExt;
785
+ } else if (arg === "--help" || arg === "-h") options.help = true;
786
+ else if (!arg.startsWith("-")) patterns.push(arg);
787
+ }
788
+ return {
789
+ patterns,
790
+ options
791
+ };
792
+ }
793
+ async function runReady(args) {
794
+ const { patterns, options } = parseReadyCommand(args);
795
+ if (options.help) {
796
+ printReadyUsage();
797
+ return;
798
+ }
799
+ process.stderr.write("vize ready: fmt\n");
800
+ await runFmt(["--write", ...patterns]);
801
+ process.stderr.write("vize ready: lint\n");
802
+ await runLint(patterns);
803
+ process.stderr.write("vize ready: check\n");
804
+ await runCheck(patterns);
805
+ process.stderr.write("vize ready: build\n");
806
+ await runBuild([
807
+ "--output",
808
+ options.output,
809
+ "--script-ext",
810
+ options.scriptExt,
811
+ ...options.ssr ? ["--ssr"] : [],
812
+ ...patterns
813
+ ]);
814
+ }
815
+ const NAPI_COMMANDS = new Set([
816
+ "build",
817
+ "check",
818
+ "fmt",
819
+ "lint"
820
+ ]);
821
+ const JS_COMMANDS = new Set([
822
+ "musea",
823
+ "ready",
824
+ "upgrade"
825
+ ]);
168
826
  async function main() {
169
827
  const args = process.argv.slice(2);
170
828
  const command = args[0];
@@ -175,6 +833,15 @@ async function main() {
175
833
  if (NAPI_COMMANDS.has(command)) {
176
834
  const commandArgs = args.slice(1);
177
835
  switch (command) {
836
+ case "build":
837
+ await runBuild(commandArgs);
838
+ break;
839
+ case "check":
840
+ await runCheck(commandArgs);
841
+ break;
842
+ case "fmt":
843
+ await runFmt(commandArgs);
844
+ break;
178
845
  case "lint":
179
846
  await runLint(commandArgs);
180
847
  break;
@@ -185,6 +852,12 @@ async function main() {
185
852
  case "musea":
186
853
  runMusea(commandArgs);
187
854
  break;
855
+ case "ready":
856
+ await runReady(commandArgs);
857
+ break;
858
+ case "upgrade":
859
+ runUpgrade(commandArgs);
860
+ break;
188
861
  }
189
862
  } else {
190
863
  printUsage();