vite-plus 1.2.0 → 1.4.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/AGENTS.md +4 -0
- package/LICENSE +1 -849
- package/README.md +1 -1
- package/bin/vpx +9 -0
- package/binding/index.cjs +59 -54
- package/binding/index.d.cts +132 -2
- package/dist/{agent-KeDc6vmg.js → agent-D8oxxhVR.js} +7 -9
- package/dist/bin.js +27 -34
- package/dist/{cli-parse-DHE8NrBU.js → cli-parse-aeUeFDlC.js} +2 -3
- package/dist/command-TjS_6y9O.js +1 -0
- package/dist/{command-CguLh2KL.js → command-rY79K5NI.js} +40 -42
- package/dist/{comparison-CmVirWIW-DUGaiYqF.js → comparison-CmVirWIW-UlEf2maz.js} +1 -2
- package/dist/config/bin.js +8 -10
- package/dist/{constants-BW3g7VnD.js → constants-DRUnJ_Dz.js} +4 -6
- package/dist/create/bin.d.ts +4 -8
- package/dist/create/bin.js +64 -68
- package/dist/{define-config-CIlk9scU.js → define-config-ChwJcu5e.js} +6 -9
- package/dist/{define-config-Dq_23Eyr.cjs → define-config-Cv2P6NT2.cjs} +8 -13
- package/dist/{define-config-Bexdd6RZ.d.ts → define-config-rPhYIFSn.d.ts} +1 -11
- package/dist/define-config.cjs +1 -1
- package/dist/define-config.d.ts +1 -1
- package/dist/define-config.js +1 -1
- package/dist/{dist-Bi7ZEK3c.js → dist-Bs0SObNC.js} +77 -149
- package/dist/{dist-Cqz8OtiO.js → dist-CdkMvIiQ.js} +11 -5
- package/dist/dist-zDZV5eEc.js +3 -0
- package/dist/{documentation-DqcZSthV.js → documentation-cv_y0Auv.js} +1 -2
- package/dist/{editor-b1XUwt9v.js → editor-ClL78D8u.js} +1492 -4211
- package/dist/{help-BmKpeOP9.js → help-B03t_iQ4.js} +1 -2
- package/dist/hooks/bin.js +4 -5
- package/dist/{hooks-DhwsWb46.js → hooks-xRcJPc-O.js} +5 -4
- package/dist/index.cjs +2 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{json-JP24qHUF.js → json-kl84kDyH.js} +9 -18
- package/dist/lint-plugins.cjs +1 -1
- package/dist/migration/bin.js +719 -184
- package/dist/migration/compat/worker.js +4 -6
- package/dist/{oxlint-plugin-config-Dx-ZY2MN.js → oxlint-plugin-config-BVpqYdcC.js} +3 -4
- package/dist/oxlint-plugin.d.ts +0 -2
- package/dist/oxlint-plugin.js +6 -7
- package/dist/{pack-BK38MLVZ.d.ts → pack-ARPFMefW.d.ts} +0 -2
- package/dist/pack-bin.d.ts +1 -3
- package/dist/pack-bin.js +3 -4
- package/dist/pack.d.ts +1 -1
- package/dist/{package-BP16iQby.js → package-DzsomcUD.js} +10 -95
- package/dist/{package-overrides-D1RcXSFl.js → package-overrides-Ci8GXouY.js} +5 -8
- package/dist/picomatch-DEUoj81q.js +1916 -0
- package/dist/{prompts-BGpQtC5z.js → prompts-GL1vj0wk.js} +113 -239
- package/dist/{protocol-D99W10Qi.js → protocol-CkNmY-Mr.js} +1 -2
- package/dist/{report-NhQk8VHb.js → report-DLHO30yZ.js} +2 -2
- package/dist/{resolve-vite-config-x47JetlI.js → resolve-vite-config-Dn5896A4.js} +9 -25
- package/dist/resolve-vite-config-QO6cC0SP.js +1 -0
- package/dist/{rolldown-runtime-CMFfr-1z.js → rolldown-runtime-DegdyBIC.js} +2 -3
- package/dist/script-esm-hooks.d.ts +8 -0
- package/dist/script-esm-hooks.js +24 -0
- package/dist/script-hooks-9Fof5UMY.js +176 -0
- package/dist/script-preload.cjs +468 -0
- package/dist/script-register.d.ts +1 -0
- package/dist/script-register.js +22 -0
- package/dist/script-watch.d.ts +1 -0
- package/dist/script-watch.js +1908 -0
- package/dist/staged/bin.js +81 -2011
- package/dist/sync-versions/bin.js +29 -50
- package/dist/sync-versions/bin.mjs +117 -224
- package/dist/{terminal-MKGAuy-p.js → terminal-BcuPUy-j.js} +1 -2
- package/dist/toolchain.js +36 -10
- package/dist/toolchain.json +36 -10
- package/dist/{tsconfig-C5PnJfW4.js → tsconfig-Do6MAG_f.js} +1 -1
- package/dist/{tsconfig-D2DzAQ_D.js → tsconfig-LE_mAwe_.js} +8 -10
- package/dist/version.d.ts +1 -3
- package/dist/version.js +5 -6
- package/dist/versions.js +5 -5
- package/dist/{vitest-ecosystem-BU0AqZq-.js → vitest-ecosystem-BeEYz-qF.js} +1 -2
- package/dist/{vitest-v5-entry-points-DthU1e_O.js → vitest-v5-entry-points-NyJsFeux.js} +1 -2
- package/dist/vpx-bin.d.ts +1 -0
- package/dist/vpx-bin.js +232 -0
- package/dist/vpx-script-EXgV5QOf.js +434 -0
- package/docs/config/run.md +4 -0
- package/docs/guide/check.md +1 -0
- package/docs/guide/ci.md +13 -3
- package/docs/guide/create.md +34 -0
- package/docs/guide/env.md +27 -13
- package/docs/guide/global-cli.md +52 -0
- package/docs/guide/local-cli.md +4 -4
- package/docs/guide/migrate-rules.md +48 -5
- package/docs/guide/migrate.md +6 -6
- package/docs/guide/package-management.md +19 -2
- package/docs/guide/test.md +1 -1
- package/docs/guide/vitest-v5.md +2 -2
- package/docs/guide/vpx.md +170 -9
- package/package.json +32 -29
- package/dist/command-DaRqSNpP.js +0 -1
- package/dist/dist-CBWGibx8.js +0 -3
- package/dist/resolve-vite-config-CGzlyhGr.js +0 -1
package/dist/migration/bin.js
CHANGED
|
@@ -1,129 +1,623 @@
|
|
|
1
|
-
import { t as documentationUrl$1 } from "../documentation-
|
|
2
|
-
import { a as muted, c as warnMsg, i as log, o as printHeader, r as formatDuration, t as accent } from "../terminal-
|
|
3
|
-
import { _ as engines, a as SETUP_VP_VERSION, d as VITE_PLUS_OVERRIDE_PACKAGES, f as VITE_PLUS_VERSION, m as isForceOverrideMode } from "../constants-
|
|
4
|
-
import { a as writeJsonFile, c as modify, i as readJsonFile, l as parse, o as applyEdits, s as findNodeAtLocation, u as parseTree } from "../json-
|
|
5
|
-
import { a as hasVitePlusDependency, i as hasPackageManagerDeclaration, n as detectPackageMetadata, o as readNearestPackageJson } from "../package-
|
|
6
|
-
import { t as vitest_v5_entry_points_default } from "../vitest-v5-entry-points-
|
|
7
|
-
import { C as confirm, D as outro, M as isCancel, O as select, S as normalize$1, T as log$1, c as selectPackageManager, i as promptGitHooks, k as spinner, l as upgradeYarn, n as defaultInteractive, o as runViteFmt, r as downloadPackageManager$1, s as runViteInstall, t as cancelAndExit, x as getMajor, y as PackageManager } from "../prompts-
|
|
8
|
-
import { _ as isGreaterThanOrEqual, p as compare } from "../comparison-CmVirWIW-
|
|
9
|
-
import { a as rangeToComparators, i as normalizeRange, n as findMinimumForRange, o as rangesIntersect, r as isRangeSubset, s as satisfies, t as extractOverrideTargetName } from "../package-overrides-
|
|
10
|
-
import { i as selectAgentTargetPaths, l as displayRelative, n as detectAgentConflicts, r as detectExistingAgentTargetPaths, s as writeAgentInstructions } from "../agent-
|
|
11
|
-
import { n as addMigrationWarning, r as createMigrationReport } from "../report-
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { t as require_dist } from "../dist-
|
|
15
|
-
import { a as hasBaseUrlInTsconfig, i as fixBaseUrlInTsconfig, t as confirmBaseUrlFix } from "../tsconfig-
|
|
16
|
-
import { t as
|
|
17
|
-
import { t as
|
|
1
|
+
import { t as documentationUrl$1 } from "../documentation-cv_y0Auv.js";
|
|
2
|
+
import { a as muted, c as warnMsg, i as log, o as printHeader, r as formatDuration, t as accent } from "../terminal-BcuPUy-j.js";
|
|
3
|
+
import { _ as engines, a as SETUP_VP_VERSION, d as VITE_PLUS_OVERRIDE_PACKAGES, f as VITE_PLUS_VERSION, m as isForceOverrideMode } from "../constants-DRUnJ_Dz.js";
|
|
4
|
+
import { a as writeJsonFile, c as modify, i as readJsonFile, l as parse, n as editJsonFile, o as applyEdits, r as isJsonFile, s as findNodeAtLocation, u as parseTree } from "../json-kl84kDyH.js";
|
|
5
|
+
import { a as hasVitePlusDependency, i as hasPackageManagerDeclaration, n as detectPackageMetadata, o as readNearestPackageJson } from "../package-DzsomcUD.js";
|
|
6
|
+
import { t as vitest_v5_entry_points_default } from "../vitest-v5-entry-points-NyJsFeux.js";
|
|
7
|
+
import { C as confirm, D as outro, M as isCancel, O as select, S as normalize$1, T as log$1, c as selectPackageManager, i as promptGitHooks, k as spinner, l as upgradeYarn, n as defaultInteractive, o as runViteFmt, r as downloadPackageManager$1, s as runViteInstall, t as cancelAndExit, x as getMajor, y as PackageManager } from "../prompts-GL1vj0wk.js";
|
|
8
|
+
import { _ as isGreaterThanOrEqual, p as compare } from "../comparison-CmVirWIW-UlEf2maz.js";
|
|
9
|
+
import { a as rangeToComparators, i as normalizeRange, n as findMinimumForRange, o as rangesIntersect, r as isRangeSubset, s as satisfies, t as extractOverrideTargetName } from "../package-overrides-Ci8GXouY.js";
|
|
10
|
+
import { i as selectAgentTargetPaths, l as displayRelative, n as detectAgentConflicts, r as detectExistingAgentTargetPaths, s as writeAgentInstructions } from "../agent-D8oxxhVR.js";
|
|
11
|
+
import { n as addMigrationWarning, r as createMigrationReport } from "../report-DLHO30yZ.js";
|
|
12
|
+
import { A as detectVitePlusBootstrapPending, C as removeMigratedOxlintConfigs, Ct as warnMigration, D as mergeViteConfigFiles, E as mergeTsdownConfigFile, F as detectYarnPnpMode, I as createCatalogDependencyResolver, J as detectFramework, K as extractOverrideParentSegments, M as sourceTreeMatches, N as usesWebdriverioProvider, O as retryPreservedOxlintConfigs, P as configureYarnNodeModulesMode, Q as migrateTsupToTsdown, R as detectWorkspace$1, S as prepareOxlintConfigs, St as readPackageJsonIfExists, T as injectLintTypeCheckDefaults, U as minimatch, W as findGitRoot, X as confirmTsupMigration, Y as hasFrameworkShim, Z as detectTsupProject, _ as hasExistingViteHooksPolicy, _t as WEBDRIVERIO_PROVIDER, at as warnPackageLevelPrettier, b as preflightGitHooksSetup, bt as getCommandStartIndex, ct as detectIncompatibleEslintIntegration, d as finalizeCoreMigrationForExistingVitePlus, dt as warnIncompatibleEslintIntegration, et as warnMissingTsupConfig, f as detectNodeVersionManagerFile, ft as warnLegacyEslintConfig, g as detectLegacyGitHooksMigrationCandidate, gt as SHELL_STATE_COMMANDS, ht as RETENTION_SCAN_SKIP_DIRS, i as selectEditor, j as ensureVitePlusBootstrap, k as collectToolchainVersionChanges, l as rewriteStandaloneProject, lt as migrateEslintToOxlint, m as migrateSetupVpVersion, mt as LINT_STAGED_JSON_CONFIG_FILES, nt as detectPrettierProject, o as writeEditorConfigs, ot as confirmEslintMigration, p as migrateNodeVersionManagerFile, pt as warnPackageLevelEslint, q as addFrameworkShim, r as isJsonLikeFile, rt as migratePrettierToOxfmt, s as rewriteMonorepo, st as detectEslintProject, t as detectEditorConflicts, tt as confirmPrettierMigration, u as detectPendingCoreMigration, v as installGitHooks, vt as checkViteVersion, x as shouldSkipStagedMigrationForHooks, xt as isPlainRecord, y as isViteHooksSetupComplete, yt as checkVitestVersion } from "../editor-ClL78D8u.js";
|
|
13
|
+
import { i as runCommandSilently } from "../command-rY79K5NI.js";
|
|
14
|
+
import { t as require_dist } from "../dist-Bs0SObNC.js";
|
|
15
|
+
import { a as hasBaseUrlInTsconfig, i as fixBaseUrlInTsconfig, t as confirmBaseUrlFix } from "../tsconfig-LE_mAwe_.js";
|
|
16
|
+
import { i as WATCH_OPTIONS_WITH_VALUE, n as SCRIPT_EXTENSIONS, t as NODE_OPTIONS_WITH_VALUE } from "../vpx-script-EXgV5QOf.js";
|
|
17
|
+
import { t as unwrapCliParseOutcome } from "../cli-parse-aeUeFDlC.js";
|
|
18
|
+
import { t as ROLLDOWN_COMPAT_RESULT_PREFIX } from "../protocol-CkNmY-Mr.js";
|
|
18
19
|
import path from "node:path";
|
|
19
20
|
import { fileURLToPath } from "node:url";
|
|
20
|
-
import { analyzeMigrationSource, isDirectoryGitignored, parseMigrateArgs } from "../../binding/index.js";
|
|
21
|
+
import { analyzeMigrationSource, isDirectoryGitignored, parseLiteralCommands, parseMigrateArgs } from "../../binding/index.js";
|
|
21
22
|
import { styleText } from "node:util";
|
|
22
23
|
import fs, { existsSync } from "node:fs";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
24
|
+
import os from "node:os";
|
|
25
|
+
// src/migration/migrator/tsx.ts
|
|
26
|
+
const TSX_COMMAND = /(?:^|[\s;&|("'=/\\`])tsx(?:\.(?:cmd|exe|ps1))?(?:\/(?:esm|cjs))?(?=$|[\s;&|)"'`])/i;
|
|
27
|
+
const LOADER_OPTIONS = /* @__PURE__ */ new Set([
|
|
28
|
+
"--import",
|
|
29
|
+
"--loader",
|
|
30
|
+
"--experimental-loader",
|
|
31
|
+
"--require",
|
|
32
|
+
"-r"
|
|
33
|
+
]);
|
|
34
|
+
const TSX_LOADER = /^tsx(?:\/(?:esm|cjs))?$/;
|
|
35
|
+
const LOADER_SETTINGS = /* @__PURE__ */ new Set([
|
|
36
|
+
"import",
|
|
37
|
+
"loader",
|
|
38
|
+
"require"
|
|
39
|
+
]);
|
|
40
|
+
const KEEP_EVAL = "`vpx` does not run TypeScript passed to --eval or --print";
|
|
41
|
+
const KEEP_CHECK = "`vpx` does not transform TypeScript for -c or --check";
|
|
42
|
+
const KEEP_REPL = "`vpx` does not start a TypeScript REPL";
|
|
43
|
+
const KEEP_TEST_GLOBS = "`--test` without files finds `.ts` tests with tsx globs; list the test files for `vpx --test`";
|
|
44
|
+
const KEEP_WATCH_PATH = "`--watch-path` has no `vpx` equivalent; use `--include`";
|
|
45
|
+
const KEEP_SHELL = "the command uses shell syntax that vp migrate does not rewrite, such as pipes, variables, or subshells";
|
|
46
|
+
const KEEP_OTHER = "vp migrate does not rewrite this use of tsx";
|
|
47
|
+
const KEEP_TSX_TSCONFIG = "`TSX_TSCONFIG_PATH` selects the tsconfig for tsx only; pass it to `vpx` as `--tsconfig`";
|
|
48
|
+
const KEEP_NODE_OPTIONS = "`NODE_OPTIONS` loads tsx, whose hooks would compete with those of `vpx`; remove it first";
|
|
49
|
+
const KEEP_WATCH_TEST = "`vpx --watch` does not run `--test`";
|
|
50
|
+
const KEEP_OPTIONS = /* @__PURE__ */ new Map([
|
|
51
|
+
["-e", KEEP_EVAL],
|
|
52
|
+
["--eval", KEEP_EVAL],
|
|
53
|
+
["-p", KEEP_EVAL],
|
|
54
|
+
["--print", KEEP_EVAL],
|
|
55
|
+
["-pe", KEEP_EVAL],
|
|
56
|
+
["-c", KEEP_CHECK],
|
|
57
|
+
["--check", KEEP_CHECK],
|
|
58
|
+
["-i", KEEP_REPL],
|
|
59
|
+
["--interactive", KEEP_REPL],
|
|
60
|
+
["--watch-path", KEEP_WATCH_PATH]
|
|
61
|
+
]);
|
|
62
|
+
const TSX_ENV_SHEBANG = /^(#!\s*\S*\benv\s+(?:-S\s+)?)(tsx(?=[ \t\r\n]|$)[^\r\n]*)/;
|
|
63
|
+
const SHEBANG_ENTRY = "./shebang.ts";
|
|
64
|
+
const TSX_SHEBANG = /^#![^\n]*[\s/]tsx(?=\s|$)/;
|
|
65
|
+
const TSX_LOADER_FLAG = /(?:(?:--import|--loader|--experimental-loader|--require|-r)[= \t]+|(?<![\w-])(?:import|loader|experimental-loader|require)=)['"]?tsx(?:\/(?:esm|cjs))?(?![\w./-])/;
|
|
66
|
+
const TSX_CODE_REFERENCE = new RegExp([
|
|
67
|
+
/(?:\bfrom|\bimport|\bimport\.meta\.resolve|\brequire(?:\.resolve)?)\s*\(?\s*(?:\/\*[\s\S]*?\*\/\s*)*['"`]tsx(?:\/[^'"`]*)?['"`]/.source,
|
|
68
|
+
/['"`](?:npx |pnpm (?:exec )?|yarn |bunx )?tsx\s/.source,
|
|
69
|
+
/\(\s*\[?\s*['"`]tsx['"`]\s*[,)\]]/.source,
|
|
70
|
+
TSX_LOADER_FLAG.source,
|
|
71
|
+
/['"`](?:--import|--loader|--experimental-loader|--require|-r)['"`]\s*,\s*['"`]tsx(?:\/(?:esm|cjs))?['"`]/.source
|
|
72
|
+
].join("|"));
|
|
73
|
+
const TSX_TEXT_REFERENCE = new RegExp([/(?:^|[\s;&|("'=/\\`])tsx(?:\.(?:cmd|exe|ps1))?(?:\/(?:esm|cjs))?['"]?(?:[ \t]+\S|[ \t]*$)/.source, TSX_LOADER_FLAG.source].join("|"), "m");
|
|
74
|
+
const TSX_CONFIG_VALUE = new RegExp([
|
|
75
|
+
/(?:^|[\s;&|("'=/\\`])tsx(?:\.(?:cmd|exe|ps1))?[ \t]+\S/.source,
|
|
76
|
+
TSX_LOADER_FLAG.source,
|
|
77
|
+
/^tsx\//.source
|
|
78
|
+
].join("|"), "i");
|
|
79
|
+
const IGNORED_FIELDS = /* @__PURE__ */ new Set([
|
|
80
|
+
"bundleDependencies",
|
|
81
|
+
"bundledDependencies",
|
|
82
|
+
"dependencies",
|
|
83
|
+
"description",
|
|
84
|
+
"devDependencies",
|
|
85
|
+
"keywords",
|
|
86
|
+
"name",
|
|
87
|
+
"optionalDependencies",
|
|
88
|
+
"overrides",
|
|
89
|
+
"peerDependencies",
|
|
90
|
+
"peerDependenciesMeta",
|
|
91
|
+
"pnpm",
|
|
92
|
+
"resolutions"
|
|
93
|
+
]);
|
|
94
|
+
const CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
95
|
+
...SCRIPT_EXTENSIONS,
|
|
96
|
+
".astro",
|
|
97
|
+
".svelte",
|
|
98
|
+
".vue"
|
|
99
|
+
]);
|
|
100
|
+
const REFERENCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
101
|
+
...CODE_EXTENSIONS,
|
|
102
|
+
".json",
|
|
103
|
+
".jsonc",
|
|
104
|
+
".toml",
|
|
105
|
+
".yml",
|
|
106
|
+
".yaml",
|
|
107
|
+
".sh",
|
|
108
|
+
".bash",
|
|
109
|
+
".zsh",
|
|
110
|
+
".fish",
|
|
111
|
+
".ps1",
|
|
112
|
+
".cmd",
|
|
113
|
+
".bat"
|
|
114
|
+
]);
|
|
115
|
+
const LOCKFILES = /* @__PURE__ */ new Set([
|
|
116
|
+
"bun.lock",
|
|
117
|
+
"npm-shrinkwrap.json",
|
|
118
|
+
"package-lock.json",
|
|
119
|
+
"pnpm-lock.yaml",
|
|
120
|
+
"yarn.lock"
|
|
121
|
+
]);
|
|
122
|
+
const USE_SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
123
|
+
...RETENTION_SCAN_SKIP_DIRS,
|
|
124
|
+
".cache",
|
|
125
|
+
".next",
|
|
126
|
+
".nuxt",
|
|
127
|
+
".svelte-kit",
|
|
128
|
+
".turbo",
|
|
129
|
+
".vite",
|
|
130
|
+
"coverage"
|
|
131
|
+
]);
|
|
132
|
+
const ESM_SYNTAX = /(?:^|;)\s*(?:import(?![\w$])\s*(?![\w$]+\s*=)[\w*{'"]|export(?![\w$])\s*[\w*{])|\bimport\.meta\b/m;
|
|
133
|
+
const COMMONJS_GLOBALS = [
|
|
134
|
+
["__dirname", /(?<![.\w$])__dirname\b/],
|
|
135
|
+
["__filename", /(?<![.\w$])__filename\b/],
|
|
136
|
+
["require", /(?<![.\w$])require\s*(?:\(|\.\s*[\w$])/],
|
|
137
|
+
["exports", /(?<![.\w$'"`])exports(?![\w$'"`]|\s*:)/],
|
|
138
|
+
["module", /(?<![.\w$])module\s*(?:\.\s*[\w$]|\[)/]
|
|
139
|
+
];
|
|
140
|
+
const EXPORT_ASSIGNMENT = /(?:^|;)\s*export\s*=/m;
|
|
141
|
+
const RELATIVE_REQUIRE = /(?<![.\w$])require\s*\(\s*['"`](\.{1,2}\/[^'"`]+?)\.(js|cjs|mjs|jsx)['"`]/g;
|
|
142
|
+
const TYPESCRIPT_EXTENSIONS = {
|
|
143
|
+
js: ["ts", "tsx"],
|
|
144
|
+
cjs: ["cts"],
|
|
145
|
+
mjs: ["mts"],
|
|
146
|
+
jsx: ["tsx"]
|
|
147
|
+
};
|
|
148
|
+
const shellCommand = (previous) => /^-[a-z]*c$/.test(previous);
|
|
149
|
+
const COMMAND_LAUNCHERS = /* @__PURE__ */ new Map([
|
|
150
|
+
["bash", shellCommand],
|
|
151
|
+
["concurrently", () => true],
|
|
152
|
+
["nodemon", (previous) => previous === "--exec" || previous === "-x"],
|
|
153
|
+
["sh", shellCommand],
|
|
154
|
+
["zsh", shellCommand]
|
|
155
|
+
]);
|
|
156
|
+
/**
|
|
157
|
+
* Rewrite the tsx invocations in a command to `vpx`: `tsx a.ts` becomes `vpx a.ts`,
|
|
158
|
+
* `tsx watch a.ts` becomes `vpx --watch a.ts` (`vpx watch` runs the npm package `watch`),
|
|
159
|
+
* and `node --import tsx a.ts` becomes `vpx a.ts`. Package-manager wrappers such as
|
|
160
|
+
* `npx tsx` go away with tsx, since `vpx` is a bin of vite-plus. A form `vpx` cannot run
|
|
161
|
+
* stays as it is, with the reason in `kept`. With `cwd`, the directory the command runs
|
|
162
|
+
* in, the scripts it runs are checked for syntax `vpx` runs differently.
|
|
163
|
+
*/
|
|
164
|
+
function rewriteTsxCommand(command, cwd) {
|
|
165
|
+
if (!TSX_COMMAND.test(command)) return {
|
|
166
|
+
command,
|
|
167
|
+
kept: []
|
|
168
|
+
};
|
|
169
|
+
const segments = parseLiteralCommands(command, true);
|
|
170
|
+
if (!segments || /%\w+%/.test(command)) return {
|
|
171
|
+
command,
|
|
172
|
+
kept: [KEEP_SHELL]
|
|
173
|
+
};
|
|
174
|
+
const rewrite = {
|
|
175
|
+
command,
|
|
176
|
+
cwd,
|
|
177
|
+
edits: [],
|
|
178
|
+
kept: []
|
|
179
|
+
};
|
|
180
|
+
for (const tokens of segments) {
|
|
181
|
+
const name = tokens[0].value;
|
|
182
|
+
if (SHELL_STATE_COMMANDS.includes(name) || name === "chdir") {
|
|
183
|
+
rewrite.cwd = void 0;
|
|
184
|
+
continue;
|
|
56
185
|
}
|
|
186
|
+
rewriteSegment(rewrite, tokens);
|
|
57
187
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
188
|
+
let rewritten = command;
|
|
189
|
+
for (const edit of rewrite.edits.toSorted((a, b) => b.start - a.start)) rewritten = rewritten.slice(0, edit.start) + edit.text + rewritten.slice(edit.end);
|
|
190
|
+
if (!rewrite.kept.length && TSX_COMMAND.test(rewritten)) rewrite.kept.push(KEEP_OTHER);
|
|
191
|
+
return {
|
|
192
|
+
command: rewritten,
|
|
193
|
+
kept: rewrite.kept
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function rewriteSegment(rewrite, tokens) {
|
|
197
|
+
const values = tokens.map(({ value }) => value);
|
|
198
|
+
let first = 0;
|
|
199
|
+
const skipAssignments = () => {
|
|
200
|
+
while (/^[A-Za-z_]\w*=/.test(values[first] ?? "")) first++;
|
|
201
|
+
};
|
|
202
|
+
skipAssignments();
|
|
203
|
+
const shell = values[first] === "cross-env-shell";
|
|
204
|
+
if (values[first] === "cross-env" || shell) {
|
|
205
|
+
first++;
|
|
206
|
+
skipAssignments();
|
|
207
|
+
}
|
|
208
|
+
if (first >= values.length) return;
|
|
209
|
+
const envReason = envKeepReason(values.slice(0, first));
|
|
210
|
+
const start = first + getCommandStartIndex(values.slice(first));
|
|
211
|
+
const launcher = COMMAND_LAUNCHERS.get(values[start]);
|
|
212
|
+
if (values[start] === "tsx") rewriteTsx(rewrite, tokens, first, start, envReason);
|
|
213
|
+
else if (values[start] === "node") rewriteNodeLoader(rewrite, tokens, first, start, envReason);
|
|
214
|
+
else if (shell) for (const token of tokens.slice(start)) rewriteQuotedCommand(rewrite, token, envReason);
|
|
215
|
+
else if (launcher) {
|
|
216
|
+
for (let index = start + 1; index < tokens.length; index++) if (launcher(values[index - 1])) rewriteQuotedCommand(rewrite, tokens[index], envReason);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Why environment assignments keep tsx for the command they apply to, or undefined.
|
|
221
|
+
* Names match in any case, as on Windows.
|
|
222
|
+
*/
|
|
223
|
+
function envKeepReason(assignments) {
|
|
224
|
+
if (assignments.some((value) => /^TSX_TSCONFIG_PATH=/i.test(value))) return KEEP_TSX_TSCONFIG;
|
|
225
|
+
if (assignments.some((value) => /^NODE_OPTIONS=/i.test(value) && TSX_COMMAND.test(value.slice(13)))) return KEEP_NODE_OPTIONS;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* An option's name without `=value`. Node.js reads `_` as `-` in long option names, as
|
|
229
|
+
* in `--watch_path`, and so does `vpx`.
|
|
230
|
+
*/
|
|
231
|
+
function optionName(arg) {
|
|
232
|
+
const name = arg.split("=", 1)[0];
|
|
233
|
+
return name.startsWith("--") ? `--${name.slice(2).replaceAll("_", "-")}` : name;
|
|
234
|
+
}
|
|
235
|
+
/** Remove `tokens[index]` with the space before it. */
|
|
236
|
+
function removal(tokens, index) {
|
|
237
|
+
return {
|
|
238
|
+
start: tokens[index - 1].end,
|
|
239
|
+
end: tokens[index].end,
|
|
240
|
+
text: ""
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Why `vpx` would fail on a script that tsx runs, judged from its source. tsx resolves
|
|
245
|
+
* `require('./a.js')` to `a.ts`, and compiles ES module syntax to CommonJS in a `.cts`
|
|
246
|
+
* file and in a package without `"type": "module"`. `vpx` resolves `require()` as
|
|
247
|
+
* Node.js does, rejects ES module syntax in a `.cts` file, and runs the other files as ES
|
|
248
|
+
* modules, so `__dirname`, `__filename`, and `require` are not defined.
|
|
249
|
+
*/
|
|
250
|
+
function scriptKeepReason(cwd, script) {
|
|
251
|
+
if (cwd === void 0 || /[*?[{]/.test(script) || !SCRIPT_EXTENSIONS.some((extension) => script.endsWith(extension))) return;
|
|
252
|
+
const file = script.startsWith("~/") ? path.join(os.homedir(), script.slice(2)) : path.resolve(cwd, script);
|
|
253
|
+
let source;
|
|
254
|
+
try {
|
|
255
|
+
source = fs.readFileSync(file, "utf8");
|
|
256
|
+
} catch (error) {
|
|
257
|
+
return error.code === "ENOENT" ? `\`${script}\` does not exist yet, so vp migrate cannot check it` : void 0;
|
|
258
|
+
}
|
|
259
|
+
for (const [, base, extension] of source.matchAll(RELATIVE_REQUIRE)) {
|
|
260
|
+
const resolve = (ext) => path.resolve(path.dirname(file), `${base}.${ext}`);
|
|
261
|
+
const typescript = fs.existsSync(resolve(extension)) ? void 0 : TYPESCRIPT_EXTENSIONS[extension].find((ext) => fs.existsSync(resolve(ext)));
|
|
262
|
+
if (typescript) return `\`${script}\` requires \`${base}.${extension}\`, which only tsx resolves to \`${base}.${typescript}\`; write the TypeScript extension`;
|
|
263
|
+
}
|
|
264
|
+
const code = stripComments(source);
|
|
265
|
+
if (!/\.(?:c?ts|tsx|jsx?)$/.test(script) || !(ESM_SYNTAX.test(source) || ESM_SYNTAX.test(code))) return;
|
|
266
|
+
if (script.endsWith(".cts")) return `\`${script}\` uses import or export, which \`vpx\` does not compile to CommonJS; rename it to .mts`;
|
|
267
|
+
if ((usesCommonJsGlobals(code) || EXPORT_ASSIGNMENT.test(code)) && readNearestPackageJson(path.dirname(file))?.type !== "module") return `\`${script}\` mixes import or export with CommonJS globals such as __dirname, require, or module.exports, and \`vpx\` runs it as an ES module; use import.meta.dirname, import.meta.filename, createRequire(), or export`;
|
|
268
|
+
}
|
|
269
|
+
/** `source` without comments; a string that only looks like one, as in `'http://x'`, stays. */
|
|
270
|
+
function stripComments(source) {
|
|
271
|
+
return source.replaceAll(/("(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'|`(?:\\.|[^`\\])*`)|\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, (_, string) => string ?? "");
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Whether `code`, without comments, uses a CommonJS global it does not declare or import
|
|
275
|
+
* at module level, as in `const require = createRequire(import.meta.url)` or `import
|
|
276
|
+
* module from 'node:module'`. A declaration inside a function, which an unindented line
|
|
277
|
+
* does not start, covers only that function.
|
|
278
|
+
*/
|
|
279
|
+
function usesCommonJsGlobals(code) {
|
|
280
|
+
return COMMONJS_GLOBALS.some(([name, use]) => use.test(code) && !new RegExp(`^(?:(?:export\\s+)?(?:const|let|var|function|class)\\s+${name}\\b|import\\s+(?:\\*\\s+as\\s+)?${name}\\b)`, "m").test(code));
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Why `vpx` cannot run what tsx or `node --import tsx` runs: `positionals` are the entry
|
|
284
|
+
* and its arguments, or with `--test`, the test files; `preloads` are local `--require`
|
|
285
|
+
* and `--import` modules, which load first.
|
|
286
|
+
*/
|
|
287
|
+
function runKeepReason(cwd, test, positionals, preloads) {
|
|
288
|
+
const [entry] = positionals;
|
|
289
|
+
if (entry === void 0) return test ? KEEP_TEST_GLOBS : KEEP_REPL;
|
|
290
|
+
if (!test && !SCRIPT_EXTENSIONS.some((extension) => entry.endsWith(extension))) return `\`${entry}\` has no file extension, which \`vpx\` needs to run it as a script`;
|
|
291
|
+
const scripts = test ? positionals.filter((value) => !value.startsWith("-")) : [entry];
|
|
292
|
+
for (const script of [...preloads, ...scripts]) {
|
|
293
|
+
const reason = scriptKeepReason(cwd, script);
|
|
294
|
+
if (reason) return reason;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function rewriteTsx(rewrite, tokens, first, start, keepReason) {
|
|
298
|
+
if (keepReason) {
|
|
299
|
+
rewrite.kept.push(keepReason);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
let index = start + 1;
|
|
303
|
+
const watch = tokens[index]?.value === "watch";
|
|
304
|
+
if (watch) index++;
|
|
305
|
+
const scanned = scanOptions(tokens, index, true);
|
|
306
|
+
const reason = runReason(rewrite.cwd, tokens, scanned, watch);
|
|
307
|
+
if (reason) {
|
|
308
|
+
rewrite.kept.push(reason);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
rewrite.edits.push({
|
|
312
|
+
start: tokens[first].start,
|
|
313
|
+
end: tokens[index - 1].end,
|
|
314
|
+
text: watch ? "vpx --watch" : "vpx"
|
|
315
|
+
}, ...scanned.removals);
|
|
316
|
+
}
|
|
317
|
+
function rewriteNodeLoader(rewrite, tokens, first, start, keepReason) {
|
|
318
|
+
const scanned = scanOptions(tokens, start + 1, false);
|
|
319
|
+
if (!scanned.loaders) {
|
|
320
|
+
if (keepReason === KEEP_NODE_OPTIONS) rewrite.kept.push(keepReason);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const reason = keepReason ?? runReason(rewrite.cwd, tokens, scanned, false);
|
|
324
|
+
if (reason) {
|
|
325
|
+
rewrite.kept.push(reason);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
rewrite.edits.push({
|
|
329
|
+
start: tokens[first].start,
|
|
330
|
+
end: tokens[start].end,
|
|
331
|
+
text: "vpx"
|
|
332
|
+
}, ...scanned.removals);
|
|
333
|
+
}
|
|
334
|
+
/** Read the options of `tsx` (`tsx` true) or `node` from `tokens[index]` up to the entry. */
|
|
335
|
+
function scanOptions(tokens, index, tsx) {
|
|
336
|
+
const scanned = {
|
|
337
|
+
entryIndex: tokens.length,
|
|
338
|
+
removals: [],
|
|
339
|
+
loaders: 0,
|
|
340
|
+
preloads: [],
|
|
341
|
+
test: false,
|
|
342
|
+
watch: false
|
|
343
|
+
};
|
|
344
|
+
for (; index < tokens.length; index++) {
|
|
345
|
+
const { value } = tokens[index];
|
|
346
|
+
if (value === "--" || !value.startsWith("-")) {
|
|
347
|
+
scanned.entryIndex = value === "--" ? index + 1 : index;
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
const name = optionName(value);
|
|
351
|
+
const inline = value.includes("=");
|
|
352
|
+
if (tsx && name === "--no-cache") {
|
|
353
|
+
scanned.removals.push(removal(tokens, index));
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (LOADER_OPTIONS.has(name)) {
|
|
357
|
+
const loader = inline ? value.slice(value.indexOf("=") + 1) : tokens[index + 1]?.value;
|
|
358
|
+
if (loader !== void 0 && TSX_LOADER.test(loader)) {
|
|
359
|
+
scanned.removals.push({
|
|
360
|
+
...removal(tokens, index),
|
|
361
|
+
end: tokens[inline ? index : index + 1].end
|
|
362
|
+
});
|
|
363
|
+
scanned.loaders++;
|
|
364
|
+
} else if (loader !== void 0 && (loader.startsWith(".") || path.isAbsolute(loader))) scanned.preloads.push(loader);
|
|
365
|
+
if (!inline) index++;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
scanned.keepReason ??= KEEP_OPTIONS.get(name);
|
|
369
|
+
scanned.test ||= name === "--test";
|
|
370
|
+
scanned.watch ||= name === "--watch";
|
|
371
|
+
if (!inline && (NODE_OPTIONS_WITH_VALUE.has(name) || tsx && (WATCH_OPTIONS_WITH_VALUE.has(name) || name === "--tsconfig"))) index++;
|
|
372
|
+
}
|
|
373
|
+
return scanned;
|
|
374
|
+
}
|
|
375
|
+
/** Why `vpx` cannot run what the scanned options and the scripts after them run. */
|
|
376
|
+
function runReason(cwd, tokens, scanned, watch) {
|
|
377
|
+
if (scanned.keepReason) return scanned.keepReason;
|
|
378
|
+
if ((watch || scanned.watch) && scanned.test) return KEEP_WATCH_TEST;
|
|
379
|
+
return runKeepReason(cwd, scanned.test, tokens.slice(scanned.entryIndex).map(({ value }) => value), scanned.preloads);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Rewrite a command that a launcher runs, given as one quoted argument. `inherited` keeps
|
|
383
|
+
* tsx for it, as when the launcher passes on `TSX_TSCONFIG_PATH`.
|
|
384
|
+
*/
|
|
385
|
+
function rewriteQuotedCommand(rewrite, token, inherited) {
|
|
386
|
+
const raw = rewrite.command.slice(token.start, token.end);
|
|
387
|
+
const quote = raw[0];
|
|
388
|
+
if (quote !== "\"" && quote !== "'" || raw.length < 2 || !raw.endsWith(quote)) return;
|
|
389
|
+
const inner = rewriteTsxCommand(token.value, rewrite.cwd);
|
|
390
|
+
rewrite.kept.push(...inner.kept);
|
|
391
|
+
if (inner.command === token.value) return;
|
|
392
|
+
if (inherited) rewrite.kept.push(inherited);
|
|
393
|
+
else if (!inner.command.includes(quote)) rewrite.edits.push({
|
|
394
|
+
start: token.start,
|
|
395
|
+
end: token.end,
|
|
396
|
+
text: `${quote}${inner.command}${quote}`
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/** The commands in a scripts or lint-staged record. */
|
|
400
|
+
function recordCommands(record) {
|
|
401
|
+
if (!isPlainRecord(record)) return [];
|
|
402
|
+
return Object.values(record).flatMap((value) => Array.isArray(value) ? value : [value]).filter((command) => typeof command === "string");
|
|
403
|
+
}
|
|
404
|
+
/** The commands a package.json runs: its scripts and lint-staged tasks. */
|
|
405
|
+
function packageCommands(pkg) {
|
|
406
|
+
return [...recordCommands(pkg.scripts), ...recordCommands(pkg["lint-staged"])];
|
|
407
|
+
}
|
|
408
|
+
function lintStagedJsonFiles(dir) {
|
|
409
|
+
return LINT_STAGED_JSON_CONFIG_FILES.map((name) => path.join(dir, name)).filter((file) => fs.existsSync(file) && isJsonFile(file));
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* A package that lists tsx outside devDependencies keeps it: its published code or
|
|
413
|
+
* production scripts can need tsx, and `vpx` comes from the vite-plus devDependency.
|
|
414
|
+
*/
|
|
415
|
+
function ownsTsxAtRuntime(pkg) {
|
|
416
|
+
return pkg.dependencies?.tsx !== void 0 || pkg.optionalDependencies?.tsx !== void 0 || pkg.peerDependencies?.tsx !== void 0;
|
|
417
|
+
}
|
|
418
|
+
function packageDirs(rootDir, packages) {
|
|
419
|
+
return [rootDir, ...(packages ?? []).map((pkg) => path.join(rootDir, pkg.path))];
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Whether a script, lint-staged task, or `#!/usr/bin/env tsx` shebang runs tsx in a form
|
|
423
|
+
* `vpx` replaces.
|
|
424
|
+
*/
|
|
425
|
+
function detectTsxProject(rootDir, packages) {
|
|
426
|
+
return packageDirs(rootDir, packages).some((dir) => {
|
|
427
|
+
const pkg = readPackageJsonIfExists(path.join(dir, "package.json"));
|
|
428
|
+
if (!pkg || ownsTsxAtRuntime(pkg)) return false;
|
|
429
|
+
return [...packageCommands(pkg), ...lintStagedJsonFiles(dir).flatMap((file) => recordCommands(readJsonFile(file)))].some((command) => rewriteTsxCommand(command, dir).command !== command) || sourceTreeMatches(dir, (content, file) => {
|
|
430
|
+
const result = rewriteTsxShebang(content, file);
|
|
431
|
+
return result !== void 0 && "content" in result;
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
/** Rewrite each command of a scripts or lint-staged record; returns whether one changed. */
|
|
436
|
+
function rewriteCommandRecord(record, label, cwd, report) {
|
|
437
|
+
let edited = false;
|
|
438
|
+
for (const [key, value] of Object.entries(record)) {
|
|
439
|
+
const commands = Array.isArray(value) ? value : [value];
|
|
440
|
+
const rewritten = commands.map((command) => {
|
|
441
|
+
if (typeof command !== "string") return command;
|
|
442
|
+
const result = rewriteTsxCommand(command, cwd);
|
|
443
|
+
for (const reason of result.kept) warnMigration(`Kept tsx in ${label} "${key}": ${reason}.`, report);
|
|
444
|
+
return result.command;
|
|
445
|
+
});
|
|
446
|
+
if (rewritten.some((command, index) => command !== commands[index])) {
|
|
447
|
+
record[key] = Array.isArray(value) ? rewritten : rewritten[0];
|
|
448
|
+
edited = true;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return edited;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* The `vpx` version of a script file's tsx shebang, or why it keeps tsx; undefined when
|
|
455
|
+
* the file has no `#!/usr/bin/env tsx` shebang.
|
|
456
|
+
*/
|
|
457
|
+
function rewriteTsxShebang(content, file) {
|
|
458
|
+
const match = TSX_ENV_SHEBANG.exec(content);
|
|
459
|
+
if (!match) return;
|
|
460
|
+
const [line, prefix, command] = match;
|
|
461
|
+
const result = rewriteTsxCommand(`${command.trimEnd()} ${SHEBANG_ENTRY}`);
|
|
462
|
+
const reason = result.kept[0] ?? scriptKeepReason(path.dirname(file), `./${path.basename(file)}`);
|
|
463
|
+
if (reason) return { reason };
|
|
464
|
+
return { content: `${prefix}${result.command.slice(0, -` ${SHEBANG_ENTRY}`.length)}${content.slice(line.length)}` };
|
|
465
|
+
}
|
|
466
|
+
/** Point `#!/usr/bin/env tsx` shebangs of a package's script files at `vpx`. */
|
|
467
|
+
function rewriteTsxShebangs(dir, report) {
|
|
468
|
+
let changed = false;
|
|
469
|
+
sourceTreeMatches(dir, (content, file) => {
|
|
470
|
+
const result = rewriteTsxShebang(content, file);
|
|
471
|
+
if (result && "reason" in result) warnMigration(`Kept tsx in the shebang of ${displayRelative(file)}: ${result.reason}.`, report);
|
|
472
|
+
else if (result) {
|
|
473
|
+
fs.writeFileSync(file, result.content);
|
|
474
|
+
changed = true;
|
|
475
|
+
}
|
|
476
|
+
return false;
|
|
63
477
|
});
|
|
64
|
-
return
|
|
478
|
+
return changed;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* What still runs or imports tsx under `dir`, or undefined. The scan crosses into nested
|
|
482
|
+
* packages, which can resolve this package's tsx, and reads build output, which can
|
|
483
|
+
* still load it.
|
|
484
|
+
*/
|
|
485
|
+
function findTsxUse(dir) {
|
|
486
|
+
let found;
|
|
487
|
+
sourceTreeMatches(dir, (content, file) => {
|
|
488
|
+
if (LOCKFILES.has(path.basename(file))) return false;
|
|
489
|
+
const uses = fileUsesTsx(content, file);
|
|
490
|
+
if (uses) found = `${displayRelative(file)} still uses tsx`;
|
|
491
|
+
return uses;
|
|
492
|
+
}, {
|
|
493
|
+
crossPackageBoundaries: true,
|
|
494
|
+
extensions: REFERENCE_EXTENSIONS,
|
|
495
|
+
includeExtensionless: true,
|
|
496
|
+
includePackageReferences: true,
|
|
497
|
+
matchesPackage: (packageDir, pkg) => {
|
|
498
|
+
const field = Object.entries(pkg).find(([key, value]) => !IGNORED_FIELDS.has(key) && jsonUsesTsx(value, [
|
|
499
|
+
"imports",
|
|
500
|
+
"lint-staged",
|
|
501
|
+
"scripts"
|
|
502
|
+
].includes(key)))?.[0];
|
|
503
|
+
if (field !== void 0) found = `\`${field}\` in ${displayRelative(path.join(packageDir, "package.json"))} still uses tsx`;
|
|
504
|
+
return field !== void 0;
|
|
505
|
+
},
|
|
506
|
+
skipDirs: USE_SCAN_SKIP_DIRS
|
|
507
|
+
});
|
|
508
|
+
return found;
|
|
65
509
|
}
|
|
66
|
-
/**
|
|
67
|
-
|
|
510
|
+
/** Whether `file` runs, imports, or loads tsx. */
|
|
511
|
+
function fileUsesTsx(content, file) {
|
|
512
|
+
if (TSX_SHEBANG.test(content)) return true;
|
|
513
|
+
const name = path.basename(file);
|
|
514
|
+
if (LINT_STAGED_JSON_CONFIG_FILES.some((config) => config === name) && jsonUsesTsx(parse(content), true)) return true;
|
|
515
|
+
const extension = path.extname(file);
|
|
516
|
+
if (extension === ".json" || extension === ".jsonc") return jsonUsesTsx(parse(content), false);
|
|
517
|
+
if (CODE_EXTENSIONS.has(extension)) return TSX_CODE_REFERENCE.test(content);
|
|
518
|
+
return TSX_TEXT_REFERENCE.test(content) || extension === "" && TSX_CODE_REFERENCE.test(content);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Whether a JSON value holds a string that runs or loads tsx: a tsx command with an
|
|
522
|
+
* argument, a loader flag such as `--import=tsx` or `["--import", "tsx"]`, or a subpath
|
|
523
|
+
* such as `tsx/cjs`. A bare `tsx` counts only in `commands` (scripts, lint-staged tasks,
|
|
524
|
+
* and `imports` aliases) and as a loader setting such as Mocha's `"import": "tsx"`;
|
|
525
|
+
* elsewhere it is often a file extension, as in `moduleFileExtensions`.
|
|
526
|
+
*/
|
|
527
|
+
function jsonUsesTsx(value, commands) {
|
|
528
|
+
if (typeof value === "string") return commands && TSX_COMMAND.test(value) || TSX_CONFIG_VALUE.test(value);
|
|
529
|
+
if (Array.isArray(value)) return value.some((item, index) => jsonUsesTsx(item, commands) || typeof item === "string" && LOADER_OPTIONS.has(item) && TSX_LOADER.test(String(value[index + 1])));
|
|
530
|
+
return isPlainRecord(value) && Object.entries(value).some(([key, item]) => jsonUsesTsx(item, commands) || LOADER_SETTINGS.has(key) && [item].flat().some((loader) => TSX_LOADER.test(String(loader))));
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Move tsx scripts to `vpx`: rewrite package.json scripts, lint-staged tasks, and
|
|
534
|
+
* `#!/usr/bin/env tsx` shebangs, then remove the tsx devDependency from each package
|
|
535
|
+
* where nothing uses tsx anymore.
|
|
536
|
+
*/
|
|
537
|
+
function migrateTsxToVpx(rootDir, packages, report) {
|
|
538
|
+
let changed = false;
|
|
539
|
+
const migrated = [];
|
|
540
|
+
for (const dir of packageDirs(rootDir, packages)) {
|
|
541
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
542
|
+
const pkg = readPackageJsonIfExists(packageJsonPath);
|
|
543
|
+
if (!pkg) continue;
|
|
544
|
+
if (ownsTsxAtRuntime(pkg)) {
|
|
545
|
+
if (packageCommands(pkg).some((c) => TSX_COMMAND.test(c))) warnMigration(`Kept tsx in ${displayRelative(packageJsonPath)}: it is listed in dependencies, optionalDependencies, or peerDependencies there, and \`vpx\` comes from the vite-plus devDependency.`, report);
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
migrated.push(dir);
|
|
549
|
+
editJsonFile(packageJsonPath, (json) => {
|
|
550
|
+
let edited = false;
|
|
551
|
+
for (const field of ["scripts", "lint-staged"]) {
|
|
552
|
+
const record = json[field];
|
|
553
|
+
if (isPlainRecord(record) && rewriteCommandRecord(record, `${displayRelative(packageJsonPath)} ${field}`, dir, report)) edited = true;
|
|
554
|
+
}
|
|
555
|
+
changed ||= edited;
|
|
556
|
+
return edited ? json : void 0;
|
|
557
|
+
});
|
|
558
|
+
for (const file of lintStagedJsonFiles(dir)) editJsonFile(file, (config) => {
|
|
559
|
+
const edited = rewriteCommandRecord(config, displayRelative(file), dir, report);
|
|
560
|
+
changed ||= edited;
|
|
561
|
+
return edited ? config : void 0;
|
|
562
|
+
});
|
|
563
|
+
if (rewriteTsxShebangs(dir, report)) changed = true;
|
|
564
|
+
}
|
|
565
|
+
let dependencyRemoved = false;
|
|
566
|
+
for (const dir of migrated) {
|
|
567
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
568
|
+
if (readPackageJsonIfExists(packageJsonPath)?.devDependencies?.tsx === void 0) continue;
|
|
569
|
+
const use = findTsxUse(dir);
|
|
570
|
+
if (use) {
|
|
571
|
+
warnMigration(`Kept the tsx devDependency in ${displayRelative(packageJsonPath)}: ${use}.`, report);
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
editJsonFile(packageJsonPath, (json) => {
|
|
575
|
+
delete json.devDependencies.tsx;
|
|
576
|
+
return json;
|
|
577
|
+
});
|
|
578
|
+
dependencyRemoved = true;
|
|
579
|
+
}
|
|
580
|
+
if (report && changed) report.tsxMigrated = true;
|
|
581
|
+
return {
|
|
582
|
+
changed: changed || dependencyRemoved,
|
|
583
|
+
dependencyRemoved
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async function confirmTsxMigration(interactive) {
|
|
587
|
+
if (interactive) {
|
|
588
|
+
const confirmed = await confirm({
|
|
589
|
+
message: "Migrate tsx scripts to vpx?\n " + styleText("gray", "vpx is Vite+'s built-in TypeScript script runner: `tsx <file>` becomes `vpx <file>`, `tsx watch` becomes `vpx --watch`, and the tsx dependency is removed once nothing uses it."),
|
|
590
|
+
initialValue: true
|
|
591
|
+
});
|
|
592
|
+
if (isCancel(confirmed)) cancelAndExit();
|
|
593
|
+
return confirmed;
|
|
594
|
+
}
|
|
595
|
+
log$1.info("tsx scripts detected. Auto-migrating to vpx...");
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
// src/migration/vitest-v5/commands.ts
|
|
599
|
+
/** Read test commands without evaluating shell state or directory changes. */
|
|
68
600
|
function literalTestCommands(command) {
|
|
69
|
-
const commands =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const tokens = literalArgv(command.slice(start, end));
|
|
74
|
-
if (!tokens?.length) return false;
|
|
601
|
+
const commands = parseLiteralCommands(command, true);
|
|
602
|
+
if (!commands) return;
|
|
603
|
+
const result = [];
|
|
604
|
+
for (const tokens of commands) {
|
|
75
605
|
const values = tokens.map(({ value }) => value);
|
|
76
606
|
while (/^[A-Za-z_]\w*=/.test(values[0] ?? "")) values.shift();
|
|
77
|
-
if (!values.length || [
|
|
78
|
-
|
|
79
|
-
"pushd",
|
|
80
|
-
"popd",
|
|
81
|
-
".",
|
|
82
|
-
"source",
|
|
83
|
-
"eval",
|
|
84
|
-
"export",
|
|
85
|
-
"set",
|
|
86
|
-
"unset"
|
|
87
|
-
].includes(values[0])) return false;
|
|
88
|
-
commands.push(values);
|
|
89
|
-
return true;
|
|
90
|
-
};
|
|
91
|
-
for (let index = 0; index < command.length; index++) {
|
|
92
|
-
const char = command[index];
|
|
93
|
-
if (char === "\\") return;
|
|
94
|
-
if (quote) {
|
|
95
|
-
if (char === quote) quote = "";
|
|
96
|
-
} else if (char === "\"" || char === "'") quote = char;
|
|
97
|
-
else if (char === "#") return;
|
|
98
|
-
else if (char === ";" || command.slice(index, index + 2) === "&&" || command.slice(index, index + 2) === "||") {
|
|
99
|
-
if (!append(index)) return;
|
|
100
|
-
if (char !== ";") index++;
|
|
101
|
-
start = index + 1;
|
|
102
|
-
}
|
|
607
|
+
if (!values.length || SHELL_STATE_COMMANDS.includes(values[0])) return;
|
|
608
|
+
result.push(values);
|
|
103
609
|
}
|
|
104
|
-
return
|
|
610
|
+
return result;
|
|
105
611
|
}
|
|
106
612
|
/** Locate the runner's arguments after a supported package-manager wrapper. */
|
|
107
613
|
function vitestCommandArgsStart(values) {
|
|
108
|
-
|
|
109
|
-
if ([
|
|
110
|
-
"pnpm",
|
|
111
|
-
"npm",
|
|
112
|
-
"yarn",
|
|
113
|
-
"bun",
|
|
114
|
-
"npx",
|
|
115
|
-
"bunx"
|
|
116
|
-
].includes(values[0])) {
|
|
117
|
-
start = 1;
|
|
118
|
-
if (values[start] === "exec" || values[start] === "x") start++;
|
|
119
|
-
if (values[start] === "--") start++;
|
|
120
|
-
}
|
|
614
|
+
const start = getCommandStartIndex(values);
|
|
121
615
|
if (values[start] === "vitest") return start + 1;
|
|
122
616
|
if (values[start] === "vp" && values[start + 1] === "test") return start + 2;
|
|
123
617
|
return -1;
|
|
124
618
|
}
|
|
125
619
|
function migrateBenchmarkOutput(command) {
|
|
126
|
-
const argv =
|
|
620
|
+
const argv = parseLiteralCommands(command, false)?.[0];
|
|
127
621
|
if (!argv) return command;
|
|
128
622
|
const values = argv.map(({ value }) => value);
|
|
129
623
|
const start = vitestCommandArgsStart(values);
|
|
@@ -182,7 +676,7 @@ function migrateVitestV5Command(file, command, preserveV4, line = 1, reviewV4 =
|
|
|
182
676
|
findings
|
|
183
677
|
};
|
|
184
678
|
command = migrateBenchmarkOutput(command);
|
|
185
|
-
const argv =
|
|
679
|
+
const argv = parseLiteralCommands(command, false)?.[0];
|
|
186
680
|
const values = argv?.map(({ value }) => value);
|
|
187
681
|
const runner = values ? vitestCommandArgsStart(values) : -1;
|
|
188
682
|
let removedFlag = /(?:^|\s)(?:--compare|--outputJson)(?:\s|=|$)/.test(command);
|
|
@@ -230,8 +724,7 @@ function migrateVitestV5Command(file, command, preserveV4, line = 1, reviewV4 =
|
|
|
230
724
|
findings
|
|
231
725
|
};
|
|
232
726
|
}
|
|
233
|
-
|
|
234
|
-
//#region src/migration/vitest-v5/ast.ts
|
|
727
|
+
// src/migration/vitest-v5/ast.ts
|
|
235
728
|
function isNode(value) {
|
|
236
729
|
return typeof value === "object" && value !== null && "type" in value && "start" in value;
|
|
237
730
|
}
|
|
@@ -505,7 +998,7 @@ var SourceEditor = class {
|
|
|
505
998
|
}
|
|
506
999
|
}
|
|
507
1000
|
const propertyIndent = multilinePrefix && object.properties.length ? indent : `${objectIndent} `;
|
|
508
|
-
const entries =
|
|
1001
|
+
const entries = Array.from(additions.values(), (property) => printAddedProperty(property, propertyIndent, newline));
|
|
509
1002
|
const suffix = multiline && !multilinePrefix ? `${newline}${object.properties.length ? propertyIndent : objectIndent}` : "";
|
|
510
1003
|
this.edit(start, multiline && !multilinePrefix && /^\s*$/.test(prefix) ? first : start, multiline ? `${newline}${propertyIndent}${entries.join(`,${newline}${propertyIndent}`)}${object.properties.length ? "," : ""}${suffix}` : ` ${entries.join(", ")}${object.properties.length ? "," : ""} `);
|
|
511
1004
|
}
|
|
@@ -538,8 +1031,7 @@ var SourceEditor = class {
|
|
|
538
1031
|
};
|
|
539
1032
|
}
|
|
540
1033
|
};
|
|
541
|
-
|
|
542
|
-
//#region src/migration/vitest-v5/benchmark-config.ts
|
|
1034
|
+
// src/migration/vitest-v5/benchmark-config.ts
|
|
543
1035
|
const REPORTERS = /* @__PURE__ */ new Set([
|
|
544
1036
|
"default",
|
|
545
1037
|
"verbose",
|
|
@@ -607,8 +1099,7 @@ function migrateBenchmarkConfig(editor, test) {
|
|
|
607
1099
|
oldJson
|
|
608
1100
|
]) if (prop) editor.remove(benchmark, prop);
|
|
609
1101
|
}
|
|
610
|
-
|
|
611
|
-
//#region src/migration/vitest-v5/compatibility.ts
|
|
1102
|
+
// src/migration/vitest-v5/compatibility.ts
|
|
612
1103
|
const settings = {
|
|
613
1104
|
clearMocks: {
|
|
614
1105
|
value: "false",
|
|
@@ -656,8 +1147,7 @@ function compatibilityProperty(key) {
|
|
|
656
1147
|
comment: `// Vitest v4 compatibility: ${reason}\n// ${adoption}\n// ${documentationUrl$1("/guide/vitest-v5#remove-unneeded-compatibility-settings")}\n// https://vitest.dev/guide/migration/#${section}`
|
|
657
1148
|
};
|
|
658
1149
|
}
|
|
659
|
-
|
|
660
|
-
//#region src/migration/vitest-v5/config-object.ts
|
|
1150
|
+
// src/migration/vitest-v5/config-object.ts
|
|
661
1151
|
const CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
662
1152
|
"root",
|
|
663
1153
|
"test",
|
|
@@ -703,8 +1193,7 @@ function resolveConfigObject(editor, node) {
|
|
|
703
1193
|
}
|
|
704
1194
|
return node;
|
|
705
1195
|
}
|
|
706
|
-
|
|
707
|
-
//#region src/migration/vitest-v5/config.ts
|
|
1196
|
+
// src/migration/vitest-v5/config.ts
|
|
708
1197
|
function isTrue(node) {
|
|
709
1198
|
return isBoolean(node) && node.value;
|
|
710
1199
|
}
|
|
@@ -1001,8 +1490,7 @@ function migrateConfig(file, source, options, mergedConfig, benchmarkOnly = fals
|
|
|
1001
1490
|
});
|
|
1002
1491
|
return editor.finish();
|
|
1003
1492
|
}
|
|
1004
|
-
|
|
1005
|
-
//#region src/migration/vitest-v5/documentation.ts
|
|
1493
|
+
// src/migration/vitest-v5/documentation.ts
|
|
1006
1494
|
const guide = documentationUrl$1("/guide/vitest-v5");
|
|
1007
1495
|
const upstream = "https://vitest.dev/guide/migration/";
|
|
1008
1496
|
const sections = {
|
|
@@ -1054,8 +1542,7 @@ function vitestV5Documentation(code) {
|
|
|
1054
1542
|
if (sections[code]) return `${upstream}#${sections[code]}`;
|
|
1055
1543
|
return `${guide}#${localSections[code] ?? "resolve-migration-findings"}`;
|
|
1056
1544
|
}
|
|
1057
|
-
|
|
1058
|
-
//#region src/migration/vitest-v5/lockfile.ts
|
|
1545
|
+
// src/migration/vitest-v5/lockfile.ts
|
|
1059
1546
|
var import_dist = require_dist();
|
|
1060
1547
|
function record$1(value) {
|
|
1061
1548
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
@@ -1143,8 +1630,7 @@ function lockedVitestVersion(root, directory, manager, spec) {
|
|
|
1143
1630
|
}
|
|
1144
1631
|
} catch {}
|
|
1145
1632
|
}
|
|
1146
|
-
|
|
1147
|
-
//#region src/migration/vitest-v5/storybook.ts
|
|
1633
|
+
// src/migration/vitest-v5/storybook.ts
|
|
1148
1634
|
const STORYBOOK_PLUGIN = /* @__PURE__ */ new Set(["@storybook/addon-vitest/vitest-plugin"]);
|
|
1149
1635
|
const NODE_PATH = /* @__PURE__ */ new Set(["node:path", "path"]);
|
|
1150
1636
|
const NODE_URL = /* @__PURE__ */ new Set(["node:url", "url"]);
|
|
@@ -1268,8 +1754,7 @@ function resolveStorybookScope(editor, object, sources, file, cwd) {
|
|
|
1268
1754
|
return unknown(`cannot parse ${label}/main`);
|
|
1269
1755
|
}
|
|
1270
1756
|
}
|
|
1271
|
-
|
|
1272
|
-
//#region src/migration/vitest-v5/scopes.ts
|
|
1757
|
+
// src/migration/vitest-v5/scopes.ts
|
|
1273
1758
|
const DEFAULT_TEST_INCLUDE = ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
|
|
1274
1759
|
const DEFAULT_TEST_EXCLUDE = ["**/node_modules/**", "**/.git/**"];
|
|
1275
1760
|
const DEFAULT_BENCHMARK_INCLUDE = ["**/*.{bench,benchmark}.?(c|m)[jt]s?(x)"];
|
|
@@ -1659,7 +2144,7 @@ function resolveVitestV5TestModes(sources, configFiles, preserveV4, entries) {
|
|
|
1659
2144
|
if (!found) unknown();
|
|
1660
2145
|
}
|
|
1661
2146
|
return {
|
|
1662
|
-
modes: new Map(
|
|
2147
|
+
modes: new Map(Array.from(sources.keys(), (file) => {
|
|
1663
2148
|
const matching = [];
|
|
1664
2149
|
let benchmarkFile = false;
|
|
1665
2150
|
const hasInSourceTests = sources.get(file)?.includes("import.meta.vitest");
|
|
@@ -1696,8 +2181,7 @@ function resolveVitestV5TestModes(sources, configFiles, preserveV4, entries) {
|
|
|
1696
2181
|
findings
|
|
1697
2182
|
};
|
|
1698
2183
|
}
|
|
1699
|
-
|
|
1700
|
-
//#region src/migration/vitest-v5/benchmarks.ts
|
|
2184
|
+
// src/migration/vitest-v5/benchmarks.ts
|
|
1701
2185
|
/** Keep the workload as a separate callback: making it the test callback
|
|
1702
2186
|
* would measure nothing, and inlining it into a new closure can change scope. */
|
|
1703
2187
|
function migrateBenchmarks(editor, globals = false) {
|
|
@@ -1840,8 +2324,7 @@ function migrateBenchmarks(editor, globals = false) {
|
|
|
1840
2324
|
calls
|
|
1841
2325
|
};
|
|
1842
2326
|
}
|
|
1843
|
-
|
|
1844
|
-
//#region src/migration/vitest-v5/source.ts
|
|
2327
|
+
// src/migration/vitest-v5/source.ts
|
|
1845
2328
|
const RUNNER_SYMBOLS = {
|
|
1846
2329
|
test: "test",
|
|
1847
2330
|
it: "it",
|
|
@@ -2074,6 +2557,45 @@ function looksLikeConstructorMock(editor, node) {
|
|
|
2074
2557
|
}
|
|
2075
2558
|
return false;
|
|
2076
2559
|
}
|
|
2560
|
+
function unwrapTypeExpression(node) {
|
|
2561
|
+
while (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression" || node.type === "TSTypeAssertion") node = node.expression;
|
|
2562
|
+
return node;
|
|
2563
|
+
}
|
|
2564
|
+
/** Recognize a local promise consumed by a later statement without crossing
|
|
2565
|
+
* control flow or function boundaries. Ambiguous paths still need review. */
|
|
2566
|
+
function isAssertionBindingHandled(editor, node) {
|
|
2567
|
+
let initializer = node;
|
|
2568
|
+
let declarator = editor.parent(initializer);
|
|
2569
|
+
while (declarator && unwrapTypeExpression(declarator) === node) {
|
|
2570
|
+
initializer = declarator;
|
|
2571
|
+
declarator = editor.parent(initializer);
|
|
2572
|
+
}
|
|
2573
|
+
if (declarator?.type !== "VariableDeclarator" || declarator.init !== initializer || declarator.id.type !== "Identifier") return false;
|
|
2574
|
+
const binding = editor.binding(declarator.id);
|
|
2575
|
+
const declaration = editor.parent(declarator);
|
|
2576
|
+
const block = declaration && editor.parent(declaration);
|
|
2577
|
+
const fn = editor.functionParent(node);
|
|
2578
|
+
if (!binding?.constant || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const" || block?.type !== "BlockStatement" || !fn || fn.generator) return false;
|
|
2579
|
+
for (let ancestor = editor.parent(block); ancestor && ancestor !== fn; ancestor = editor.parent(ancestor)) if (ancestor.type === "TryStatement") return false;
|
|
2580
|
+
for (const statement of block.body.slice(block.body.indexOf(declaration) + 1)) switch (statement.type) {
|
|
2581
|
+
case "ReturnStatement": {
|
|
2582
|
+
if (!statement.argument) return false;
|
|
2583
|
+
let expression = unwrapTypeExpression(statement.argument);
|
|
2584
|
+
if (expression.type === "AwaitExpression") expression = unwrapTypeExpression(expression.argument);
|
|
2585
|
+
return editor.binding(expression) === binding;
|
|
2586
|
+
}
|
|
2587
|
+
case "ExpressionStatement": {
|
|
2588
|
+
const expression = unwrapTypeExpression(statement.expression);
|
|
2589
|
+
if (expression.type === "AwaitExpression" && editor.binding(unwrapTypeExpression(expression.argument)) === binding) return true;
|
|
2590
|
+
break;
|
|
2591
|
+
}
|
|
2592
|
+
case "VariableDeclaration":
|
|
2593
|
+
case "FunctionDeclaration":
|
|
2594
|
+
case "EmptyStatement": break;
|
|
2595
|
+
default: return false;
|
|
2596
|
+
}
|
|
2597
|
+
return false;
|
|
2598
|
+
}
|
|
2077
2599
|
function chain(node, isApi) {
|
|
2078
2600
|
const members = [];
|
|
2079
2601
|
const calls = [];
|
|
@@ -2268,7 +2790,7 @@ function rewriteSource(file, source, options) {
|
|
|
2268
2790
|
if (asynchronous && parent?.type === "ExpressionStatement") {
|
|
2269
2791
|
if (canAwait(node)) editor.edit(node.start, node.start, "await ");
|
|
2270
2792
|
else editor.report(node, "unawaited-assertion", "Await or return this asynchronous assertion in an async-compatible function.");
|
|
2271
|
-
} else if (asynchronous && parent?.type !== "MemberExpression" && parent?.type !== "AwaitExpression" && parent?.type !== "ReturnStatement") editor.report(node, "unawaited-assertion", "Check that the result of this asynchronous assertion is awaited or returned.");
|
|
2793
|
+
} else if (asynchronous && parent?.type !== "MemberExpression" && parent?.type !== "AwaitExpression" && parent?.type !== "ReturnStatement" && !isAssertionBindingHandled(editor, node)) editor.report(node, "unawaited-assertion", "Check that the result of this asynchronous assertion is awaited or returned.");
|
|
2272
2794
|
}
|
|
2273
2795
|
if (importedName(editor, node.callee, RENDER_SOURCES) === "render" && parent?.type !== "AwaitExpression" && parent?.type !== "ReturnStatement") {
|
|
2274
2796
|
if (canAwait(node)) {
|
|
@@ -2352,8 +2874,7 @@ function rewriteSource(file, source, options) {
|
|
|
2352
2874
|
if (benchmark && result.content === source) editor.report(benchmark, "benchmark-api", "The benchmark rewrite was discarded. Migrate the remaining legacy bench API manually before upgrading to Vitest v5.", "block");
|
|
2353
2875
|
return result;
|
|
2354
2876
|
}
|
|
2355
|
-
|
|
2356
|
-
//#region src/migration/vitest-v5/webdriverio.ts
|
|
2877
|
+
// src/migration/vitest-v5/webdriverio.ts
|
|
2357
2878
|
const MIN_VERSION = "5.0.0";
|
|
2358
2879
|
const DEFAULT_SPEC = "^5.0.0";
|
|
2359
2880
|
const REGISTRY_ALIAS = `npm:${WEBDRIVERIO_PROVIDER}@`;
|
|
@@ -2573,8 +3094,7 @@ function migrateWebdriverioDependencies(workspace, sources, inputs) {
|
|
|
2573
3094
|
overrideMap(path.join(workspace.rootDir, "pnpm-workspace.yaml"), ["overrides"]);
|
|
2574
3095
|
return findings;
|
|
2575
3096
|
}
|
|
2576
|
-
|
|
2577
|
-
//#region src/migration/migrator/vitest-v5.ts
|
|
3097
|
+
// src/migration/migrator/vitest-v5.ts
|
|
2578
3098
|
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2579
3099
|
"node_modules",
|
|
2580
3100
|
".git",
|
|
@@ -2905,7 +3425,7 @@ function planVitestV5Migration(workspace, originalProjects) {
|
|
|
2905
3425
|
const findings = [];
|
|
2906
3426
|
const directories = [workspace.rootDir, ...(workspace.packages ?? []).map((pkg) => path.resolve(workspace.rootDir, pkg.path))];
|
|
2907
3427
|
const projects = [];
|
|
2908
|
-
const projectSources = new Map(
|
|
3428
|
+
const projectSources = new Map(Array.from(new Set(directories), (directory) => [directory, new Map(filesInProject(directory).map((file) => [file, fs.readFileSync(file, "utf8")]))]));
|
|
2909
3429
|
const allSources = /* @__PURE__ */ new Map();
|
|
2910
3430
|
for (const sources of projectSources.values()) for (const [file, source] of sources) allSources.set(file, source);
|
|
2911
3431
|
const rewrittenSources = new Map(allSources);
|
|
@@ -3032,7 +3552,7 @@ function currentProjectConfigs(plan) {
|
|
|
3032
3552
|
const filesByProject = new Map(plan.projects.map((project) => [project.directory, filesInProject(project.directory)]));
|
|
3033
3553
|
const sources = new Map([...filesByProject.values()].flat().map((file) => [file, fs.readFileSync(file, "utf8")]));
|
|
3034
3554
|
const configs = findVitestV5ConfigFiles(sources, findVitestV5ConfigEntries(sources, filesByProject.keys()).flatMap((entry) => entry.file ? [entry.file] : []));
|
|
3035
|
-
return new Map(
|
|
3555
|
+
return new Map(Array.from(filesByProject, ([directory, files]) => [directory, files.filter((file) => configs.has(file))]));
|
|
3036
3556
|
}
|
|
3037
3557
|
/** Run after other migration steps create or merge configs. Keep the source
|
|
3038
3558
|
* version and deferred reviews in memory until the final report; no state file. */
|
|
@@ -3080,8 +3600,7 @@ function finishVitestV5Migration(plan) {
|
|
|
3080
3600
|
const after = planVitestV5Migration(workspaceFromPlan(plan), new Map(plan.projects.map((project) => [project.directory, completedProjects.get(project.directory) ?? project])));
|
|
3081
3601
|
return [...findings, ...after.findings];
|
|
3082
3602
|
}
|
|
3083
|
-
|
|
3084
|
-
//#region src/migration/compat/runner.ts
|
|
3603
|
+
// src/migration/compat/runner.ts
|
|
3085
3604
|
/**
|
|
3086
3605
|
* Resolve the isolated compat worker emitted at `migration/compat/worker.js`.
|
|
3087
3606
|
*
|
|
@@ -3137,8 +3656,7 @@ async function checkRolldownCompatibility(rootDir, report) {
|
|
|
3137
3656
|
for (const warning of compatibilityResult?.warnings ?? []) addMigrationWarning(report, warning);
|
|
3138
3657
|
} catch {}
|
|
3139
3658
|
}
|
|
3140
|
-
|
|
3141
|
-
//#region src/migration/format.ts
|
|
3659
|
+
// src/migration/format.ts
|
|
3142
3660
|
const FORMAT_FAILURE_MESSAGE = "Automatic formatting failed. Run `vp fmt` manually after migration.";
|
|
3143
3661
|
const MAX_FORMAT_ARG_BYTES = process.platform === "win32" ? 24e3 : 1e5;
|
|
3144
3662
|
function chunkPathsByArgLength(paths) {
|
|
@@ -3264,8 +3782,29 @@ async function formatMigratedProject(projectRoot, interactive, report, options =
|
|
|
3264
3782
|
addMigrationWarning(report, FORMAT_FAILURE_MESSAGE);
|
|
3265
3783
|
return false;
|
|
3266
3784
|
}
|
|
3267
|
-
|
|
3268
|
-
|
|
3785
|
+
// src/migration/install-result.ts
|
|
3786
|
+
/**
|
|
3787
|
+
* Reconcile a CommandRunSummary from `runViteInstall` with the migration's
|
|
3788
|
+
* duration counter and exit-code state. `runViteInstall` returns
|
|
3789
|
+
* `{ status: 'failed', exitCode }` without throwing; treating that as a success
|
|
3790
|
+
* (incrementing duration unconditionally) would let the migration claim
|
|
3791
|
+
* "Dependencies installed" while node_modules is desynced from the just-mutated
|
|
3792
|
+
* package.json. This helper centralizes the right handling: credit duration on
|
|
3793
|
+
* success, warn + flip exitCode on failure, stay silent on skip.
|
|
3794
|
+
*/
|
|
3795
|
+
function handleInstallResult(installSummary, rootDir, report, options) {
|
|
3796
|
+
if (installSummary.status === "installed") return installSummary.durationMs;
|
|
3797
|
+
if (installSummary.status === "failed") {
|
|
3798
|
+
const exitCode = installSummary.exitCode ?? 1;
|
|
3799
|
+
const message = `Dependency installation failed (exit code ${exitCode}). Run \`vp install\` manually in ${rootDir} to resync node_modules.`;
|
|
3800
|
+
warnMsg(message);
|
|
3801
|
+
report.warnings.push(message);
|
|
3802
|
+
if (options?.propagateExitCode !== false) process.exitCode = exitCode;
|
|
3803
|
+
return 0;
|
|
3804
|
+
}
|
|
3805
|
+
return 0;
|
|
3806
|
+
}
|
|
3807
|
+
// src/migration/npm-reinstall.ts
|
|
3269
3808
|
const VITE_PLUS_CORE_PACKAGE = "@voidzero-dev/vite-plus-core";
|
|
3270
3809
|
function isViteInstallPath(packagePath) {
|
|
3271
3810
|
return packagePath === "node_modules/vite" || packagePath.endsWith("/node_modules/vite");
|
|
@@ -3354,18 +3893,20 @@ function prepareNpmViteAliasReinstall(rootDir, projectPaths = [rootDir]) {
|
|
|
3354
3893
|
}
|
|
3355
3894
|
};
|
|
3356
3895
|
}
|
|
3357
|
-
|
|
3358
|
-
//#region src/migration/setup-plan.ts
|
|
3896
|
+
// src/migration/setup-plan.ts
|
|
3359
3897
|
async function collectGitHooksDecision(rootDir, packageManager, options, packages = []) {
|
|
3360
|
-
|
|
3361
|
-
if (
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
log$1.warn(`⚠ ${reason}`);
|
|
3365
|
-
shouldSetupHooks = false;
|
|
3366
|
-
}
|
|
3898
|
+
const hasExistingHooks = options.hooks === void 0 && hasExistingViteHooksPolicy(rootDir);
|
|
3899
|
+
if (hasExistingHooks && isViteHooksSetupComplete(rootDir)) {
|
|
3900
|
+
log(`${muted("•")} Git hooks already configured, skipping`);
|
|
3901
|
+
return false;
|
|
3367
3902
|
}
|
|
3368
|
-
return
|
|
3903
|
+
if (!hasExistingHooks && !await promptGitHooks(options)) return false;
|
|
3904
|
+
const reason = preflightGitHooksSetup(rootDir, packageManager, packages);
|
|
3905
|
+
if (reason) {
|
|
3906
|
+
log$1.warn(`⚠ ${reason}`);
|
|
3907
|
+
return false;
|
|
3908
|
+
}
|
|
3909
|
+
return true;
|
|
3369
3910
|
}
|
|
3370
3911
|
async function collectAgentInstructionPlan(rootDir, options) {
|
|
3371
3912
|
const existingAgentTargetPaths = options.agent !== void 0 || !options.interactive ? void 0 : detectExistingAgentTargetPaths(rootDir);
|
|
@@ -3469,13 +4010,9 @@ async function collectMigrationSetupPlan(rootDir, packageManager, options, packa
|
|
|
3469
4010
|
...eslintPlan
|
|
3470
4011
|
};
|
|
3471
4012
|
}
|
|
3472
|
-
|
|
3473
|
-
//#region src/migration/bin.ts
|
|
4013
|
+
// src/migration/bin.ts
|
|
3474
4014
|
async function confirmNodeVersionFileMigration(interactive, detection) {
|
|
3475
|
-
const message = {
|
|
3476
|
-
"package.json": "Migrate Volta node version (package.json) to .node-version?",
|
|
3477
|
-
".nvmrc": "Migrate .nvmrc to .node-version?"
|
|
3478
|
-
}[detection.file];
|
|
4015
|
+
const message = { "package.json": "Migrate Volta node version (package.json) to .node-version?" }[detection.file];
|
|
3479
4016
|
if (interactive) {
|
|
3480
4017
|
const confirmed = await confirm({
|
|
3481
4018
|
message,
|
|
@@ -3609,7 +4146,7 @@ function hasExistingVitePlusMigrationCandidates(workspaceInfo, options) {
|
|
|
3609
4146
|
const eslintProject = detectEslintProject(workspaceInfo.rootDir, workspaceInfo.packages);
|
|
3610
4147
|
const prettierProject = detectPrettierProject(workspaceInfo.rootDir, workspaceInfo.packages);
|
|
3611
4148
|
const tsupProject = detectTsupProject(workspaceInfo.rootDir, workspaceInfo.packages);
|
|
3612
|
-
return hasExplicitExistingVitePlusSetupRequest(options) || detectLegacyGitHooksMigrationCandidate(workspaceInfo.rootDir) || hasBaseUrlInWorkspace(workspaceInfo) || eslintProject.hasDependency || prettierProject.hasDependency || tsupProject.hasDependency || detectNodeVersionManagerFile(workspaceInfo.rootDir) !== void 0 || getFrameworkShimCandidates(workspaceInfo.rootDir, workspaceInfo.packages).length > 0;
|
|
4149
|
+
return hasExplicitExistingVitePlusSetupRequest(options) || detectLegacyGitHooksMigrationCandidate(workspaceInfo.rootDir) || hasBaseUrlInWorkspace(workspaceInfo) || eslintProject.hasDependency || prettierProject.hasDependency || tsupProject.hasDependency || detectTsxProject(workspaceInfo.rootDir, workspaceInfo.packages) || detectNodeVersionManagerFile(workspaceInfo.rootDir) !== void 0 || getFrameworkShimCandidates(workspaceInfo.rootDir, workspaceInfo.packages).length > 0;
|
|
3613
4150
|
}
|
|
3614
4151
|
function getExistingVitePlusSetupOptions(options, useFullMigrationDefaults = false) {
|
|
3615
4152
|
if (useFullMigrationDefaults) return options;
|
|
@@ -3629,6 +4166,7 @@ async function collectMigrationPlan(rootDir, detectedPackageManager, detectedPac
|
|
|
3629
4166
|
if (prettierProject.hasDependency && prettierProject.configFile) migratePrettier = await confirmPrettierMigration(options.interactive);
|
|
3630
4167
|
else if (prettierProject.hasDependency) warnPackageLevelPrettier();
|
|
3631
4168
|
const { migrateTsup, tsupConfigFile } = await collectTsupMigrationDecision(rootDir, options, packages);
|
|
4169
|
+
const migrateTsx = detectTsxProject(rootDir, packages) && await confirmTsxMigration(options.interactive);
|
|
3632
4170
|
const fixBaseUrl = hasBaseUrlInWorkspace({
|
|
3633
4171
|
rootDir,
|
|
3634
4172
|
packages
|
|
@@ -3646,33 +4184,13 @@ async function collectMigrationPlan(rootDir, detectedPackageManager, detectedPac
|
|
|
3646
4184
|
prettierConfigFile: prettierProject.configFile,
|
|
3647
4185
|
migrateTsup,
|
|
3648
4186
|
tsupConfigFile,
|
|
4187
|
+
migrateTsx,
|
|
3649
4188
|
fixBaseUrl,
|
|
3650
4189
|
migrateNodeVersionFile,
|
|
3651
4190
|
nodeVersionDetection,
|
|
3652
4191
|
frameworkShimFrameworks
|
|
3653
4192
|
};
|
|
3654
4193
|
}
|
|
3655
|
-
/**
|
|
3656
|
-
* Reconcile a CommandRunSummary from `runViteInstall` with the migration's
|
|
3657
|
-
* duration counter and exit-code state. `runViteInstall` returns
|
|
3658
|
-
* `{ status: 'failed', exitCode }` without throwing; treating that as a success
|
|
3659
|
-
* (incrementing duration unconditionally) would let the migration claim
|
|
3660
|
-
* "Dependencies installed" while node_modules is desynced from the just-mutated
|
|
3661
|
-
* package.json. This helper centralizes the right handling: credit duration on
|
|
3662
|
-
* success, warn + flip exitCode on failure, stay silent on skip.
|
|
3663
|
-
*/
|
|
3664
|
-
function handleInstallResult(installSummary, rootDir, report, options) {
|
|
3665
|
-
if (installSummary.status === "installed") return installSummary.durationMs;
|
|
3666
|
-
if (installSummary.status === "failed") {
|
|
3667
|
-
const exitCode = installSummary.exitCode ?? 1;
|
|
3668
|
-
const message = `Dependency installation failed (exit code ${exitCode}). Run \`vp install\` manually in ${rootDir} to resync node_modules.`;
|
|
3669
|
-
warnMsg(message);
|
|
3670
|
-
report.warnings.push(message);
|
|
3671
|
-
if (options?.propagateExitCode !== false) process.exitCode = exitCode;
|
|
3672
|
-
return 0;
|
|
3673
|
-
}
|
|
3674
|
-
return 0;
|
|
3675
|
-
}
|
|
3676
4194
|
const FULL_MIGRATION_HINT = `${styleText("gray", "•")} Skipped editor, hooks, and lint setup. Run \`vp migrate --full\` to apply them.`;
|
|
3677
4195
|
function showMigrationSummary(options) {
|
|
3678
4196
|
const { projectRoot, packageManager, packageManagerVersion, installDurationMs, finalInstallOk, report, updatedExistingVitePlus, suggestFullMigration } = options;
|
|
@@ -3702,6 +4220,7 @@ function showMigrationSummary(options) {
|
|
|
3702
4220
|
if (report.eslintMigrated) log(`${styleText("gray", "•")} ESLint rules migrated to Oxlint`);
|
|
3703
4221
|
if (report.prettierMigrated) log(`${styleText("gray", "•")} Prettier migrated to Oxfmt`);
|
|
3704
4222
|
if (report.tsupMigrated) log(`${styleText("gray", "•")} tsup config migrated to tsdown (\`vp pack\`)`);
|
|
4223
|
+
if (report.tsxMigrated) log(`${styleText("gray", "•")} tsx scripts migrated to \`vpx\``);
|
|
3705
4224
|
if (report.nodeVersionFileMigrated) log(`${styleText("gray", "•")} Node version manager file migrated to .node-version`);
|
|
3706
4225
|
const setupVpFileCount = report.setupVpVersionUpdatedFileCount;
|
|
3707
4226
|
if (setupVpFileCount > 0) {
|
|
@@ -3855,6 +4374,10 @@ async function executeMigrationPlan(workspaceInfoOptional, plan, interactive, vi
|
|
|
3855
4374
|
cancelAndExit("Complete the tsup migration manually, then re-run `vp migrate`.", 1);
|
|
3856
4375
|
}
|
|
3857
4376
|
}
|
|
4377
|
+
if (plan.migrateTsx) {
|
|
4378
|
+
updateMigrationProgress("Migrating tsx scripts");
|
|
4379
|
+
migrateTsxToVpx(workspaceInfo.rootDir, workspaceInfo.packages, report);
|
|
4380
|
+
}
|
|
3858
4381
|
vitestV5Plan = applyRefreshedVitestV5Migration(vitestV5Plan);
|
|
3859
4382
|
const skipStagedMigration = shouldSkipStagedMigrationForHooks(workspaceInfo.rootDir, plan.shouldSetupHooks, plan.packageManager, workspaceInfo.packages);
|
|
3860
4383
|
updateMigrationProgress("Rewriting configs");
|
|
@@ -3987,6 +4510,7 @@ async function main() {
|
|
|
3987
4510
|
updateMigrationProgress("Rewriting toolchain imports across the workspace");
|
|
3988
4511
|
const coreMigrationResult = finalizeCoreMigrationForExistingVitePlus(workspaceInfoOptional, true, report, pendingCoreMigration);
|
|
3989
4512
|
if (coreMigrationResult.dependencies || coreMigrationResult.scripts || coreMigrationResult.tsconfigTypes || coreMigrationResult.imports || coreMigrationResult.tsdownConfig || coreMigrationResult.taskCacheConfig) didMigrate = true;
|
|
4513
|
+
if (retryPreservedOxlintConfigs(workspaceInfoOptional.rootDir, true, report, workspaceInfoOptional.packages)) didMigrate = true;
|
|
3990
4514
|
clearMigrationProgress();
|
|
3991
4515
|
const fullSetup = options.full === true;
|
|
3992
4516
|
const skippedSetupCandidates = !fullSetup && !hasExplicitExistingVitePlusSetupRequest(options) && hasExistingVitePlusMigrationCandidates(workspaceInfoOptional, options);
|
|
@@ -4077,6 +4601,15 @@ async function main() {
|
|
|
4077
4601
|
tsupMigrated = true;
|
|
4078
4602
|
}
|
|
4079
4603
|
}
|
|
4604
|
+
if (fullSetup && detectTsxProject(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages)) {
|
|
4605
|
+
if (options.interactive) clearMigrationProgress();
|
|
4606
|
+
if (await confirmTsxMigration(options.interactive)) {
|
|
4607
|
+
updateMigrationProgress("Migrating tsx scripts");
|
|
4608
|
+
const tsxResult = migrateTsxToVpx(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages, report);
|
|
4609
|
+
didMigrate ||= tsxResult.changed;
|
|
4610
|
+
needsInstall ||= tsxResult.dependencyRemoved;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4080
4613
|
if (fullSetup) {
|
|
4081
4614
|
const nodeVersionDetection = detectNodeVersionManagerFile(workspaceInfoOptional.rootDir);
|
|
4082
4615
|
if (nodeVersionDetection) {
|
|
@@ -4102,7 +4635,10 @@ async function main() {
|
|
|
4102
4635
|
].includes(path.basename(file)));
|
|
4103
4636
|
if (eslintMigrated || prettierMigrated || tsupMigrated) {
|
|
4104
4637
|
updateMigrationProgress("Rewriting configs");
|
|
4105
|
-
|
|
4638
|
+
const rootDir = workspaceInfoOptional.rootDir;
|
|
4639
|
+
const oxlintConfigs = workspaceInfoOptional.isMonorepo ? prepareOxlintConfigs([rootDir, ...workspaceInfoOptional.packages.map((pkg) => path.join(rootDir, pkg.path))]) : void 0;
|
|
4640
|
+
mergeViteConfigFiles(rootDir, true, report, workspaceInfoOptional.packages, rootDir, oxlintConfigs);
|
|
4641
|
+
if (oxlintConfigs) removeMigratedOxlintConfigs(oxlintConfigs);
|
|
4106
4642
|
if (tsupMigrated) {
|
|
4107
4643
|
mergeTsdownConfigFile(workspaceInfoOptional.rootDir, true, report);
|
|
4108
4644
|
for (const pkg of workspaceInfoOptional.packages ?? []) mergeTsdownConfigFile(path.join(workspaceInfoOptional.rootDir, pkg.path), true, report);
|
|
@@ -4208,5 +4744,4 @@ main().catch((err) => {
|
|
|
4208
4744
|
console.error(err);
|
|
4209
4745
|
process.exit(1);
|
|
4210
4746
|
});
|
|
4211
|
-
//#endregion
|
|
4212
4747
|
export {};
|