vite-plus 0.2.6 → 0.2.8
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 +5 -1
- package/README.md +1 -1
- package/bin/vpr +3 -2
- package/binding/index.cjs +193 -80
- package/binding/index.d.cts +99 -8
- package/dist/agent-CI79DuSy.js +600 -0
- package/dist/bin.js +1367 -11
- package/dist/{cli-truncate-BVOlIIPy.js → cli-truncate-Bg3RDXpi.js} +0 -2
- package/dist/client/bundledDevClient.mjs +1 -0
- package/dist/config/bin.js +13 -126
- package/dist/{constants-CaXMuYUG.js → constants-BppDcRc1.js} +1 -1
- package/dist/create/bin.js +90 -77
- package/dist/{define-config-iGV0CGUk.cjs → define-config-CEm_MtvJ.cjs} +1 -1
- package/dist/{define-config-hV8qyU8p.js → define-config-DPnHe1Mx.js} +2 -2
- package/dist/{define-config-eLQH9us2.d.ts → define-config-DWoAkNdp.d.ts} +16 -0
- 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-CKz3vcoG.js → dist-CSQWHI6y.js} +3 -8
- package/dist/dist-DT25H9pj.js +3 -0
- package/dist/{agent-0gFJkIKu.js → editor-CGWdbLgD.js} +709 -579
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{json-DiRs8ceZ.js → json-BU88uu6o.js} +2 -6
- package/dist/migration/bin.js +60 -50
- package/dist/{oxlint-plugin-config-CMSCIkks.js → oxlint-plugin-config-BHOzlwMw.js} +1 -1
- package/dist/oxlint-plugin.js +1 -1
- package/dist/pack-bin.js +2 -2
- package/dist/{package-CP5EUXlu.js → package-hV-77cBb.js} +2 -2
- package/dist/{tsconfig-Cac-NNdT.js → prompts-DYap08te.js} +32 -174
- package/dist/{resolve-vite-config-BY7PB-LW.js → resolve-vite-config-EWXclqvV.js} +1 -1
- package/dist/{rolldown-runtime-C7HZzL1F.js → rolldown-runtime-CMFfr-1z.js} +1 -1
- package/dist/staged/bin.js +20 -23
- package/dist/{help-YP84FSEz.js → terminal-CrqqK8WT.js} +39 -2
- package/dist/tsconfig-CVUKLfL8.js +151 -0
- package/dist/{tsgolint-path-B-yOos8p.js → tsgolint-path-eMZT-oea.js} +4 -2
- package/dist/tsgolint-path.js +1 -1
- package/dist/version.js +5 -5
- package/dist/versions.js +5 -5
- package/docs/config/index.md +13 -0
- package/docs/config/staged.md +2 -1
- package/docs/guide/build.md +1 -1
- package/docs/guide/ci.md +28 -3
- package/docs/guide/commit-hooks.md +76 -9
- package/docs/guide/dev.md +4 -0
- package/docs/guide/env.md +8 -4
- package/docs/guide/index.md +1 -1
- package/docs/guide/install.md +2 -0
- package/docs/guide/installer-env-vars.md +3 -3
- package/docs/guide/migrate.md +11 -3
- package/docs/guide/monorepo.md +94 -6
- package/docs/guide/run.md +15 -0
- package/docs/guide/test.md +4 -0
- package/docs/guide/troubleshooting.md +9 -5
- package/package.json +18 -24
- package/dist/dist-CtM2JRbM.js +0 -3
- package/dist/editor-D4T70tKk.js +0 -675
- package/dist/terminal-uTv0ZaMr.js +0 -33
- package/dist/{log-update-DHZRyJ2m.js → log-update-CoW8Z4Dl.js} +1 -1
|
@@ -1,40 +1,26 @@
|
|
|
1
|
-
import { r as __toESM } from "./rolldown-runtime-
|
|
2
|
-
import { a as VITEST_AGE_GATE_EXEMPT_PACKAGES, c as VITE_PLUS_OVERRIDE_PACKAGES, d as isForceOverrideMode, l as VITE_PLUS_VERSION, n as BASEURL_TSCONFIG_WARNING, o as VITEST_VERSION, s as VITE_PLUS_NAME } from "./constants-
|
|
3
|
-
import { i as ensureVitePlusImportRuleDefaults, r as createDefaultVitePlusLintConfig } from "./oxlint-plugin-config-
|
|
4
|
-
import {
|
|
5
|
-
import { a as writeJsonFile, i as readJsonFile, n as editJsonFile, r as isJsonFile } from "./json-
|
|
6
|
-
import { t as require_dist } from "./dist-
|
|
7
|
-
import { n as
|
|
1
|
+
import { r as __toESM } from "./rolldown-runtime-CMFfr-1z.js";
|
|
2
|
+
import { a as VITEST_AGE_GATE_EXEMPT_PACKAGES, c as VITE_PLUS_OVERRIDE_PACKAGES, d as isForceOverrideMode, l as VITE_PLUS_VERSION, n as BASEURL_TSCONFIG_WARNING, o as VITEST_VERSION, s as VITE_PLUS_NAME } from "./constants-BppDcRc1.js";
|
|
3
|
+
import { i as ensureVitePlusImportRuleDefaults, r as createDefaultVitePlusLintConfig } from "./oxlint-plugin-config-BHOzlwMw.js";
|
|
4
|
+
import { A as isCancel, C as log, E as select, M as runCommandSilently, N as require_cross_spawn, d as readYamlFile, f as scalarString, g as getSpinner, t as cancelAndExit, u as editYamlFile, v as PackageManager, w as multiselect, x as confirm, y as require_semver } from "./prompts-DYap08te.js";
|
|
5
|
+
import { a as writeJsonFile, c as parse, i as readJsonFile, n as editJsonFile, o as applyEdits, r as isJsonFile, s as modify, t as detectFormattingOptions } from "./json-BU88uu6o.js";
|
|
6
|
+
import { t as require_dist } from "./dist-CSQWHI6y.js";
|
|
7
|
+
import { a as hasTypesToRewriteInTsconfig, c as rewriteTypesInTsconfig, i as hasBaseUrlInTsconfig, n as findTsconfigFiles, o as hasVitestTypesInTsconfig, s as removeDeprecatedTsconfigFalseOption } from "./tsconfig-CVUKLfL8.js";
|
|
8
|
+
import { n as detectPackageMetadata, r as getScopeFromPackageName } from "./package-hV-77cBb.js";
|
|
9
|
+
import { f as displayRelative, l as SUPPORTED_GIT_HOOK_NAMES, p as rulesDir, u as findUnsafeHookInstallPath } from "./agent-CI79DuSy.js";
|
|
8
10
|
import { n as addMigrationWarning, t as addManualStep } from "./report-BS7ULx7A.js";
|
|
9
11
|
import path, { posix, win32 } from "node:path";
|
|
10
|
-
import { hasConfigKey, mergeJsonConfig, mergeTsdownConfig, rewriteEslint, rewriteImportsInDirectory, rewriteScripts, wrapLazyPlugins } from "../binding/index.js";
|
|
12
|
+
import { detectWorkspace, hasConfigKey, mergeJsonConfig, mergeTsdownConfig, rewriteEslint, rewriteImportsInDirectory, rewritePrettier, rewriteScripts, wrapLazyPlugins } from "../binding/index.js";
|
|
13
|
+
import { styleText } from "node:util";
|
|
11
14
|
import * as actualFS from "node:fs";
|
|
12
15
|
import fs from "node:fs";
|
|
13
|
-
import { lstatSync, readdir, readdirSync, readlinkSync, realpathSync as realpathSync$1 } from "fs";
|
|
14
|
-
import { styleText } from "node:util";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
16
|
import os from "node:os";
|
|
17
|
+
import { lstatSync as lstatSync$1, readdir, readdirSync, readlinkSync, realpathSync as realpathSync$1 } from "fs";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
17
19
|
import "oxlint";
|
|
18
20
|
import fsPromises, { lstat, readdir as readdir$1, readlink, realpath } from "node:fs/promises";
|
|
19
21
|
import { EventEmitter } from "node:events";
|
|
20
22
|
import Stream from "node:stream";
|
|
21
23
|
import { StringDecoder } from "node:string_decoder";
|
|
22
|
-
//#region src/utils/path.ts
|
|
23
|
-
function findPkgRoot() {
|
|
24
|
-
let dir = import.meta.dirname;
|
|
25
|
-
while (dir !== path.dirname(dir)) {
|
|
26
|
-
if (fs.existsSync(path.join(dir, "package.json"))) return dir;
|
|
27
|
-
dir = path.dirname(dir);
|
|
28
|
-
}
|
|
29
|
-
return dir;
|
|
30
|
-
}
|
|
31
|
-
const pkgRoot = findPkgRoot();
|
|
32
|
-
const templatesDir = path.join(pkgRoot, "templates");
|
|
33
|
-
const rulesDir = path.join(pkgRoot, "rules");
|
|
34
|
-
function displayRelative(to, from = process.cwd()) {
|
|
35
|
-
return path.relative(from, to).replaceAll("\\", "/");
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
24
|
//#region src/migration/migrator/shared.ts
|
|
39
25
|
var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
|
|
40
26
|
const LINT_STAGED_JSON_CONFIG_FILES = [".lintstagedrc.json", ".lintstagedrc"];
|
|
@@ -743,6 +729,184 @@ async function promptEslintMigration(projectPath, interactive, packages) {
|
|
|
743
729
|
return true;
|
|
744
730
|
}
|
|
745
731
|
//#endregion
|
|
732
|
+
//#region src/migration/migrator/prettier.ts
|
|
733
|
+
function detectPrettierProject(projectPath, packages) {
|
|
734
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
735
|
+
if (!fs.existsSync(packageJsonPath)) return { hasDependency: false };
|
|
736
|
+
const pkg = readJsonFile(packageJsonPath);
|
|
737
|
+
let hasDependency = !!(pkg.devDependencies?.prettier || pkg.dependencies?.prettier);
|
|
738
|
+
const configFile = detectConfigs(projectPath).prettierConfig;
|
|
739
|
+
if (!hasDependency && packages) for (const wp of packages) {
|
|
740
|
+
const pkgJsonPath = path.join(projectPath, wp.path, "package.json");
|
|
741
|
+
if (!fs.existsSync(pkgJsonPath)) continue;
|
|
742
|
+
const wpPkg = readJsonFile(pkgJsonPath);
|
|
743
|
+
if (wpPkg.devDependencies?.prettier || wpPkg.dependencies?.prettier) {
|
|
744
|
+
hasDependency = true;
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
hasDependency,
|
|
750
|
+
configFile
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Run `vp fmt --migrate=prettier` step with graceful error handling.
|
|
755
|
+
* Returns true on success, false on failure.
|
|
756
|
+
*/
|
|
757
|
+
async function runPrettierMigrateStep(vpBin, cwd, spinner, failMessage, manualHint) {
|
|
758
|
+
try {
|
|
759
|
+
const result = await runCommandSilently({
|
|
760
|
+
command: vpBin,
|
|
761
|
+
args: ["fmt", "--migrate=prettier"],
|
|
762
|
+
cwd,
|
|
763
|
+
envs: process.env
|
|
764
|
+
});
|
|
765
|
+
if (result.exitCode !== 0) {
|
|
766
|
+
spinner.stop(failMessage);
|
|
767
|
+
const stderr = result.stderr.toString().trim();
|
|
768
|
+
if (stderr) log.warn(`⚠ ${stderr}`);
|
|
769
|
+
log.info(manualHint);
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
return true;
|
|
773
|
+
} catch {
|
|
774
|
+
spinner.stop(failMessage);
|
|
775
|
+
log.info(manualHint);
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
async function migratePrettierToOxfmt(projectPath, interactive, prettierConfigFile, packages, options) {
|
|
780
|
+
const vpBin = process.env.VP_CLI_BIN ?? "vp";
|
|
781
|
+
const spinner = options?.silent ? {
|
|
782
|
+
start: () => {},
|
|
783
|
+
stop: () => {},
|
|
784
|
+
pause: () => {},
|
|
785
|
+
resume: () => {},
|
|
786
|
+
cancel: () => {},
|
|
787
|
+
error: () => {},
|
|
788
|
+
clear: () => {},
|
|
789
|
+
message: () => {},
|
|
790
|
+
isCancelled: false
|
|
791
|
+
} : getSpinner(interactive);
|
|
792
|
+
if (prettierConfigFile) {
|
|
793
|
+
let tempPrettierConfig;
|
|
794
|
+
if (prettierConfigFile === "package.json#prettier") {
|
|
795
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
796
|
+
const pkg = readJsonFile(packageJsonPath);
|
|
797
|
+
if (pkg.prettier) {
|
|
798
|
+
tempPrettierConfig = path.join(projectPath, ".prettierrc.json");
|
|
799
|
+
fs.writeFileSync(tempPrettierConfig, JSON.stringify(pkg.prettier, null, 2));
|
|
800
|
+
} else return true;
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
spinner.start("Migrating Prettier config to Oxfmt...");
|
|
804
|
+
if (!await runPrettierMigrateStep(vpBin, projectPath, spinner, "Prettier migration failed", "You can run `vp fmt --migrate=prettier` manually later")) return false;
|
|
805
|
+
spinner.stop("Prettier config migrated to .oxfmtrc.json");
|
|
806
|
+
} finally {
|
|
807
|
+
if (tempPrettierConfig) try {
|
|
808
|
+
fs.unlinkSync(tempPrettierConfig);
|
|
809
|
+
} catch {}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (options?.report) options.report.prettierMigrated = true;
|
|
813
|
+
deletePrettierConfigFiles(projectPath, options?.report, options?.silent);
|
|
814
|
+
rewritePrettierPackageJson(path.join(projectPath, "package.json"));
|
|
815
|
+
if (packages) for (const pkg of packages) rewritePrettierPackageJson(path.join(projectPath, pkg.path, "package.json"));
|
|
816
|
+
rewritePrettierLintStagedConfigFiles(projectPath, options?.report);
|
|
817
|
+
const prettierIgnorePath = path.join(projectPath, ".prettierignore");
|
|
818
|
+
if (fs.existsSync(prettierIgnorePath)) warnMigration(`${displayRelative(prettierIgnorePath)} found — Oxfmt supports .prettierignore, but using the \`ignorePatterns\` option is recommended.`, options?.report);
|
|
819
|
+
return true;
|
|
820
|
+
}
|
|
821
|
+
function deletePrettierConfigFiles(basePath, report, silent = false) {
|
|
822
|
+
const configs = detectConfigs(basePath);
|
|
823
|
+
if (configs.prettierConfig && configs.prettierConfig !== "package.json#prettier") {
|
|
824
|
+
const configPath = path.join(basePath, configs.prettierConfig);
|
|
825
|
+
if (fs.existsSync(configPath)) {
|
|
826
|
+
fs.unlinkSync(configPath);
|
|
827
|
+
if (report) report.removedConfigCount++;
|
|
828
|
+
if (!silent) log.success(`✔ Removed ${displayRelative(configPath)}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
for (const file of PRETTIER_CONFIG_FILES) {
|
|
832
|
+
if (file === configs.prettierConfig) continue;
|
|
833
|
+
const configPath = path.join(basePath, file);
|
|
834
|
+
if (fs.existsSync(configPath)) {
|
|
835
|
+
fs.unlinkSync(configPath);
|
|
836
|
+
if (report) report.removedConfigCount++;
|
|
837
|
+
if (!silent) log.success(`✔ Removed ${displayRelative(configPath)}`);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
editJsonFile(path.join(basePath, "package.json"), (pkg) => {
|
|
841
|
+
if (pkg.prettier) {
|
|
842
|
+
delete pkg.prettier;
|
|
843
|
+
return pkg;
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
function rewritePrettierPackageJson(packageJsonPath) {
|
|
848
|
+
if (!fs.existsSync(packageJsonPath)) return;
|
|
849
|
+
editJsonFile(packageJsonPath, (pkg) => {
|
|
850
|
+
let changed = false;
|
|
851
|
+
if (pkg.devDependencies) {
|
|
852
|
+
for (const dep of Object.keys(pkg.devDependencies)) if (dep === "prettier" || dep.startsWith("prettier-plugin-")) {
|
|
853
|
+
delete pkg.devDependencies[dep];
|
|
854
|
+
changed = true;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (pkg.dependencies) {
|
|
858
|
+
for (const dep of Object.keys(pkg.dependencies)) if (dep === "prettier" || dep.startsWith("prettier-plugin-")) {
|
|
859
|
+
delete pkg.dependencies[dep];
|
|
860
|
+
changed = true;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (pkg.scripts) {
|
|
864
|
+
const updated = rewritePrettier(JSON.stringify(pkg.scripts));
|
|
865
|
+
if (updated) {
|
|
866
|
+
pkg.scripts = JSON.parse(updated);
|
|
867
|
+
changed = true;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (pkg["lint-staged"]) {
|
|
871
|
+
const updated = rewritePrettier(JSON.stringify(pkg["lint-staged"]));
|
|
872
|
+
if (updated) {
|
|
873
|
+
pkg["lint-staged"] = JSON.parse(updated);
|
|
874
|
+
changed = true;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return changed ? pkg : void 0;
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
function rewritePrettierLintStagedConfigFiles(projectPath, report) {
|
|
881
|
+
rewriteToolLintStagedConfigFiles(projectPath, rewritePrettier, "prettier", report);
|
|
882
|
+
}
|
|
883
|
+
function warnPackageLevelPrettier() {
|
|
884
|
+
log.warn("Prettier detected in workspace packages but no root config found. Package-level Prettier must be migrated manually.");
|
|
885
|
+
}
|
|
886
|
+
async function confirmPrettierMigration(interactive) {
|
|
887
|
+
if (interactive) {
|
|
888
|
+
const confirmed = await confirm({
|
|
889
|
+
message: "Migrate Prettier to Oxfmt?\n " + styleText("gray", "Oxfmt is Vite+'s built-in formatter that replaces Prettier with faster performance. Your configuration will be converted automatically."),
|
|
890
|
+
initialValue: true
|
|
891
|
+
});
|
|
892
|
+
if (isCancel(confirmed)) cancelAndExit();
|
|
893
|
+
return confirmed;
|
|
894
|
+
}
|
|
895
|
+
log.info("Prettier configuration detected. Auto-migrating to Oxfmt...");
|
|
896
|
+
return true;
|
|
897
|
+
}
|
|
898
|
+
async function promptPrettierMigration(projectPath, interactive, packages) {
|
|
899
|
+
const prettierProject = detectPrettierProject(projectPath, packages);
|
|
900
|
+
if (!prettierProject.hasDependency) return false;
|
|
901
|
+
if (!prettierProject.configFile) {
|
|
902
|
+
warnPackageLevelPrettier();
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
if (!await confirmPrettierMigration(interactive)) return false;
|
|
906
|
+
if (!await migratePrettierToOxfmt(projectPath, interactive, prettierProject.configFile, packages)) cancelAndExit("Prettier migration failed.", 1);
|
|
907
|
+
return true;
|
|
908
|
+
}
|
|
909
|
+
//#endregion
|
|
746
910
|
//#region src/migration/migrator/tsconfig.ts
|
|
747
911
|
function cleanupDeprecatedTsconfigOptions(projectPath, silent = false, report) {
|
|
748
912
|
const deprecatedOptions = ["esModuleInterop", "allowSyntheticDefaultImports"];
|
|
@@ -767,6 +931,66 @@ function hasTsconfigTypesToRewrite(projectPath) {
|
|
|
767
931
|
return findTsconfigFiles(projectPath).some((filePath) => hasTypesToRewriteInTsconfig(filePath));
|
|
768
932
|
}
|
|
769
933
|
//#endregion
|
|
934
|
+
//#region src/migration/migrator/framework-shim.ts
|
|
935
|
+
const FRAMEWORK_SHIMS = {
|
|
936
|
+
vue: [
|
|
937
|
+
"declare module '*.vue' {",
|
|
938
|
+
" import type { DefineComponent } from 'vue';",
|
|
939
|
+
" const component: DefineComponent<{}, {}, unknown>;",
|
|
940
|
+
" export default component;",
|
|
941
|
+
"}"
|
|
942
|
+
].join("\n"),
|
|
943
|
+
astro: "/// <reference types=\"astro/client\" />"
|
|
944
|
+
};
|
|
945
|
+
function detectFramework(projectPath) {
|
|
946
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
947
|
+
if (!fs.existsSync(packageJsonPath)) return [];
|
|
948
|
+
const pkg = readJsonFile(packageJsonPath);
|
|
949
|
+
const allDeps = {
|
|
950
|
+
...pkg.dependencies,
|
|
951
|
+
...pkg.devDependencies
|
|
952
|
+
};
|
|
953
|
+
return ["vue", "astro"].filter((framework) => !!allDeps[framework]);
|
|
954
|
+
}
|
|
955
|
+
function getEnvDtsPath(projectPath) {
|
|
956
|
+
const srcEnvDts = path.join(projectPath, "src", "env.d.ts");
|
|
957
|
+
const rootEnvDts = path.join(projectPath, "env.d.ts");
|
|
958
|
+
for (const candidate of [srcEnvDts, rootEnvDts]) if (fs.existsSync(candidate)) return candidate;
|
|
959
|
+
return fs.existsSync(path.join(projectPath, "src")) ? srcEnvDts : rootEnvDts;
|
|
960
|
+
}
|
|
961
|
+
function hasFrameworkShim(projectPath, framework) {
|
|
962
|
+
const dirsToScan = [projectPath, path.join(projectPath, "src")];
|
|
963
|
+
for (const dir of dirsToScan) {
|
|
964
|
+
if (!fs.existsSync(dir)) continue;
|
|
965
|
+
let entries;
|
|
966
|
+
try {
|
|
967
|
+
entries = fs.readdirSync(dir);
|
|
968
|
+
} catch {
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
for (const entry of entries) {
|
|
972
|
+
if (!entry.endsWith(".d.ts")) continue;
|
|
973
|
+
const content = fs.readFileSync(path.join(dir, entry), "utf-8");
|
|
974
|
+
if (framework === "astro") {
|
|
975
|
+
if (content.includes("astro/client")) return true;
|
|
976
|
+
} else if (content.includes(`'*.${framework}'`) || content.includes(`"*.${framework}"`)) return true;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
function addFrameworkShim(projectPath, framework, report) {
|
|
982
|
+
const envDtsPath = getEnvDtsPath(projectPath);
|
|
983
|
+
const shim = FRAMEWORK_SHIMS[framework];
|
|
984
|
+
if (fs.existsSync(envDtsPath)) {
|
|
985
|
+
const existing = fs.readFileSync(envDtsPath, "utf-8");
|
|
986
|
+
fs.writeFileSync(envDtsPath, `${existing.trimEnd()}\n\n${shim}\n`, "utf-8");
|
|
987
|
+
} else {
|
|
988
|
+
fs.mkdirSync(path.dirname(envDtsPath), { recursive: true });
|
|
989
|
+
fs.writeFileSync(envDtsPath, `${shim}\n`, "utf-8");
|
|
990
|
+
}
|
|
991
|
+
if (report) report.frameworkShimAdded = true;
|
|
992
|
+
}
|
|
993
|
+
//#endregion
|
|
770
994
|
//#region src/migration/migrator/vitest-ecosystem.ts
|
|
771
995
|
var import_dist = require_dist();
|
|
772
996
|
const VITEST_ALIGN_EXCLUDED = /* @__PURE__ */ new Set(["@vitest/eslint-plugin", "@vitest/coverage-c8"]);
|
|
@@ -1062,7 +1286,6 @@ function collectVitestEcosystemInstallDependencyNames(rootDir, packages) {
|
|
|
1062
1286
|
}
|
|
1063
1287
|
//#endregion
|
|
1064
1288
|
//#region ../../node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js
|
|
1065
|
-
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
1066
1289
|
const balanced = (a, b, str) => {
|
|
1067
1290
|
const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
|
|
1068
1291
|
const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
|
|
@@ -1252,7 +1475,7 @@ function expand_(str, max, isTop) {
|
|
|
1252
1475
|
}
|
|
1253
1476
|
//#endregion
|
|
1254
1477
|
//#region ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/assert-valid-pattern.js
|
|
1255
|
-
const MAX_PATTERN_LENGTH =
|
|
1478
|
+
const MAX_PATTERN_LENGTH = 65536;
|
|
1256
1479
|
const assertValidPattern = (pattern) => {
|
|
1257
1480
|
if (typeof pattern !== "string") throw new TypeError("invalid pattern");
|
|
1258
1481
|
if (pattern.length > MAX_PATTERN_LENGTH) throw new TypeError("pattern is too long");
|
|
@@ -3982,12 +4205,15 @@ while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && this[BUFFER].length);
|
|
|
3982
4205
|
return isStream;
|
|
3983
4206
|
}
|
|
3984
4207
|
};
|
|
4208
|
+
//#endregion
|
|
4209
|
+
//#region ../../node_modules/.pnpm/path-scurry@2.0.1/node_modules/path-scurry/dist/esm/index.js
|
|
4210
|
+
const realpathSync$2 = realpathSync$1.native;
|
|
3985
4211
|
const defaultFS = {
|
|
3986
|
-
lstatSync,
|
|
4212
|
+
lstatSync: lstatSync$1,
|
|
3987
4213
|
readdir,
|
|
3988
4214
|
readdirSync,
|
|
3989
4215
|
readlinkSync,
|
|
3990
|
-
realpathSync: realpathSync$
|
|
4216
|
+
realpathSync: realpathSync$2,
|
|
3991
4217
|
promises: {
|
|
3992
4218
|
lstat,
|
|
3993
4219
|
readdir: readdir$1,
|
|
@@ -4055,7 +4281,7 @@ var ResolveCache = class extends L {
|
|
|
4055
4281
|
* @internal
|
|
4056
4282
|
*/
|
|
4057
4283
|
var ChildrenCache = class extends L {
|
|
4058
|
-
constructor(maxSize =
|
|
4284
|
+
constructor(maxSize = 16384) {
|
|
4059
4285
|
super({
|
|
4060
4286
|
maxSize,
|
|
4061
4287
|
sizeCalculation: (a) => a.length + 1
|
|
@@ -4388,7 +4614,7 @@ var PathBase = class {
|
|
|
4388
4614
|
return this[`is${type}`]();
|
|
4389
4615
|
}
|
|
4390
4616
|
getType() {
|
|
4391
|
-
return this.isUnknown() ? "Unknown" : this.isDirectory() ? "Directory" : this.isFile() ? "File" : this.isSymbolicLink() ? "SymbolicLink" : this.isFIFO() ? "FIFO" : this.isCharacterDevice() ? "CharacterDevice" : this.isBlockDevice() ? "BlockDevice" :
|
|
4617
|
+
return this.isUnknown() ? "Unknown" : this.isDirectory() ? "Directory" : this.isFile() ? "File" : this.isSymbolicLink() ? "SymbolicLink" : this.isFIFO() ? "FIFO" : this.isCharacterDevice() ? "CharacterDevice" : this.isBlockDevice() ? "BlockDevice" : /* c8 ignore start */ this.isSocket() ? "Socket" : "Unknown";
|
|
4392
4618
|
/* c8 ignore stop */
|
|
4393
4619
|
}
|
|
4394
4620
|
/**
|
|
@@ -5013,7 +5239,7 @@ var PathScurryBase = class {
|
|
|
5013
5239
|
*
|
|
5014
5240
|
* @internal
|
|
5015
5241
|
*/
|
|
5016
|
-
constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize =
|
|
5242
|
+
constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16384, fs = defaultFS } = {}) {
|
|
5017
5243
|
this.#fs = fsFromOption(fs);
|
|
5018
5244
|
if (cwd instanceof URL || cwd.startsWith("file://")) cwd = fileURLToPath(cwd);
|
|
5019
5245
|
const cwdPath = pathImpl.resolve(cwd);
|
|
@@ -6504,6 +6730,96 @@ const glob = Object.assign(glob_, {
|
|
|
6504
6730
|
});
|
|
6505
6731
|
glob.glob = glob;
|
|
6506
6732
|
//#endregion
|
|
6733
|
+
//#region src/utils/workspace.ts
|
|
6734
|
+
async function detectWorkspace$1(rootDir) {
|
|
6735
|
+
const bindingResult = await detectWorkspace(rootDir);
|
|
6736
|
+
const result = {
|
|
6737
|
+
rootDir,
|
|
6738
|
+
packageManager: void 0,
|
|
6739
|
+
packageManagerVersion: "latest",
|
|
6740
|
+
isMonorepo: false,
|
|
6741
|
+
monorepoScope: "",
|
|
6742
|
+
workspacePatterns: [],
|
|
6743
|
+
parentDirs: [],
|
|
6744
|
+
packages: []
|
|
6745
|
+
};
|
|
6746
|
+
if (bindingResult.packageManagerName) result.packageManager = bindingResult.packageManagerName;
|
|
6747
|
+
if (bindingResult.packageManagerVersion) result.packageManagerVersion = bindingResult.packageManagerVersion;
|
|
6748
|
+
if (bindingResult.isMonorepo) result.isMonorepo = bindingResult.isMonorepo;
|
|
6749
|
+
if (bindingResult.root) result.rootDir = bindingResult.root;
|
|
6750
|
+
if (result.isMonorepo) {
|
|
6751
|
+
const pnpmWorkspaceFile = path.join(result.rootDir, "pnpm-workspace.yaml");
|
|
6752
|
+
const packageJsonFile = path.join(result.rootDir, "package.json");
|
|
6753
|
+
if (fs.existsSync(pnpmWorkspaceFile)) {
|
|
6754
|
+
const workspaceConfig = readYamlFile(pnpmWorkspaceFile);
|
|
6755
|
+
if (Array.isArray(workspaceConfig.packages)) result.workspacePatterns = workspaceConfig.packages;
|
|
6756
|
+
} else if (fs.existsSync(packageJsonFile)) {
|
|
6757
|
+
const pkg = readJsonFile(packageJsonFile);
|
|
6758
|
+
if (Array.isArray(pkg.workspaces)) result.workspacePatterns = pkg.workspaces;
|
|
6759
|
+
else if (pkg.workspaces && Array.isArray(pkg.workspaces.packages)) result.workspacePatterns = pkg.workspaces.packages;
|
|
6760
|
+
}
|
|
6761
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
6762
|
+
for (const pattern of result.workspacePatterns) {
|
|
6763
|
+
if (!pattern.endsWith("*")) continue;
|
|
6764
|
+
const dir = pattern.replace(/\/\*{1,2}$/, "");
|
|
6765
|
+
if (dir) dirs.add(dir);
|
|
6766
|
+
}
|
|
6767
|
+
result.parentDirs = Array.from(dirs).sort();
|
|
6768
|
+
const pkg = readJsonFile(packageJsonFile);
|
|
6769
|
+
if (pkg.name) result.monorepoScope = getScopeFromPackageName(pkg.name);
|
|
6770
|
+
result.packages = discoverWorkspacePackages(result.workspacePatterns, result.rootDir);
|
|
6771
|
+
}
|
|
6772
|
+
return result;
|
|
6773
|
+
}
|
|
6774
|
+
function isBingoTemplate(pkg) {
|
|
6775
|
+
return !!pkg.dependencies?.bingo;
|
|
6776
|
+
}
|
|
6777
|
+
function discoverWorkspacePackages(workspacePatterns, rootDir) {
|
|
6778
|
+
const packages = [];
|
|
6779
|
+
if (workspacePatterns.length === 0) return packages;
|
|
6780
|
+
const packageJsonRelativePaths = globSync(workspacePatterns.map((pattern) => `${pattern}/package.json`), {
|
|
6781
|
+
absolute: false,
|
|
6782
|
+
cwd: rootDir,
|
|
6783
|
+
ignore: ["**/node_modules/**"]
|
|
6784
|
+
});
|
|
6785
|
+
for (const packageJsonRelativePath of packageJsonRelativePaths) {
|
|
6786
|
+
const packageJsonPath = path.join(rootDir, packageJsonRelativePath);
|
|
6787
|
+
const pkg = readJsonFile(packageJsonPath);
|
|
6788
|
+
if (!pkg.name) continue;
|
|
6789
|
+
packages.push({
|
|
6790
|
+
name: pkg.name,
|
|
6791
|
+
path: path.dirname(packageJsonRelativePath).split(path.sep).join("/"),
|
|
6792
|
+
description: pkg.description,
|
|
6793
|
+
version: pkg.version
|
|
6794
|
+
});
|
|
6795
|
+
}
|
|
6796
|
+
return packages;
|
|
6797
|
+
}
|
|
6798
|
+
function updatePackageJsonWithDeps(rootDir, projectDir, dependencies, dependencyType) {
|
|
6799
|
+
const packageJsonPath = path.join(rootDir, projectDir, "package.json");
|
|
6800
|
+
editJsonFile(packageJsonPath, (pkg) => {
|
|
6801
|
+
if (!pkg[dependencyType]) pkg[dependencyType] = {};
|
|
6802
|
+
for (const dep of dependencies) pkg[dependencyType][dep] = "workspace:*";
|
|
6803
|
+
return pkg;
|
|
6804
|
+
});
|
|
6805
|
+
}
|
|
6806
|
+
function updateWorkspaceConfig(projectPath, workspaceInfo) {
|
|
6807
|
+
for (const pattern of workspaceInfo.workspacePatterns) if (minimatch(projectPath, pattern)) return;
|
|
6808
|
+
const parentDir = path.dirname(projectPath);
|
|
6809
|
+
const pattern = parentDir === "." ? projectPath : `${parentDir}/*`;
|
|
6810
|
+
if (workspaceInfo.packageManager === PackageManager.pnpm) editYamlFile(path.join(workspaceInfo.rootDir, "pnpm-workspace.yaml"), (doc) => {
|
|
6811
|
+
let packages = doc.getIn(["packages"]);
|
|
6812
|
+
if (!packages) packages = new import_dist.YAMLSeq();
|
|
6813
|
+
packages.add(new import_dist.Scalar(pattern));
|
|
6814
|
+
doc.setIn(["packages"], packages);
|
|
6815
|
+
});
|
|
6816
|
+
else editJsonFile(path.join(workspaceInfo.rootDir, "package.json"), (pkg) => {
|
|
6817
|
+
if (pkg.workspaces && !Array.isArray(pkg.workspaces)) pkg.workspaces.packages = [...pkg.workspaces.packages || [], pattern];
|
|
6818
|
+
else pkg.workspaces = [...pkg.workspaces || [], pattern];
|
|
6819
|
+
return pkg;
|
|
6820
|
+
});
|
|
6821
|
+
}
|
|
6822
|
+
//#endregion
|
|
6507
6823
|
//#region src/migration/migrator/catalog.ts
|
|
6508
6824
|
const BROWSER_PROVIDER_POSTINSTALL_PACKAGES = ["edgedriver", "geckodriver"];
|
|
6509
6825
|
const PUBLIC_PEER_DEPENDENCY_FALLBACKS = {
|
|
@@ -6684,7 +7000,8 @@ function rewritePnpmWorkspaceYaml(projectPath, pnpmMajorVersion, shouldAllowBrow
|
|
|
6684
7000
|
* so all overrides must live in pnpm-workspace.yaml.
|
|
6685
7001
|
*/
|
|
6686
7002
|
function migratePnpmOverridesToWorkspaceYaml(projectPath, overrides) {
|
|
6687
|
-
|
|
7003
|
+
const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
|
|
7004
|
+
editYamlFile(pnpmWorkspaceYamlPath, (doc) => {
|
|
6688
7005
|
for (const [key, value] of Object.entries(overrides)) doc.setIn(["overrides", scalarString(key)], scalarString(value));
|
|
6689
7006
|
});
|
|
6690
7007
|
}
|
|
@@ -8059,6 +8376,24 @@ function isProtocolPinnedSpec(spec) {
|
|
|
8059
8376
|
}
|
|
8060
8377
|
//#endregion
|
|
8061
8378
|
//#region src/migration/migrator/vite-config.ts
|
|
8379
|
+
const SVELTE_RUNE_GLOBALS = {
|
|
8380
|
+
$state: "readonly",
|
|
8381
|
+
$derived: "readonly",
|
|
8382
|
+
$effect: "readonly",
|
|
8383
|
+
$props: "readonly",
|
|
8384
|
+
$bindable: "readonly",
|
|
8385
|
+
$inspect: "readonly",
|
|
8386
|
+
$host: "readonly"
|
|
8387
|
+
};
|
|
8388
|
+
function ensureSvelteRuneGlobals(config) {
|
|
8389
|
+
for (const override of config.overrides ?? []) {
|
|
8390
|
+
if (!override.files.some((file) => !file.startsWith("!") && braceExpand(file).some((pattern) => pattern.includes(".svelte")))) continue;
|
|
8391
|
+
override.globals = {
|
|
8392
|
+
...SVELTE_RUNE_GLOBALS,
|
|
8393
|
+
...override.globals
|
|
8394
|
+
};
|
|
8395
|
+
}
|
|
8396
|
+
}
|
|
8062
8397
|
function removeLintStagedFromPackageJson(packageJsonPath) {
|
|
8063
8398
|
editJsonFile(packageJsonPath, (pkg) => {
|
|
8064
8399
|
if (pkg["lint-staged"]) {
|
|
@@ -8125,7 +8460,8 @@ function mergeTsdownConfigFile(projectPath, silent = false, report) {
|
|
|
8125
8460
|
mergeAndRemoveJsonConfig(projectPath, viteConfig, configs.tsdownConfig, "pack", silent, report);
|
|
8126
8461
|
return;
|
|
8127
8462
|
}
|
|
8128
|
-
const
|
|
8463
|
+
const tsdownRelativePath = `./${configs.tsdownConfig}`;
|
|
8464
|
+
const result = mergeTsdownConfig(fullViteConfigPath, tsdownRelativePath);
|
|
8129
8465
|
if (result.updated) {
|
|
8130
8466
|
fs.writeFileSync(fullViteConfigPath, result.content);
|
|
8131
8467
|
if (report) report.tsdownImportCount++;
|
|
@@ -8149,7 +8485,9 @@ function mergeViteConfigFiles(projectPath, silent = false, report, packages, wor
|
|
|
8149
8485
|
if (oxlintJson.options.typeCheck === void 0) oxlintJson.options.typeCheck = true;
|
|
8150
8486
|
} else warnMigration(BASEURL_TSCONFIG_WARNING, report);
|
|
8151
8487
|
sanitizeMigratedOxlintConfig(oxlintJson, collectInstalledPackageNames(workspaceRoot ?? projectPath, packages), report);
|
|
8152
|
-
|
|
8488
|
+
ensureSvelteRuneGlobals(oxlintJson);
|
|
8489
|
+
const normalizedOxlintConfig = ensureVitePlusImportRuleDefaults(oxlintJson);
|
|
8490
|
+
writeJsonFile(fullOxlintPath, normalizedOxlintConfig);
|
|
8153
8491
|
mergeAndRemoveJsonConfig(projectPath, viteConfig, configs.oxlintConfig, "lint", silent, report);
|
|
8154
8492
|
}
|
|
8155
8493
|
if (configs.oxfmtConfig) mergeAndRemoveJsonConfig(projectPath, viteConfig, configs.oxfmtConfig, "fmt", silent, report);
|
|
@@ -8296,54 +8634,40 @@ function rewriteAllImports(projectPath, silent = false, report, preserveNuxtVite
|
|
|
8296
8634
|
}
|
|
8297
8635
|
//#endregion
|
|
8298
8636
|
//#region src/migration/migrator/git-hooks.ts
|
|
8299
|
-
|
|
8300
|
-
* Check if the project has an unsupported husky version (<9.0.0).
|
|
8301
|
-
* Uses `semver.coerce` to handle ranges like `^8.0.0` → `8.0.0`.
|
|
8302
|
-
* When the specifier is a catalog reference (e.g. `"catalog:"`), resolves
|
|
8303
|
-
* it from the active package manager's catalog first — a `catalog:` spec is
|
|
8304
|
-
* only meaningful to the manager that owns the workspace, so we never read a
|
|
8305
|
-
* leftover/foreign catalog file. When it is still not coercible (e.g.
|
|
8306
|
-
* `"latest"`), falls back to the installed version in node_modules via
|
|
8307
|
-
* `detectPackageMetadata`.
|
|
8308
|
-
* Returns a reason string if hooks migration should be skipped, or null
|
|
8309
|
-
* if husky is absent or compatible.
|
|
8310
|
-
*/
|
|
8311
|
-
function checkUnsupportedHuskyVersion(projectPath, deps, prodDeps, packageManager) {
|
|
8312
|
-
const huskyVersion = deps?.husky ?? prodDeps?.husky;
|
|
8313
|
-
if (!huskyVersion) return null;
|
|
8314
|
-
let coerced = import_semver.default.coerce(huskyVersion);
|
|
8315
|
-
if (coerced == null && packageManager != null && huskyVersion.startsWith("catalog:")) {
|
|
8316
|
-
const resolved = createCatalogDependencyResolver(projectPath, packageManager)?.(huskyVersion, "husky");
|
|
8317
|
-
if (resolved) coerced = import_semver.default.coerce(resolved);
|
|
8318
|
-
}
|
|
8319
|
-
if (coerced == null) {
|
|
8320
|
-
const installed = detectPackageMetadata(projectPath, "husky");
|
|
8321
|
-
if (installed) coerced = import_semver.default.coerce(installed.version);
|
|
8322
|
-
if (coerced == null) return `Could not determine husky version from "${huskyVersion}" — please specify a semver-compatible version (e.g., "^9.0.0") and re-run migration.`;
|
|
8323
|
-
}
|
|
8324
|
-
if (import_semver.default.satisfies(coerced, "<9.0.0")) return "Detected husky <9.0.0 — please upgrade to husky v9+ first, then re-run migration.";
|
|
8325
|
-
return null;
|
|
8326
|
-
}
|
|
8637
|
+
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
8327
8638
|
const OTHER_HOOK_TOOLS = [
|
|
8328
8639
|
"simple-git-hooks",
|
|
8329
8640
|
"lefthook",
|
|
8330
8641
|
"yorkie"
|
|
8331
8642
|
];
|
|
8332
|
-
const
|
|
8333
|
-
function
|
|
8643
|
+
const SUPPORTED_GIT_HOOK_NAME_SET = new Set(SUPPORTED_GIT_HOOK_NAMES);
|
|
8644
|
+
function removeReplacedStagedPackage(packageJsonPath) {
|
|
8334
8645
|
editJsonFile(packageJsonPath, (pkg) => {
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
if (pkg.dependencies?.[name]) delete pkg.dependencies[name];
|
|
8338
|
-
}
|
|
8646
|
+
if (pkg.devDependencies?.["lint-staged"]) delete pkg.devDependencies["lint-staged"];
|
|
8647
|
+
if (pkg.dependencies?.["lint-staged"]) delete pkg.dependencies["lint-staged"];
|
|
8339
8648
|
return pkg;
|
|
8340
8649
|
});
|
|
8341
8650
|
}
|
|
8342
8651
|
function detectLegacyGitHooksMigrationCandidate(projectPath) {
|
|
8343
8652
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
8344
8653
|
if (!fs.existsSync(packageJsonPath)) return false;
|
|
8345
|
-
|
|
8346
|
-
|
|
8654
|
+
return readJsonFile(packageJsonPath)["lint-staged"] !== void 0;
|
|
8655
|
+
}
|
|
8656
|
+
function hasHuskySetup(projectPath, pkg) {
|
|
8657
|
+
return pkg.devDependencies?.husky !== void 0 || pkg.dependencies?.husky !== void 0 || pkg.optionalDependencies?.husky !== void 0 || pkg.peerDependencies?.husky !== void 0 || pkg.husky !== void 0 || (pkg.scripts?.prepare ? /\bhusky\b/.test(pkg.scripts.prepare) : false) || fs.existsSync(path.join(projectPath, ".husky"));
|
|
8658
|
+
}
|
|
8659
|
+
function hasExistingViteHooksPolicy(projectPath) {
|
|
8660
|
+
const hooksDir = path.join(projectPath, ".vite-hooks");
|
|
8661
|
+
if (!fs.lstatSync(hooksDir, { throwIfNoEntry: false })?.isDirectory()) return false;
|
|
8662
|
+
return fs.readdirSync(hooksDir, { withFileTypes: true }).some((entry) => entry.isFile() && SUPPORTED_GIT_HOOK_NAME_SET.has(entry.name));
|
|
8663
|
+
}
|
|
8664
|
+
function findNonRegularViteHookPath(projectPath) {
|
|
8665
|
+
for (const hookName of SUPPORTED_GIT_HOOK_NAMES) {
|
|
8666
|
+
const hookPath = path.join(projectPath, ".vite-hooks", hookName);
|
|
8667
|
+
const stats = fs.lstatSync(hookPath, { throwIfNoEntry: false });
|
|
8668
|
+
if (stats && !stats.isFile()) return path.join(".vite-hooks", hookName);
|
|
8669
|
+
}
|
|
8670
|
+
return null;
|
|
8347
8671
|
}
|
|
8348
8672
|
/**
|
|
8349
8673
|
* Walk up from `startPath` looking for `.git` (directory or file — submodules
|
|
@@ -8358,36 +8682,45 @@ function findGitRoot(startPath) {
|
|
|
8358
8682
|
dir = parent;
|
|
8359
8683
|
}
|
|
8360
8684
|
}
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
}
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8685
|
+
function findWorkspacePackageHookPolicy(workspaceRoot, packages) {
|
|
8686
|
+
for (const pkg of packages) {
|
|
8687
|
+
const packagePath = path.join(workspaceRoot, pkg.path);
|
|
8688
|
+
if (hasExistingViteHooksPolicy(packagePath)) return `Detected project-owned Vite+ hooks in workspace package "${pkg.path}" — leaving the existing hook setup unchanged.`;
|
|
8689
|
+
const packageJsonPath = path.join(packagePath, "package.json");
|
|
8690
|
+
const pkgContent = fs.existsSync(packageJsonPath) ? readJsonFile(packageJsonPath) : {};
|
|
8691
|
+
if (hasHuskySetup(packagePath, pkgContent)) return `Detected Husky in workspace package "${pkg.path}" — leaving its hooks, configuration, and dependencies unchanged.`;
|
|
8692
|
+
const deps = pkgContent.devDependencies;
|
|
8693
|
+
const prodDeps = pkgContent.dependencies;
|
|
8694
|
+
for (const tool of OTHER_HOOK_TOOLS) if (deps?.[tool] || prodDeps?.[tool] || pkgContent[tool]) return `Detected ${tool} in workspace package "${pkg.path}" — leaving the existing hook setup unchanged.`;
|
|
8695
|
+
const gitRoot = findGitRoot(packagePath);
|
|
8696
|
+
if (gitRoot && path.resolve(packagePath) === path.resolve(gitRoot)) {
|
|
8697
|
+
const configuredHooksPath = getConfiguredHooksPath(packagePath);
|
|
8698
|
+
const normalizedHooksPath = normalizeGitHooksPath(configuredHooksPath);
|
|
8699
|
+
if (configuredHooksPath && normalizedHooksPath !== ".vite-hooks/_") return `core.hooksPath is already set to "${configuredHooksPath}" in workspace package "${pkg.path}" — leaving the existing hook setup unchanged.`;
|
|
8700
|
+
}
|
|
8376
8701
|
}
|
|
8377
|
-
return
|
|
8702
|
+
return null;
|
|
8703
|
+
}
|
|
8704
|
+
function getConfiguredHooksPath(projectPath) {
|
|
8705
|
+
const result = import_cross_spawn.default.sync("git", [
|
|
8706
|
+
"config",
|
|
8707
|
+
"--get",
|
|
8708
|
+
"core.hooksPath"
|
|
8709
|
+
], {
|
|
8710
|
+
cwd: projectPath,
|
|
8711
|
+
stdio: "pipe"
|
|
8712
|
+
});
|
|
8713
|
+
return result.status === 0 ? result.stdout?.toString().trim() ?? "" : "";
|
|
8714
|
+
}
|
|
8715
|
+
function normalizeGitHooksPath(hooksPath) {
|
|
8716
|
+
return path.posix.normalize(hooksPath.replaceAll("\\", "/")).replace(/\/$/, "");
|
|
8378
8717
|
}
|
|
8379
8718
|
/**
|
|
8380
|
-
*
|
|
8381
|
-
*
|
|
8719
|
+
* High-level helper that sets up Vite+ hooks when no other hook tool owns the
|
|
8720
|
+
* project. Husky setups are deliberately preserved for a dedicated migration.
|
|
8382
8721
|
*/
|
|
8383
|
-
function
|
|
8384
|
-
|
|
8385
|
-
if (!fs.existsSync(packageJsonPath)) return;
|
|
8386
|
-
const pkg = readJsonFile(packageJsonPath);
|
|
8387
|
-
if (!pkg.scripts?.prepare) return;
|
|
8388
|
-
const match = collapseHuskyInstall(pkg.scripts.prepare).match(/\bhusky(?:\s+([\w./-]+))?/);
|
|
8389
|
-
if (!match) return;
|
|
8390
|
-
return match[1] ?? ".husky";
|
|
8722
|
+
function installGitHooks(projectPath, silent = false, report, packageManager, packages = []) {
|
|
8723
|
+
return setupGitHooks(projectPath, silent, report, packageManager, packages);
|
|
8391
8724
|
}
|
|
8392
8725
|
/**
|
|
8393
8726
|
* Pre-flight check: verify that git hooks can be set up for this project.
|
|
@@ -8397,10 +8730,9 @@ function getOldHooksDir(rootDir) {
|
|
|
8397
8730
|
* These checks are deterministic and read-only — they do not modify
|
|
8398
8731
|
* the project in any way, making them safe to call before migration.
|
|
8399
8732
|
*
|
|
8400
|
-
*
|
|
8401
|
-
* resolution to that manager's catalog so a foreign catalog file is ignored.
|
|
8733
|
+
* The package manager argument remains for API compatibility with callers.
|
|
8402
8734
|
*/
|
|
8403
|
-
function preflightGitHooksSetup(projectPath,
|
|
8735
|
+
function preflightGitHooksSetup(projectPath, _packageManager, packages = []) {
|
|
8404
8736
|
const gitRoot = findGitRoot(projectPath);
|
|
8405
8737
|
if (gitRoot && path.resolve(projectPath) !== path.resolve(gitRoot)) return "Subdirectory project detected — skipping git hooks setup. Configure hooks at the repository root.";
|
|
8406
8738
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
@@ -8408,19 +8740,41 @@ function preflightGitHooksSetup(projectPath, packageManager) {
|
|
|
8408
8740
|
const pkgContent = readJsonFile(packageJsonPath);
|
|
8409
8741
|
const deps = pkgContent.devDependencies;
|
|
8410
8742
|
const prodDeps = pkgContent.dependencies;
|
|
8743
|
+
const configuredHooksPath = gitRoot ? getConfiguredHooksPath(projectPath) : "";
|
|
8744
|
+
const normalizedHooksPath = normalizeGitHooksPath(configuredHooksPath);
|
|
8745
|
+
if (hasHuskySetup(projectPath, pkgContent) || normalizedHooksPath === ".husky" || normalizedHooksPath.startsWith(".husky/")) return "Detected Husky — leaving its hooks, configuration, and dependencies unchanged. Migrate Husky manually before enabling Vite+ hooks.";
|
|
8411
8746
|
for (const tool of OTHER_HOOK_TOOLS) if (deps?.[tool] || prodDeps?.[tool] || pkgContent[tool]) return `Detected ${tool} — skipping git hooks setup. Please configure git hooks manually, see https://viteplus.dev/guide/migrate#git-hook-tools`;
|
|
8412
|
-
const
|
|
8413
|
-
if (
|
|
8747
|
+
const workspacePackageReason = findWorkspacePackageHookPolicy(projectPath, packages);
|
|
8748
|
+
if (workspacePackageReason) return workspacePackageReason;
|
|
8749
|
+
const disabledHooksEnvironment = [
|
|
8750
|
+
"HUSKY",
|
|
8751
|
+
"VP_GIT_HOOKS",
|
|
8752
|
+
"VITE_GIT_HOOKS"
|
|
8753
|
+
].find((name) => process.env[name] === "0");
|
|
8754
|
+
if (disabledHooksEnvironment) return `Git hooks are disabled through ${disabledHooksEnvironment}=0 — skipping git hooks setup.`;
|
|
8755
|
+
if (configuredHooksPath && normalizedHooksPath !== ".vite-hooks/_") return `core.hooksPath is already set to "${configuredHooksPath}" — leaving the existing hook setup unchanged.`;
|
|
8756
|
+
const unsafeInstallPath = findUnsafeHookInstallPath(projectPath, ".vite-hooks");
|
|
8757
|
+
if (unsafeInstallPath) return `Git hook dispatcher path "${unsafeInstallPath.relativePath}" is unsafe — leaving the existing hook setup unchanged.`;
|
|
8758
|
+
const nonRegularHookPath = findNonRegularViteHookPath(projectPath);
|
|
8759
|
+
if (nonRegularHookPath) return `Git hook path "${nonRegularHookPath}" is not a regular file — leaving the existing hook setup unchanged.`;
|
|
8414
8760
|
if (hasUnsupportedLintStagedConfig(projectPath)) return "Unsupported lint-staged config format — skipping git hooks setup. Please configure git hooks manually.";
|
|
8415
8761
|
return null;
|
|
8416
8762
|
}
|
|
8417
8763
|
/**
|
|
8764
|
+
* Decide whether config rewriting must leave lint-staged configuration in
|
|
8765
|
+
* place. Call this after scaffolding but before rewriting the project so an
|
|
8766
|
+
* existing hook owner never observes a partially migrated configuration.
|
|
8767
|
+
*/
|
|
8768
|
+
function shouldSkipStagedMigrationForHooks(projectPath, shouldSetupHooks, packageManager, packages = []) {
|
|
8769
|
+
return !shouldSetupHooks || hasExistingViteHooksPolicy(projectPath) || preflightGitHooksSetup(projectPath, packageManager, packages) !== null;
|
|
8770
|
+
}
|
|
8771
|
+
/**
|
|
8418
8772
|
* Set up git hooks with husky + lint-staged via vp commands.
|
|
8419
8773
|
* Skips if another hook tool is detected (warns user).
|
|
8420
8774
|
* Returns true if hooks were successfully set up, false if skipped.
|
|
8421
8775
|
*/
|
|
8422
|
-
function setupGitHooks(projectPath,
|
|
8423
|
-
const reason = preflightGitHooksSetup(projectPath, packageManager);
|
|
8776
|
+
function setupGitHooks(projectPath, silent = false, report, packageManager, packages = []) {
|
|
8777
|
+
const reason = preflightGitHooksSetup(projectPath, packageManager, packages);
|
|
8424
8778
|
if (reason) {
|
|
8425
8779
|
warnMigration(reason, report);
|
|
8426
8780
|
return false;
|
|
@@ -8428,73 +8782,29 @@ function setupGitHooks(projectPath, oldHooksDir, silent = false, report, package
|
|
|
8428
8782
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
8429
8783
|
if (!fs.existsSync(packageJsonPath)) return false;
|
|
8430
8784
|
const gitRoot = findGitRoot(projectPath);
|
|
8431
|
-
const
|
|
8432
|
-
const
|
|
8785
|
+
const hooksDir = ".vite-hooks";
|
|
8786
|
+
const hasExistingHookPolicy = hasExistingViteHooksPolicy(projectPath);
|
|
8433
8787
|
editJsonFile(packageJsonPath, (pkg) => {
|
|
8434
8788
|
if (!pkg.scripts) pkg.scripts = {};
|
|
8435
8789
|
if (!pkg.scripts.prepare) pkg.scripts.prepare = "vp config";
|
|
8436
|
-
else if (!pkg.scripts.prepare.includes("vp config")
|
|
8790
|
+
else if (!pkg.scripts.prepare.includes("vp config")) pkg.scripts.prepare = `vp config && ${pkg.scripts.prepare}`;
|
|
8437
8791
|
return pkg;
|
|
8438
8792
|
});
|
|
8439
8793
|
let stagedMerged = hasStagedConfigInViteConfig(projectPath);
|
|
8440
8794
|
const hasStandaloneConfig = hasStandaloneLintStagedConfig(projectPath);
|
|
8441
|
-
if (!stagedMerged && !hasStandaloneConfig) {
|
|
8795
|
+
if (!hasExistingHookPolicy && !stagedMerged && !hasStandaloneConfig) {
|
|
8442
8796
|
const stagedConfig = readJsonFile(packageJsonPath)?.["lint-staged"] ?? DEFAULT_STAGED_CONFIG;
|
|
8443
8797
|
const updated = rewriteScripts(JSON.stringify(stagedConfig), readRulesYaml());
|
|
8444
8798
|
stagedMerged = mergeStagedConfigToViteConfig(projectPath, updated ? JSON.parse(updated) : stagedConfig, silent, report);
|
|
8445
8799
|
}
|
|
8446
|
-
if (stagedMerged) removeLintStagedFromPackageJson(packageJsonPath);
|
|
8447
|
-
if (
|
|
8448
|
-
const oldDir = path.join(projectPath, oldHooksDir);
|
|
8449
|
-
if (fs.existsSync(oldDir)) {
|
|
8450
|
-
const targetDir = path.join(projectPath, hooksDir);
|
|
8451
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
8452
|
-
for (const entry of fs.readdirSync(oldDir, { withFileTypes: true })) {
|
|
8453
|
-
if (entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
8454
|
-
const src = path.join(oldDir, entry.name);
|
|
8455
|
-
const dest = path.join(targetDir, entry.name);
|
|
8456
|
-
fs.copyFileSync(src, dest);
|
|
8457
|
-
fs.chmodSync(dest, 493);
|
|
8458
|
-
}
|
|
8459
|
-
fs.rmSync(oldDir, {
|
|
8460
|
-
recursive: true,
|
|
8461
|
-
force: true
|
|
8462
|
-
});
|
|
8463
|
-
}
|
|
8464
|
-
}
|
|
8465
|
-
if (stagedMerged) createPreCommitHook(projectPath, hooksDir);
|
|
8800
|
+
if (!hasExistingHookPolicy && stagedMerged) removeLintStagedFromPackageJson(packageJsonPath);
|
|
8801
|
+
if (!hasExistingHookPolicy && stagedMerged) createPreCommitHook(projectPath, hooksDir);
|
|
8466
8802
|
if (!gitRoot) {
|
|
8467
|
-
|
|
8803
|
+
if (!hasExistingHookPolicy && stagedMerged) removeReplacedStagedPackage(packageJsonPath);
|
|
8468
8804
|
return true;
|
|
8469
8805
|
}
|
|
8470
|
-
if (oldHooksDir) {
|
|
8471
|
-
const checkResult = import_cross_spawn.default.sync("git", [
|
|
8472
|
-
"config",
|
|
8473
|
-
"--local",
|
|
8474
|
-
"core.hooksPath"
|
|
8475
|
-
], {
|
|
8476
|
-
cwd: projectPath,
|
|
8477
|
-
stdio: "pipe"
|
|
8478
|
-
});
|
|
8479
|
-
const existingPath = checkResult.status === 0 ? checkResult.stdout?.toString().trim() : "";
|
|
8480
|
-
if (existingPath === `${oldHooksDir}/_` || existingPath === oldHooksDir) import_cross_spawn.default.sync("git", [
|
|
8481
|
-
"config",
|
|
8482
|
-
"--local",
|
|
8483
|
-
"--unset",
|
|
8484
|
-
"core.hooksPath"
|
|
8485
|
-
], {
|
|
8486
|
-
cwd: projectPath,
|
|
8487
|
-
stdio: "pipe"
|
|
8488
|
-
});
|
|
8489
|
-
}
|
|
8490
8806
|
const vpBin = process.env.VP_CLI_BIN ?? "vp";
|
|
8491
|
-
const
|
|
8492
|
-
"config",
|
|
8493
|
-
"--no-agent",
|
|
8494
|
-
"--hooks-dir",
|
|
8495
|
-
hooksDir
|
|
8496
|
-
] : ["config", "--no-agent"];
|
|
8497
|
-
const configResult = import_cross_spawn.default.sync(vpBin, configArgs, {
|
|
8807
|
+
const configResult = import_cross_spawn.default.sync(vpBin, ["config", "--no-agent"], {
|
|
8498
8808
|
cwd: projectPath,
|
|
8499
8809
|
stdio: "pipe"
|
|
8500
8810
|
});
|
|
@@ -8504,7 +8814,7 @@ function setupGitHooks(projectPath, oldHooksDir, silent = false, report, package
|
|
|
8504
8814
|
warnMigration(`Git hooks not configured — ${stdout}`, report);
|
|
8505
8815
|
return false;
|
|
8506
8816
|
}
|
|
8507
|
-
|
|
8817
|
+
if (!hasExistingHookPolicy && stagedMerged) removeReplacedStagedPackage(packageJsonPath);
|
|
8508
8818
|
if (report) report.gitHooksConfigured = true;
|
|
8509
8819
|
if (!silent) log.success("✔ Git hooks configured");
|
|
8510
8820
|
return true;
|
|
@@ -8529,85 +8839,14 @@ function hasUnsupportedLintStagedConfig(projectPath) {
|
|
|
8529
8839
|
if (fs.existsSync(lintstagedrcPath) && !isJsonFile(lintstagedrcPath)) return true;
|
|
8530
8840
|
return false;
|
|
8531
8841
|
}
|
|
8532
|
-
/**
|
|
8533
|
-
* Create pre-commit hook file in the hooks directory.
|
|
8534
|
-
*/
|
|
8535
|
-
const STALE_LINT_STAGED_PATTERNS = [/^((?:[A-Z_][A-Z0-9_]*(?:=\S*)?\s+)*)(pnpm|pnpm exec|npx|yarn|yarn run|npm exec|npm run|bunx|bun run|bun x)\s+lint-staged\b/, /^((?:[A-Z_][A-Z0-9_]*(?:=\S*)?\s+)*)lint-staged\b/];
|
|
8536
8842
|
const DEFAULT_STAGED_CONFIG = { "*": "vp check --fix" };
|
|
8537
|
-
/**
|
|
8538
|
-
* Ensure the pre-commit hook exists with `vp staged`, and that
|
|
8539
|
-
* vite.config.ts contains a `staged` block (using the default config
|
|
8540
|
-
* if none is present). Called by `vp config` after hook installation.
|
|
8541
|
-
*/
|
|
8542
|
-
function ensurePreCommitHook(projectPath, dir = ".vite-hooks") {
|
|
8543
|
-
if (!hasStagedConfigInViteConfig(projectPath)) mergeStagedConfigToViteConfig(projectPath, DEFAULT_STAGED_CONFIG, true);
|
|
8544
|
-
createPreCommitHook(projectPath, dir);
|
|
8545
|
-
}
|
|
8546
8843
|
function createPreCommitHook(projectPath, dir = ".vite-hooks") {
|
|
8547
8844
|
const huskyDir = path.join(projectPath, dir);
|
|
8548
8845
|
fs.mkdirSync(huskyDir, { recursive: true });
|
|
8549
8846
|
const hookPath = path.join(huskyDir, "pre-commit");
|
|
8550
|
-
if (fs.existsSync(hookPath))
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
const lines = existing.split("\n");
|
|
8554
|
-
let replaced = false;
|
|
8555
|
-
const result = [];
|
|
8556
|
-
for (const line of lines) {
|
|
8557
|
-
const trimmed = line.trim();
|
|
8558
|
-
if (!replaced) {
|
|
8559
|
-
let matched = false;
|
|
8560
|
-
for (const pattern of STALE_LINT_STAGED_PATTERNS) {
|
|
8561
|
-
const match = pattern.exec(trimmed);
|
|
8562
|
-
if (match) {
|
|
8563
|
-
const parts = [
|
|
8564
|
-
match[1]?.trim() ?? "",
|
|
8565
|
-
"vp staged",
|
|
8566
|
-
trimmed.slice(match[0].length).trim()
|
|
8567
|
-
].filter(Boolean);
|
|
8568
|
-
result.push(parts.join(" "));
|
|
8569
|
-
replaced = true;
|
|
8570
|
-
matched = true;
|
|
8571
|
-
break;
|
|
8572
|
-
}
|
|
8573
|
-
}
|
|
8574
|
-
if (matched) continue;
|
|
8575
|
-
}
|
|
8576
|
-
result.push(line);
|
|
8577
|
-
}
|
|
8578
|
-
if (!replaced) fs.writeFileSync(hookPath, `${result.join("\n").trimEnd()}\nvp staged\n`);
|
|
8579
|
-
else fs.writeFileSync(hookPath, result.join("\n"));
|
|
8580
|
-
} else {
|
|
8581
|
-
fs.writeFileSync(hookPath, "vp staged\n");
|
|
8582
|
-
fs.chmodSync(hookPath, 493);
|
|
8583
|
-
}
|
|
8584
|
-
}
|
|
8585
|
-
/**
|
|
8586
|
-
* Rewrite only `scripts.prepare` in the root package.json using vite-prepare.yml rules.
|
|
8587
|
-
* Collapses "husky install" → "husky" before applying ast-grep so that the
|
|
8588
|
-
* replace-husky rule produces "vp config" with any directory argument preserved.
|
|
8589
|
-
* Returns the old husky hooks dir (if any) for migration to .vite-hooks.
|
|
8590
|
-
* Called only when hooks are being set up (not with --no-hooks).
|
|
8591
|
-
*/
|
|
8592
|
-
function rewritePrepareScript(rootDir) {
|
|
8593
|
-
const packageJsonPath = path.join(rootDir, "package.json");
|
|
8594
|
-
if (!fs.existsSync(packageJsonPath)) return;
|
|
8595
|
-
let oldDir;
|
|
8596
|
-
editJsonFile(packageJsonPath, (pkg) => {
|
|
8597
|
-
if (!pkg.scripts?.prepare) return pkg;
|
|
8598
|
-
const prepare = collapseHuskyInstall(pkg.scripts.prepare);
|
|
8599
|
-
const updated = rewriteScripts(JSON.stringify({ prepare }), readPrepareRulesYaml());
|
|
8600
|
-
if (updated) {
|
|
8601
|
-
let newPrepare = JSON.parse(updated).prepare;
|
|
8602
|
-
newPrepare = newPrepare.replace(/\bvp config(?:\s+(?!-)([\w./-]+))?/, (_match, dir) => {
|
|
8603
|
-
oldDir = dir ?? ".husky";
|
|
8604
|
-
return dir ? `vp config --hooks-dir ${dir}` : "vp config";
|
|
8605
|
-
});
|
|
8606
|
-
pkg.scripts.prepare = newPrepare;
|
|
8607
|
-
} else if (prepare !== pkg.scripts.prepare) pkg.scripts.prepare = prepare;
|
|
8608
|
-
return pkg;
|
|
8609
|
-
});
|
|
8610
|
-
return oldDir;
|
|
8847
|
+
if (fs.existsSync(hookPath)) return;
|
|
8848
|
+
fs.writeFileSync(hookPath, "vp staged\n");
|
|
8849
|
+
fs.chmodSync(hookPath, 493);
|
|
8611
8850
|
}
|
|
8612
8851
|
//#endregion
|
|
8613
8852
|
//#region src/migration/migrator/setup.ts
|
|
@@ -8750,10 +8989,9 @@ function migrateNodeVersionManagerFile(projectPath, detection, report) {
|
|
|
8750
8989
|
//#endregion
|
|
8751
8990
|
//#region src/migration/migrator/core-finalization.ts
|
|
8752
8991
|
const RULES_YAML_PATH = path.join(rulesDir, "vite-tools.yml");
|
|
8753
|
-
|
|
8992
|
+
path.join(rulesDir, "vite-prepare.yml");
|
|
8754
8993
|
let cachedRulesYaml;
|
|
8755
8994
|
let cachedRulesYamlNoLintStaged;
|
|
8756
|
-
let cachedPrepareRulesYaml;
|
|
8757
8995
|
function readRulesYaml() {
|
|
8758
8996
|
cachedRulesYaml ??= fs.readFileSync(RULES_YAML_PATH, "utf8");
|
|
8759
8997
|
return cachedRulesYaml;
|
|
@@ -8764,10 +9002,6 @@ function getScriptRulesYaml(skipStagedMigration) {
|
|
|
8764
9002
|
cachedRulesYamlNoLintStaged ??= yaml.split("\n\n\n").filter((block) => !block.includes("id: replace-lint-staged")).join("\n\n\n");
|
|
8765
9003
|
return cachedRulesYamlNoLintStaged;
|
|
8766
9004
|
}
|
|
8767
|
-
function readPrepareRulesYaml() {
|
|
8768
|
-
cachedPrepareRulesYaml ??= fs.readFileSync(PREPARE_RULES_YAML_PATH, "utf8");
|
|
8769
|
-
return cachedPrepareRulesYaml;
|
|
8770
|
-
}
|
|
8771
9005
|
function getCoreMigrationProjectPaths(workspaceInfo) {
|
|
8772
9006
|
return [workspaceInfo.rootDir, ...(workspaceInfo.packages ?? []).map((pkg) => path.join(workspaceInfo.rootDir, pkg.path))];
|
|
8773
9007
|
}
|
|
@@ -8995,300 +9229,186 @@ function rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration
|
|
|
8995
9229
|
if (!deferLazyPluginWrapping) wrapLazyPluginsInViteConfig(projectPath, silent, report);
|
|
8996
9230
|
}
|
|
8997
9231
|
//#endregion
|
|
8998
|
-
//#region src/utils/
|
|
8999
|
-
const
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
"
|
|
9015
|
-
"
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
hint: "Claude Code",
|
|
9024
|
-
aliases: ["claude.md", "claude-code"]
|
|
9025
|
-
},
|
|
9026
|
-
{
|
|
9027
|
-
id: "gemini",
|
|
9028
|
-
label: "GEMINI.md",
|
|
9029
|
-
targetPath: "GEMINI.md",
|
|
9030
|
-
hint: "Gemini CLI",
|
|
9031
|
-
aliases: ["gemini.md", "gemini-cli"]
|
|
9032
|
-
},
|
|
9033
|
-
{
|
|
9034
|
-
id: "copilot",
|
|
9035
|
-
label: ".github/copilot-instructions.md",
|
|
9036
|
-
targetPath: ".github/copilot-instructions.md",
|
|
9037
|
-
hint: "GitHub Copilot",
|
|
9038
|
-
aliases: ["github-copilot", "copilot-instructions.md"]
|
|
9039
|
-
},
|
|
9040
|
-
{
|
|
9041
|
-
id: "cursor",
|
|
9042
|
-
label: ".cursor/rules/viteplus.mdc",
|
|
9043
|
-
targetPath: ".cursor/rules/viteplus.mdc",
|
|
9044
|
-
hint: "Cursor",
|
|
9045
|
-
aliases: ["viteplus.mdc"]
|
|
9232
|
+
//#region src/utils/editor.ts
|
|
9233
|
+
const VSCODE_SETTINGS = {
|
|
9234
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
9235
|
+
"[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
9236
|
+
"[javascriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
9237
|
+
"[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
9238
|
+
"[typescriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
|
|
9239
|
+
"oxc.fmt.configPath": "./vite.config.ts",
|
|
9240
|
+
"editor.formatOnSave": true,
|
|
9241
|
+
"editor.formatOnSaveMode": "file",
|
|
9242
|
+
"editor.codeActionsOnSave": { "source.fixAll.oxc": "explicit" }
|
|
9243
|
+
};
|
|
9244
|
+
const VSCODE_EXTENSIONS = { recommendations: ["VoidZero.vite-plus-extension-pack"] };
|
|
9245
|
+
const ZED_SETTINGS = {
|
|
9246
|
+
lsp: {
|
|
9247
|
+
oxlint: { initialization_options: { settings: {
|
|
9248
|
+
run: "onType",
|
|
9249
|
+
fixKind: "safe_fix",
|
|
9250
|
+
typeAware: true,
|
|
9251
|
+
unusedDisableDirectives: "deny"
|
|
9252
|
+
} } },
|
|
9253
|
+
oxfmt: { initialization_options: { settings: {
|
|
9254
|
+
"fmt.configPath": "./vite.config.ts",
|
|
9255
|
+
run: "onSave"
|
|
9256
|
+
} } }
|
|
9046
9257
|
},
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9258
|
+
languages: Object.fromEntries([
|
|
9259
|
+
"CSS",
|
|
9260
|
+
"GraphQL",
|
|
9261
|
+
"Handlebars",
|
|
9262
|
+
"HTML",
|
|
9263
|
+
"JavaScript",
|
|
9264
|
+
"JSX",
|
|
9265
|
+
"JSON",
|
|
9266
|
+
"JSON5",
|
|
9267
|
+
"JSONC",
|
|
9268
|
+
"Less",
|
|
9269
|
+
"Markdown",
|
|
9270
|
+
"MDX",
|
|
9271
|
+
"SCSS",
|
|
9272
|
+
"TypeScript",
|
|
9273
|
+
"TSX",
|
|
9274
|
+
"Vue.js",
|
|
9275
|
+
"YAML"
|
|
9276
|
+
].map((language) => [language, {
|
|
9277
|
+
format_on_save: "on",
|
|
9278
|
+
prettier: { allowed: false },
|
|
9279
|
+
formatter: [{ language_server: { name: "oxfmt" } }],
|
|
9280
|
+
...language === "JavaScript" ? { code_action: "source.fixAll.oxc" } : {}
|
|
9281
|
+
}]))
|
|
9282
|
+
};
|
|
9283
|
+
const EDITORS = [{
|
|
9284
|
+
id: "vscode",
|
|
9285
|
+
label: "VSCode",
|
|
9286
|
+
targetDir: ".vscode",
|
|
9287
|
+
files: {
|
|
9288
|
+
"settings.json": VSCODE_SETTINGS,
|
|
9289
|
+
"extensions.json": VSCODE_EXTENSIONS
|
|
9290
|
+
}
|
|
9291
|
+
}, {
|
|
9292
|
+
id: "zed",
|
|
9293
|
+
label: "Zed",
|
|
9294
|
+
targetDir: ".zed",
|
|
9295
|
+
files: { "settings.json": ZED_SETTINGS }
|
|
9296
|
+
}];
|
|
9297
|
+
async function selectEditor({ interactive, editor, onCancel }) {
|
|
9298
|
+
if (editor === false) return;
|
|
9299
|
+
if (interactive && !editor) {
|
|
9300
|
+
const editorOptions = EDITORS.map((option) => ({
|
|
9301
|
+
label: option.label,
|
|
9302
|
+
value: option.id,
|
|
9303
|
+
hint: option.targetDir
|
|
9304
|
+
}));
|
|
9305
|
+
const otherOption = {
|
|
9306
|
+
label: "Other",
|
|
9307
|
+
value: null,
|
|
9308
|
+
hint: "Skip writing editor configs"
|
|
9309
|
+
};
|
|
9310
|
+
const selectedEditor = await select({
|
|
9311
|
+
message: "Which editor are you using?\n " + styleText("gray", "Writes editor config files to enable recommended extensions and Oxlint/Oxfmt integrations."),
|
|
9312
|
+
options: [...editorOptions, otherOption],
|
|
9313
|
+
initialValue: "vscode"
|
|
9314
|
+
});
|
|
9315
|
+
if (isCancel(selectedEditor)) {
|
|
9316
|
+
onCancel();
|
|
9317
|
+
return;
|
|
9318
|
+
}
|
|
9319
|
+
if (selectedEditor === null) return;
|
|
9320
|
+
return resolveEditorId(selectedEditor);
|
|
9058
9321
|
}
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
const
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
const COPILOT_SETUP_WORKFLOW_CONTENT = `name: "Copilot Setup Steps"
|
|
9068
|
-
|
|
9069
|
-
on:
|
|
9070
|
-
workflow_dispatch:
|
|
9071
|
-
push:
|
|
9072
|
-
paths:
|
|
9073
|
-
- .github/workflows/copilot-setup-steps.yml
|
|
9074
|
-
pull_request:
|
|
9075
|
-
paths:
|
|
9076
|
-
- .github/workflows/copilot-setup-steps.yml
|
|
9077
|
-
|
|
9078
|
-
jobs:
|
|
9079
|
-
copilot-setup-steps:
|
|
9080
|
-
runs-on: ubuntu-latest
|
|
9081
|
-
permissions:
|
|
9082
|
-
contents: read
|
|
9083
|
-
steps:
|
|
9084
|
-
- name: Checkout code
|
|
9085
|
-
uses: actions/checkout@v6
|
|
9086
|
-
with:
|
|
9087
|
-
persist-credentials: false
|
|
9088
|
-
- name: Set up Vite+
|
|
9089
|
-
uses: voidzero-dev/setup-vp@v1
|
|
9090
|
-
with:
|
|
9091
|
-
cache: true
|
|
9092
|
-
run-install: true
|
|
9093
|
-
- name: Verify Vite+
|
|
9094
|
-
run: vp --version
|
|
9095
|
-
`;
|
|
9096
|
-
async function selectAgentTargets({ interactive, agent, onCancel }) {
|
|
9097
|
-
if (agent === false) return {
|
|
9098
|
-
targetPaths: void 0,
|
|
9099
|
-
selectedAgents: []
|
|
9100
|
-
};
|
|
9101
|
-
if (interactive && !agent) {
|
|
9102
|
-
const selectedAgentIds = await multiselect({
|
|
9103
|
-
message: "Which coding agent instruction files should Vite+ create?",
|
|
9104
|
-
options: AGENTS.map((option) => ({
|
|
9322
|
+
if (editor) return resolveEditorId(editor);
|
|
9323
|
+
}
|
|
9324
|
+
async function selectEditors({ interactive, editor, onCancel }) {
|
|
9325
|
+
if (editor === false) return;
|
|
9326
|
+
if (interactive && !editor) {
|
|
9327
|
+
const selectedEditors = await multiselect({
|
|
9328
|
+
message: "Which editors are you using?\n " + styleText("gray", "Writes editor config files to enable recommended extensions and Oxlint/Oxfmt integrations."),
|
|
9329
|
+
options: EDITORS.map((option) => ({
|
|
9105
9330
|
label: option.label,
|
|
9106
9331
|
value: option.id,
|
|
9107
|
-
hint: option.
|
|
9332
|
+
hint: option.targetDir
|
|
9108
9333
|
})),
|
|
9109
|
-
initialValues: [
|
|
9334
|
+
initialValues: ["vscode"],
|
|
9110
9335
|
required: false
|
|
9111
9336
|
});
|
|
9112
|
-
if (isCancel(
|
|
9337
|
+
if (isCancel(selectedEditors)) {
|
|
9113
9338
|
onCancel();
|
|
9114
|
-
return
|
|
9115
|
-
targetPaths: void 0,
|
|
9116
|
-
selectedAgents: []
|
|
9117
|
-
};
|
|
9339
|
+
return;
|
|
9118
9340
|
}
|
|
9119
|
-
|
|
9120
|
-
targetPaths: void 0,
|
|
9121
|
-
selectedAgents: []
|
|
9122
|
-
};
|
|
9123
|
-
const selectedAgents = resolveAgentOptions(selectedAgentIds);
|
|
9124
|
-
return {
|
|
9125
|
-
targetPaths: getAgentTargetPaths(selectedAgents),
|
|
9126
|
-
selectedAgents
|
|
9127
|
-
};
|
|
9341
|
+
return selectedEditors.length === 0 ? void 0 : resolveEditorIds(selectedEditors);
|
|
9128
9342
|
}
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
selectedAgents
|
|
9133
|
-
};
|
|
9134
|
-
}
|
|
9135
|
-
async function selectAgentTargetPaths({ interactive, agent, onCancel }) {
|
|
9136
|
-
return (await selectAgentTargets({
|
|
9137
|
-
interactive,
|
|
9138
|
-
agent,
|
|
9139
|
-
onCancel
|
|
9140
|
-
})).targetPaths;
|
|
9141
|
-
}
|
|
9142
|
-
function detectExistingAgentTargetPaths(projectRoot) {
|
|
9143
|
-
const detectedPaths = [];
|
|
9144
|
-
const seenTargetPaths = /* @__PURE__ */ new Set();
|
|
9145
|
-
for (const option of AGENTS) {
|
|
9146
|
-
if (seenTargetPaths.has(option.targetPath)) continue;
|
|
9147
|
-
seenTargetPaths.add(option.targetPath);
|
|
9148
|
-
const targetPath = path.join(projectRoot, option.targetPath);
|
|
9149
|
-
if (fs.existsSync(targetPath) && !fs.lstatSync(targetPath).isSymbolicLink()) detectedPaths.push(option.targetPath);
|
|
9343
|
+
if (editor) {
|
|
9344
|
+
const editorId = resolveEditorId(editor);
|
|
9345
|
+
return editorId ? [editorId] : void 0;
|
|
9150
9346
|
}
|
|
9151
|
-
return detectedPaths.length > 0 ? detectedPaths : void 0;
|
|
9152
|
-
}
|
|
9153
|
-
/**
|
|
9154
|
-
* Silently update agent instruction files that contain Vite+ markers.
|
|
9155
|
-
* - No agent files → no writes
|
|
9156
|
-
* - No Vite+ markers → no writes
|
|
9157
|
-
* - Markers present, content up to date → no writes
|
|
9158
|
-
* - Markers present, content outdated → update marked section
|
|
9159
|
-
*/
|
|
9160
|
-
function updateExistingAgentInstructions(projectRoot) {
|
|
9161
|
-
const targetPaths = detectExistingAgentTargetPaths(projectRoot);
|
|
9162
|
-
if (!targetPaths) return;
|
|
9163
|
-
const templatePath = path.join(pkgRoot, "AGENTS.md");
|
|
9164
|
-
if (!fs.existsSync(templatePath)) return;
|
|
9165
|
-
const templateContent = fs.readFileSync(templatePath, "utf-8");
|
|
9166
|
-
for (const targetPath of targetPaths) try {
|
|
9167
|
-
const fullPath = path.join(projectRoot, targetPath);
|
|
9168
|
-
const existing = fs.readFileSync(fullPath, "utf-8");
|
|
9169
|
-
const updated = replaceMarkedAgentInstructionsSection(existing, templateContent);
|
|
9170
|
-
if (updated !== void 0 && updated !== existing) fs.writeFileSync(fullPath, updated);
|
|
9171
|
-
} catch {}
|
|
9172
9347
|
}
|
|
9173
|
-
function
|
|
9174
|
-
const
|
|
9175
|
-
const
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
}
|
|
9184
|
-
return dedupedAgents;
|
|
9185
|
-
}
|
|
9186
|
-
function getAgentTargetPaths(agents) {
|
|
9187
|
-
const dedupedTargetPaths = [];
|
|
9188
|
-
const seenTargetPaths = /* @__PURE__ */ new Set();
|
|
9189
|
-
for (const agent of agents) {
|
|
9190
|
-
if (seenTargetPaths.has(agent.targetPath)) continue;
|
|
9191
|
-
seenTargetPaths.add(agent.targetPath);
|
|
9192
|
-
dedupedTargetPaths.push(agent.targetPath);
|
|
9193
|
-
}
|
|
9194
|
-
return dedupedTargetPaths;
|
|
9195
|
-
}
|
|
9196
|
-
function parseAgentNames(agent) {
|
|
9197
|
-
if (!agent) return [];
|
|
9198
|
-
return (Array.isArray(agent) ? agent : [agent]).filter((value) => typeof value === "string").flatMap((value) => value.split(",")).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
9199
|
-
}
|
|
9200
|
-
function resolveSingleAgentOption(agent) {
|
|
9201
|
-
const normalized = normalizeAgentName(agent);
|
|
9202
|
-
const alias = AGENT_ALIASES[normalized];
|
|
9203
|
-
const resolved = alias ? normalizeAgentName(alias) : normalized;
|
|
9204
|
-
return AGENTS.find((option) => normalizeAgentName(option.id) === resolved || normalizeAgentName(option.label) === resolved || normalizeAgentName(option.targetPath) === resolved || option.aliases?.some((candidate) => normalizeAgentName(candidate) === resolved)) ?? AGENTS.find((option) => option.id === AGENT_DEFAULT_ID);
|
|
9348
|
+
function detectExistingEditors(projectRoot) {
|
|
9349
|
+
const editors = [];
|
|
9350
|
+
for (const option of EDITORS) for (const fileName of Object.keys(option.files)) {
|
|
9351
|
+
const filePath = path.join(projectRoot, option.targetDir, fileName);
|
|
9352
|
+
if (fs.existsSync(filePath)) {
|
|
9353
|
+
editors.push(option.id);
|
|
9354
|
+
break;
|
|
9355
|
+
}
|
|
9356
|
+
}
|
|
9357
|
+
return editors.length === 0 ? void 0 : editors;
|
|
9205
9358
|
}
|
|
9206
9359
|
/**
|
|
9207
|
-
* Detect
|
|
9208
|
-
* Returns only files that need a user decision (append or skip).
|
|
9360
|
+
* Detect editor config files that would conflict (already exist).
|
|
9209
9361
|
* Read-only — does not write or modify any files.
|
|
9210
9362
|
*/
|
|
9211
|
-
|
|
9212
|
-
if (!
|
|
9213
|
-
const
|
|
9214
|
-
if (!
|
|
9215
|
-
const incomingContent = await fsPromises.readFile(sourcePath, "utf-8");
|
|
9216
|
-
const shouldLinkToAgents = targetPaths.includes(AGENT_STANDARD_PATH);
|
|
9217
|
-
const orderedPaths = shouldLinkToAgents ? [AGENT_STANDARD_PATH, ...targetPaths.filter((p) => p !== AGENT_STANDARD_PATH)] : targetPaths;
|
|
9363
|
+
function detectEditorConflicts({ projectRoot, editorId }) {
|
|
9364
|
+
if (!editorId) return [];
|
|
9365
|
+
const editorConfig = EDITORS.find((e) => e.id === editorId);
|
|
9366
|
+
if (!editorConfig) return [];
|
|
9218
9367
|
const conflicts = [];
|
|
9219
|
-
const
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
seenDestinationPaths.add(destinationKey);
|
|
9226
|
-
if (shouldLinkToAgents && targetPathToCheck !== AGENT_STANDARD_PATH) {
|
|
9227
|
-
if (await getExistingPathKind(destinationPath) !== "file") continue;
|
|
9228
|
-
}
|
|
9229
|
-
if (fs.existsSync(destinationPath)) {
|
|
9230
|
-
if (fs.lstatSync(destinationPath).isSymbolicLink()) continue;
|
|
9231
|
-
const destinationRealPath = await fsPromises.realpath(destinationPath);
|
|
9232
|
-
if (seenRealPaths.has(destinationRealPath)) continue;
|
|
9233
|
-
if (replaceMarkedAgentInstructionsSection(await fsPromises.readFile(destinationPath, "utf-8"), incomingContent) !== void 0) {
|
|
9234
|
-
seenRealPaths.add(destinationRealPath);
|
|
9235
|
-
continue;
|
|
9236
|
-
}
|
|
9237
|
-
conflicts.push({ targetPath: targetPathToCheck });
|
|
9238
|
-
seenRealPaths.add(destinationRealPath);
|
|
9239
|
-
}
|
|
9368
|
+
for (const fileName of Object.keys(editorConfig.files)) {
|
|
9369
|
+
const filePath = path.join(projectRoot, editorConfig.targetDir, fileName);
|
|
9370
|
+
if (fs.existsSync(filePath)) conflicts.push({
|
|
9371
|
+
fileName,
|
|
9372
|
+
displayPath: `${editorConfig.targetDir}/${fileName}`
|
|
9373
|
+
});
|
|
9240
9374
|
}
|
|
9241
9375
|
return conflicts;
|
|
9242
9376
|
}
|
|
9243
|
-
async function
|
|
9244
|
-
const
|
|
9245
|
-
if (
|
|
9246
|
-
const
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
}
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
continue;
|
|
9269
|
-
}
|
|
9270
|
-
const destinationRealPath = await fsPromises.realpath(destinationPath);
|
|
9271
|
-
if (seenRealPaths.has(destinationRealPath)) {
|
|
9272
|
-
if (!silent) log.info(`Skipped writing ${targetPathToWrite} (duplicate target)`);
|
|
9273
|
-
continue;
|
|
9274
|
-
}
|
|
9275
|
-
const existingContent = await fsPromises.readFile(destinationPath, "utf-8");
|
|
9276
|
-
const updatedContent = replaceMarkedAgentInstructionsSection(existingContent, incomingContent);
|
|
9277
|
-
if (updatedContent !== void 0) {
|
|
9278
|
-
if (updatedContent !== existingContent) await fsPromises.writeFile(destinationPath, updatedContent);
|
|
9279
|
-
seenRealPaths.add(destinationRealPath);
|
|
9280
|
-
continue;
|
|
9281
|
-
}
|
|
9377
|
+
async function writeEditorConfigs({ projectRoot, editorId, interactive, conflictDecisions, silent = false, extraVsCodeSettings }) {
|
|
9378
|
+
const editorIds = normalizeEditorSelection(editorId);
|
|
9379
|
+
if (editorIds.length === 0) return;
|
|
9380
|
+
for (const currentEditorId of editorIds) await writeEditorConfig({
|
|
9381
|
+
projectRoot,
|
|
9382
|
+
editorId: currentEditorId,
|
|
9383
|
+
interactive,
|
|
9384
|
+
conflictDecisions,
|
|
9385
|
+
silent,
|
|
9386
|
+
extraVsCodeSettings
|
|
9387
|
+
});
|
|
9388
|
+
}
|
|
9389
|
+
async function writeEditorConfig({ projectRoot, editorId, interactive, conflictDecisions, silent, extraVsCodeSettings }) {
|
|
9390
|
+
const editorConfig = EDITORS.find((e) => e.id === editorId);
|
|
9391
|
+
if (!editorConfig) return;
|
|
9392
|
+
const targetDir = path.join(projectRoot, editorConfig.targetDir);
|
|
9393
|
+
await fsPromises.mkdir(targetDir, { recursive: true });
|
|
9394
|
+
for (const [fileName, baseIncoming] of Object.entries(editorConfig.files)) {
|
|
9395
|
+
const incoming = editorId === "vscode" && fileName === "settings.json" && extraVsCodeSettings ? {
|
|
9396
|
+
...extraVsCodeSettings,
|
|
9397
|
+
...baseIncoming
|
|
9398
|
+
} : baseIncoming;
|
|
9399
|
+
const filePath = path.join(targetDir, fileName);
|
|
9400
|
+
if (fs.existsSync(filePath)) {
|
|
9401
|
+
const displayPath = `${editorConfig.targetDir}/${fileName}`;
|
|
9282
9402
|
let conflictAction;
|
|
9283
|
-
const preResolved = conflictDecisions?.get(
|
|
9403
|
+
const preResolved = conflictDecisions?.get(displayPath) ?? conflictDecisions?.get(fileName);
|
|
9284
9404
|
if (preResolved) conflictAction = preResolved;
|
|
9285
9405
|
else if (interactive) {
|
|
9286
9406
|
const action = await select({
|
|
9287
|
-
message:
|
|
9407
|
+
message: `${displayPath} already exists.\n ` + styleText("gray", `Vite+ adds ${editorConfig.label} settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones.`),
|
|
9288
9408
|
options: [{
|
|
9289
|
-
label: "
|
|
9290
|
-
value: "
|
|
9291
|
-
hint: "
|
|
9409
|
+
label: "Merge",
|
|
9410
|
+
value: "merge",
|
|
9411
|
+
hint: "Merge new settings into existing file"
|
|
9292
9412
|
}, {
|
|
9293
9413
|
label: "Skip",
|
|
9294
9414
|
value: "skip",
|
|
@@ -9296,86 +9416,96 @@ async function writeAgentInstructions({ projectRoot, targetPath, targetPaths, in
|
|
|
9296
9416
|
}],
|
|
9297
9417
|
initialValue: "skip"
|
|
9298
9418
|
});
|
|
9299
|
-
conflictAction = isCancel(action) || action === "skip" ? "skip" : "
|
|
9300
|
-
} else conflictAction = "
|
|
9301
|
-
if (conflictAction === "
|
|
9302
|
-
else {
|
|
9303
|
-
const suffix = !preResolved && !interactive ? " (already exists)" : "";
|
|
9304
|
-
if (!silent) log.info(`Skipped writing ${targetPathToWrite}${suffix}`);
|
|
9305
|
-
}
|
|
9306
|
-
seenRealPaths.add(destinationRealPath);
|
|
9419
|
+
conflictAction = isCancel(action) || action === "skip" ? "skip" : "merge";
|
|
9420
|
+
} else conflictAction = "merge";
|
|
9421
|
+
if (conflictAction === "merge") mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
|
|
9422
|
+
else if (!silent) log.info(`Skipped writing ${displayPath}`);
|
|
9307
9423
|
continue;
|
|
9308
9424
|
}
|
|
9309
|
-
|
|
9310
|
-
if (!silent) log.success(`Wrote
|
|
9311
|
-
seenRealPaths.add(await fsPromises.realpath(destinationPath));
|
|
9425
|
+
writeJsonFile(filePath, incoming);
|
|
9426
|
+
if (!silent) log.success(`Wrote editor config to ${editorConfig.targetDir}/${fileName}`);
|
|
9312
9427
|
}
|
|
9313
9428
|
}
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9429
|
+
function normalizeEditorSelection(editorId) {
|
|
9430
|
+
if (!editorId) return [];
|
|
9431
|
+
return [...new Set(Array.isArray(editorId) ? editorId : [editorId])];
|
|
9432
|
+
}
|
|
9433
|
+
/**
|
|
9434
|
+
* Merge incoming settings into an existing editor JSON/JSONC file by patching the
|
|
9435
|
+
* original text with `jsonc-parser` instead of re-serializing a merged object.
|
|
9436
|
+
* This preserves comments, key order, trailing commas, and untouched formatting.
|
|
9437
|
+
* Existing values always win; only missing keys/branches are inserted.
|
|
9438
|
+
*/
|
|
9439
|
+
function mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent = false) {
|
|
9440
|
+
const originalText = fs.readFileSync(filePath, "utf-8");
|
|
9441
|
+
const existing = parse(originalText);
|
|
9442
|
+
if (!isPlainObject(existing)) throw new Error(`Cannot merge editor config: ${displayPath} is not a JSON object`);
|
|
9443
|
+
const formattingOptions = detectFormattingOptions(originalText);
|
|
9444
|
+
const newText = fileName === "extensions.json" ? mergeExtensionsText(originalText, existing, incoming, formattingOptions) : mergeSettingsText(originalText, existing, incoming, formattingOptions);
|
|
9445
|
+
if (newText === originalText) {
|
|
9446
|
+
if (!silent) log.info(`No changes needed for ${displayPath}`);
|
|
9319
9447
|
return;
|
|
9320
9448
|
}
|
|
9321
|
-
|
|
9322
|
-
if (!silent) log.success(`
|
|
9323
|
-
}
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
const existing = await getExistingPathKind(destinationPath);
|
|
9344
|
-
if (existing === "file") return false;
|
|
9345
|
-
if (existing === "symlink") {
|
|
9346
|
-
const currentLink = await fsPromises.readlink(destinationPath);
|
|
9347
|
-
if (path.resolve(path.dirname(destinationPath), currentLink) === agentsPath) {
|
|
9348
|
-
if (!silent) log.info(`Skipped linking ${targetPath} (already linked to ${AGENT_STANDARD_PATH})`);
|
|
9349
|
-
return true;
|
|
9350
|
-
}
|
|
9351
|
-
await fsPromises.unlink(destinationPath);
|
|
9352
|
-
}
|
|
9353
|
-
try {
|
|
9354
|
-
await fsPromises.symlink(symlinkTarget, destinationPath);
|
|
9355
|
-
} catch (err) {
|
|
9356
|
-
if (err.code === "EPERM") {
|
|
9357
|
-
await fsPromises.copyFile(agentsPath, destinationPath);
|
|
9358
|
-
if (!silent) log.success(`Copied ${AGENT_STANDARD_PATH} to ${targetPath}`);
|
|
9359
|
-
return true;
|
|
9449
|
+
fs.writeFileSync(filePath, newText, "utf-8");
|
|
9450
|
+
if (!silent) log.success(`Merged editor config into ${displayPath}`);
|
|
9451
|
+
}
|
|
9452
|
+
function isPlainObject(value) {
|
|
9453
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9454
|
+
}
|
|
9455
|
+
/** Apply a single `jsonc-parser` modification to `text` and return the patched text. */
|
|
9456
|
+
function applyJsoncEdit(text, path, value, options) {
|
|
9457
|
+
return applyEdits(text, modify(text, path, value, options));
|
|
9458
|
+
}
|
|
9459
|
+
/**
|
|
9460
|
+
* Deep-merge missing keys from `incoming` into the existing text. Inserts a whole
|
|
9461
|
+
* branch when it is absent, and recurses only when both sides are non-array objects
|
|
9462
|
+
* so comments inside existing branches are preserved.
|
|
9463
|
+
*/
|
|
9464
|
+
function mergeSettingsText(text, existing, incoming, formattingOptions) {
|
|
9465
|
+
let currentText = text;
|
|
9466
|
+
const insertMissing = (existingNode, incomingNode, basePath) => {
|
|
9467
|
+
for (const [key, value] of Object.entries(incomingNode)) {
|
|
9468
|
+
const fullPath = [...basePath, key];
|
|
9469
|
+
if (!(key in existingNode)) currentText = applyJsoncEdit(currentText, fullPath, value, { formattingOptions });
|
|
9470
|
+
else if (isPlainObject(existingNode[key]) && isPlainObject(value)) insertMissing(existingNode[key], value, fullPath);
|
|
9360
9471
|
}
|
|
9361
|
-
|
|
9472
|
+
};
|
|
9473
|
+
insertMissing(existing, incoming, []);
|
|
9474
|
+
return currentText;
|
|
9475
|
+
}
|
|
9476
|
+
/**
|
|
9477
|
+
* For `extensions.json`, append missing recommendations without rebuilding the array,
|
|
9478
|
+
* so comments inside the array survive. Existing entries always win.
|
|
9479
|
+
*/
|
|
9480
|
+
function mergeExtensionsText(text, existing, incoming, formattingOptions) {
|
|
9481
|
+
const incomingRecs = Array.isArray(incoming["recommendations"]) ? incoming["recommendations"] : [];
|
|
9482
|
+
const existingValue = existing["recommendations"];
|
|
9483
|
+
if (!("recommendations" in existing)) return applyJsoncEdit(text, ["recommendations"], incomingRecs, { formattingOptions });
|
|
9484
|
+
if (!Array.isArray(existingValue)) return text;
|
|
9485
|
+
const existingRecs = new Set(existingValue);
|
|
9486
|
+
let currentText = text;
|
|
9487
|
+
let nextIndex = existingValue.length;
|
|
9488
|
+
for (const rec of incomingRecs) {
|
|
9489
|
+
if (existingRecs.has(rec)) continue;
|
|
9490
|
+
currentText = applyJsoncEdit(currentText, ["recommendations", nextIndex], rec, {
|
|
9491
|
+
formattingOptions,
|
|
9492
|
+
isArrayInsertion: true
|
|
9493
|
+
});
|
|
9494
|
+
nextIndex++;
|
|
9362
9495
|
}
|
|
9363
|
-
|
|
9364
|
-
return true;
|
|
9496
|
+
return currentText;
|
|
9365
9497
|
}
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
return
|
|
9498
|
+
function resolveEditorId(editor) {
|
|
9499
|
+
const normalized = editor.trim().toLowerCase();
|
|
9500
|
+
return EDITORS.find((option) => option.id === normalized || option.label.toLowerCase() === normalized)?.id;
|
|
9369
9501
|
}
|
|
9370
|
-
function
|
|
9371
|
-
const
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
end: endMarkerIndex + endMarker.length
|
|
9378
|
-
};
|
|
9502
|
+
function resolveEditorIds(editors) {
|
|
9503
|
+
const editorIds = editors.flatMap((editor) => {
|
|
9504
|
+
const editorId = resolveEditorId(editor);
|
|
9505
|
+
return editorId ? [editorId] : [];
|
|
9506
|
+
});
|
|
9507
|
+
const uniqueEditorIds = [...new Set(editorIds)];
|
|
9508
|
+
return uniqueEditorIds.length === 0 ? void 0 : uniqueEditorIds;
|
|
9379
9509
|
}
|
|
9380
9510
|
//#endregion
|
|
9381
|
-
export {
|
|
9511
|
+
export { addFrameworkShim as A, detectIncompatibleEslintIntegration as B, ensureVitePlusBootstrap as C, isBingoTemplate as D, detectWorkspace$1 as E, migratePrettierToOxfmt as F, warnPackageLevelEslint as G, promptEslintMigration as H, promptPrettierMigration as I, checkViteVersion as K, warnPackageLevelPrettier as L, hasFrameworkShim as M, confirmPrettierMigration as N, updatePackageJsonWithDeps as O, detectPrettierProject as P, confirmEslintMigration as R, detectVitePlusBootstrapPending as S, detectYarnPnpMode as T, warnIncompatibleEslintIntegration as U, migrateEslintToOxlint as V, warnLegacyEslintConfig as W, shouldSkipStagedMigrationForHooks as _, writeEditorConfigs as a, mergeViteConfigFiles as b, rewriteStandaloneProject as c, detectNodeVersionManagerFile as d, migrateNodeVersionManagerFile as f, preflightGitHooksSetup as g, installGitHooks as h, selectEditors as i, detectFramework as j, updateWorkspaceConfig as k, detectPendingCoreMigration as l, detectLegacyGitHooksMigrationCandidate as m, detectExistingEditors as n, rewriteMonorepo as o, setPackageManager as p, checkVitestVersion as q, selectEditor as r, rewriteMonorepoProject as s, detectEditorConflicts as t, finalizeCoreMigrationForExistingVitePlus as u, injectCreateDefaultTemplate as v, configureYarnNodeModulesMode as w, collectToolchainVersionChanges as x, injectLintTypeCheckDefaults as y, detectEslintProject as z };
|