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/src/cli.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { readFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import * as path from "node:path";
4
4
  import { createRequire } from "node:module";
@@ -63,6 +63,12 @@ function getBindingPackageName(): string {
63
63
  }
64
64
 
65
65
  interface NativeBinding {
66
+ compileSfcBatchWithResults: (
67
+ files: BatchFileInput[],
68
+ options?: NativeBuildOptions,
69
+ ) => BatchCompileResult;
70
+ formatSfc: (source: string, options?: NativeFormatOptions) => FormatResult;
71
+ typeCheck: (source: string, options?: NativeTypeCheckOptions) => TypeCheckResult;
66
72
  lint: (
67
73
  patterns: string[],
68
74
  options?: {
@@ -76,15 +82,25 @@ interface NativeBinding {
76
82
  ) => LintResult;
77
83
  }
78
84
 
79
- function loadNative(): NativeBinding {
85
+ type NativeCommand = "build" | "check" | "fmt" | "lint";
86
+
87
+ const REQUIRED_BINDINGS: Record<NativeCommand, keyof NativeBinding> = {
88
+ build: "compileSfcBatchWithResults",
89
+ check: "typeCheck",
90
+ fmt: "formatSfc",
91
+ lint: "lint",
92
+ };
93
+
94
+ function loadNative(command: NativeCommand): NativeBinding {
80
95
  const attemptedPackages = getAttemptedPackages();
81
96
  let lastError: unknown = null;
97
+ const requiredBinding = REQUIRED_BINDINGS[command];
82
98
 
83
99
  for (const packageName of attemptedPackages) {
84
100
  try {
85
101
  const binding = require(packageName) as Partial<NativeBinding>;
86
- if (typeof binding.lint !== "function") {
87
- throw new Error(`${packageName} does not expose the lint binding.`);
102
+ if (typeof binding[requiredBinding] !== "function") {
103
+ throw new Error(`${packageName} does not expose the ${command} binding.`);
88
104
  }
89
105
  return binding as NativeBinding;
90
106
  } catch (error) {
@@ -161,7 +177,62 @@ interface ParsedLintCommand {
161
177
 
162
178
  function printUsage(): void {
163
179
  console.error("Usage: vize <command> [options]");
164
- console.error("Commands: lint, musea");
180
+ console.error("Commands: build, fmt, check, lint, upgrade, ready, musea");
181
+ }
182
+
183
+ function printBuildUsage(): void {
184
+ console.error("Usage: vize build [options] [files-or-directories]");
185
+ console.error("Options:");
186
+ console.error(" -o, --output <dir> Output directory");
187
+ console.error(" -f, --format <js|json|stats> Output format");
188
+ console.error(" --ssr Enable SSR compilation");
189
+ console.error(" --script-ext <mode> preserve or downcompile");
190
+ console.error(" -j, --threads <number> Worker thread count");
191
+ }
192
+
193
+ function printFmtUsage(): void {
194
+ console.error("Usage: vize fmt [options] [files-or-directories]");
195
+ console.error("Options:");
196
+ console.error(" --check Exit with an error if files need formatting");
197
+ console.error(" -w, --write Write formatted output");
198
+ console.error(" --single-quote Use single quotes");
199
+ console.error(" --print-width <number> Maximum line width");
200
+ console.error(" --tab-width <number> Indentation width");
201
+ console.error(" --use-tabs Indent with tabs");
202
+ console.error(" --no-semi Omit semicolons");
203
+ }
204
+
205
+ function printCheckUsage(): void {
206
+ console.error("Usage: vize check [options] [files-or-directories]");
207
+ console.error("Options:");
208
+ console.error(" -f, --format <text|json> Output format");
209
+ console.error(" -q, --quiet Show summary only");
210
+ console.error(" --strict Enable strict checks");
211
+ console.error(" --show-virtual-ts Print generated Virtual TS");
212
+ console.error(" --max-warnings <number> Fail when warnings exceed the limit");
213
+ console.error(" -c, --config <path> Use a specific vize config file");
214
+ console.error(" --no-config Disable config discovery");
215
+ console.error("");
216
+ console.error(
217
+ "Note: npm `vize check` uses the packaged NAPI checker. Install the Rust CLI for project-backed Corsa diagnostics.",
218
+ );
219
+ }
220
+
221
+ function printUpgradeUsage(): void {
222
+ console.error("Usage: vize upgrade [options]");
223
+ console.error("Options:");
224
+ console.error(" --package-manager <name> npm, pnpm, yarn, bun, or vp");
225
+ console.error(" -g, --global Upgrade the global installation");
226
+ console.error(" --dry-run Print the command without running it");
227
+ }
228
+
229
+ function printReadyUsage(): void {
230
+ console.error("Usage: vize ready [options] [files-or-directories]");
231
+ console.error("Runs: fmt --write -> lint -> check -> build");
232
+ console.error("Options:");
233
+ console.error(" -o, --output <dir> Output directory for build");
234
+ console.error(" --ssr Enable SSR compilation for build");
235
+ console.error(" --script-ext <mode> preserve or downcompile");
165
236
  }
166
237
 
167
238
  function resolvePackageBinaryFromCwd(packageName: string, binName: string = packageName): string {
@@ -242,6 +313,878 @@ function parseLintCommand(args: string[]): ParsedLintCommand {
242
313
  return { patterns, options, sharedConfig };
243
314
  }
244
315
 
316
+ // ============================================================================
317
+ // Build command
318
+ // ============================================================================
319
+
320
+ interface NativeBuildOptions {
321
+ ssr?: boolean;
322
+ vapor?: boolean;
323
+ customRenderer?: boolean;
324
+ custom_renderer?: boolean;
325
+ isTs?: boolean;
326
+ is_ts?: boolean;
327
+ threads?: number;
328
+ }
329
+
330
+ interface BatchFileInput {
331
+ path: string;
332
+ source: string;
333
+ }
334
+
335
+ interface BatchFileResult {
336
+ path: string;
337
+ code: string;
338
+ css?: string;
339
+ errors: string[];
340
+ warnings: string[];
341
+ scopeId?: string;
342
+ scope_id?: string;
343
+ hasScoped?: boolean;
344
+ has_scoped?: boolean;
345
+ }
346
+
347
+ interface BatchCompileResult {
348
+ results: BatchFileResult[];
349
+ successCount?: number;
350
+ success_count?: number;
351
+ failedCount?: number;
352
+ failed_count?: number;
353
+ timeMs?: number;
354
+ time_ms?: number;
355
+ }
356
+
357
+ interface BuildOptions {
358
+ output: string;
359
+ format: "js" | "json" | "stats";
360
+ ssr?: boolean;
361
+ vapor?: boolean;
362
+ customRenderer?: boolean;
363
+ scriptExt: "preserve" | "downcompile";
364
+ threads?: number;
365
+ help?: boolean;
366
+ }
367
+
368
+ interface ParsedBuildCommand {
369
+ patterns: string[];
370
+ options: BuildOptions;
371
+ sharedConfig: SharedConfigOptions;
372
+ }
373
+
374
+ function parseBuildCommand(args: string[]): ParsedBuildCommand {
375
+ const patterns: string[] = [];
376
+ const options: BuildOptions = {
377
+ output: "./dist",
378
+ format: "js",
379
+ scriptExt: "downcompile",
380
+ };
381
+ const sharedConfig: SharedConfigOptions = {
382
+ configMode: "root",
383
+ };
384
+
385
+ for (let i = 0; i < args.length; i++) {
386
+ const arg = args[i];
387
+ if (arg === "--output" || arg === "-o") {
388
+ options.output = args[++i] ?? options.output;
389
+ } else if (arg === "--format" || arg === "-f") {
390
+ const format = args[++i];
391
+ if (format === "js" || format === "json" || format === "stats") {
392
+ options.format = format;
393
+ }
394
+ } else if (arg === "--ssr") {
395
+ options.ssr = true;
396
+ } else if (arg === "--vapor") {
397
+ options.vapor = true;
398
+ } else if (arg === "--custom-renderer") {
399
+ options.customRenderer = true;
400
+ } else if (arg === "--script-ext") {
401
+ const scriptExt = args[++i];
402
+ if (scriptExt === "preserve" || scriptExt === "downcompile") {
403
+ options.scriptExt = scriptExt;
404
+ }
405
+ } else if (arg === "--threads" || arg === "-j") {
406
+ options.threads = Number.parseInt(args[++i], 10);
407
+ } else if (arg === "--config" || arg === "-c") {
408
+ const configFile = args[++i];
409
+ if (!configFile) {
410
+ throw new Error("Missing path after --config");
411
+ }
412
+ sharedConfig.configFile = configFile;
413
+ } else if (arg === "--no-config") {
414
+ sharedConfig.configMode = "none";
415
+ } else if (arg === "--profile" || arg === "--continue-on-error") {
416
+ // Accepted for command compatibility. The npm build path prints a compact summary.
417
+ } else if (arg === "--help" || arg === "-h") {
418
+ options.help = true;
419
+ } else if (!arg.startsWith("-")) {
420
+ patterns.push(arg);
421
+ }
422
+ }
423
+
424
+ return { patterns, options, sharedConfig };
425
+ }
426
+
427
+ function getScriptLang(source: string): string {
428
+ const match = source.match(/<script\b[^>]*\blang=["']([^"']+)["']/i);
429
+ return match?.[1] ?? "js";
430
+ }
431
+
432
+ function getOutputExtension(source: string, scriptExt: BuildOptions["scriptExt"]): string {
433
+ if (scriptExt === "downcompile") {
434
+ return "js";
435
+ }
436
+ const lang = getScriptLang(source);
437
+ return lang === "ts" || lang === "tsx" || lang === "jsx" ? lang : "js";
438
+ }
439
+
440
+ function outputFileName(file: string, extension: string): string {
441
+ return path.basename(file).replace(/\.vue$/i, `.${extension}`);
442
+ }
443
+
444
+ function toNativeBuildOptions(options: BuildOptions): NativeBuildOptions {
445
+ const isTs = options.scriptExt === "preserve";
446
+ return {
447
+ ssr: options.ssr,
448
+ vapor: options.vapor,
449
+ customRenderer: options.customRenderer,
450
+ custom_renderer: options.customRenderer,
451
+ isTs,
452
+ is_ts: isTs,
453
+ threads: options.threads,
454
+ };
455
+ }
456
+
457
+ async function runBuild(args: string[]): Promise<void> {
458
+ const { patterns, options, sharedConfig } = parseBuildCommand(args);
459
+ if (options.help) {
460
+ printBuildUsage();
461
+ return;
462
+ }
463
+
464
+ const config = await loadConfig(process.cwd(), {
465
+ mode: sharedConfig.configMode,
466
+ configFile: sharedConfig.configFile,
467
+ env: {
468
+ mode: process.env.NODE_ENV ?? "development",
469
+ command: "build",
470
+ },
471
+ });
472
+
473
+ if (sharedConfig.configFile && !config) {
474
+ throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
475
+ }
476
+
477
+ options.ssr ??= config?.compiler?.ssr;
478
+ options.vapor ??= config?.compiler?.vapor;
479
+ options.customRenderer ??= config?.compiler?.customRenderer;
480
+ if (config?.compiler?.scriptExt === "ts") {
481
+ options.scriptExt = "preserve";
482
+ } else if (config?.compiler?.scriptExt === "js") {
483
+ options.scriptExt = "downcompile";
484
+ }
485
+
486
+ const files = collectVueFiles(patterns);
487
+ if (files.length === 0) {
488
+ process.stderr.write(`No Vue files found matching inputs: ${JSON.stringify(patterns)}\n`);
489
+ process.exit(1);
490
+ }
491
+
492
+ const inputs = files.map((file) => ({
493
+ path: file,
494
+ source: readFileSync(file, "utf8"),
495
+ }));
496
+ const native = loadNative("build");
497
+ const startedAt = performance.now();
498
+ const result = native.compileSfcBatchWithResults(inputs, toNativeBuildOptions(options));
499
+ const timeMs = result.timeMs ?? result.time_ms ?? performance.now() - startedAt;
500
+ const results = [...result.results].sort((left, right) => left.path.localeCompare(right.path));
501
+
502
+ if (options.format !== "stats") {
503
+ mkdirSync(options.output, { recursive: true });
504
+ }
505
+
506
+ for (const fileResult of results) {
507
+ const source = inputs.find((input) => input.path === fileResult.path)?.source ?? "";
508
+ for (const warning of fileResult.warnings) {
509
+ process.stderr.write(`warning: ${displayPath(fileResult.path)} ${warning}\n`);
510
+ }
511
+ for (const error of fileResult.errors) {
512
+ process.stderr.write(`error: ${displayPath(fileResult.path)} ${error}\n`);
513
+ }
514
+
515
+ if (fileResult.errors.length > 0 || options.format === "stats") {
516
+ continue;
517
+ }
518
+
519
+ const extension =
520
+ options.format === "json" ? "json" : getOutputExtension(source, options.scriptExt);
521
+ const outputPath = path.join(options.output, outputFileName(fileResult.path, extension));
522
+ const content =
523
+ options.format === "json" ? JSON.stringify(fileResult, null, 2) : fileResult.code;
524
+ writeFileSync(outputPath, content);
525
+ }
526
+
527
+ const failed =
528
+ result.failedCount ?? result.failed_count ?? results.filter((r) => r.errors.length).length;
529
+ const success = result.successCount ?? result.success_count ?? results.length - failed;
530
+ process.stderr.write(
531
+ `\x1b[32mOK\x1b[0m Built ${success} Vue file(s) in ${timeMs.toFixed(2)}ms\n`,
532
+ );
533
+
534
+ if (failed > 0) {
535
+ process.stderr.write(`\x1b[31mERR\x1b[0m ${failed} file(s) failed\n`);
536
+ process.exit(1);
537
+ }
538
+ }
539
+
540
+ // ============================================================================
541
+ // Format command
542
+ // ============================================================================
543
+
544
+ interface NativeFormatOptions {
545
+ printWidth?: number;
546
+ print_width?: number;
547
+ tabWidth?: number;
548
+ tab_width?: number;
549
+ useTabs?: boolean;
550
+ use_tabs?: boolean;
551
+ semi?: boolean;
552
+ singleQuote?: boolean;
553
+ single_quote?: boolean;
554
+ sortAttributes?: boolean;
555
+ sort_attributes?: boolean;
556
+ singleAttributePerLine?: boolean;
557
+ single_attribute_per_line?: boolean;
558
+ maxAttributesPerLine?: number;
559
+ max_attributes_per_line?: number;
560
+ normalizeDirectiveShorthands?: boolean;
561
+ normalize_directive_shorthands?: boolean;
562
+ }
563
+
564
+ interface FormatResult {
565
+ code: string;
566
+ changed: boolean;
567
+ }
568
+
569
+ interface FmtOptions extends NativeFormatOptions {
570
+ check?: boolean;
571
+ write?: boolean;
572
+ help?: boolean;
573
+ }
574
+
575
+ interface ParsedFmtCommand {
576
+ patterns: string[];
577
+ options: FmtOptions;
578
+ sharedConfig: SharedConfigOptions;
579
+ }
580
+
581
+ function parseFmtCommand(args: string[]): ParsedFmtCommand {
582
+ const patterns: string[] = [];
583
+ const options: FmtOptions = {};
584
+ const sharedConfig: SharedConfigOptions = {
585
+ configMode: "root",
586
+ };
587
+
588
+ for (let i = 0; i < args.length; i++) {
589
+ const arg = args[i];
590
+ if (arg === "--check") {
591
+ options.check = true;
592
+ } else if (arg === "--write" || arg === "-w") {
593
+ options.write = true;
594
+ } else if (arg === "--single-quote") {
595
+ options.singleQuote = true;
596
+ } else if (arg === "--print-width") {
597
+ options.printWidth = Number.parseInt(args[++i], 10);
598
+ } else if (arg === "--tab-width") {
599
+ options.tabWidth = Number.parseInt(args[++i], 10);
600
+ } else if (arg === "--use-tabs") {
601
+ options.useTabs = true;
602
+ } else if (arg === "--no-semi") {
603
+ options.semi = false;
604
+ } else if (arg === "--sort-attributes") {
605
+ options.sortAttributes = true;
606
+ } else if (arg === "--single-attribute-per-line") {
607
+ options.singleAttributePerLine = true;
608
+ } else if (arg === "--max-attributes-per-line") {
609
+ options.maxAttributesPerLine = Number.parseInt(args[++i], 10);
610
+ } else if (arg === "--normalize-directive-shorthands") {
611
+ options.normalizeDirectiveShorthands = true;
612
+ } else if (arg === "--config" || arg === "-c") {
613
+ const configFile = args[++i];
614
+ if (!configFile) {
615
+ throw new Error("Missing path after --config");
616
+ }
617
+ sharedConfig.configFile = configFile;
618
+ } else if (arg === "--no-config") {
619
+ sharedConfig.configMode = "none";
620
+ } else if (arg === "--profile") {
621
+ // Accepted for command compatibility. The npm fmt path prints a compact summary.
622
+ } else if (arg === "--help" || arg === "-h") {
623
+ options.help = true;
624
+ } else if (!arg.startsWith("-")) {
625
+ patterns.push(arg);
626
+ }
627
+ }
628
+
629
+ return { patterns, options, sharedConfig };
630
+ }
631
+
632
+ function toNativeFormatOptions(options: FmtOptions): NativeFormatOptions {
633
+ return {
634
+ printWidth: options.printWidth,
635
+ print_width: options.printWidth,
636
+ tabWidth: options.tabWidth,
637
+ tab_width: options.tabWidth,
638
+ useTabs: options.useTabs,
639
+ use_tabs: options.useTabs,
640
+ semi: options.semi,
641
+ singleQuote: options.singleQuote,
642
+ single_quote: options.singleQuote,
643
+ sortAttributes: options.sortAttributes,
644
+ sort_attributes: options.sortAttributes,
645
+ singleAttributePerLine: options.singleAttributePerLine,
646
+ single_attribute_per_line: options.singleAttributePerLine,
647
+ maxAttributesPerLine: options.maxAttributesPerLine,
648
+ max_attributes_per_line: options.maxAttributesPerLine,
649
+ normalizeDirectiveShorthands: options.normalizeDirectiveShorthands,
650
+ normalize_directive_shorthands: options.normalizeDirectiveShorthands,
651
+ };
652
+ }
653
+
654
+ async function runFmt(args: string[]): Promise<void> {
655
+ const { patterns, options, sharedConfig } = parseFmtCommand(args);
656
+ if (options.help) {
657
+ printFmtUsage();
658
+ return;
659
+ }
660
+
661
+ const config = await loadConfig(process.cwd(), {
662
+ mode: sharedConfig.configMode,
663
+ configFile: sharedConfig.configFile,
664
+ env: {
665
+ mode: process.env.NODE_ENV ?? "development",
666
+ command: "fmt",
667
+ },
668
+ });
669
+
670
+ if (sharedConfig.configFile && !config) {
671
+ throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
672
+ }
673
+
674
+ options.printWidth ??= config?.formatter?.printWidth;
675
+ options.tabWidth ??= config?.formatter?.tabWidth;
676
+ options.useTabs ??= config?.formatter?.useTabs;
677
+ options.semi ??= config?.formatter?.semi;
678
+ options.singleQuote ??= config?.formatter?.singleQuote;
679
+
680
+ const files = collectVueFiles(patterns);
681
+ if (files.length === 0) {
682
+ process.stderr.write(`No Vue files found matching inputs: ${JSON.stringify(patterns)}\n`);
683
+ return;
684
+ }
685
+
686
+ const native = loadNative("fmt");
687
+ let changed = 0;
688
+ let errored = 0;
689
+
690
+ for (const file of files) {
691
+ const source = readFileSync(file, "utf8");
692
+ try {
693
+ const result = native.formatSfc(source, toNativeFormatOptions(options));
694
+ if (!result.changed) {
695
+ continue;
696
+ }
697
+ changed++;
698
+ if (options.check) {
699
+ process.stderr.write(`Would reformat: ${displayPath(file)}\n`);
700
+ } else if (options.write) {
701
+ writeFileSync(file, result.code);
702
+ process.stderr.write(`Reformatted: ${displayPath(file)}\n`);
703
+ } else {
704
+ process.stderr.write(`Would reformat: ${displayPath(file)}\n`);
705
+ }
706
+ } catch (error) {
707
+ errored++;
708
+ process.stderr.write(
709
+ `Error formatting ${displayPath(file)}: ${error instanceof Error ? error.message : String(error)}\n`,
710
+ );
711
+ }
712
+ }
713
+
714
+ process.stderr.write(
715
+ `\x1b[32mOK\x1b[0m Formatted ${files.length} Vue file(s), ${changed} changed\n`,
716
+ );
717
+
718
+ if (errored > 0 || (options.check && changed > 0)) {
719
+ process.exit(1);
720
+ }
721
+ }
722
+
723
+ // ============================================================================
724
+ // Check command
725
+ // ============================================================================
726
+
727
+ interface NativeTypeCheckOptions {
728
+ filename?: string;
729
+ strict?: boolean;
730
+ includeVirtualTs?: boolean;
731
+ include_virtual_ts?: boolean;
732
+ checkProps?: boolean;
733
+ check_props?: boolean;
734
+ checkEmits?: boolean;
735
+ check_emits?: boolean;
736
+ checkTemplateBindings?: boolean;
737
+ check_template_bindings?: boolean;
738
+ checkReactivity?: boolean;
739
+ check_reactivity?: boolean;
740
+ checkSetupContext?: boolean;
741
+ check_setup_context?: boolean;
742
+ checkInvalidExports?: boolean;
743
+ check_invalid_exports?: boolean;
744
+ checkFallthroughAttrs?: boolean;
745
+ check_fallthrough_attrs?: boolean;
746
+ }
747
+
748
+ interface TypeDiagnostic {
749
+ severity: string;
750
+ message: string;
751
+ start: number;
752
+ end: number;
753
+ code?: string;
754
+ help?: string;
755
+ related?: Array<{
756
+ message: string;
757
+ start: number;
758
+ end: number;
759
+ filename?: string;
760
+ }>;
761
+ }
762
+
763
+ interface TypeCheckResult {
764
+ diagnostics: TypeDiagnostic[];
765
+ virtualTs?: string;
766
+ errorCount: number;
767
+ warningCount: number;
768
+ analysisTimeMs?: number;
769
+ }
770
+
771
+ interface CheckOptions {
772
+ format?: string;
773
+ quiet?: boolean;
774
+ strict?: boolean;
775
+ includeVirtualTs?: boolean;
776
+ maxWarnings?: number;
777
+ checkProps?: boolean;
778
+ checkEmits?: boolean;
779
+ checkTemplateBindings?: boolean;
780
+ checkReactivity?: boolean;
781
+ checkSetupContext?: boolean;
782
+ checkInvalidExports?: boolean;
783
+ checkFallthroughAttrs?: boolean;
784
+ help?: boolean;
785
+ }
786
+
787
+ interface ParsedCheckCommand {
788
+ patterns: string[];
789
+ options: CheckOptions;
790
+ sharedConfig: SharedConfigOptions;
791
+ }
792
+
793
+ interface CheckedFileResult {
794
+ file: string;
795
+ source: string;
796
+ result: TypeCheckResult;
797
+ }
798
+
799
+ function parseCheckCommand(args: string[]): ParsedCheckCommand {
800
+ const patterns: string[] = [];
801
+ const options: CheckOptions = {};
802
+ const sharedConfig: SharedConfigOptions = {
803
+ configMode: "root",
804
+ };
805
+
806
+ for (let i = 0; i < args.length; i++) {
807
+ const arg = args[i];
808
+ if (arg === "--format" || arg === "-f") {
809
+ options.format = args[++i];
810
+ } else if (arg === "--quiet" || arg === "-q") {
811
+ options.quiet = true;
812
+ } else if (arg === "--strict") {
813
+ options.strict = true;
814
+ } else if (arg === "--no-strict") {
815
+ options.strict = false;
816
+ } else if (arg === "--show-virtual-ts" || arg === "--include-virtual-ts") {
817
+ options.includeVirtualTs = true;
818
+ } else if (arg === "--max-warnings") {
819
+ options.maxWarnings = Number.parseInt(args[++i], 10);
820
+ } else if (arg === "--no-check-props") {
821
+ options.checkProps = false;
822
+ } else if (arg === "--no-check-emits") {
823
+ options.checkEmits = false;
824
+ } else if (arg === "--no-check-template-bindings") {
825
+ options.checkTemplateBindings = false;
826
+ } else if (arg === "--no-check-reactivity") {
827
+ options.checkReactivity = false;
828
+ } else if (arg === "--no-check-setup-context") {
829
+ options.checkSetupContext = false;
830
+ } else if (arg === "--no-check-invalid-exports") {
831
+ options.checkInvalidExports = false;
832
+ } else if (arg === "--no-check-fallthrough-attrs") {
833
+ options.checkFallthroughAttrs = false;
834
+ } else if (arg === "--config" || arg === "-c") {
835
+ const configFile = args[++i];
836
+ if (!configFile) {
837
+ throw new Error("Missing path after --config");
838
+ }
839
+ sharedConfig.configFile = configFile;
840
+ } else if (arg === "--no-config") {
841
+ sharedConfig.configMode = "none";
842
+ } else if (arg === "--help" || arg === "-h") {
843
+ options.help = true;
844
+ } else if (arg === "--tsconfig" || arg === "--corsa-path" || arg === "--servers") {
845
+ i++;
846
+ } else if (arg === "--socket" || arg === "-s" || arg === "--declaration-dir") {
847
+ i++;
848
+ } else if (arg === "--profile" || arg === "--declaration") {
849
+ // Accepted for package-script compatibility with the Rust CLI. The npm
850
+ // checker does not currently emit project profiles or declarations.
851
+ } else if (!arg.startsWith("-")) {
852
+ patterns.push(arg);
853
+ }
854
+ }
855
+
856
+ return { patterns, options, sharedConfig };
857
+ }
858
+
859
+ function hasGlobSyntax(pattern: string): boolean {
860
+ return pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
861
+ }
862
+
863
+ function normalizePath(filePath: string): string {
864
+ return filePath.split(path.sep).join("/");
865
+ }
866
+
867
+ function displayPath(filePath: string): string {
868
+ const relative = path.relative(process.cwd(), filePath);
869
+ if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
870
+ return normalizePath(relative);
871
+ }
872
+ return normalizePath(filePath);
873
+ }
874
+
875
+ function isVueFile(filePath: string): boolean {
876
+ return path.extname(filePath) === ".vue";
877
+ }
878
+
879
+ function collectVueFilesFromDirectory(directory: string, recursive: boolean): string[] {
880
+ const files: string[] = [];
881
+ const entries = readdirSync(directory, { withFileTypes: true });
882
+
883
+ for (const entry of entries) {
884
+ const entryPath = path.join(directory, entry.name);
885
+ if (entry.isDirectory()) {
886
+ if (entry.name === "node_modules" || entry.name === ".git") {
887
+ continue;
888
+ }
889
+ if (recursive) {
890
+ files.push(...collectVueFilesFromDirectory(entryPath, true));
891
+ }
892
+ } else if (entry.isFile() && isVueFile(entryPath)) {
893
+ files.push(entryPath);
894
+ }
895
+ }
896
+
897
+ return files;
898
+ }
899
+
900
+ function globBase(pattern: string): string {
901
+ const normalized = normalizePath(pattern);
902
+ const globIndex = normalized.search(/[*?[]/);
903
+ if (globIndex === -1) {
904
+ return normalized;
905
+ }
906
+
907
+ const beforeGlob = normalized.slice(0, globIndex);
908
+ const slashIndex = beforeGlob.lastIndexOf("/");
909
+ if (slashIndex === -1) {
910
+ return ".";
911
+ }
912
+ return beforeGlob.slice(0, slashIndex) || "/";
913
+ }
914
+
915
+ function globToRegExp(pattern: string): RegExp {
916
+ const normalized = normalizePath(pattern);
917
+ let source = "";
918
+
919
+ for (let i = 0; i < normalized.length; i++) {
920
+ const char = normalized[i];
921
+ const next = normalized[i + 1];
922
+ const afterNext = normalized[i + 2];
923
+
924
+ if (char === "*" && next === "*" && afterNext === "/") {
925
+ source += "(?:.*/)?";
926
+ i += 2;
927
+ } else if (char === "*" && next === "*") {
928
+ source += ".*";
929
+ i++;
930
+ } else if (char === "*") {
931
+ source += "[^/]*";
932
+ } else if (char === "?") {
933
+ source += "[^/]";
934
+ } else if ("\\^$+?.()|{}[]".includes(char)) {
935
+ source += `\\${char}`;
936
+ } else {
937
+ source += char;
938
+ }
939
+ }
940
+
941
+ return new RegExp(`^${source}$`);
942
+ }
943
+
944
+ function shouldRecurseGlob(pattern: string, base: string): boolean {
945
+ const normalizedPattern = normalizePath(pattern);
946
+ const normalizedBase = normalizePath(base);
947
+ const rest =
948
+ normalizedBase === "."
949
+ ? normalizedPattern
950
+ : normalizedPattern.slice(normalizedBase.length).replace(/^\/+/, "");
951
+ return rest.includes("/");
952
+ }
953
+
954
+ function collectVueFilesFromGlob(pattern: string): string[] {
955
+ const basePattern = globBase(pattern);
956
+ const base = path.resolve(process.cwd(), basePattern);
957
+ if (!existsSync(base)) {
958
+ return [];
959
+ }
960
+
961
+ const isAbsolutePattern = path.isAbsolute(pattern);
962
+ const normalizedPattern = normalizePath(isAbsolutePattern ? path.resolve(pattern) : pattern);
963
+ const regex = globToRegExp(normalizedPattern);
964
+ const candidates = collectVueFilesFromDirectory(base, shouldRecurseGlob(pattern, basePattern));
965
+
966
+ return candidates.filter((file) => {
967
+ const comparable = isAbsolutePattern
968
+ ? normalizePath(file)
969
+ : normalizePath(path.relative(process.cwd(), file));
970
+ return regex.test(comparable);
971
+ });
972
+ }
973
+
974
+ function collectVueFiles(patterns: string[]): string[] {
975
+ const files = new Set<string>();
976
+ const inputs = patterns.length === 0 ? ["."] : patterns;
977
+
978
+ for (const input of inputs) {
979
+ if (hasGlobSyntax(input)) {
980
+ for (const file of collectVueFilesFromGlob(input)) {
981
+ files.add(path.resolve(file));
982
+ }
983
+ continue;
984
+ }
985
+
986
+ const resolved = path.resolve(process.cwd(), input);
987
+ if (!existsSync(resolved)) {
988
+ continue;
989
+ }
990
+
991
+ const stats = statSync(resolved);
992
+ if (stats.isDirectory()) {
993
+ for (const file of collectVueFilesFromDirectory(resolved, true)) {
994
+ files.add(path.resolve(file));
995
+ }
996
+ } else if (stats.isFile() && isVueFile(resolved)) {
997
+ files.add(resolved);
998
+ }
999
+ }
1000
+
1001
+ return Array.from(files).sort();
1002
+ }
1003
+
1004
+ function lineStarts(source: string): number[] {
1005
+ const starts = [0];
1006
+ for (let i = 0; i < source.length; i++) {
1007
+ if (source.charCodeAt(i) === 10) {
1008
+ starts.push(i + 1);
1009
+ }
1010
+ }
1011
+ return starts;
1012
+ }
1013
+
1014
+ function offsetToLineColumn(starts: number[], offset: number): { line: number; column: number } {
1015
+ let low = 0;
1016
+ let high = starts.length - 1;
1017
+ while (low <= high) {
1018
+ const mid = Math.floor((low + high) / 2);
1019
+ if (starts[mid] <= offset) {
1020
+ low = mid + 1;
1021
+ } else {
1022
+ high = mid - 1;
1023
+ }
1024
+ }
1025
+
1026
+ const lineIndex = Math.max(0, high);
1027
+ return {
1028
+ line: lineIndex + 1,
1029
+ column: offset - starts[lineIndex] + 1,
1030
+ };
1031
+ }
1032
+
1033
+ function toNativeTypeCheckOptions(file: string, options: CheckOptions): NativeTypeCheckOptions {
1034
+ return {
1035
+ filename: file,
1036
+ strict: options.strict,
1037
+ includeVirtualTs: options.includeVirtualTs,
1038
+ include_virtual_ts: options.includeVirtualTs,
1039
+ checkProps: options.checkProps,
1040
+ check_props: options.checkProps,
1041
+ checkEmits: options.checkEmits,
1042
+ check_emits: options.checkEmits,
1043
+ checkTemplateBindings: options.checkTemplateBindings,
1044
+ check_template_bindings: options.checkTemplateBindings,
1045
+ checkReactivity: options.checkReactivity,
1046
+ check_reactivity: options.checkReactivity,
1047
+ checkSetupContext: options.checkSetupContext,
1048
+ check_setup_context: options.checkSetupContext,
1049
+ checkInvalidExports: options.checkInvalidExports,
1050
+ check_invalid_exports: options.checkInvalidExports,
1051
+ checkFallthroughAttrs: options.checkFallthroughAttrs,
1052
+ check_fallthrough_attrs: options.checkFallthroughAttrs,
1053
+ };
1054
+ }
1055
+
1056
+ function renderCheckText(
1057
+ results: CheckedFileResult[],
1058
+ options: CheckOptions,
1059
+ timeMs: number,
1060
+ ): void {
1061
+ let totalErrors = 0;
1062
+ let totalWarnings = 0;
1063
+
1064
+ for (const { file, source, result } of results) {
1065
+ totalErrors += result.errorCount;
1066
+ totalWarnings += result.warningCount;
1067
+
1068
+ if (options.includeVirtualTs && result.virtualTs) {
1069
+ process.stderr.write(`\n=== ${displayPath(file)} ===\n${result.virtualTs}\n`);
1070
+ }
1071
+
1072
+ if (options.quiet || result.diagnostics.length === 0) {
1073
+ continue;
1074
+ }
1075
+
1076
+ const starts = lineStarts(source);
1077
+ process.stdout.write(`\n\x1b[4m${displayPath(file)}\x1b[0m\n`);
1078
+ for (const diagnostic of result.diagnostics) {
1079
+ const color = diagnostic.severity === "error" ? "\x1b[31m" : "\x1b[33m";
1080
+ const location = offsetToLineColumn(starts, diagnostic.start);
1081
+ const code = diagnostic.code ? ` [${diagnostic.code}]` : "";
1082
+ process.stdout.write(
1083
+ ` ${color}${diagnostic.severity}:${location.line}:${location.column}\x1b[0m${code} ${diagnostic.message}\n`,
1084
+ );
1085
+ if (diagnostic.help) {
1086
+ process.stdout.write(` help: ${diagnostic.help}\n`);
1087
+ }
1088
+ }
1089
+ }
1090
+
1091
+ const status = totalErrors > 0 ? "\x1b[31mERR\x1b[0m" : "\x1b[32mOK\x1b[0m";
1092
+ process.stdout.write(
1093
+ `\n${status} Type checked ${results.length} Vue files in ${timeMs.toFixed(2)}ms\n`,
1094
+ );
1095
+ if (totalErrors > 0) {
1096
+ process.stdout.write(` \x1b[31m${totalErrors} error(s)\x1b[0m\n`);
1097
+ } else {
1098
+ process.stdout.write(" \x1b[32mNo type errors found!\x1b[0m\n");
1099
+ }
1100
+ if (totalWarnings > 0) {
1101
+ process.stdout.write(` \x1b[33m${totalWarnings} warning(s)\x1b[0m\n`);
1102
+ }
1103
+ }
1104
+
1105
+ async function runCheck(args: string[]): Promise<void> {
1106
+ const { patterns, options, sharedConfig } = parseCheckCommand(args);
1107
+ if (options.help) {
1108
+ printCheckUsage();
1109
+ return;
1110
+ }
1111
+
1112
+ const config = await loadConfig(process.cwd(), {
1113
+ mode: sharedConfig.configMode,
1114
+ configFile: sharedConfig.configFile,
1115
+ env: {
1116
+ mode: process.env.NODE_ENV ?? "development",
1117
+ command: "check",
1118
+ },
1119
+ });
1120
+
1121
+ if (sharedConfig.configFile && !config) {
1122
+ throw new Error(`Could not find config file: ${sharedConfig.configFile}`);
1123
+ }
1124
+
1125
+ if (config?.typeChecker?.enabled === false) {
1126
+ process.stderr.write(
1127
+ "[vize] Skipping check because typeChecker.enabled is false in vize.config.\n",
1128
+ );
1129
+ return;
1130
+ }
1131
+
1132
+ options.strict ??= config?.typeChecker?.strict;
1133
+ options.checkProps ??= config?.typeChecker?.checkProps;
1134
+ options.checkEmits ??= config?.typeChecker?.checkEmits;
1135
+ options.checkTemplateBindings ??= config?.typeChecker?.checkTemplateBindings;
1136
+
1137
+ const files = collectVueFiles(patterns);
1138
+ if (files.length === 0) {
1139
+ process.stderr.write(`No Vue files found matching inputs: ${JSON.stringify(patterns)}\n`);
1140
+ return;
1141
+ }
1142
+
1143
+ const native = loadNative("check");
1144
+ const start = performance.now();
1145
+ const results = files.map((file) => {
1146
+ const source = readFileSync(file, "utf8");
1147
+ return {
1148
+ file,
1149
+ source,
1150
+ result: native.typeCheck(source, toNativeTypeCheckOptions(file, options)),
1151
+ };
1152
+ });
1153
+ const timeMs = performance.now() - start;
1154
+ const totalErrors = results.reduce((sum, { result }) => sum + result.errorCount, 0);
1155
+ const totalWarnings = results.reduce((sum, { result }) => sum + result.warningCount, 0);
1156
+
1157
+ if (options.format === "json") {
1158
+ process.stdout.write(
1159
+ `${JSON.stringify(
1160
+ {
1161
+ files: results.map(({ file, result }) => ({
1162
+ file: displayPath(file),
1163
+ diagnostics: result.diagnostics,
1164
+ virtualTs: result.virtualTs,
1165
+ })),
1166
+ errorCount: totalErrors,
1167
+ warningCount: totalWarnings,
1168
+ fileCount: results.length,
1169
+ },
1170
+ null,
1171
+ 2,
1172
+ )}\n`,
1173
+ );
1174
+ } else {
1175
+ renderCheckText(results, options, timeMs);
1176
+ }
1177
+
1178
+ if (totalErrors > 0) {
1179
+ process.exit(1);
1180
+ }
1181
+
1182
+ if (options.maxWarnings !== undefined && totalWarnings > options.maxWarnings) {
1183
+ process.stderr.write(`\nToo many warnings (${totalWarnings} > max ${options.maxWarnings})\n`);
1184
+ process.exit(1);
1185
+ }
1186
+ }
1187
+
245
1188
  async function runLint(args: string[]): Promise<void> {
246
1189
  const { patterns, options, sharedConfig } = parseLintCommand(args);
247
1190
  const config = await loadConfig(process.cwd(), {
@@ -268,7 +1211,7 @@ async function runLint(args: string[]): Promise<void> {
268
1211
  patterns.push(".");
269
1212
  }
270
1213
 
271
- const native = loadNative();
1214
+ const native = loadNative("lint");
272
1215
  const result = native.lint(patterns, {
273
1216
  format: options.format,
274
1217
  max_warnings: options.maxWarnings,
@@ -301,12 +1244,236 @@ async function runLint(args: string[]): Promise<void> {
301
1244
  }
302
1245
  }
303
1246
 
1247
+ // ============================================================================
1248
+ // Upgrade command
1249
+ // ============================================================================
1250
+
1251
+ type PackageManager = "bun" | "npm" | "pnpm" | "vp" | "yarn";
1252
+
1253
+ interface UpgradeOptions {
1254
+ packageManager?: PackageManager;
1255
+ global?: boolean;
1256
+ dryRun?: boolean;
1257
+ help?: boolean;
1258
+ }
1259
+
1260
+ function parseUpgradeCommand(args: string[]): UpgradeOptions {
1261
+ const options: UpgradeOptions = {};
1262
+
1263
+ for (let i = 0; i < args.length; i++) {
1264
+ const arg = args[i];
1265
+ if (arg === "--package-manager") {
1266
+ const packageManager = args[++i];
1267
+ if (
1268
+ packageManager === "bun" ||
1269
+ packageManager === "npm" ||
1270
+ packageManager === "pnpm" ||
1271
+ packageManager === "vp" ||
1272
+ packageManager === "yarn"
1273
+ ) {
1274
+ options.packageManager = packageManager;
1275
+ }
1276
+ } else if (arg === "--global" || arg === "-g") {
1277
+ options.global = true;
1278
+ } else if (arg === "--dry-run") {
1279
+ options.dryRun = true;
1280
+ } else if (arg === "--help" || arg === "-h") {
1281
+ options.help = true;
1282
+ }
1283
+ }
1284
+
1285
+ return options;
1286
+ }
1287
+
1288
+ function readCwdPackageJson(): {
1289
+ packageManager?: string;
1290
+ dependencies?: Record<string, string>;
1291
+ devDependencies?: Record<string, string>;
1292
+ } | null {
1293
+ const packageJsonPath = path.join(process.cwd(), "package.json");
1294
+ if (!existsSync(packageJsonPath)) {
1295
+ return null;
1296
+ }
1297
+ return JSON.parse(readFileSync(packageJsonPath, "utf8"));
1298
+ }
1299
+
1300
+ function detectPackageManager(explicit?: PackageManager): PackageManager {
1301
+ if (explicit) {
1302
+ return explicit;
1303
+ }
1304
+
1305
+ const userAgent = process.env.npm_config_user_agent ?? "";
1306
+ if (userAgent.startsWith("pnpm")) {
1307
+ return "pnpm";
1308
+ }
1309
+ if (userAgent.startsWith("yarn")) {
1310
+ return "yarn";
1311
+ }
1312
+ if (userAgent.startsWith("bun")) {
1313
+ return "bun";
1314
+ }
1315
+ if (userAgent.startsWith("npm")) {
1316
+ return "npm";
1317
+ }
1318
+
1319
+ const packageManager = readCwdPackageJson()?.packageManager;
1320
+ if (packageManager?.startsWith("pnpm")) {
1321
+ return "pnpm";
1322
+ }
1323
+ if (packageManager?.startsWith("yarn")) {
1324
+ return "yarn";
1325
+ }
1326
+ if (packageManager?.startsWith("bun")) {
1327
+ return "bun";
1328
+ }
1329
+ return "npm";
1330
+ }
1331
+
1332
+ function buildUpgradeCommand(
1333
+ packageManager: PackageManager,
1334
+ options: UpgradeOptions,
1335
+ ): { command: string; args: string[] } {
1336
+ const packageJson = readCwdPackageJson();
1337
+ const saveDev = !packageJson?.dependencies?.vize;
1338
+ const packageSpec = "vize@latest";
1339
+
1340
+ if (packageManager === "vp") {
1341
+ return {
1342
+ command: "vp",
1343
+ args: ["install", ...(options.global ? ["-g"] : saveDev ? ["-D"] : []), packageSpec],
1344
+ };
1345
+ }
1346
+ if (packageManager === "pnpm") {
1347
+ return {
1348
+ command: "pnpm",
1349
+ args: ["add", ...(options.global ? ["-g"] : saveDev ? ["-D"] : []), packageSpec],
1350
+ };
1351
+ }
1352
+ if (packageManager === "yarn") {
1353
+ return {
1354
+ command: "yarn",
1355
+ args: options.global
1356
+ ? ["global", "add", packageSpec]
1357
+ : ["add", ...(saveDev ? ["-D"] : []), packageSpec],
1358
+ };
1359
+ }
1360
+ if (packageManager === "bun") {
1361
+ return {
1362
+ command: "bun",
1363
+ args: ["add", ...(options.global ? ["-g"] : saveDev ? ["-d"] : []), packageSpec],
1364
+ };
1365
+ }
1366
+ return {
1367
+ command: "npm",
1368
+ args: ["install", ...(options.global ? ["-g"] : saveDev ? ["-D"] : []), packageSpec],
1369
+ };
1370
+ }
1371
+
1372
+ function runUpgrade(args: string[]): void {
1373
+ const options = parseUpgradeCommand(args);
1374
+ if (options.help) {
1375
+ printUpgradeUsage();
1376
+ return;
1377
+ }
1378
+
1379
+ const packageManager = detectPackageManager(options.packageManager);
1380
+ const command = buildUpgradeCommand(packageManager, options);
1381
+
1382
+ if (options.dryRun) {
1383
+ process.stdout.write(`${command.command} ${command.args.join(" ")}\n`);
1384
+ return;
1385
+ }
1386
+
1387
+ const result = spawnSync(command.command, command.args, {
1388
+ stdio: "inherit",
1389
+ cwd: process.cwd(),
1390
+ env: process.env,
1391
+ });
1392
+
1393
+ if (result.error) {
1394
+ throw result.error;
1395
+ }
1396
+
1397
+ process.exit(result.status ?? 1);
1398
+ }
1399
+
1400
+ // ============================================================================
1401
+ // Ready command
1402
+ // ============================================================================
1403
+
1404
+ interface ReadyOptions {
1405
+ output: string;
1406
+ ssr?: boolean;
1407
+ scriptExt: "preserve" | "downcompile";
1408
+ help?: boolean;
1409
+ }
1410
+
1411
+ interface ParsedReadyCommand {
1412
+ patterns: string[];
1413
+ options: ReadyOptions;
1414
+ }
1415
+
1416
+ function parseReadyCommand(args: string[]): ParsedReadyCommand {
1417
+ const patterns: string[] = [];
1418
+ const options: ReadyOptions = {
1419
+ output: "./dist",
1420
+ scriptExt: "downcompile",
1421
+ };
1422
+
1423
+ for (let i = 0; i < args.length; i++) {
1424
+ const arg = args[i];
1425
+ if (arg === "--output" || arg === "-o") {
1426
+ options.output = args[++i] ?? options.output;
1427
+ } else if (arg === "--ssr") {
1428
+ options.ssr = true;
1429
+ } else if (arg === "--script-ext") {
1430
+ const scriptExt = args[++i];
1431
+ if (scriptExt === "preserve" || scriptExt === "downcompile") {
1432
+ options.scriptExt = scriptExt;
1433
+ }
1434
+ } else if (arg === "--help" || arg === "-h") {
1435
+ options.help = true;
1436
+ } else if (!arg.startsWith("-")) {
1437
+ patterns.push(arg);
1438
+ }
1439
+ }
1440
+
1441
+ return { patterns, options };
1442
+ }
1443
+
1444
+ async function runReady(args: string[]): Promise<void> {
1445
+ const { patterns, options } = parseReadyCommand(args);
1446
+ if (options.help) {
1447
+ printReadyUsage();
1448
+ return;
1449
+ }
1450
+
1451
+ process.stderr.write("vize ready: fmt\n");
1452
+ await runFmt(["--write", ...patterns]);
1453
+
1454
+ process.stderr.write("vize ready: lint\n");
1455
+ await runLint(patterns);
1456
+
1457
+ process.stderr.write("vize ready: check\n");
1458
+ await runCheck(patterns);
1459
+
1460
+ process.stderr.write("vize ready: build\n");
1461
+ await runBuild([
1462
+ "--output",
1463
+ options.output,
1464
+ "--script-ext",
1465
+ options.scriptExt,
1466
+ ...(options.ssr ? ["--ssr"] : []),
1467
+ ...patterns,
1468
+ ]);
1469
+ }
1470
+
304
1471
  // ============================================================================
305
1472
  // Command router
306
1473
  // ============================================================================
307
1474
 
308
- const NAPI_COMMANDS = new Set(["lint"]);
309
- const JS_COMMANDS = new Set(["musea"]);
1475
+ const NAPI_COMMANDS = new Set(["build", "check", "fmt", "lint"]);
1476
+ const JS_COMMANDS = new Set(["musea", "ready", "upgrade"]);
310
1477
 
311
1478
  async function main(): Promise<void> {
312
1479
  const args = process.argv.slice(2);
@@ -320,6 +1487,15 @@ async function main(): Promise<void> {
320
1487
  if (NAPI_COMMANDS.has(command)) {
321
1488
  const commandArgs = args.slice(1);
322
1489
  switch (command) {
1490
+ case "build":
1491
+ await runBuild(commandArgs);
1492
+ break;
1493
+ case "check":
1494
+ await runCheck(commandArgs);
1495
+ break;
1496
+ case "fmt":
1497
+ await runFmt(commandArgs);
1498
+ break;
323
1499
  case "lint":
324
1500
  await runLint(commandArgs);
325
1501
  break;
@@ -330,6 +1506,12 @@ async function main(): Promise<void> {
330
1506
  case "musea":
331
1507
  runMusea(commandArgs);
332
1508
  break;
1509
+ case "ready":
1510
+ await runReady(commandArgs);
1511
+ break;
1512
+ case "upgrade":
1513
+ runUpgrade(commandArgs);
1514
+ break;
333
1515
  }
334
1516
  } else {
335
1517
  printUsage();