vite-plus 0.2.7 → 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/LICENSE +5 -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 +1368 -13
- 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-BsuzPowC.js → constants-BppDcRc1.js} +1 -1
- package/dist/create/bin.js +90 -77
- package/dist/{define-config-DLh9fE95.cjs → define-config-CEm_MtvJ.cjs} +1 -1
- package/dist/{define-config-C2KfLHpC.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-CPwWJ4qf.js → editor-CGWdbLgD.js} +690 -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 +58 -48
- package/dist/{oxlint-plugin-config-Bxgc4cwF.js → oxlint-plugin-config-BHOzlwMw.js} +1 -1
- package/dist/oxlint-plugin.js +1 -1
- package/dist/pack-bin.js +1 -1
- package/dist/{package-Ds3uThLb.js → package-hV-77cBb.js} +2 -2
- package/dist/{tsconfig-Jia2vgMG.js → prompts-DYap08te.js} +32 -174
- package/dist/{resolve-vite-config-BiuLZnn8.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 +4 -4
- package/docs/config/index.md +13 -0
- package/docs/config/staged.md +2 -1
- package/docs/guide/commit-hooks.md +38 -20
- 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 +10 -2
- package/docs/guide/monorepo.md +94 -6
- package/docs/guide/troubleshooting.md +3 -2
- package/package.json +17 -23
- package/dist/dist-CtM2JRbM.js +0 -3
- package/dist/editor-BbT1lTNc.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"]);
|
|
@@ -1251,7 +1475,7 @@ function expand_(str, max, isTop) {
|
|
|
1251
1475
|
}
|
|
1252
1476
|
//#endregion
|
|
1253
1477
|
//#region ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/assert-valid-pattern.js
|
|
1254
|
-
const MAX_PATTERN_LENGTH =
|
|
1478
|
+
const MAX_PATTERN_LENGTH = 65536;
|
|
1255
1479
|
const assertValidPattern = (pattern) => {
|
|
1256
1480
|
if (typeof pattern !== "string") throw new TypeError("invalid pattern");
|
|
1257
1481
|
if (pattern.length > MAX_PATTERN_LENGTH) throw new TypeError("pattern is too long");
|
|
@@ -2556,7 +2780,6 @@ minimatch.escape = escape;
|
|
|
2556
2780
|
minimatch.unescape = unescape;
|
|
2557
2781
|
//#endregion
|
|
2558
2782
|
//#region ../../node_modules/.pnpm/lru-cache@11.2.7/node_modules/lru-cache/dist/esm/index.min.js
|
|
2559
|
-
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
2560
2783
|
var x = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
|
|
2561
2784
|
var I = /* @__PURE__ */ new Set();
|
|
2562
2785
|
var R = typeof process == "object" && process ? process : {};
|
|
@@ -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
|
}
|
|
@@ -8143,7 +8460,8 @@ function mergeTsdownConfigFile(projectPath, silent = false, report) {
|
|
|
8143
8460
|
mergeAndRemoveJsonConfig(projectPath, viteConfig, configs.tsdownConfig, "pack", silent, report);
|
|
8144
8461
|
return;
|
|
8145
8462
|
}
|
|
8146
|
-
const
|
|
8463
|
+
const tsdownRelativePath = `./${configs.tsdownConfig}`;
|
|
8464
|
+
const result = mergeTsdownConfig(fullViteConfigPath, tsdownRelativePath);
|
|
8147
8465
|
if (result.updated) {
|
|
8148
8466
|
fs.writeFileSync(fullViteConfigPath, result.content);
|
|
8149
8467
|
if (report) report.tsdownImportCount++;
|
|
@@ -8168,7 +8486,8 @@ function mergeViteConfigFiles(projectPath, silent = false, report, packages, wor
|
|
|
8168
8486
|
} else warnMigration(BASEURL_TSCONFIG_WARNING, report);
|
|
8169
8487
|
sanitizeMigratedOxlintConfig(oxlintJson, collectInstalledPackageNames(workspaceRoot ?? projectPath, packages), report);
|
|
8170
8488
|
ensureSvelteRuneGlobals(oxlintJson);
|
|
8171
|
-
|
|
8489
|
+
const normalizedOxlintConfig = ensureVitePlusImportRuleDefaults(oxlintJson);
|
|
8490
|
+
writeJsonFile(fullOxlintPath, normalizedOxlintConfig);
|
|
8172
8491
|
mergeAndRemoveJsonConfig(projectPath, viteConfig, configs.oxlintConfig, "lint", silent, report);
|
|
8173
8492
|
}
|
|
8174
8493
|
if (configs.oxfmtConfig) mergeAndRemoveJsonConfig(projectPath, viteConfig, configs.oxfmtConfig, "fmt", silent, report);
|
|
@@ -8315,54 +8634,40 @@ function rewriteAllImports(projectPath, silent = false, report, preserveNuxtVite
|
|
|
8315
8634
|
}
|
|
8316
8635
|
//#endregion
|
|
8317
8636
|
//#region src/migration/migrator/git-hooks.ts
|
|
8318
|
-
|
|
8319
|
-
* Check if the project has an unsupported husky version (<9.0.0).
|
|
8320
|
-
* Uses `semver.coerce` to handle ranges like `^8.0.0` → `8.0.0`.
|
|
8321
|
-
* When the specifier is a catalog reference (e.g. `"catalog:"`), resolves
|
|
8322
|
-
* it from the active package manager's catalog first — a `catalog:` spec is
|
|
8323
|
-
* only meaningful to the manager that owns the workspace, so we never read a
|
|
8324
|
-
* leftover/foreign catalog file. When it is still not coercible (e.g.
|
|
8325
|
-
* `"latest"`), falls back to the installed version in node_modules via
|
|
8326
|
-
* `detectPackageMetadata`.
|
|
8327
|
-
* Returns a reason string if hooks migration should be skipped, or null
|
|
8328
|
-
* if husky is absent or compatible.
|
|
8329
|
-
*/
|
|
8330
|
-
function checkUnsupportedHuskyVersion(projectPath, deps, prodDeps, packageManager) {
|
|
8331
|
-
const huskyVersion = deps?.husky ?? prodDeps?.husky;
|
|
8332
|
-
if (!huskyVersion) return null;
|
|
8333
|
-
let coerced = import_semver.default.coerce(huskyVersion);
|
|
8334
|
-
if (coerced == null && packageManager != null && huskyVersion.startsWith("catalog:")) {
|
|
8335
|
-
const resolved = createCatalogDependencyResolver(projectPath, packageManager)?.(huskyVersion, "husky");
|
|
8336
|
-
if (resolved) coerced = import_semver.default.coerce(resolved);
|
|
8337
|
-
}
|
|
8338
|
-
if (coerced == null) {
|
|
8339
|
-
const installed = detectPackageMetadata(projectPath, "husky");
|
|
8340
|
-
if (installed) coerced = import_semver.default.coerce(installed.version);
|
|
8341
|
-
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.`;
|
|
8342
|
-
}
|
|
8343
|
-
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.";
|
|
8344
|
-
return null;
|
|
8345
|
-
}
|
|
8637
|
+
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
8346
8638
|
const OTHER_HOOK_TOOLS = [
|
|
8347
8639
|
"simple-git-hooks",
|
|
8348
8640
|
"lefthook",
|
|
8349
8641
|
"yorkie"
|
|
8350
8642
|
];
|
|
8351
|
-
const
|
|
8352
|
-
function
|
|
8643
|
+
const SUPPORTED_GIT_HOOK_NAME_SET = new Set(SUPPORTED_GIT_HOOK_NAMES);
|
|
8644
|
+
function removeReplacedStagedPackage(packageJsonPath) {
|
|
8353
8645
|
editJsonFile(packageJsonPath, (pkg) => {
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
if (pkg.dependencies?.[name]) delete pkg.dependencies[name];
|
|
8357
|
-
}
|
|
8646
|
+
if (pkg.devDependencies?.["lint-staged"]) delete pkg.devDependencies["lint-staged"];
|
|
8647
|
+
if (pkg.dependencies?.["lint-staged"]) delete pkg.dependencies["lint-staged"];
|
|
8358
8648
|
return pkg;
|
|
8359
8649
|
});
|
|
8360
8650
|
}
|
|
8361
8651
|
function detectLegacyGitHooksMigrationCandidate(projectPath) {
|
|
8362
8652
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
8363
8653
|
if (!fs.existsSync(packageJsonPath)) return false;
|
|
8364
|
-
|
|
8365
|
-
|
|
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;
|
|
8366
8671
|
}
|
|
8367
8672
|
/**
|
|
8368
8673
|
* Walk up from `startPath` looking for `.git` (directory or file — submodules
|
|
@@ -8377,36 +8682,45 @@ function findGitRoot(startPath) {
|
|
|
8377
8682
|
dir = parent;
|
|
8378
8683
|
}
|
|
8379
8684
|
}
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
}
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
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
|
+
}
|
|
8395
8701
|
}
|
|
8396
|
-
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(/\/$/, "");
|
|
8397
8717
|
}
|
|
8398
8718
|
/**
|
|
8399
|
-
*
|
|
8400
|
-
*
|
|
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.
|
|
8401
8721
|
*/
|
|
8402
|
-
function
|
|
8403
|
-
|
|
8404
|
-
if (!fs.existsSync(packageJsonPath)) return;
|
|
8405
|
-
const pkg = readJsonFile(packageJsonPath);
|
|
8406
|
-
if (!pkg.scripts?.prepare) return;
|
|
8407
|
-
const match = collapseHuskyInstall(pkg.scripts.prepare).match(/\bhusky(?:\s+([\w./-]+))?/);
|
|
8408
|
-
if (!match) return;
|
|
8409
|
-
return match[1] ?? ".husky";
|
|
8722
|
+
function installGitHooks(projectPath, silent = false, report, packageManager, packages = []) {
|
|
8723
|
+
return setupGitHooks(projectPath, silent, report, packageManager, packages);
|
|
8410
8724
|
}
|
|
8411
8725
|
/**
|
|
8412
8726
|
* Pre-flight check: verify that git hooks can be set up for this project.
|
|
@@ -8416,10 +8730,9 @@ function getOldHooksDir(rootDir) {
|
|
|
8416
8730
|
* These checks are deterministic and read-only — they do not modify
|
|
8417
8731
|
* the project in any way, making them safe to call before migration.
|
|
8418
8732
|
*
|
|
8419
|
-
*
|
|
8420
|
-
* resolution to that manager's catalog so a foreign catalog file is ignored.
|
|
8733
|
+
* The package manager argument remains for API compatibility with callers.
|
|
8421
8734
|
*/
|
|
8422
|
-
function preflightGitHooksSetup(projectPath,
|
|
8735
|
+
function preflightGitHooksSetup(projectPath, _packageManager, packages = []) {
|
|
8423
8736
|
const gitRoot = findGitRoot(projectPath);
|
|
8424
8737
|
if (gitRoot && path.resolve(projectPath) !== path.resolve(gitRoot)) return "Subdirectory project detected — skipping git hooks setup. Configure hooks at the repository root.";
|
|
8425
8738
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
@@ -8427,19 +8740,41 @@ function preflightGitHooksSetup(projectPath, packageManager) {
|
|
|
8427
8740
|
const pkgContent = readJsonFile(packageJsonPath);
|
|
8428
8741
|
const deps = pkgContent.devDependencies;
|
|
8429
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.";
|
|
8430
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`;
|
|
8431
|
-
const
|
|
8432
|
-
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.`;
|
|
8433
8760
|
if (hasUnsupportedLintStagedConfig(projectPath)) return "Unsupported lint-staged config format — skipping git hooks setup. Please configure git hooks manually.";
|
|
8434
8761
|
return null;
|
|
8435
8762
|
}
|
|
8436
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
|
+
/**
|
|
8437
8772
|
* Set up git hooks with husky + lint-staged via vp commands.
|
|
8438
8773
|
* Skips if another hook tool is detected (warns user).
|
|
8439
8774
|
* Returns true if hooks were successfully set up, false if skipped.
|
|
8440
8775
|
*/
|
|
8441
|
-
function setupGitHooks(projectPath,
|
|
8442
|
-
const reason = preflightGitHooksSetup(projectPath, packageManager);
|
|
8776
|
+
function setupGitHooks(projectPath, silent = false, report, packageManager, packages = []) {
|
|
8777
|
+
const reason = preflightGitHooksSetup(projectPath, packageManager, packages);
|
|
8443
8778
|
if (reason) {
|
|
8444
8779
|
warnMigration(reason, report);
|
|
8445
8780
|
return false;
|
|
@@ -8447,73 +8782,29 @@ function setupGitHooks(projectPath, oldHooksDir, silent = false, report, package
|
|
|
8447
8782
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
8448
8783
|
if (!fs.existsSync(packageJsonPath)) return false;
|
|
8449
8784
|
const gitRoot = findGitRoot(projectPath);
|
|
8450
|
-
const
|
|
8451
|
-
const
|
|
8785
|
+
const hooksDir = ".vite-hooks";
|
|
8786
|
+
const hasExistingHookPolicy = hasExistingViteHooksPolicy(projectPath);
|
|
8452
8787
|
editJsonFile(packageJsonPath, (pkg) => {
|
|
8453
8788
|
if (!pkg.scripts) pkg.scripts = {};
|
|
8454
8789
|
if (!pkg.scripts.prepare) pkg.scripts.prepare = "vp config";
|
|
8455
|
-
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}`;
|
|
8456
8791
|
return pkg;
|
|
8457
8792
|
});
|
|
8458
8793
|
let stagedMerged = hasStagedConfigInViteConfig(projectPath);
|
|
8459
8794
|
const hasStandaloneConfig = hasStandaloneLintStagedConfig(projectPath);
|
|
8460
|
-
if (!stagedMerged && !hasStandaloneConfig) {
|
|
8795
|
+
if (!hasExistingHookPolicy && !stagedMerged && !hasStandaloneConfig) {
|
|
8461
8796
|
const stagedConfig = readJsonFile(packageJsonPath)?.["lint-staged"] ?? DEFAULT_STAGED_CONFIG;
|
|
8462
8797
|
const updated = rewriteScripts(JSON.stringify(stagedConfig), readRulesYaml());
|
|
8463
8798
|
stagedMerged = mergeStagedConfigToViteConfig(projectPath, updated ? JSON.parse(updated) : stagedConfig, silent, report);
|
|
8464
8799
|
}
|
|
8465
|
-
if (stagedMerged) removeLintStagedFromPackageJson(packageJsonPath);
|
|
8466
|
-
if (
|
|
8467
|
-
const oldDir = path.join(projectPath, oldHooksDir);
|
|
8468
|
-
if (fs.existsSync(oldDir)) {
|
|
8469
|
-
const targetDir = path.join(projectPath, hooksDir);
|
|
8470
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
8471
|
-
for (const entry of fs.readdirSync(oldDir, { withFileTypes: true })) {
|
|
8472
|
-
if (entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
8473
|
-
const src = path.join(oldDir, entry.name);
|
|
8474
|
-
const dest = path.join(targetDir, entry.name);
|
|
8475
|
-
fs.copyFileSync(src, dest);
|
|
8476
|
-
fs.chmodSync(dest, 493);
|
|
8477
|
-
}
|
|
8478
|
-
fs.rmSync(oldDir, {
|
|
8479
|
-
recursive: true,
|
|
8480
|
-
force: true
|
|
8481
|
-
});
|
|
8482
|
-
}
|
|
8483
|
-
}
|
|
8484
|
-
if (stagedMerged) createPreCommitHook(projectPath, hooksDir);
|
|
8800
|
+
if (!hasExistingHookPolicy && stagedMerged) removeLintStagedFromPackageJson(packageJsonPath);
|
|
8801
|
+
if (!hasExistingHookPolicy && stagedMerged) createPreCommitHook(projectPath, hooksDir);
|
|
8485
8802
|
if (!gitRoot) {
|
|
8486
|
-
|
|
8803
|
+
if (!hasExistingHookPolicy && stagedMerged) removeReplacedStagedPackage(packageJsonPath);
|
|
8487
8804
|
return true;
|
|
8488
8805
|
}
|
|
8489
|
-
if (oldHooksDir) {
|
|
8490
|
-
const checkResult = import_cross_spawn.default.sync("git", [
|
|
8491
|
-
"config",
|
|
8492
|
-
"--local",
|
|
8493
|
-
"core.hooksPath"
|
|
8494
|
-
], {
|
|
8495
|
-
cwd: projectPath,
|
|
8496
|
-
stdio: "pipe"
|
|
8497
|
-
});
|
|
8498
|
-
const existingPath = checkResult.status === 0 ? checkResult.stdout?.toString().trim() : "";
|
|
8499
|
-
if (existingPath === `${oldHooksDir}/_` || existingPath === oldHooksDir) import_cross_spawn.default.sync("git", [
|
|
8500
|
-
"config",
|
|
8501
|
-
"--local",
|
|
8502
|
-
"--unset",
|
|
8503
|
-
"core.hooksPath"
|
|
8504
|
-
], {
|
|
8505
|
-
cwd: projectPath,
|
|
8506
|
-
stdio: "pipe"
|
|
8507
|
-
});
|
|
8508
|
-
}
|
|
8509
8806
|
const vpBin = process.env.VP_CLI_BIN ?? "vp";
|
|
8510
|
-
const
|
|
8511
|
-
"config",
|
|
8512
|
-
"--no-agent",
|
|
8513
|
-
"--hooks-dir",
|
|
8514
|
-
hooksDir
|
|
8515
|
-
] : ["config", "--no-agent"];
|
|
8516
|
-
const configResult = import_cross_spawn.default.sync(vpBin, configArgs, {
|
|
8807
|
+
const configResult = import_cross_spawn.default.sync(vpBin, ["config", "--no-agent"], {
|
|
8517
8808
|
cwd: projectPath,
|
|
8518
8809
|
stdio: "pipe"
|
|
8519
8810
|
});
|
|
@@ -8523,7 +8814,7 @@ function setupGitHooks(projectPath, oldHooksDir, silent = false, report, package
|
|
|
8523
8814
|
warnMigration(`Git hooks not configured — ${stdout}`, report);
|
|
8524
8815
|
return false;
|
|
8525
8816
|
}
|
|
8526
|
-
|
|
8817
|
+
if (!hasExistingHookPolicy && stagedMerged) removeReplacedStagedPackage(packageJsonPath);
|
|
8527
8818
|
if (report) report.gitHooksConfigured = true;
|
|
8528
8819
|
if (!silent) log.success("✔ Git hooks configured");
|
|
8529
8820
|
return true;
|
|
@@ -8548,85 +8839,14 @@ function hasUnsupportedLintStagedConfig(projectPath) {
|
|
|
8548
8839
|
if (fs.existsSync(lintstagedrcPath) && !isJsonFile(lintstagedrcPath)) return true;
|
|
8549
8840
|
return false;
|
|
8550
8841
|
}
|
|
8551
|
-
/**
|
|
8552
|
-
* Create pre-commit hook file in the hooks directory.
|
|
8553
|
-
*/
|
|
8554
|
-
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/];
|
|
8555
8842
|
const DEFAULT_STAGED_CONFIG = { "*": "vp check --fix" };
|
|
8556
|
-
/**
|
|
8557
|
-
* Ensure the pre-commit hook exists with `vp staged`, and that
|
|
8558
|
-
* vite.config.ts contains a `staged` block (using the default config
|
|
8559
|
-
* if none is present). Called by `vp config` after hook installation.
|
|
8560
|
-
*/
|
|
8561
|
-
function ensurePreCommitHook(projectPath, dir = ".vite-hooks") {
|
|
8562
|
-
if (!hasStagedConfigInViteConfig(projectPath)) mergeStagedConfigToViteConfig(projectPath, DEFAULT_STAGED_CONFIG, true);
|
|
8563
|
-
createPreCommitHook(projectPath, dir);
|
|
8564
|
-
}
|
|
8565
8843
|
function createPreCommitHook(projectPath, dir = ".vite-hooks") {
|
|
8566
8844
|
const huskyDir = path.join(projectPath, dir);
|
|
8567
8845
|
fs.mkdirSync(huskyDir, { recursive: true });
|
|
8568
8846
|
const hookPath = path.join(huskyDir, "pre-commit");
|
|
8569
|
-
if (fs.existsSync(hookPath))
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
const lines = existing.split("\n");
|
|
8573
|
-
let replaced = false;
|
|
8574
|
-
const result = [];
|
|
8575
|
-
for (const line of lines) {
|
|
8576
|
-
const trimmed = line.trim();
|
|
8577
|
-
if (!replaced) {
|
|
8578
|
-
let matched = false;
|
|
8579
|
-
for (const pattern of STALE_LINT_STAGED_PATTERNS) {
|
|
8580
|
-
const match = pattern.exec(trimmed);
|
|
8581
|
-
if (match) {
|
|
8582
|
-
const parts = [
|
|
8583
|
-
match[1]?.trim() ?? "",
|
|
8584
|
-
"vp staged",
|
|
8585
|
-
trimmed.slice(match[0].length).trim()
|
|
8586
|
-
].filter(Boolean);
|
|
8587
|
-
result.push(parts.join(" "));
|
|
8588
|
-
replaced = true;
|
|
8589
|
-
matched = true;
|
|
8590
|
-
break;
|
|
8591
|
-
}
|
|
8592
|
-
}
|
|
8593
|
-
if (matched) continue;
|
|
8594
|
-
}
|
|
8595
|
-
result.push(line);
|
|
8596
|
-
}
|
|
8597
|
-
if (!replaced) fs.writeFileSync(hookPath, `${result.join("\n").trimEnd()}\nvp staged\n`);
|
|
8598
|
-
else fs.writeFileSync(hookPath, result.join("\n"));
|
|
8599
|
-
} else {
|
|
8600
|
-
fs.writeFileSync(hookPath, "vp staged\n");
|
|
8601
|
-
fs.chmodSync(hookPath, 493);
|
|
8602
|
-
}
|
|
8603
|
-
}
|
|
8604
|
-
/**
|
|
8605
|
-
* Rewrite only `scripts.prepare` in the root package.json using vite-prepare.yml rules.
|
|
8606
|
-
* Collapses "husky install" → "husky" before applying ast-grep so that the
|
|
8607
|
-
* replace-husky rule produces "vp config" with any directory argument preserved.
|
|
8608
|
-
* Returns the old husky hooks dir (if any) for migration to .vite-hooks.
|
|
8609
|
-
* Called only when hooks are being set up (not with --no-hooks).
|
|
8610
|
-
*/
|
|
8611
|
-
function rewritePrepareScript(rootDir) {
|
|
8612
|
-
const packageJsonPath = path.join(rootDir, "package.json");
|
|
8613
|
-
if (!fs.existsSync(packageJsonPath)) return;
|
|
8614
|
-
let oldDir;
|
|
8615
|
-
editJsonFile(packageJsonPath, (pkg) => {
|
|
8616
|
-
if (!pkg.scripts?.prepare) return pkg;
|
|
8617
|
-
const prepare = collapseHuskyInstall(pkg.scripts.prepare);
|
|
8618
|
-
const updated = rewriteScripts(JSON.stringify({ prepare }), readPrepareRulesYaml());
|
|
8619
|
-
if (updated) {
|
|
8620
|
-
let newPrepare = JSON.parse(updated).prepare;
|
|
8621
|
-
newPrepare = newPrepare.replace(/\bvp config(?:\s+(?!-)([\w./-]+))?/, (_match, dir) => {
|
|
8622
|
-
oldDir = dir ?? ".husky";
|
|
8623
|
-
return dir ? `vp config --hooks-dir ${dir}` : "vp config";
|
|
8624
|
-
});
|
|
8625
|
-
pkg.scripts.prepare = newPrepare;
|
|
8626
|
-
} else if (prepare !== pkg.scripts.prepare) pkg.scripts.prepare = prepare;
|
|
8627
|
-
return pkg;
|
|
8628
|
-
});
|
|
8629
|
-
return oldDir;
|
|
8847
|
+
if (fs.existsSync(hookPath)) return;
|
|
8848
|
+
fs.writeFileSync(hookPath, "vp staged\n");
|
|
8849
|
+
fs.chmodSync(hookPath, 493);
|
|
8630
8850
|
}
|
|
8631
8851
|
//#endregion
|
|
8632
8852
|
//#region src/migration/migrator/setup.ts
|
|
@@ -8769,10 +8989,9 @@ function migrateNodeVersionManagerFile(projectPath, detection, report) {
|
|
|
8769
8989
|
//#endregion
|
|
8770
8990
|
//#region src/migration/migrator/core-finalization.ts
|
|
8771
8991
|
const RULES_YAML_PATH = path.join(rulesDir, "vite-tools.yml");
|
|
8772
|
-
|
|
8992
|
+
path.join(rulesDir, "vite-prepare.yml");
|
|
8773
8993
|
let cachedRulesYaml;
|
|
8774
8994
|
let cachedRulesYamlNoLintStaged;
|
|
8775
|
-
let cachedPrepareRulesYaml;
|
|
8776
8995
|
function readRulesYaml() {
|
|
8777
8996
|
cachedRulesYaml ??= fs.readFileSync(RULES_YAML_PATH, "utf8");
|
|
8778
8997
|
return cachedRulesYaml;
|
|
@@ -8783,10 +9002,6 @@ function getScriptRulesYaml(skipStagedMigration) {
|
|
|
8783
9002
|
cachedRulesYamlNoLintStaged ??= yaml.split("\n\n\n").filter((block) => !block.includes("id: replace-lint-staged")).join("\n\n\n");
|
|
8784
9003
|
return cachedRulesYamlNoLintStaged;
|
|
8785
9004
|
}
|
|
8786
|
-
function readPrepareRulesYaml() {
|
|
8787
|
-
cachedPrepareRulesYaml ??= fs.readFileSync(PREPARE_RULES_YAML_PATH, "utf8");
|
|
8788
|
-
return cachedPrepareRulesYaml;
|
|
8789
|
-
}
|
|
8790
9005
|
function getCoreMigrationProjectPaths(workspaceInfo) {
|
|
8791
9006
|
return [workspaceInfo.rootDir, ...(workspaceInfo.packages ?? []).map((pkg) => path.join(workspaceInfo.rootDir, pkg.path))];
|
|
8792
9007
|
}
|
|
@@ -9014,300 +9229,186 @@ function rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration
|
|
|
9014
9229
|
if (!deferLazyPluginWrapping) wrapLazyPluginsInViteConfig(projectPath, silent, report);
|
|
9015
9230
|
}
|
|
9016
9231
|
//#endregion
|
|
9017
|
-
//#region src/utils/
|
|
9018
|
-
const
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
"
|
|
9034
|
-
"
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
hint: "Claude Code",
|
|
9043
|
-
aliases: ["claude.md", "claude-code"]
|
|
9044
|
-
},
|
|
9045
|
-
{
|
|
9046
|
-
id: "gemini",
|
|
9047
|
-
label: "GEMINI.md",
|
|
9048
|
-
targetPath: "GEMINI.md",
|
|
9049
|
-
hint: "Gemini CLI",
|
|
9050
|
-
aliases: ["gemini.md", "gemini-cli"]
|
|
9051
|
-
},
|
|
9052
|
-
{
|
|
9053
|
-
id: "copilot",
|
|
9054
|
-
label: ".github/copilot-instructions.md",
|
|
9055
|
-
targetPath: ".github/copilot-instructions.md",
|
|
9056
|
-
hint: "GitHub Copilot",
|
|
9057
|
-
aliases: ["github-copilot", "copilot-instructions.md"]
|
|
9058
|
-
},
|
|
9059
|
-
{
|
|
9060
|
-
id: "cursor",
|
|
9061
|
-
label: ".cursor/rules/viteplus.mdc",
|
|
9062
|
-
targetPath: ".cursor/rules/viteplus.mdc",
|
|
9063
|
-
hint: "Cursor",
|
|
9064
|
-
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
|
+
} } }
|
|
9065
9257
|
},
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
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);
|
|
9077
9321
|
}
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
const
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
const COPILOT_SETUP_WORKFLOW_CONTENT = `name: "Copilot Setup Steps"
|
|
9087
|
-
|
|
9088
|
-
on:
|
|
9089
|
-
workflow_dispatch:
|
|
9090
|
-
push:
|
|
9091
|
-
paths:
|
|
9092
|
-
- .github/workflows/copilot-setup-steps.yml
|
|
9093
|
-
pull_request:
|
|
9094
|
-
paths:
|
|
9095
|
-
- .github/workflows/copilot-setup-steps.yml
|
|
9096
|
-
|
|
9097
|
-
jobs:
|
|
9098
|
-
copilot-setup-steps:
|
|
9099
|
-
runs-on: ubuntu-latest
|
|
9100
|
-
permissions:
|
|
9101
|
-
contents: read
|
|
9102
|
-
steps:
|
|
9103
|
-
- name: Checkout code
|
|
9104
|
-
uses: actions/checkout@v6
|
|
9105
|
-
with:
|
|
9106
|
-
persist-credentials: false
|
|
9107
|
-
- name: Set up Vite+
|
|
9108
|
-
uses: voidzero-dev/setup-vp@v1
|
|
9109
|
-
with:
|
|
9110
|
-
cache: true
|
|
9111
|
-
run-install: true
|
|
9112
|
-
- name: Verify Vite+
|
|
9113
|
-
run: vp --version
|
|
9114
|
-
`;
|
|
9115
|
-
async function selectAgentTargets({ interactive, agent, onCancel }) {
|
|
9116
|
-
if (agent === false) return {
|
|
9117
|
-
targetPaths: void 0,
|
|
9118
|
-
selectedAgents: []
|
|
9119
|
-
};
|
|
9120
|
-
if (interactive && !agent) {
|
|
9121
|
-
const selectedAgentIds = await multiselect({
|
|
9122
|
-
message: "Which coding agent instruction files should Vite+ create?",
|
|
9123
|
-
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) => ({
|
|
9124
9330
|
label: option.label,
|
|
9125
9331
|
value: option.id,
|
|
9126
|
-
hint: option.
|
|
9332
|
+
hint: option.targetDir
|
|
9127
9333
|
})),
|
|
9128
|
-
initialValues: [
|
|
9334
|
+
initialValues: ["vscode"],
|
|
9129
9335
|
required: false
|
|
9130
9336
|
});
|
|
9131
|
-
if (isCancel(
|
|
9337
|
+
if (isCancel(selectedEditors)) {
|
|
9132
9338
|
onCancel();
|
|
9133
|
-
return
|
|
9134
|
-
targetPaths: void 0,
|
|
9135
|
-
selectedAgents: []
|
|
9136
|
-
};
|
|
9339
|
+
return;
|
|
9137
9340
|
}
|
|
9138
|
-
|
|
9139
|
-
targetPaths: void 0,
|
|
9140
|
-
selectedAgents: []
|
|
9141
|
-
};
|
|
9142
|
-
const selectedAgents = resolveAgentOptions(selectedAgentIds);
|
|
9143
|
-
return {
|
|
9144
|
-
targetPaths: getAgentTargetPaths(selectedAgents),
|
|
9145
|
-
selectedAgents
|
|
9146
|
-
};
|
|
9341
|
+
return selectedEditors.length === 0 ? void 0 : resolveEditorIds(selectedEditors);
|
|
9147
9342
|
}
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
selectedAgents
|
|
9152
|
-
};
|
|
9153
|
-
}
|
|
9154
|
-
async function selectAgentTargetPaths({ interactive, agent, onCancel }) {
|
|
9155
|
-
return (await selectAgentTargets({
|
|
9156
|
-
interactive,
|
|
9157
|
-
agent,
|
|
9158
|
-
onCancel
|
|
9159
|
-
})).targetPaths;
|
|
9160
|
-
}
|
|
9161
|
-
function detectExistingAgentTargetPaths(projectRoot) {
|
|
9162
|
-
const detectedPaths = [];
|
|
9163
|
-
const seenTargetPaths = /* @__PURE__ */ new Set();
|
|
9164
|
-
for (const option of AGENTS) {
|
|
9165
|
-
if (seenTargetPaths.has(option.targetPath)) continue;
|
|
9166
|
-
seenTargetPaths.add(option.targetPath);
|
|
9167
|
-
const targetPath = path.join(projectRoot, option.targetPath);
|
|
9168
|
-
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;
|
|
9169
9346
|
}
|
|
9170
|
-
return detectedPaths.length > 0 ? detectedPaths : void 0;
|
|
9171
|
-
}
|
|
9172
|
-
/**
|
|
9173
|
-
* Silently update agent instruction files that contain Vite+ markers.
|
|
9174
|
-
* - No agent files → no writes
|
|
9175
|
-
* - No Vite+ markers → no writes
|
|
9176
|
-
* - Markers present, content up to date → no writes
|
|
9177
|
-
* - Markers present, content outdated → update marked section
|
|
9178
|
-
*/
|
|
9179
|
-
function updateExistingAgentInstructions(projectRoot) {
|
|
9180
|
-
const targetPaths = detectExistingAgentTargetPaths(projectRoot);
|
|
9181
|
-
if (!targetPaths) return;
|
|
9182
|
-
const templatePath = path.join(pkgRoot, "AGENTS.md");
|
|
9183
|
-
if (!fs.existsSync(templatePath)) return;
|
|
9184
|
-
const templateContent = fs.readFileSync(templatePath, "utf-8");
|
|
9185
|
-
for (const targetPath of targetPaths) try {
|
|
9186
|
-
const fullPath = path.join(projectRoot, targetPath);
|
|
9187
|
-
const existing = fs.readFileSync(fullPath, "utf-8");
|
|
9188
|
-
const updated = replaceMarkedAgentInstructionsSection(existing, templateContent);
|
|
9189
|
-
if (updated !== void 0 && updated !== existing) fs.writeFileSync(fullPath, updated);
|
|
9190
|
-
} catch {}
|
|
9191
9347
|
}
|
|
9192
|
-
function
|
|
9193
|
-
const
|
|
9194
|
-
const
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
}
|
|
9203
|
-
return dedupedAgents;
|
|
9204
|
-
}
|
|
9205
|
-
function getAgentTargetPaths(agents) {
|
|
9206
|
-
const dedupedTargetPaths = [];
|
|
9207
|
-
const seenTargetPaths = /* @__PURE__ */ new Set();
|
|
9208
|
-
for (const agent of agents) {
|
|
9209
|
-
if (seenTargetPaths.has(agent.targetPath)) continue;
|
|
9210
|
-
seenTargetPaths.add(agent.targetPath);
|
|
9211
|
-
dedupedTargetPaths.push(agent.targetPath);
|
|
9212
|
-
}
|
|
9213
|
-
return dedupedTargetPaths;
|
|
9214
|
-
}
|
|
9215
|
-
function parseAgentNames(agent) {
|
|
9216
|
-
if (!agent) return [];
|
|
9217
|
-
return (Array.isArray(agent) ? agent : [agent]).filter((value) => typeof value === "string").flatMap((value) => value.split(",")).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
9218
|
-
}
|
|
9219
|
-
function resolveSingleAgentOption(agent) {
|
|
9220
|
-
const normalized = normalizeAgentName(agent);
|
|
9221
|
-
const alias = AGENT_ALIASES[normalized];
|
|
9222
|
-
const resolved = alias ? normalizeAgentName(alias) : normalized;
|
|
9223
|
-
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;
|
|
9224
9358
|
}
|
|
9225
9359
|
/**
|
|
9226
|
-
* Detect
|
|
9227
|
-
* Returns only files that need a user decision (append or skip).
|
|
9360
|
+
* Detect editor config files that would conflict (already exist).
|
|
9228
9361
|
* Read-only — does not write or modify any files.
|
|
9229
9362
|
*/
|
|
9230
|
-
|
|
9231
|
-
if (!
|
|
9232
|
-
const
|
|
9233
|
-
if (!
|
|
9234
|
-
const incomingContent = await fsPromises.readFile(sourcePath, "utf-8");
|
|
9235
|
-
const shouldLinkToAgents = targetPaths.includes(AGENT_STANDARD_PATH);
|
|
9236
|
-
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 [];
|
|
9237
9367
|
const conflicts = [];
|
|
9238
|
-
const
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
seenDestinationPaths.add(destinationKey);
|
|
9245
|
-
if (shouldLinkToAgents && targetPathToCheck !== AGENT_STANDARD_PATH) {
|
|
9246
|
-
if (await getExistingPathKind(destinationPath) !== "file") continue;
|
|
9247
|
-
}
|
|
9248
|
-
if (fs.existsSync(destinationPath)) {
|
|
9249
|
-
if (fs.lstatSync(destinationPath).isSymbolicLink()) continue;
|
|
9250
|
-
const destinationRealPath = await fsPromises.realpath(destinationPath);
|
|
9251
|
-
if (seenRealPaths.has(destinationRealPath)) continue;
|
|
9252
|
-
if (replaceMarkedAgentInstructionsSection(await fsPromises.readFile(destinationPath, "utf-8"), incomingContent) !== void 0) {
|
|
9253
|
-
seenRealPaths.add(destinationRealPath);
|
|
9254
|
-
continue;
|
|
9255
|
-
}
|
|
9256
|
-
conflicts.push({ targetPath: targetPathToCheck });
|
|
9257
|
-
seenRealPaths.add(destinationRealPath);
|
|
9258
|
-
}
|
|
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
|
+
});
|
|
9259
9374
|
}
|
|
9260
9375
|
return conflicts;
|
|
9261
9376
|
}
|
|
9262
|
-
async function
|
|
9263
|
-
const
|
|
9264
|
-
if (
|
|
9265
|
-
const
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
}
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
continue;
|
|
9288
|
-
}
|
|
9289
|
-
const destinationRealPath = await fsPromises.realpath(destinationPath);
|
|
9290
|
-
if (seenRealPaths.has(destinationRealPath)) {
|
|
9291
|
-
if (!silent) log.info(`Skipped writing ${targetPathToWrite} (duplicate target)`);
|
|
9292
|
-
continue;
|
|
9293
|
-
}
|
|
9294
|
-
const existingContent = await fsPromises.readFile(destinationPath, "utf-8");
|
|
9295
|
-
const updatedContent = replaceMarkedAgentInstructionsSection(existingContent, incomingContent);
|
|
9296
|
-
if (updatedContent !== void 0) {
|
|
9297
|
-
if (updatedContent !== existingContent) await fsPromises.writeFile(destinationPath, updatedContent);
|
|
9298
|
-
seenRealPaths.add(destinationRealPath);
|
|
9299
|
-
continue;
|
|
9300
|
-
}
|
|
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}`;
|
|
9301
9402
|
let conflictAction;
|
|
9302
|
-
const preResolved = conflictDecisions?.get(
|
|
9403
|
+
const preResolved = conflictDecisions?.get(displayPath) ?? conflictDecisions?.get(fileName);
|
|
9303
9404
|
if (preResolved) conflictAction = preResolved;
|
|
9304
9405
|
else if (interactive) {
|
|
9305
9406
|
const action = await select({
|
|
9306
|
-
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.`),
|
|
9307
9408
|
options: [{
|
|
9308
|
-
label: "
|
|
9309
|
-
value: "
|
|
9310
|
-
hint: "
|
|
9409
|
+
label: "Merge",
|
|
9410
|
+
value: "merge",
|
|
9411
|
+
hint: "Merge new settings into existing file"
|
|
9311
9412
|
}, {
|
|
9312
9413
|
label: "Skip",
|
|
9313
9414
|
value: "skip",
|
|
@@ -9315,86 +9416,96 @@ async function writeAgentInstructions({ projectRoot, targetPath, targetPaths, in
|
|
|
9315
9416
|
}],
|
|
9316
9417
|
initialValue: "skip"
|
|
9317
9418
|
});
|
|
9318
|
-
conflictAction = isCancel(action) || action === "skip" ? "skip" : "
|
|
9319
|
-
} else conflictAction = "
|
|
9320
|
-
if (conflictAction === "
|
|
9321
|
-
else {
|
|
9322
|
-
const suffix = !preResolved && !interactive ? " (already exists)" : "";
|
|
9323
|
-
if (!silent) log.info(`Skipped writing ${targetPathToWrite}${suffix}`);
|
|
9324
|
-
}
|
|
9325
|
-
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}`);
|
|
9326
9423
|
continue;
|
|
9327
9424
|
}
|
|
9328
|
-
|
|
9329
|
-
if (!silent) log.success(`Wrote
|
|
9330
|
-
seenRealPaths.add(await fsPromises.realpath(destinationPath));
|
|
9425
|
+
writeJsonFile(filePath, incoming);
|
|
9426
|
+
if (!silent) log.success(`Wrote editor config to ${editorConfig.targetDir}/${fileName}`);
|
|
9331
9427
|
}
|
|
9332
9428
|
}
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
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}`);
|
|
9338
9447
|
return;
|
|
9339
9448
|
}
|
|
9340
|
-
|
|
9341
|
-
if (!silent) log.success(`
|
|
9342
|
-
}
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
const existing = await getExistingPathKind(destinationPath);
|
|
9363
|
-
if (existing === "file") return false;
|
|
9364
|
-
if (existing === "symlink") {
|
|
9365
|
-
const currentLink = await fsPromises.readlink(destinationPath);
|
|
9366
|
-
if (path.resolve(path.dirname(destinationPath), currentLink) === agentsPath) {
|
|
9367
|
-
if (!silent) log.info(`Skipped linking ${targetPath} (already linked to ${AGENT_STANDARD_PATH})`);
|
|
9368
|
-
return true;
|
|
9369
|
-
}
|
|
9370
|
-
await fsPromises.unlink(destinationPath);
|
|
9371
|
-
}
|
|
9372
|
-
try {
|
|
9373
|
-
await fsPromises.symlink(symlinkTarget, destinationPath);
|
|
9374
|
-
} catch (err) {
|
|
9375
|
-
if (err.code === "EPERM") {
|
|
9376
|
-
await fsPromises.copyFile(agentsPath, destinationPath);
|
|
9377
|
-
if (!silent) log.success(`Copied ${AGENT_STANDARD_PATH} to ${targetPath}`);
|
|
9378
|
-
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);
|
|
9379
9471
|
}
|
|
9380
|
-
|
|
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++;
|
|
9381
9495
|
}
|
|
9382
|
-
|
|
9383
|
-
return true;
|
|
9496
|
+
return currentText;
|
|
9384
9497
|
}
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
return
|
|
9498
|
+
function resolveEditorId(editor) {
|
|
9499
|
+
const normalized = editor.trim().toLowerCase();
|
|
9500
|
+
return EDITORS.find((option) => option.id === normalized || option.label.toLowerCase() === normalized)?.id;
|
|
9388
9501
|
}
|
|
9389
|
-
function
|
|
9390
|
-
const
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
end: endMarkerIndex + endMarker.length
|
|
9397
|
-
};
|
|
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;
|
|
9398
9509
|
}
|
|
9399
9510
|
//#endregion
|
|
9400
|
-
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 };
|