From fc7659b79b1a0700c0abf6fa9d21d2f67b8cd52c Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 19 Oct 2026 14:36:14 +0800 Subject: [PATCH 1/2] fix(migrate): protect Oxc references and recover interrupted migrations --- .typos.toml | 3 + .../snapshots.toml | 4 +- ...reate_monorepo_local_template_shorthand.md | 14 +- .../.oxfmtrc.json | 4 + .../package.json | 9 + .../snapshots.toml | 97 +++ .../migration_existing_oxc_configs.md | 91 +++ ...ion_existing_oxc_configs_extends.global.md | 137 ++++ ...tion_existing_oxc_configs_extends.local.md | 135 ++++ ...migration_existing_oxc_configs_imported.md | 95 +++ ...gration_existing_oxc_configs_inline_fmt.md | 84 +++ .../migration_existing_oxc_configs_script.md | 46 ++ ..._existing_oxc_configs_unreferenced_lint.md | 155 ++++ .../src/index.ts | 1 + .../vite.config.ts | 1 + .../.oxlintrc.json | 4 + .../package.json | 14 + .../packages/app/.oxlintrc.json | 4 + .../packages/app/index.js | 1 + .../packages/app/package.json | 12 + .../packages/app/vite.config.mjs | 1 + .../root.js | 1 + .../snapshots.toml | 36 + ...ndependent_workspace_oxc_configs.global.md | 164 +++++ ...independent_workspace_oxc_configs.local.md | 158 ++++ ...ed_oxc_config_converted_consumer.global.md | 156 ++++ ...ced_oxc_config_converted_consumer.local.md | 150 ++++ .../vite.config.mjs | 1 + .../.oxfmtrc.json | 4 + .../.oxlintrc.json | 9 + .../config/lint.json | 3 + .../config/shared.ts | 4 + .../config/unused.cjs | 1 + .../package.json | 9 + .../snapshots.toml | 77 ++ .../migration_custom_lint_config_root.md | 123 ++++ .../migration_directory_package_import.md | 143 ++++ .../migration_package_import_alias.md | 139 ++++ .../migration_shared_vite_config_module.md | 139 ++++ .../src/index.js | 1 + .../vite.config.ts | 1 + .../.oxfmtrc.json | 4 + .../.oxlintrc.json | 9 + .../package.json | 12 + .../packages/app/package.json | 5 + .../packages/app/src/index.ts | 1 + .../pnpm-workspace.yaml | 2 + .../snapshots.toml | 16 + .../migration_oxc_ecosystem_delta_comic.md | 115 +++ .../vite.config.ts | 11 + .../.oxfmtrc.json | 4 + .../.oxlintrc.json | 9 + .../package.json | 14 + .../snapshots.toml | 17 + .../migration_oxc_ecosystem_freecut.md | 122 ++++ .../src/index.ts | 1 + .../vite.config.ts | 16 + .../.oxfmtrc.json | 4 + .../.oxlintrc.json | 9 + .../package.json | 18 + .../packages/common/package.json | 9 + .../packages/common/src/index.ts | 1 + .../playground/.oxlintrc.json | 11 + .../playground/package.json | 5 + .../playground/src/index.ts | 1 + .../snapshots.toml | 45 ++ .../migration_oxc_ecosystem_pdfme.md | 202 ++++++ ...xc_ecosystem_pdfme_inline_config.global.md | 158 ++++ ...oxc_ecosystem_pdfme_inline_config.local.md | 144 ++++ .../vite.config.mts | 7 + .../.oxlintrc.json | 5 + .../apps/custom-domain/.oxlintrc.json | 6 + .../apps/custom-domain/package.json | 5 + .../apps/custom-domain/src/index.ts | 1 + .../apps/sigle/.oxlintrc.json | 6 + .../apps/sigle/package.json | 5 + .../apps/sigle/src/index.ts | 1 + .../apps/sigle/vite.config.ts | 7 + .../package.json | 9 + .../pnpm-workspace.yaml | 2 + .../snapshots.toml | 21 + .../migration_oxc_ecosystem_sigle.md | 215 ++++++ .../vite.config.ts | 12 + .../snapshots.toml | 4 +- ...gration_preserves_existing_fmt_and_lint.md | 12 +- .../.oxfmtrc.json | 3 + .../.oxlintrc.json | 5 + .../package.json | 9 + .../packages/app/package.json | 5 + .../packages/app/src/index.ts | 1 + .../pnpm-workspace.yaml | 2 + .../snapshots.toml | 32 + ...eferenced_workspace_oxc_configs_extends.md | 116 +++ ...ferenced_workspace_oxc_configs_imported.md | 94 +++ .../oxc_explicit_config/custom-fmt.json | 3 + .../oxc_explicit_config/custom-lint.json | 5 + .../fixtures/oxc_explicit_config/index.js | 1 + .../fixtures/oxc_explicit_config/package.json | 9 + .../oxc_explicit_config/snapshots.toml | 31 + .../snapshots/fmt_explicit_config.global.md | 97 +++ .../snapshots/fmt_explicit_config.local.md | 83 +++ .../snapshots/lint_explicit_config.global.md | 93 +++ .../snapshots/lint_explicit_config.local.md | 78 ++ .../oxc_explicit_config/vite.config.ts | 10 + .../src/migration/__tests__/migrator.spec.ts | 682 +++++++++++++++++- .../__tests__/oxlint-extends.spec.ts | 59 ++ packages/cli/src/migration/bin.ts | 1 + packages/cli/src/migration/migrator.ts | 1 + packages/cli/src/migration/migrator/README.md | 1 + .../cli/src/migration/migrator/catalog.ts | 10 +- .../migration/migrator/core-finalization.ts | 38 + .../src/migration/migrator/orchestrators.ts | 15 +- .../src/migration/migrator/oxc-references.ts | 196 +++++ .../cli/src/migration/migrator/vite-config.ts | 49 ++ 114 files changed, 5217 insertions(+), 26 deletions(-) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/.oxfmtrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_imported.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_inline_fmt.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_script.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/vite.config.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/index.js create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/vite.config.mjs create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/root.js create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/vite.config.mjs create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxfmtrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/lint.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/shared.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/unused.cjs create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_custom_lint_config_root.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_directory_package_import.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_package_import_alias.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_shared_vite_config_module.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/src/index.js create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/vite.config.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxfmtrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/pnpm-workspace.yaml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots/migration_oxc_ecosystem_delta_comic.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/vite.config.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxfmtrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots/migration_oxc_ecosystem_freecut.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/vite.config.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxfmtrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/vite.config.mts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/vite.config.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/pnpm-workspace.yaml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/vite.config.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxfmtrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxlintrc.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/src/index.ts create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/pnpm-workspace.yaml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_imported.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-fmt.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-lint.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/index.js create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/vite.config.ts create mode 100644 packages/cli/src/migration/migrator/oxc-references.ts diff --git a/.typos.toml b/.typos.toml index 28c7a0f9ea..afff8e2a2e 100644 --- a/.typos.toml +++ b/.typos.toml @@ -9,12 +9,15 @@ PUNICODE = "PUNICODE" Jod = "Jod" # Node.js v22 LTS codename flate = "flate" # flate2 crate name (gzip/deflate compression) fpr = "fpr" # GPG colon-format record name for a key fingerprint +sigle = "sigle" # Ecosystem repository name used in migration regression fixtures [default.extend-identifiers] # Yarn Plug'n'Play — tokenizer splits `PnP` into the word `Pn`, which typos # otherwise wants to autocorrect to `On`. Whitelist the full identifier so # the parts aren't tokenized. PnP = "PnP" +# Tests pass an attached config path: `-c` followed by `custom.json`. +ccustom = "ccustom" [files] extend-exclude = [ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots.toml index defdfb38b6..12703b557e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots.toml @@ -4,6 +4,6 @@ vp = "local" steps = [ { argv = ["vp", "create", "starter", "--no-interactive", "--no-agent", "--", "--directory", "my-app"], comment = "run the local create.templates entry; generated pkg declares fmt/lint via shorthand", continue-on-failure = true }, { argv = ["vpt", "print-file", "packages/my-app/vite.config.ts"], comment = "fmt/lint stay shorthand only, no injected duplicate inline fmt:/lint: blocks (#1836)", continue-on-failure = true }, - { argv = ["vpt", "stat-file", "packages/my-app/.oxlintrc.json", "--assert-not", "file"], comment = "standalone lint config merge-skipped and removed", continue-on-failure = true }, - { argv = ["vpt", "stat-file", "packages/my-app/.oxfmtrc.json", "--assert-not", "file"], comment = "standalone fmt config merge-skipped and removed", continue-on-failure = true }, + { argv = ["vpt", "stat-file", "packages/my-app/.oxlintrc.json", "--assert", "file"], comment = "standalone lint config preserved", continue-on-failure = true }, + { argv = ["vpt", "stat-file", "packages/my-app/.oxfmtrc.json", "--assert", "file"], comment = "standalone fmt config preserved", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots/create_monorepo_local_template_shorthand.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots/create_monorepo_local_template_shorthand.md index 4d7d3ba9c2..44501b71ab 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots/create_monorepo_local_template_shorthand.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_monorepo_local_template_shorthand/snapshots/create_monorepo_local_template_shorthand.md @@ -9,8 +9,6 @@ run the local create.templates entry; generated pkg declares fmt/lint via shorth ● Running: node /packages/starter-template/bin/index.mjs --directory my-app cloned starter-template to my-app ◇ Monorepo integration... -● lint config already present in packages/my-app/vite.config.ts — removed redundant packages/my-app/.oxlintrc.json -● fmt config already present in packages/my-app/vite.config.ts — removed redundant packages/my-app/.oxfmtrc.json ● Formatting code... ● Code formatted ◇ Scaffolded packages/my-app @@ -37,18 +35,18 @@ export default defineConfig(({ mode }) => { }); ``` -## `vpt stat-file packages/my-app/.oxlintrc.json --assert-not file` +## `vpt stat-file packages/my-app/.oxlintrc.json --assert file` -standalone lint config merge-skipped and removed +standalone lint config preserved ``` -packages/my-app/.oxlintrc.json: missing +packages/my-app/.oxlintrc.json: file ``` -## `vpt stat-file packages/my-app/.oxfmtrc.json --assert-not file` +## `vpt stat-file packages/my-app/.oxfmtrc.json --assert file` -standalone fmt config merge-skipped and removed +standalone fmt config preserved ``` -packages/my-app/.oxfmtrc.json: missing +packages/my-app/.oxfmtrc.json: file ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/.oxfmtrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/.oxfmtrc.json new file mode 100644 index 0000000000..fd496a820e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/package.json new file mode 100644 index 0000000000..cdaf891df3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/package.json @@ -0,0 +1,9 @@ +{ + "name": "migration-existing-oxc-configs", + "private": true, + "type": "module", + "packageManager": "pnpm@12.3.4", + "devDependencies": { + "vite-plus": "latest" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots.toml new file mode 100644 index 0000000000..d28db5bcbe --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots.toml @@ -0,0 +1,97 @@ +[[case]] +name = "migration_existing_oxc_configs" +vp = "global" +steps = [ + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "finish a leftover Oxfmt config even when Vite+ is already installed" }, + ["vpt", "print-file", "vite.config.ts"], + ["vpt", "stat-file", ".oxfmtrc.json", "--assert-not", "file"], + { argv = ["vp", "fmt", "src/index.ts"], comment = "the migrated options must affect formatting" }, + ["vpt", "print-file", "src/index.ts"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "a completed migration should be a no-op on retry" }, + ["vpt", "print-file", "vite.config.ts"], + ["vpt", "stat-file", "AGENTS.md", "--assert-not", "file"], + ["vpt", "stat-file", ".vite-hooks", "--assert-not", "dir"], + ["vpt", "stat-file", ".vscode", "--assert-not", "dir"], +] + +[[case]] +name = "migration_existing_oxc_configs_unreferenced_lint" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", ".oxlintrc.json", "{\"rules\":{\"no-console\":\"error\"},\"options\":{\"typeAware\":false,\"typeCheck\":false}}\n"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "finish both unreferenced Oxc configs in a project that already uses Vite+" }, + ["vpt", "stat-file", ".oxlintrc.json", "--assert-not", "file"], + ["vpt", "stat-file", ".oxfmtrc.json", "--assert-not", "file"], + ["vpt", "print-file", "vite.config.ts"], + ["vp", "fmt", "src/index.ts"], + ["vpt", "print-file", "src/index.ts"], + ["vp", "lint", "src/index.ts"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "the restored finalization must remain a no-op on retry" }, + ["vpt", "print-file", "vite.config.ts"], + ["vp", "fmt", "--check", "src/index.ts"], + { argv = ["vpt", "write-file", "src/index.ts", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "lint", "src/index.ts"], comment = "the merged lint rule must apply after the JSON config is removed", continue-on-failure = true }, +] + +[[case]] +name = "migration_existing_oxc_configs_inline_fmt" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "vite.config.ts", "export default { fmt: { singleQuote: false, semi: false } };\n"], snapshot = false }, + { argv = ["vp", "fmt", "src/index.ts"], comment = "the existing inline fmt config takes precedence over the standalone config" }, + ["vpt", "print-file", "src/index.ts"], + { argv = ["vpt", "write-file", "src/index.ts", "export const message = \"preserved\";\n"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "keep the standalone config when fmt is already configured" }, + ["vpt", "print-file", "vite.config.ts"], + ["vpt", "stat-file", ".oxfmtrc.json", "--assert", "file"], + { argv = ["vp", "fmt", "src/index.ts"], comment = "formatting must still use the existing inline options after migration" }, + ["vpt", "print-file", "src/index.ts"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "retrying the completed migration should be a no-op" }, + ["vpt", "print-file", "vite.config.ts"], +] + +[[case]] +name = "migration_existing_oxc_configs_imported" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "vite.config.ts", "import fmt from './.oxfmtrc.json' with { type: 'json' };\nimport { readFileSync } from 'node:fs';\nconst lint = JSON.parse(readFileSync(new URL('./.oxlintrc.json', import.meta.url), 'utf8'));\nexport default { fmt, lint };\n"], snapshot = false }, + { argv = ["vpt", "write-file", ".oxlintrc.json", "{\"rules\":{\"no-debugger\":\"error\"}}\n"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "preserve JSON configs loaded with imports and readFileSync" }, + ["vpt", "print-file", ".oxfmtrc.json"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vp", "fmt", "src/index.ts"], + ["vpt", "print-file", "src/index.ts"], + ["vp", "lint", "src/index.ts"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "retry preserves both imported configs" }, + ["vp", "fmt", "--check", "src/index.ts"], + ["vp", "lint", "src/index.ts"], +] + +[[case]] +name = "migration_existing_oxc_configs_script" +vp = "global" +steps = [ + { argv = ["vpt", "json-edit", "package.json", "scripts.fmt", "vp fmt -c .oxfmtrc.json src/index.ts"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "preserve the config passed explicitly by a package script" }, + ["vpt", "stat-file", ".oxfmtrc.json", "--assert", "file"], + ["vpt", "print-file", "vite.config.ts"], + ["vp", "run", "fmt"], + ["vpt", "print-file", "src/index.ts"], +] + +[[case]] +name = "migration_existing_oxc_configs_extends" +vp = ["global", "local"] +steps = [ + { argv = ["vpt", "write-file", ".oxlintrc.json", '{"extends":["./lint-base.json"]}'], snapshot = false }, + { argv = ["vpt", "write-file", "lint-base.json", '{"rules":{"no-console":"error"},"options":{"typeAware":false,"typeCheck":false}}'], snapshot = false }, + { argv = ["vpt", "write-file", "src/index.ts", "console.log('hello');\n"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "recover an interrupted inheritance migration using shared TypeScript configs" }, + ["vpt", "stat-file", ".oxlintrc.json", "--assert", "missing"], + ["vpt", "print-file", "vite.config.ts"], + ["vpt", "print-file", "lint-base.ts"], + { argv = ["vp", "lint", "src/index.ts"], comment = "plain lint must enforce the inherited no-console error", continue-on-failure = true }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], continue-on-failure = true }, + ["vpt", "print-file", "vite.config.ts"], + { argv = ["vp", "lint", "src/index.ts"], comment = "a second migration must preserve the inherited error", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs.md new file mode 100644 index 0000000000..d124881fe9 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs.md @@ -0,0 +1,91 @@ +# migration_existing_oxc_configs + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +finish a leftover Oxfmt config even when Vite+ is already installed + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 1 config update applied +• Package manager settings configured +``` + +## `vpt print-file vite.config.ts` + +``` +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + +} +``` + +## `vpt stat-file .oxfmtrc.json --assert-not file` + +``` +.oxfmtrc.json: missing +``` + +## `vp fmt src/index.ts` + +the migrated options must affect formatting + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +a completed migration should be a no-op on retry + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file vite.config.ts` + +``` +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + +} +``` + +## `vpt stat-file AGENTS.md --assert-not file` + +``` +AGENTS.md: missing +``` + +## `vpt stat-file .vite-hooks --assert-not dir` + +``` +.vite-hooks: missing +``` + +## `vpt stat-file .vscode --assert-not dir` + +``` +.vscode: missing +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md new file mode 100644 index 0000000000..3b2f42c2ea --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md @@ -0,0 +1,137 @@ +# migration_existing_oxc_configs_extends + +## `vpt write-file .oxlintrc.json '{"extends":["./lint-base.json"]}'` + + +## `vpt write-file lint-base.json '{"rules":{"no-console":"error"},"options":{"typeAware":false,"typeCheck":false}}'` + + +## `vpt write-file src/index.ts 'console.log('\''hello'\''); +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +recover an interrupted inheritance migration using shared TypeScript configs + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 2 config updates applied +• Package manager settings configured +``` + +## `vpt stat-file .oxlintrc.json --assert missing` + +``` +.oxlintrc.json: missing +``` + +## `vpt print-file vite.config.ts` + +``` +import oxlintConfig1 from "./lint-base.js"; +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + lint: { + "extends": [oxlintConfig1], + "options": {}, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }], + "rules": { + "vite-plus/prefer-vite-plus-imports": "error" + } + }, +} +``` + +## `vpt print-file lint-base.ts` + +``` +import type { OxlintConfig } from "vite-plus/lint"; + +export default { + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} satisfies OxlintConfig; +``` + +## `vp lint src/index.ts` + +plain lint must enforce the inherited no-console error + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file vite.config.ts` + +``` +import oxlintConfig1 from "./lint-base.js"; +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + lint: { + "extends": [oxlintConfig1], + "options": {}, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }], + "rules": { + "vite-plus/prefer-vite-plus-imports": "error" + } + }, +} +``` + +## `vp lint src/index.ts` + +a second migration must preserve the inherited error + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md new file mode 100644 index 0000000000..5a9687461b --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md @@ -0,0 +1,135 @@ +# migration_existing_oxc_configs_extends + +## `vpt write-file .oxlintrc.json '{"extends":["./lint-base.json"]}'` + + +## `vpt write-file lint-base.json '{"rules":{"no-console":"error"},"options":{"typeAware":false,"typeCheck":false}}'` + + +## `vpt write-file src/index.ts 'console.log('\''hello'\''); +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +recover an interrupted inheritance migration using shared TypeScript configs + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 2 config updates applied +• Package manager settings configured +``` + +## `vpt stat-file .oxlintrc.json --assert missing` + +``` +.oxlintrc.json: missing +``` + +## `vpt print-file vite.config.ts` + +``` +import oxlintConfig1 from "./lint-base.js"; +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + lint: { + "extends": [oxlintConfig1], + "options": {}, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }], + "rules": { + "vite-plus/prefer-vite-plus-imports": "error" + } + }, +} +``` + +## `vpt print-file lint-base.ts` + +``` +import type { OxlintConfig } from "vite-plus/lint"; + +export default { + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} satisfies OxlintConfig; +``` + +## `vp lint src/index.ts` + +plain lint must enforce the inherited no-console error + +**Exit code:** 1 + +``` + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file vite.config.ts` + +``` +import oxlintConfig1 from "./lint-base.js"; +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + lint: { + "extends": [oxlintConfig1], + "options": {}, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }], + "rules": { + "vite-plus/prefer-vite-plus-imports": "error" + } + }, +} +``` + +## `vp lint src/index.ts` + +a second migration must preserve the inherited error + +**Exit code:** 1 + +``` + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_imported.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_imported.md new file mode 100644 index 0000000000..80af79fd40 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_imported.md @@ -0,0 +1,95 @@ +# migration_existing_oxc_configs_imported + +## `vpt write-file vite.config.ts 'import fmt from '\''./.oxfmtrc.json'\'' with { type: '\''json'\'' }; +import { readFileSync } from '\''node:fs'\''; +const lint = JSON.parse(readFileSync(new URL('\''./.oxlintrc.json'\'', import.meta.url), '\''utf8'\'')); +export default { fmt, lint }; +'` + + +## `vpt write-file .oxlintrc.json '{"rules":{"no-debugger":"error"}} +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +preserve JSON configs loaded with imports and readFileSync + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vpt print-file .oxlintrc.json` + +``` +{"rules":{"no-debugger":"error"}} +``` + +## `vp fmt src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp lint src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +retry preserves both imported configs + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vp fmt --check src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_inline_fmt.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_inline_fmt.md new file mode 100644 index 0000000000..88b5781e23 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_inline_fmt.md @@ -0,0 +1,84 @@ +# migration_existing_oxc_configs_inline_fmt + +## `vpt write-file vite.config.ts 'export default { fmt: { singleQuote: false, semi: false } }; +'` + + +## `vp fmt src/index.ts` + +the existing inline fmt config takes precedence over the standalone config + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = "preserved" +``` + +## `vpt write-file src/index.ts 'export const message = "preserved"; +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +keep the standalone config when fmt is already configured + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file vite.config.ts` + +``` +export default { fmt: { singleQuote: false, semi: false } }; +``` + +## `vpt stat-file .oxfmtrc.json --assert file` + +``` +.oxfmtrc.json: file +``` + +## `vp fmt src/index.ts` + +formatting must still use the existing inline options after migration + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = "preserved" +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +retrying the completed migration should be a no-op + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file vite.config.ts` + +``` +export default { fmt: { singleQuote: false, semi: false } }; +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_script.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_script.md new file mode 100644 index 0000000000..716dae4e09 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_script.md @@ -0,0 +1,46 @@ +# migration_existing_oxc_configs_script + +## `vpt json-edit package.json scripts.fmt 'vp fmt -c .oxfmtrc.json src/index.ts'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +preserve the config passed explicitly by a package script + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt stat-file .oxfmtrc.json --assert file` + +``` +.oxfmtrc.json: file +``` + +## `vpt print-file vite.config.ts` + +``` +export default {}; +``` + +## `vp run fmt` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt -c .oxfmtrc.json src/index.ts ⊘ cache disabled +Finished in on 1 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = 'preserved' +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md new file mode 100644 index 0000000000..52ee943b1e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md @@ -0,0 +1,155 @@ +# migration_existing_oxc_configs_unreferenced_lint + +## `vpt write-file .oxlintrc.json '{"rules":{"no-console":"error"},"options":{"typeAware":false,"typeCheck":false}} +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +finish both unreferenced Oxc configs in a project that already uses Vite+ + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 2 config updates applied +• Package manager settings configured +``` + +## `vpt stat-file .oxlintrc.json --assert-not file` + +``` +.oxlintrc.json: missing +``` + +## `vpt stat-file .oxfmtrc.json --assert-not file` + +``` +.oxfmtrc.json: missing +``` + +## `vpt print-file vite.config.ts` + +``` +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + lint: { + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + }, + "jsPlugins": [ + { + "name": "vite-plus", + "specifier": "vite-plus/oxlint-plugin" + } + ] + }, +} +``` + +## `vp fmt src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp lint src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +the restored finalization must remain a no-op on retry + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file vite.config.ts` + +``` +export default { + fmt: { + "singleQuote": true, + "semi": false + }, + lint: { + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + }, + "jsPlugins": [ + { + "name": "vite-plus", + "specifier": "vite-plus/oxlint-plugin" + } + ] + }, +} +``` + +## `vp fmt --check src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vpt write-file src/index.ts 'console.log('\''hello'\'') +'` + + +## `vp lint src/index.ts` + +the merged lint rule must apply after the JSON config is removed + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/src/index.ts new file mode 100644 index 0000000000..38b0fa8a2f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/src/index.ts @@ -0,0 +1 @@ +export const message = "preserved"; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/vite.config.ts new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/vite.config.ts @@ -0,0 +1 @@ +export default {}; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/.oxlintrc.json new file mode 100644 index 0000000000..e54ba4dbb0 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/.oxlintrc.json @@ -0,0 +1,4 @@ +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/package.json new file mode 100644 index 0000000000..3bb6286751 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/package.json @@ -0,0 +1,14 @@ +{ + "name": "migration-independent-workspace-oxc-configs", + "private": true, + "type": "module", + "packageManager": "npm@11.8.0", + "workspaces": ["packages/*"], + "scripts": { + "lint": "oxlint -c .oxlintrc.json root.js" + }, + "devDependencies": { + "vite": "^8.3.4", + "oxlint": "^1.88.0" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/.oxlintrc.json new file mode 100644 index 0000000000..d6537e8361 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/.oxlintrc.json @@ -0,0 +1,4 @@ +{ + "rules": { "no-console": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/index.js b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/index.js new file mode 100644 index 0000000000..14c198cadb --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/index.js @@ -0,0 +1 @@ +console.log("test"); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/package.json new file mode 100644 index 0000000000..a070ce87e7 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/package.json @@ -0,0 +1,12 @@ +{ + "name": "app", + "private": true, + "type": "module", + "scripts": { + "lint": "oxlint index.js" + }, + "devDependencies": { + "vite": "^8.3.4", + "oxlint": "^1.88.0" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/vite.config.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/vite.config.mjs new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/packages/app/vite.config.mjs @@ -0,0 +1 @@ +export default {}; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/root.js b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/root.js new file mode 100644 index 0000000000..efeee5db16 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/root.js @@ -0,0 +1 @@ +export const value = 1; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots.toml new file mode 100644 index 0000000000..b96252c6b8 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots.toml @@ -0,0 +1,36 @@ +[[case]] +name = "migration_independent_workspace_oxc_configs" +vp = ["global", "local"] +comment = "A reference to the root config must not prevent merging an independent package config with the same filename." +steps = [ + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", "packages/app/package.json"], + ["vpt", "print-file", "packages/app/vite.config.mjs"], + ["vpt", "stat-file", "packages/app/.oxlintrc.json", "--assert", "missing"], + ["vp", "lint", "-c", ".oxlintrc.json", "root.js"], + { argv = ["vp", "lint", "index.js"], cwd = "packages/app", comment = "plain lint must report no-console as an error", continue-on-failure = true }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], continue-on-failure = true }, + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", "packages/app/vite.config.mjs"], + { argv = ["vp", "lint", "index.js"], cwd = "packages/app", comment = "a second migration must preserve the error and nonzero exit status", continue-on-failure = true }, +] + +[[case]] +name = "migration_referenced_oxc_config_converted_consumer" +vp = ["global", "local"] +comment = "Converting a package's inheritance must not remove JSON still used by the root script." +steps = [ + { argv = ["vpt", "write-file", "packages/app/.oxlintrc.json", '{"extends":["../../.oxlintrc.json"],"rules":{"no-console":"error"}}'], snapshot = false }, + { argv = ["vpt", "write-file", "packages/app/index.js", "debugger;\nconsole.log('test');\n"], snapshot = false }, + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", ".oxlintrc.ts"], + ["vpt", "print-file", "packages/app/vite.config.mjs"], + ["vpt", "stat-file", "packages/app/.oxlintrc.json", "--assert", "missing"], + ["vp", "lint", "-c", ".oxlintrc.json", "root.js"], + { argv = ["vp", "lint", "index.js"], cwd = "packages/app", comment = "plain lint enforces both the inherited and local rules", continue-on-failure = true }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], continue-on-failure = true }, + ["vpt", "stat-file", ".oxlintrc.json", "--assert", "file"], + { argv = ["vp", "lint", "index.js"], cwd = "packages/app", comment = "the conversion and reference protection remain intact on retry", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md new file mode 100644 index 0000000000..fbd407e645 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md @@ -0,0 +1,164 @@ +# migration_independent_workspace_oxc_configs + +A reference to the root config must not prevent merging an independent package config with the same filename. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Migrated . to Vite+ +• Node npm +• 1 config update applied +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} +``` + +## `vpt print-file packages/app/package.json` + +``` +{ + "name": "app", + "private": true, + "type": "module", + "scripts": { + "lint": "vp lint index.js" + }, + "devDependencies": { + "vite": "npm:@voidzero-dev/vite-plus-core@", + "vite-plus": "" + } +} +``` + +## `vpt print-file packages/app/vite.config.mjs` + +``` +export default { + lint: { + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + }, + "jsPlugins": [ + { + "name": "vite-plus", + "specifier": "vite-plus/oxlint-plugin" + } + ] + }, + +} +``` + +## `vpt stat-file packages/app/.oxlintrc.json --assert missing` + +``` +packages/app/.oxlintrc.json: missing +``` + +## `vp lint -c .oxlintrc.json root.js` + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd packages/app && vp lint index.js` + +plain lint must report no-console as an error + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:1:1] + 1 │ console.log("test"); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} +``` + +## `vpt print-file packages/app/vite.config.mjs` + +``` +export default { + lint: { + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + }, + "jsPlugins": [ + { + "name": "vite-plus", + "specifier": "vite-plus/oxlint-plugin" + } + ] + }, + +} +``` + +## `cd packages/app && vp lint index.js` + +a second migration must preserve the error and nonzero exit status + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:1:1] + 1 │ console.log("test"); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md new file mode 100644 index 0000000000..7bf8a2c928 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md @@ -0,0 +1,158 @@ +# migration_independent_workspace_oxc_configs + +A reference to the root config must not prevent merging an independent package config with the same filename. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Migrated . to Vite+ +• Node npm +• 1 config update applied +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} +``` + +## `vpt print-file packages/app/package.json` + +``` +{ + "name": "app", + "private": true, + "type": "module", + "scripts": { + "lint": "vp lint index.js" + }, + "devDependencies": { + "vite": "npm:@voidzero-dev/vite-plus-core@", + "vite-plus": "" + } +} +``` + +## `vpt print-file packages/app/vite.config.mjs` + +``` +export default { + lint: { + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + }, + "jsPlugins": [ + { + "name": "vite-plus", + "specifier": "vite-plus/oxlint-plugin" + } + ] + }, + +} +``` + +## `vpt stat-file packages/app/.oxlintrc.json --assert missing` + +``` +packages/app/.oxlintrc.json: missing +``` + +## `vp lint -c .oxlintrc.json root.js` + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd packages/app && vp lint index.js` + +plain lint must report no-console as an error + +**Exit code:** 1 + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:1:1] + 1 │ console.log("test"); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} +``` + +## `vpt print-file packages/app/vite.config.mjs` + +``` +export default { + lint: { + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + }, + "jsPlugins": [ + { + "name": "vite-plus", + "specifier": "vite-plus/oxlint-plugin" + } + ] + }, + +} +``` + +## `cd packages/app && vp lint index.js` + +a second migration must preserve the error and nonzero exit status + +**Exit code:** 1 + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:1:1] + 1 │ console.log("test"); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md new file mode 100644 index 0000000000..0070645e89 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md @@ -0,0 +1,156 @@ +# migration_referenced_oxc_config_converted_consumer + +Converting a package's inheritance must not remove JSON still used by the root script. + +## `vpt write-file packages/app/.oxlintrc.json '{"extends":["../../.oxlintrc.json"],"rules":{"no-console":"error"}}'` + + +## `vpt write-file packages/app/index.js 'debugger; +console.log('\''test'\''); +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Migrated . to Vite+ +• Node npm +• 1 config update applied +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} +``` + +## `vpt print-file .oxlintrc.ts` + +``` +import type { OxlintConfig } from "vite-plus/lint"; + +export default { + "rules": { + "no-debugger": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} satisfies OxlintConfig; +``` + +## `vpt print-file packages/app/vite.config.mjs` + +``` +import oxlintConfig1 from "../../.oxlintrc.ts"; +export default { + lint: { + "extends": [oxlintConfig1], + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": {}, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }] + }, + +} +``` + +## `vpt stat-file packages/app/.oxlintrc.json --assert missing` + +``` +packages/app/.oxlintrc.json: missing +``` + +## `vp lint -c .oxlintrc.json root.js` + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd packages/app && vp lint index.js` + +plain lint enforces both the inherited and local rules + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[index.js:1:1] + 1 │ debugger; + · ───────── + 2 │ console.log('test'); + ╰──── + help: Remove the debugger statement + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:2:1] + 1 │ debugger; + 2 │ console.log('test'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 2 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt stat-file .oxlintrc.json --assert file` + +``` +.oxlintrc.json: file +``` + +## `cd packages/app && vp lint index.js` + +the conversion and reference protection remain intact on retry + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[index.js:1:1] + 1 │ debugger; + · ───────── + 2 │ console.log('test'); + ╰──── + help: Remove the debugger statement + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:2:1] + 1 │ debugger; + 2 │ console.log('test'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 2 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md new file mode 100644 index 0000000000..b9adc6a9cc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md @@ -0,0 +1,150 @@ +# migration_referenced_oxc_config_converted_consumer + +Converting a package's inheritance must not remove JSON still used by the root script. + +## `vpt write-file packages/app/.oxlintrc.json '{"extends":["../../.oxlintrc.json"],"rules":{"no-console":"error"}}'` + + +## `vpt write-file packages/app/index.js 'debugger; +console.log('\''test'\''); +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Migrated . to Vite+ +• Node npm +• 1 config update applied +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { "no-debugger": "error" }, + "options": { "typeAware": false, "typeCheck": false } +} +``` + +## `vpt print-file .oxlintrc.ts` + +``` +import type { OxlintConfig } from "vite-plus/lint"; + +export default { + "rules": { + "no-debugger": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} satisfies OxlintConfig; +``` + +## `vpt print-file packages/app/vite.config.mjs` + +``` +import oxlintConfig1 from "../../.oxlintrc.ts"; +export default { + lint: { + "extends": [oxlintConfig1], + "rules": { + "no-console": "error", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": {}, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }] + }, + +} +``` + +## `vpt stat-file packages/app/.oxlintrc.json --assert missing` + +``` +packages/app/.oxlintrc.json: missing +``` + +## `vp lint -c .oxlintrc.json root.js` + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd packages/app && vp lint index.js` + +plain lint enforces both the inherited and local rules + +**Exit code:** 1 + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[index.js:1:1] + 1 │ debugger; + · ───────── + 2 │ console.log('test'); + ╰──── + help: Remove the debugger statement + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:2:1] + 1 │ debugger; + 2 │ console.log('test'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 2 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt stat-file .oxlintrc.json --assert file` + +``` +.oxlintrc.json: file +``` + +## `cd packages/app && vp lint index.js` + +the conversion and reference protection remain intact on retry + +**Exit code:** 1 + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[index.js:1:1] + 1 │ debugger; + · ───────── + 2 │ console.log('test'); + ╰──── + help: Remove the debugger statement + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:2:1] + 1 │ debugger; + 2 │ console.log('test'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 2 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/vite.config.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/vite.config.mjs new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/vite.config.mjs @@ -0,0 +1 @@ +export default {}; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxfmtrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxfmtrc.json new file mode 100644 index 0000000000..fd496a820e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxlintrc.json new file mode 100644 index 0000000000..3a438765d5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/lint.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/lint.json new file mode 100644 index 0000000000..d4ac61eb93 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/lint.json @@ -0,0 +1,3 @@ +{ + "extends": ["../.oxlintrc.json"] +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/shared.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/shared.ts new file mode 100644 index 0000000000..33d9f7c0fd --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/shared.ts @@ -0,0 +1,4 @@ +import fmt from '../.oxfmtrc.json' with { type: 'json' }; +import lint from '../.oxlintrc.json' with { type: 'json' }; + +export default { fmt, lint }; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/unused.cjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/unused.cjs new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/config/unused.cjs @@ -0,0 +1 @@ +module.exports = {}; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/package.json new file mode 100644 index 0000000000..fd4957a38c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/package.json @@ -0,0 +1,9 @@ +{ + "name": "migration-indirect-oxc-configs", + "devDependencies": { + "vite-plus": "latest" + }, + "scripts": { + "lint:custom": "vp lint --config config/lint.json src" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots.toml new file mode 100644 index 0000000000..be11ffdecc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots.toml @@ -0,0 +1,77 @@ +[[case]] +name = "migration_custom_lint_config_root" +vp = "global" +steps = [ + ["vp", "run", "lint:custom"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", "config/lint.json"], + { argv = ["vpt", "stat-file", ".oxfmtrc.json", "--assert-not", "file"], comment = "the unrelated formatter config still merges" }, + { argv = ["vp", "run", "lint:custom"], comment = "the custom entrypoint must still load its inherited JSON config" }, + ["vp", "fmt", "--check", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vp", "run", "lint:custom"], + { argv = ["vpt", "write-file", "src/index.js", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "run", "lint:custom"], comment = "the inherited no-console rule remains active after two migrations", continue-on-failure = true }, +] + +[[case]] +name = "migration_shared_vite_config_module" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "package.json", "{\"name\":\"migration-shared-vite-config\",\"devDependencies\":{\"vite-plus\":\"latest\"}}\n"], snapshot = false }, + { argv = ["vpt", "write-file", "vite.config.ts", "import shared from './config/shared.js';\nexport default { ...shared };\n"], snapshot = false }, + ["vp", "fmt", "--check", "src/index.js"], + ["vp", "lint", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", "vite.config.ts"], + { argv = ["vp", "fmt", "--check", "src/index.js"], comment = "the shared TypeScript module must still import its JSON configs" }, + ["vp", "lint", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vp", "fmt", "--check", "src/index.js"], + { argv = ["vpt", "write-file", "src/index.js", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "lint", "src/index.js"], comment = "the indirectly imported lint rule remains active after two migrations", continue-on-failure = true }, +] + +[[case]] +name = "migration_package_import_alias" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "package.json", "{\"name\":\"migration-shared-vite-config\",\"type\":\"module\",\"imports\":{\"#shared\":{\"import\":\"./config/shared.ts\",\"require\":\"./config/unused.cjs\"}},\"devDependencies\":{\"vite-plus\":\"latest\"}}\n"], snapshot = false }, + { argv = ["vpt", "write-file", "vite.config.ts", "import shared from '#shared';\nexport default { ...shared };\n"], snapshot = false }, + ["vp", "fmt", "--check", "src/index.js"], + ["vp", "lint", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", "vite.config.ts"], + { argv = ["vp", "fmt", "--check", "src/index.js"], comment = "the package import alias must still load its JSON configs" }, + ["vp", "lint", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vp", "fmt", "--check", "src/index.js"], + { argv = ["vpt", "write-file", "src/index.js", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "lint", "src/index.js"], comment = "the indirectly imported lint rule remains active after two migrations", continue-on-failure = true }, +] + +[[case]] +name = "migration_directory_package_import" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "package.json", "{\"name\":\"migration-shared-vite-config\",\"devDependencies\":{\"vite-plus\":\"latest\"}}\n"], snapshot = false }, + { argv = ["vpt", "write-file", "vite.config.ts", "import shared from './config';\nexport default { ...shared };\n"], snapshot = false }, + { argv = ["vpt", "write-file", "config/package.json", "{\"type\":\"module\",\"main\":\"./shared.ts\"}\n"], snapshot = false }, + ["vp", "fmt", "--check", "src/index.js"], + ["vp", "lint", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", "vite.config.ts"], + { argv = ["vp", "fmt", "--check", "src/index.js"], comment = "the directory package entry must still load its JSON configs" }, + ["vp", "lint", "src/index.js"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vp", "fmt", "--check", "src/index.js"], + { argv = ["vpt", "write-file", "src/index.js", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "lint", "src/index.js"], comment = "the indirectly imported lint rule remains active after two migrations", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_custom_lint_config_root.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_custom_lint_config_root.md new file mode 100644 index 0000000000..1c916ff393 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_custom_lint_config_root.md @@ -0,0 +1,123 @@ +# migration_custom_lint_config_root + +## `vp run lint:custom` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config config/lint.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 1 config update applied +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file config/lint.json` + +``` +{ + "extends": ["../.oxlintrc.json"] +} +``` + +## `vpt stat-file .oxfmtrc.json --assert-not file` + +the unrelated formatter config still merges + +``` +.oxfmtrc.json: missing +``` + +## `vp run lint:custom` + +the custom entrypoint must still load its inherited JSON config + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config config/lint.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +This project is already using Vite+! Happy coding! +``` + +## `vp run lint:custom` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config config/lint.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vpt write-file src/index.js 'console.log('\''hello'\'') +'` + + +## `vp run lint:custom` + +the inherited no-console rule remains active after two migrations + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config config/lint.json src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.js:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_directory_package_import.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_directory_package_import.md new file mode 100644 index 0000000000..4a00043732 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_directory_package_import.md @@ -0,0 +1,143 @@ +# migration_directory_package_import + +## `vpt write-file package.json '{"name":"migration-shared-vite-config","devDependencies":{"vite-plus":"latest"}} +'` + + +## `vpt write-file vite.config.ts 'import shared from '\''./config'\''; +export default { ...shared }; +'` + + +## `vpt write-file config/package.json '{"type":"module","main":"./shared.ts"} +'` + + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file vite.config.ts` + +``` +import shared from './config'; +export default { ...shared }; +``` + +## `vp fmt --check src/index.js` + +the directory package entry must still load its JSON configs + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +This project is already using Vite+! Happy coding! +``` + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vpt write-file src/index.js 'console.log('\''hello'\'') +'` + + +## `vp lint src/index.js` + +the indirectly imported lint rule remains active after two migrations + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.js:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_package_import_alias.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_package_import_alias.md new file mode 100644 index 0000000000..b9aaf596a5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_package_import_alias.md @@ -0,0 +1,139 @@ +# migration_package_import_alias + +## `vpt write-file package.json '{"name":"migration-shared-vite-config","type":"module","imports":{"#shared":{"import":"./config/shared.ts","require":"./config/unused.cjs"}},"devDependencies":{"vite-plus":"latest"}} +'` + + +## `vpt write-file vite.config.ts 'import shared from '\''#shared'\''; +export default { ...shared }; +'` + + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file vite.config.ts` + +``` +import shared from '#shared'; +export default { ...shared }; +``` + +## `vp fmt --check src/index.js` + +the package import alias must still load its JSON configs + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +This project is already using Vite+! Happy coding! +``` + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vpt write-file src/index.js 'console.log('\''hello'\'') +'` + + +## `vp lint src/index.js` + +the indirectly imported lint rule remains active after two migrations + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.js:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_shared_vite_config_module.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_shared_vite_config_module.md new file mode 100644 index 0000000000..52e0de1dfe --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/snapshots/migration_shared_vite_config_module.md @@ -0,0 +1,139 @@ +# migration_shared_vite_config_module + +## `vpt write-file package.json '{"name":"migration-shared-vite-config","devDependencies":{"vite-plus":"latest"}} +'` + + +## `vpt write-file vite.config.ts 'import shared from '\''./config/shared.js'\''; +export default { ...shared }; +'` + + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file vite.config.ts` + +``` +import shared from './config/shared.js'; +export default { ...shared }; +``` + +## `vp fmt --check src/index.js` + +the shared TypeScript module must still import its JSON configs + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp lint src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +This project is already using Vite+! Happy coding! +``` + +## `vp fmt --check src/index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vpt write-file src/index.js 'console.log('\''hello'\'') +'` + + +## `vp lint src/index.js` + +the indirectly imported lint rule remains active after two migrations + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.js:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/src/index.js b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/src/index.js new file mode 100644 index 0000000000..854ef01aa3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/src/index.js @@ -0,0 +1 @@ +export const message = 'preserved' diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/vite.config.ts new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_indirect_oxc_configs/vite.config.ts @@ -0,0 +1 @@ +export default {}; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxfmtrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxfmtrc.json new file mode 100644 index 0000000000..fd496a820e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxlintrc.json new file mode 100644 index 0000000000..3a438765d5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/package.json new file mode 100644 index 0000000000..75aeb4229e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/package.json @@ -0,0 +1,12 @@ +{ + "name": "migration-oxc-ecosystem-delta-comic", + "private": true, + "type": "module", + "packageManager": "pnpm@12.3.4", + "devDependencies": { + "vite-plus": "latest" + }, + "scripts": { + "check": "vp check packages/app/src" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/package.json new file mode 100644 index 0000000000..33f2660adc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/package.json @@ -0,0 +1,5 @@ +{ + "name": "app", + "private": true, + "type": "module" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/src/index.ts new file mode 100644 index 0000000000..38b0fa8a2f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/packages/app/src/index.ts @@ -0,0 +1 @@ +export const message = "preserved"; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/pnpm-workspace.yaml new file mode 100644 index 0000000000..924b55f42e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - packages/* diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots.toml new file mode 100644 index 0000000000..051946a49b --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots.toml @@ -0,0 +1,16 @@ +[[case]] +name = "migration_oxc_ecosystem_delta_comic" +vp = "global" +comment = "Reproduces the typed JSON imports in https://github.com/vite-plus-ecosystem-ci/delta-comic/pull/9." +steps = [ + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vp", "fmt", "packages/app/src"], + ["vpt", "print-file", "packages/app/src/index.ts"], + ["vp", "run", "check"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "repeat migration without invalidating either JSON import" }, + ["vp", "run", "check"], + { argv = ["vpt", "write-file", "packages/app/src/index.ts", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "run", "check"], comment = "vp check must still apply the imported lint rule", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots/migration_oxc_ecosystem_delta_comic.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots/migration_oxc_ecosystem_delta_comic.md new file mode 100644 index 0000000000..9cf7709794 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/snapshots/migration_oxc_ecosystem_delta_comic.md @@ -0,0 +1,115 @@ +# migration_oxc_ecosystem_delta_comic + +Reproduces the typed JSON imports in https://github.com/vite-plus-ecosystem-ci/delta-comic/pull/9. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vp fmt packages/app/src` + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file packages/app/src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp run check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp check packages/app/src ⊘ cache disabled +VITE+ - The Unified Toolchain for the Web + +pass: All 1 file are correctly formatted (, threads) +pass: Found no warnings or lint errors in 1 file (, threads) +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +repeat migration without invalidating either JSON import + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vp run check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp check packages/app/src ⊘ cache disabled +VITE+ - The Unified Toolchain for the Web + +pass: All 1 file are correctly formatted (, threads) +pass: Found no warnings or lint errors in 1 file (, threads) +``` + +## `vpt write-file packages/app/src/index.ts 'console.log('\''hello'\'') +'` + + +## `vp run check` + +vp check must still apply the imported lint rule + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp check packages/app/src ⊘ cache disabled +VITE+ - The Unified Toolchain for the Web + +pass: All 1 file are correctly formatted (, threads) +error: Lint issues found +× eslint(no-console): Unexpected console statement. + ╭─[packages/app/src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 1 error and 0 warnings in 1 file (, threads) +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/vite.config.ts new file mode 100644 index 0000000000..c2e9c9eec2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_delta_comic/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite-plus'; +import type { OxfmtConfig } from 'vite-plus/fmt'; +import type { OxlintConfig } from 'vite-plus/lint'; + +import fmt from './.oxfmtrc.json' with { type: 'json' }; +import lint from './.oxlintrc.json' with { type: 'json' }; + +export default defineConfig({ + fmt: fmt as OxfmtConfig, + lint: lint as OxlintConfig, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxfmtrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxfmtrc.json new file mode 100644 index 0000000000..fd496a820e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxlintrc.json new file mode 100644 index 0000000000..3a438765d5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/package.json new file mode 100644 index 0000000000..b8383e58a3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/package.json @@ -0,0 +1,14 @@ +{ + "name": "migration-oxc-ecosystem-freecut", + "private": true, + "type": "module", + "packageManager": "npm@11.16.0", + "devDependencies": { + "vite-plus": "latest" + }, + "scripts": { + "lint": "vp lint src", + "format": "vp fmt src .oxlintrc.json .oxfmtrc.json", + "format:check": "vp fmt src .oxlintrc.json .oxfmtrc.json --check" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots.toml new file mode 100644 index 0000000000..b4611bbb48 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots.toml @@ -0,0 +1,17 @@ +[[case]] +name = "migration_oxc_ecosystem_freecut" +vp = "global" +comment = "Reproduces the config file reads and script inputs in https://github.com/vite-plus-ecosystem-ci/freecut/pull/9." +steps = [ + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vp", "run", "format"], + ["vpt", "print-file", "src/index.ts"], + ["vp", "run", "lint"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "repeat migration without deleting either file read by vite.config.ts" }, + ["vp", "run", "format:check"], + ["vp", "run", "lint"], + { argv = ["vpt", "write-file", "src/index.ts", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "run", "lint"], comment = "the rules read from the retained JSON must still apply", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots/migration_oxc_ecosystem_freecut.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots/migration_oxc_ecosystem_freecut.md new file mode 100644 index 0000000000..77b1cbed9f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/snapshots/migration_oxc_ecosystem_freecut.md @@ -0,0 +1,122 @@ +# migration_oxc_ecosystem_freecut + +Reproduces the config file reads and script inputs in https://github.com/vite-plus-ecosystem-ci/freecut/pull/9. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node npm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vp run format` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt src .oxlintrc.json .oxfmtrc.json ⊘ cache disabled +Finished in on 3 files using threads. +``` + +## `vpt print-file src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp run lint` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +repeat migration without deleting either file read by vite.config.ts + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vp run format:check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt src .oxlintrc.json .oxfmtrc.json --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 3 files using threads. +``` + +## `vp run lint` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vpt write-file src/index.ts 'console.log('\''hello'\'') +'` + + +## `vp run lint` + +the rules read from the retained JSON must still apply + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/src/index.ts new file mode 100644 index 0000000000..38b0fa8a2f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/src/index.ts @@ -0,0 +1 @@ +export const message = "preserved"; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/vite.config.ts new file mode 100644 index 0000000000..4473993950 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_freecut/vite.config.ts @@ -0,0 +1,16 @@ +import { readFileSync } from 'node:fs'; +import { defineConfig } from 'vite-plus'; + +const oxlintConfig = JSON.parse(readFileSync(new URL('./.oxlintrc.json', import.meta.url), 'utf8')); +const oxfmtConfig = JSON.parse(readFileSync(new URL('./.oxfmtrc.json', import.meta.url), 'utf8')); + +export default defineConfig({ + lint: { + ...oxlintConfig, + ignorePatterns: ['dist/**'], + }, + fmt: { + ...oxfmtConfig, + ignorePatterns: ['dist/**'], + }, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxfmtrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxfmtrc.json new file mode 100644 index 0000000000..fd496a820e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxlintrc.json new file mode 100644 index 0000000000..3a438765d5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/package.json new file mode 100644 index 0000000000..be73e816c3 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/package.json @@ -0,0 +1,18 @@ +{ + "name": "migration-oxc-ecosystem-pdfme", + "private": true, + "type": "module", + "packageManager": "npm@11.16.0", + "workspaces": [ + "packages/*" + ], + "devDependencies": { + "vite-plus": "latest" + }, + "scripts": { + "lint": "vp run --filter '@pdfme/*' lint", + "lint:root": "vp lint --config .oxlintrc.json packages/common/src", + "fmt": "vp run --filter '@pdfme/*' fmt", + "fmt:check": "vp fmt -c .oxfmtrc.json packages/common/src --check" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/package.json new file mode 100644 index 0000000000..ccc47b9cb6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/package.json @@ -0,0 +1,9 @@ +{ + "name": "@pdfme/common", + "private": true, + "type": "module", + "scripts": { + "lint": "vp lint -c ../../.oxlintrc.json src", + "fmt": "vp fmt -c ../../.oxfmtrc.json src --write" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/src/index.ts new file mode 100644 index 0000000000..38b0fa8a2f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/packages/common/src/index.ts @@ -0,0 +1 @@ +export const message = "preserved"; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/.oxlintrc.json new file mode 100644 index 0000000000..4c4b01a512 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/.oxlintrc.json @@ -0,0 +1,11 @@ +{ + "extends": ["../.oxlintrc.json"], + "overrides": [ + { + "files": ["e2e/**/*.ts"], + "rules": { + "no-console": "off" + } + } + ] +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/package.json new file mode 100644 index 0000000000..b25713e9bc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/package.json @@ -0,0 +1,5 @@ +{ + "name": "playground", + "private": true, + "type": "module" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/src/index.ts new file mode 100644 index 0000000000..d6e8d0722e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/playground/src/index.ts @@ -0,0 +1 @@ +export const message = 'playground'; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots.toml new file mode 100644 index 0000000000..efa53dbe35 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots.toml @@ -0,0 +1,45 @@ +[[case]] +name = "migration_oxc_ecosystem_pdfme" +vp = "global" +comment = "Reproduces the root and workspace config flags and playground inheritance in https://github.com/vite-plus-ecosystem-ci/pdfme/pull/7." +steps = [ + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vpt", "print-file", "playground/.oxlintrc.json"], + ["vp", "run", "fmt"], + ["vpt", "print-file", "packages/common/src/index.ts"], + ["vp", "run", "fmt:check"], + ["vp", "run", "lint"], + ["vp", "run", "lint:root"], + { argv = ["vp", "lint", "-c", ".oxlintrc.json", "src"], cwd = "playground" }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "repeat migration and rerun scripts from both the root and a workspace" }, + ["vp", "run", "fmt:check"], + { argv = ["vp", "run", "fmt"], cwd = "packages/common" }, + { argv = ["vp", "run", "lint"], cwd = "packages/common" }, + { argv = ["vpt", "write-file", "packages/common/src/index.ts", "console.log('hello')\n"], snapshot = false }, + { argv = ["vpt", "write-file", "playground/src/index.ts", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "run", "lint"], comment = "the workspace script must still use the root JSON rules", continue-on-failure = true }, + { argv = ["vp", "lint", "-c", ".oxlintrc.json", "src"], cwd = "playground", comment = "the playground must still inherit the root JSON rules", continue-on-failure = true }, +] + +[[case]] +name = "migration_oxc_ecosystem_pdfme_inline_config" +vp = ["local", "global"] +comment = "Explicit config paths in pdfme-style scripts must also work when inline lint and fmt options exist." +steps = [ + { argv = ["vpt", "write-file", "vite.config.mts", "export default { lint: { rules: { 'no-console': 'off' } }, fmt: { singleQuote: false, semi: true } };\n"], snapshot = false }, + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "print-file", ".oxfmtrc.json"], + ["vp", "run", "fmt"], + ["vpt", "print-file", "packages/common/src/index.ts"], + ["vp", "run", "fmt:check"], + ["vp", "run", "lint"], + ["vp", "run", "lint:root"], + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vp", "run", "fmt:check"], + { argv = ["vpt", "write-file", "packages/common/src/index.ts", "console.log('hello')\n"], snapshot = false }, + { argv = ["vp", "run", "lint"], comment = "the explicit JSON rule must override the inline no-console setting", continue-on-failure = true }, + { argv = ["vp", "run", "lint:root"], comment = "the root --config script must also use the explicit JSON rule", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme.md new file mode 100644 index 0000000000..af300fde79 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme.md @@ -0,0 +1,202 @@ +# migration_oxc_ecosystem_pdfme + +Reproduces the root and workspace config flags and playground inheritance in https://github.com/vite-plus-ecosystem-ci/pdfme/pull/7. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node npm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vpt print-file playground/.oxlintrc.json` + +``` +{ + "extends": ["../.oxlintrc.json"], + "overrides": [ + { + "files": ["e2e/**/*.ts"], + "rules": { + "no-console": "off" + } + } + ] +} +``` + +## `vp run fmt` + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp fmt -c ../../.oxfmtrc.json src --write ⊘ cache disabled +Finished in on 1 files using threads. +``` + +## `vpt print-file packages/common/src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp run fmt:check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt -c .oxfmtrc.json packages/common/src --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp run lint` + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:root` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config .oxlintrc.json packages/common/src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd playground && vp lint -c .oxlintrc.json src` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +repeat migration and rerun scripts from both the root and a workspace + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vp run fmt:check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt -c .oxfmtrc.json packages/common/src --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `cd packages/common && vp run fmt` + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp fmt -c ../../.oxfmtrc.json src --write ⊘ cache disabled +Finished in on 1 files using threads. +``` + +## `cd packages/common && vp run lint` + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vpt write-file packages/common/src/index.ts 'console.log('\''hello'\'') +'` + + +## `vpt write-file playground/src/index.ts 'console.log('\''hello'\'') +'` + + +## `vp run lint` + +the workspace script must still use the root JSON rules + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `cd playground && vp lint -c .oxlintrc.json src` + +the playground must still inherit the root JSON rules + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.global.md new file mode 100644 index 0000000000..7766dd70b6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.global.md @@ -0,0 +1,158 @@ +# migration_oxc_ecosystem_pdfme_inline_config + +Explicit config paths in pdfme-style scripts must also work when inline lint and fmt options exist. + +## `vpt write-file vite.config.mts 'export default { lint: { rules: { '\''no-console'\'': '\''off'\'' } }, fmt: { singleQuote: false, semi: true } }; +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node npm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vp run fmt` + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp fmt -c ../../.oxfmtrc.json src --write ⊘ cache disabled +Finished in on 1 files using threads. +``` + +## `vpt print-file packages/common/src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp run fmt:check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt -c .oxfmtrc.json packages/common/src --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp run lint` + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:root` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config .oxlintrc.json packages/common/src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vp run fmt:check` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp fmt -c .oxfmtrc.json packages/common/src --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vpt write-file packages/common/src/index.ts 'console.log('\''hello'\'') +'` + + +## `vp run lint` + +the explicit JSON rule must override the inline no-console setting + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:root` + +the root --config script must also use the explicit JSON rule + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config .oxlintrc.json packages/common/src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[packages/common/src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.local.md new file mode 100644 index 0000000000..e34ebfe7ef --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/snapshots/migration_oxc_ecosystem_pdfme_inline_config.local.md @@ -0,0 +1,144 @@ +# migration_oxc_ecosystem_pdfme_inline_config + +Explicit config paths in pdfme-style scripts must also work when inline lint and fmt options exist. + +## `vpt write-file vite.config.mts 'export default { lint: { rules: { '\''no-console'\'': '\''off'\'' } }, fmt: { singleQuote: false, semi: true } }; +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node npm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + }, + "options": { + "typeAware": false, + "typeCheck": false + } +} +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true, + "semi": false +} +``` + +## `vp run fmt` + +``` +~/packages/common$ vp fmt -c ../../.oxfmtrc.json src --write ⊘ cache disabled +Finished in on 1 files using threads. +``` + +## `vpt print-file packages/common/src/index.ts` + +``` +export const message = 'preserved' +``` + +## `vp run fmt:check` + +``` +$ vp fmt -c .oxfmtrc.json packages/common/src --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp run lint` + +``` +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:root` + +``` +$ vp lint --config .oxlintrc.json packages/common/src ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vp run fmt:check` + +``` +$ vp fmt -c .oxfmtrc.json packages/common/src --check ⊘ cache disabled +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vpt write-file packages/common/src/index.ts 'console.log('\''hello'\'') +'` + + +## `vp run lint` + +the explicit JSON rule must override the inline no-console setting + +**Exit code:** 1 + +``` +~/packages/common$ vp lint -c ../../.oxlintrc.json src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:root` + +the root --config script must also use the explicit JSON rule + +**Exit code:** 1 + +``` +$ vp lint --config .oxlintrc.json packages/common/src ⊘ cache disabled + + × eslint(no-console): Unexpected console statement. + ╭─[packages/common/src/index.ts:1:1] + 1 │ console.log('hello') + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/vite.config.mts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/vite.config.mts new file mode 100644 index 0000000000..c62e7a59fe --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_pdfme/vite.config.mts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + run: { + enablePrePostScripts: true, + }, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/.oxlintrc.json new file mode 100644 index 0000000000..2ff50f91ec --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/.oxlintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-console": "error" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/.oxlintrc.json new file mode 100644 index 0000000000..8e2ab53210 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/.oxlintrc.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../.oxlintrc.json"], + "rules": { + "no-alert": "off" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/package.json new file mode 100644 index 0000000000..701939fcf2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/package.json @@ -0,0 +1,5 @@ +{ + "name": "custom-domain", + "private": true, + "type": "module" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/src/index.ts new file mode 100644 index 0000000000..42838525f1 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/custom-domain/src/index.ts @@ -0,0 +1 @@ +export const message = 'custom domain'; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/.oxlintrc.json new file mode 100644 index 0000000000..8e2ab53210 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/.oxlintrc.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../.oxlintrc.json"], + "rules": { + "no-alert": "off" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/package.json new file mode 100644 index 0000000000..7e69add5a4 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/package.json @@ -0,0 +1,5 @@ +{ + "name": "sigle", + "private": true, + "type": "module" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/src/index.ts new file mode 100644 index 0000000000..1e96b75ca7 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/src/index.ts @@ -0,0 +1 @@ +export const message = 'sigle'; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/vite.config.ts new file mode 100644 index 0000000000..8abe24a2a4 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/apps/sigle/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + resolve: { + tsconfigPaths: true, + }, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/package.json new file mode 100644 index 0000000000..9d4e608c4c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/package.json @@ -0,0 +1,9 @@ +{ + "name": "migration-oxc-ecosystem-sigle", + "private": true, + "type": "module", + "packageManager": "pnpm@12.3.4", + "devDependencies": { + "vite-plus": "latest" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/pnpm-workspace.yaml new file mode 100644 index 0000000000..852bf6b8f9 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - apps/* diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots.toml new file mode 100644 index 0000000000..54eed080fd --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots.toml @@ -0,0 +1,21 @@ +[[case]] +name = "migration_oxc_ecosystem_sigle" +vp = "global" +comment = "Reproduces the two app-level JSON extends configs in https://github.com/vite-plus-ecosystem-ci/sigle/pull/8, with a shared base to verify inherited rules." +steps = [ + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", ".oxlintrc.ts"], + ["vpt", "stat-file", "apps/sigle/.oxlintrc.json", "--assert", "missing"], + ["vpt", "stat-file", "apps/custom-domain/.oxlintrc.json", "--assert", "missing"], + { argv = ["vpt", "print-file", "apps/sigle/vite.config.ts"], comment = "the existing Vite config must import the shared TypeScript rules" }, + { argv = ["vpt", "print-file", "apps/custom-domain/vite.config.ts"], comment = "create a Vite config for the migrated inherited rules" }, + { argv = ["vp", "lint", "src"], cwd = "apps/sigle" }, + { argv = ["vp", "lint", "src"], cwd = "apps/custom-domain" }, + ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], + ["vpt", "print-file", "apps/sigle/vite.config.ts"], + ["vpt", "print-file", "apps/custom-domain/vite.config.ts"], + { argv = ["vpt", "write-file", "apps/sigle/src/index.ts", "console.log('hello');\n"], snapshot = false }, + { argv = ["vpt", "write-file", "apps/custom-domain/src/index.ts", "console.log('hello');\n"], snapshot = false }, + { argv = ["vp", "lint", "src"], cwd = "apps/sigle", comment = "the app must still apply its inherited rule without -c", continue-on-failure = true }, + { argv = ["vp", "lint", "src"], cwd = "apps/custom-domain", comment = "the app without vite.config.ts must also apply its inherited rule without -c", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md new file mode 100644 index 0000000000..fd4804acbc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md @@ -0,0 +1,215 @@ +# migration_oxc_ecosystem_sigle + +Reproduces the two app-level JSON extends configs in https://github.com/vite-plus-ecosystem-ci/sigle/pull/8, with a shared base to verify inherited rules. + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 3 config updates applied +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.ts` + +``` +import type { OxlintConfig } from "vite-plus/lint"; + +export default { + "rules": { + "no-console": "error" + } +} satisfies OxlintConfig; +``` + +## `vpt stat-file apps/sigle/.oxlintrc.json --assert missing` + +``` +apps/sigle/.oxlintrc.json: missing +``` + +## `vpt stat-file apps/custom-domain/.oxlintrc.json --assert missing` + +``` +apps/custom-domain/.oxlintrc.json: missing +``` + +## `vpt print-file apps/sigle/vite.config.ts` + +the existing Vite config must import the shared TypeScript rules + +``` +import oxlintConfig1 from "../../.oxlintrc.js"; +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + "extends": [oxlintConfig1], + "rules": { + "no-alert": "off", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": true, + "typeCheck": true + }, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }] + }, + resolve: { + tsconfigPaths: true, + }, +}); +``` + +## `vpt print-file apps/custom-domain/vite.config.ts` + +create a Vite config for the migrated inherited rules + +``` +import oxlintConfig1 from "../../.oxlintrc.js"; +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + "extends": [oxlintConfig1], + "rules": { + "no-alert": "off", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": true, + "typeCheck": true + }, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }] + }, + +}); +``` + +## `cd apps/sigle && vp lint src` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd apps/custom-domain && vp lint src` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `vpt print-file apps/sigle/vite.config.ts` + +``` +import oxlintConfig1 from "../../.oxlintrc.js"; +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + "extends": [oxlintConfig1], + "rules": { + "no-alert": "off", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": true, + "typeCheck": true + }, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }] + }, + resolve: { + tsconfigPaths: true, + }, +}); +``` + +## `vpt print-file apps/custom-domain/vite.config.ts` + +``` +import oxlintConfig1 from "../../.oxlintrc.js"; +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + "extends": [oxlintConfig1], + "rules": { + "no-alert": "off", + "vite-plus/prefer-vite-plus-imports": "error" + }, + "options": { + "typeAware": true, + "typeCheck": true + }, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }] + }, + +}); +``` + +## `vpt write-file apps/sigle/src/index.ts 'console.log('\''hello'\''); +'` + + +## `vpt write-file apps/custom-domain/src/index.ts 'console.log('\''hello'\''); +'` + + +## `cd apps/sigle && vp lint src` + +the app must still apply its inherited rule without -c + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `cd apps/custom-domain && vp lint src` + +the app without vite.config.ts must also apply its inherited rule without -c + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/vite.config.ts new file mode 100644 index 0000000000..a21ea9b063 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + rules: { + 'no-console': 'off', + }, + }, + fmt: { + singleQuote: true, + }, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots.toml index 6a6506ca09..0e82facdb9 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots.toml @@ -4,6 +4,6 @@ vp = "global" steps = [ { argv = ["vp", "migrate", "--no-interactive"], comment = "should NOT duplicate fmt/lint blocks already in vite.config.ts (regression for vp create fate)", continue-on-failure = true }, { argv = ["vpt", "print-file", "vite.config.ts"], comment = "exactly one fmt: and one lint: block, preserving template values", continue-on-failure = true }, - { argv = ["vpt", "stat-file", ".oxfmtrc.jsonc", "--assert-not", "file"], comment = "redundant standalone file removed", continue-on-failure = true }, - { argv = ["vpt", "stat-file", ".oxlintrc.json", "--assert-not", "file"], comment = "redundant standalone file removed", continue-on-failure = true }, + { argv = ["vpt", "stat-file", ".oxfmtrc.jsonc", "--assert", "file"], comment = "standalone config preserved", continue-on-failure = true }, + { argv = ["vpt", "stat-file", ".oxlintrc.json", "--assert", "file"], comment = "standalone config preserved", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots/migration_preserves_existing_fmt_and_lint.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots/migration_preserves_existing_fmt_and_lint.md index 2061ff6df2..3d358547c4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots/migration_preserves_existing_fmt_and_lint.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_preserves_existing_fmt_and_lint/snapshots/migration_preserves_existing_fmt_and_lint.md @@ -70,18 +70,18 @@ export default defineConfig({ }); ``` -## `vpt stat-file .oxfmtrc.jsonc --assert-not file` +## `vpt stat-file .oxfmtrc.jsonc --assert file` -redundant standalone file removed +standalone config preserved ``` -.oxfmtrc.jsonc: missing +.oxfmtrc.jsonc: file ``` -## `vpt stat-file .oxlintrc.json --assert-not file` +## `vpt stat-file .oxlintrc.json --assert file` -redundant standalone file removed +standalone config preserved ``` -.oxlintrc.json: missing +.oxlintrc.json: file ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxfmtrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxfmtrc.json new file mode 100644 index 0000000000..544138be45 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxlintrc.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxlintrc.json new file mode 100644 index 0000000000..2ff50f91ec --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/.oxlintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-console": "error" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/package.json new file mode 100644 index 0000000000..6f52fa50d1 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/package.json @@ -0,0 +1,9 @@ +{ + "name": "migration-referenced-workspace-oxc-configs", + "private": true, + "type": "module", + "packageManager": "pnpm@12.3.4", + "devDependencies": { + "vite-plus": "latest" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/package.json new file mode 100644 index 0000000000..33f2660adc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/package.json @@ -0,0 +1,5 @@ +{ + "name": "app", + "private": true, + "type": "module" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/src/index.ts new file mode 100644 index 0000000000..38b0fa8a2f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/packages/app/src/index.ts @@ -0,0 +1 @@ +export const message = "preserved"; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/pnpm-workspace.yaml new file mode 100644 index 0000000000..924b55f42e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - packages/* diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots.toml new file mode 100644 index 0000000000..610d01f953 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots.toml @@ -0,0 +1,32 @@ +[[case]] +name = "migration_referenced_workspace_oxc_configs_imported" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "packages/app/vite.config.ts", "import fmt from '../../.oxfmtrc.json' with { type: 'json' };\nimport { readFileSync } from 'node:fs';\nconst lint = JSON.parse(readFileSync(new URL('../../.oxlintrc.json', import.meta.url), 'utf8'));\nexport default { fmt, lint };\n"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "preserve root configs loaded by a workspace Vite config" }, + ["vpt", "print-file", ".oxfmtrc.json"], + ["vpt", "print-file", ".oxlintrc.json"], + { argv = ["vp", "fmt", "src/index.ts"], cwd = "packages/app" }, + ["vpt", "print-file", "packages/app/src/index.ts"], + { argv = ["vp", "lint", "src/index.ts"], cwd = "packages/app" }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "repeated migration keeps the workspace imports usable" }, + { argv = ["vp", "fmt", "--check", "src/index.ts"], cwd = "packages/app" }, + { argv = ["vp", "lint", "src/index.ts"], cwd = "packages/app" }, +] + +[[case]] +name = "migration_referenced_workspace_oxc_configs_extends" +vp = "global" +steps = [ + { argv = ["vpt", "write-file", "packages/app/.oxlintrc.json", "{\"extends\":[\"./config/lint-base.jsonc\"]}\n"], snapshot = false }, + { argv = ["vpt", "write-file", "packages/app/config/lint-base.jsonc", "// Shared lint rules\n{\"extends\":[\"../../../.oxlintrc.json\"]}\n"], snapshot = false }, + { argv = ["vpt", "write-file", "packages/app/src/index.ts", "console.log('hello');\n"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "recover a transitive inheritance chain while retaining shared JSON consumers" }, + ["vpt", "print-file", ".oxlintrc.json"], + ["vpt", "stat-file", "packages/app/.oxlintrc.json", "--assert", "missing"], + ["vpt", "print-file", "packages/app/vite.config.ts"], + ["vpt", "print-file", "packages/app/config/lint-base.ts"], + ["vpt", "print-file", "packages/app/config/lint-base.jsonc"], + { argv = ["vp", "migrate", "--no-interactive", "--no-hooks", "--no-agent", "--no-editor"], comment = "repeated migration keeps inherited rules usable" }, + { argv = ["vp", "lint", "src/index.ts"], cwd = "packages/app", comment = "the inherited no-console rule must still report the console call", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md new file mode 100644 index 0000000000..c5ff696153 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md @@ -0,0 +1,116 @@ +# migration_referenced_workspace_oxc_configs_extends + +## `vpt write-file packages/app/.oxlintrc.json '{"extends":["./config/lint-base.jsonc"]} +'` + + +## `vpt write-file packages/app/config/lint-base.jsonc '// Shared lint rules +{"extends":["../../../.oxlintrc.json"]} +'` + + +## `vpt write-file packages/app/src/index.ts 'console.log('\''hello'\''); +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +recover a transitive inheritance chain while retaining shared JSON consumers + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• 5 config updates applied +• Package manager settings configured +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + } +} +``` + +## `vpt stat-file packages/app/.oxlintrc.json --assert missing` + +``` +packages/app/.oxlintrc.json: missing +``` + +## `vpt print-file packages/app/vite.config.ts` + +``` +import oxlintConfig1 from "./config/lint-base.js"; +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + "extends": [oxlintConfig1], + "options": { + "typeAware": true, + "typeCheck": true + }, + "jsPlugins": [{ "name": "vite-plus", "specifier": "vite-plus/oxlint-plugin" }], + "rules": { + "vite-plus/prefer-vite-plus-imports": "error" + } + }, + +}); +``` + +## `vpt print-file packages/app/config/lint-base.ts` + +``` +import type { OxlintConfig } from "vite-plus/lint"; +import oxlintConfig1 from "../../../.oxlintrc.js"; + +export default { + "extends": [oxlintConfig1] +} satisfies OxlintConfig; +``` + +## `vpt print-file packages/app/config/lint-base.jsonc` + +``` +// Shared lint rules +{"extends":["../../../.oxlintrc.json"]} +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +repeated migration keeps inherited rules usable + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `cd packages/app && vp lint src/index.ts` + +the inherited no-console rule must still report the console call + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[src/index.ts:1:1] + 1 │ console.log('hello'); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_imported.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_imported.md new file mode 100644 index 0000000000..f0c634544e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_imported.md @@ -0,0 +1,94 @@ +# migration_referenced_workspace_oxc_configs_imported + +## `vpt write-file packages/app/vite.config.ts 'import fmt from '\''../../.oxfmtrc.json'\'' with { type: '\''json'\'' }; +import { readFileSync } from '\''node:fs'\''; +const lint = JSON.parse(readFileSync(new URL('\''../../.oxlintrc.json'\'', import.meta.url), '\''utf8'\'')); +export default { fmt, lint }; +'` + + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +preserve root configs loaded by a workspace Vite config + +``` +VITE+ - The Unified Toolchain for the Web + +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite-plus latest → + vite → +• Package manager settings configured +``` + +## `vpt print-file .oxfmtrc.json` + +``` +{ + "singleQuote": true +} +``` + +## `vpt print-file .oxlintrc.json` + +``` +{ + "rules": { + "no-console": "error" + } +} +``` + +## `cd packages/app && vp fmt src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file packages/app/src/index.ts` + +``` +export const message = 'preserved'; +``` + +## `cd packages/app && vp lint src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp migrate --no-interactive --no-hooks --no-agent --no-editor` + +repeated migration keeps the workspace imports usable + +``` +VITE+ - The Unified Toolchain for the Web + +This project is already using Vite+! Happy coding! +``` + +## `cd packages/app && vp fmt --check src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `cd packages/app && vp lint src/index.ts` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-fmt.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-fmt.json new file mode 100644 index 0000000000..544138be45 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-fmt.json @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-lint.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-lint.json new file mode 100644 index 0000000000..d5ba8f9d9c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/custom-lint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-console": "off" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/index.js b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/index.js new file mode 100644 index 0000000000..702f4280ce --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/index.js @@ -0,0 +1 @@ +console.log("hello"); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/package.json new file mode 100644 index 0000000000..bb883e47c4 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/oxc-explicit-config", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "lint:custom": "vp lint --config custom-lint.json index.js" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots.toml new file mode 100644 index 0000000000..3f192e38e4 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots.toml @@ -0,0 +1,31 @@ +[[case]] +name = "lint_explicit_config" +vp = ["local", "global"] +steps = [ + { argv = ["vp", "lint", "index.js"], comment = "use inline lint rules when no config flag is supplied", continue-on-failure = true }, + { argv = ["vp", "lint", "--config", "custom-lint.json", "index.js"], comment = "an explicit config overrides the inline lint rules", continue-on-failure = true }, + ["vp", "lint", "-c", "custom-lint.json", "index.js"], + ["vp", "lint", "--config=custom-lint.json", "index.js"], + ["vp", "lint", "-c=custom-lint.json", "index.js"], + ["vp", "lint", "-ccustom-lint.json", "index.js"], + ["vp", "run", "lint:custom"], + { argv = ["vpt", "write-file", "vite.config.ts", "throw new Error('Vite config must not be loaded');\n"], snapshot = false }, + { argv = ["vp", "lint", "--config", "custom-lint.json", "index.js"], comment = "explicit lint configs do not require a working Vite config" }, +] + +[[case]] +name = "fmt_explicit_config" +vp = ["local", "global"] +steps = [ + { argv = ["vp", "fmt", "--config", "custom-fmt.json", "index.js"], comment = "an explicit config overrides the inline format options" }, + ["vpt", "print-file", "index.js"], + ["vp", "fmt", "--check", "-c", "custom-fmt.json", "index.js"], + ["vp", "fmt", "--check", "--config=custom-fmt.json", "index.js"], + ["vp", "fmt", "--check", "-c=custom-fmt.json", "index.js"], + ["vp", "fmt", "--check", "-ccustom-fmt.json", "index.js"], + { argv = ["vp", "fmt", "index.js"], comment = "use inline format options when no config flag is supplied" }, + ["vpt", "print-file", "index.js"], + { argv = ["vpt", "write-file", "vite.config.ts", "throw new Error('Vite config must not be loaded');\n"], snapshot = false }, + { argv = ["vp", "fmt", "--config", "custom-fmt.json", "index.js"], comment = "explicit format configs do not require a working Vite config" }, + ["vpt", "print-file", "index.js"], +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.global.md new file mode 100644 index 0000000000..81f07874e4 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.global.md @@ -0,0 +1,97 @@ +# fmt_explicit_config + +## `vp fmt --config custom-fmt.json index.js` + +an explicit config overrides the inline format options + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +``` +console.log('hello'); +``` + +## `vp fmt --check -c custom-fmt.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt --check --config=custom-fmt.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt --check -c=custom-fmt.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt --check -ccustom-fmt.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt index.js` + +use inline format options when no config flag is supplied + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +``` +console.log("hello"); +``` + +## `vpt write-file vite.config.ts 'throw new Error('\''Vite config must not be loaded'\''); +'` + + +## `vp fmt --config custom-fmt.json index.js` + +explicit format configs do not require a working Vite config + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +``` +console.log('hello'); +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.local.md new file mode 100644 index 0000000000..8803e19a95 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/fmt_explicit_config.local.md @@ -0,0 +1,83 @@ +# fmt_explicit_config + +## `vp fmt --config custom-fmt.json index.js` + +an explicit config overrides the inline format options + +``` +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +``` +console.log('hello'); +``` + +## `vp fmt --check -c custom-fmt.json index.js` + +``` +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt --check --config=custom-fmt.json index.js` + +``` +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt --check -c=custom-fmt.json index.js` + +``` +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt --check -ccustom-fmt.json index.js` + +``` +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp fmt index.js` + +use inline format options when no config flag is supplied + +``` +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +``` +console.log("hello"); +``` + +## `vpt write-file vite.config.ts 'throw new Error('\''Vite config must not be loaded'\''); +'` + + +## `vp fmt --config custom-fmt.json index.js` + +explicit format configs do not require a working Vite config + +``` +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +``` +console.log('hello'); +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.global.md new file mode 100644 index 0000000000..25bf0a48e6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.global.md @@ -0,0 +1,93 @@ +# lint_explicit_config + +## `vp lint index.js` + +use inline lint rules when no config flag is supplied + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:1:1] + 1 │ console.log("hello"); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp lint --config custom-lint.json index.js` + +an explicit config overrides the inline lint rules + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint -c custom-lint.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint --config=custom-lint.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint -c=custom-lint.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint -ccustom-lint.json index.js` + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:custom` + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint --config custom-lint.json index.js ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vpt write-file vite.config.ts 'throw new Error('\''Vite config must not be loaded'\''); +'` + + +## `vp lint --config custom-lint.json index.js` + +explicit lint configs do not require a working Vite config + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.local.md new file mode 100644 index 0000000000..3e8924c393 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/snapshots/lint_explicit_config.local.md @@ -0,0 +1,78 @@ +# lint_explicit_config + +## `vp lint index.js` + +use inline lint rules when no config flag is supplied + +**Exit code:** 1 + +``` + + × eslint(no-console): Unexpected console statement. + ╭─[index.js:1:1] + 1 │ console.log("hello"); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp lint --config custom-lint.json index.js` + +an explicit config overrides the inline lint rules + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint -c custom-lint.json index.js` + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint --config=custom-lint.json index.js` + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint -c=custom-lint.json index.js` + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp lint -ccustom-lint.json index.js` + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:custom` + +``` +$ vp lint --config custom-lint.json index.js ⊘ cache disabled +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vpt write-file vite.config.ts 'throw new Error('\''Vite config must not be loaded'\''); +'` + + +## `vp lint --config custom-lint.json index.js` + +explicit lint configs do not require a working Vite config + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/vite.config.ts new file mode 100644 index 0000000000..39d832bfa9 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_explicit_config/vite.config.ts @@ -0,0 +1,10 @@ +export default { + lint: { + rules: { + 'no-console': 'error', + }, + }, + fmt: { + singleQuote: false, + }, +}; diff --git a/packages/cli/src/migration/__tests__/migrator.spec.ts b/packages/cli/src/migration/__tests__/migrator.spec.ts index bd868a769a..bdcb3ee5e1 100644 --- a/packages/cli/src/migration/__tests__/migrator.spec.ts +++ b/packages/cli/src/migration/__tests__/migrator.spec.ts @@ -8759,6 +8759,7 @@ describe('existing Vite+ core migration finalization', () => { scripts: true, tsconfigTypes: true, imports: true, + oxcConfigs: false, tsdownConfig: false, taskCacheConfig: false, taskCacheWarnings: [], @@ -8792,6 +8793,608 @@ describe('existing Vite+ core migration finalization', () => { }); }); + it.each(['.oxfmtrc.json', '.oxfmtrc.jsonc'])( + 'finishes a leftover %s config and remains idempotent', + (configFile) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'test', devDependencies: { 'vite-plus': 'latest' } }), + ); + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), 'export default {};\n'); + fs.writeFileSync(path.join(tmpDir, configFile), '{"singleQuote":true,"semi":false}\n'); + const workspaceInfo = makeWorkspaceInfo(tmpDir, PackageManager.npm); + const report = createMigrationReport(); + + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true, report).oxcConfigs).toBe( + true, + ); + const config = fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8'); + expect(config).toContain('fmt:'); + expect(config).toContain('"singleQuote":true'); + expect(config).toContain('"semi":false'); + expect(fs.existsSync(path.join(tmpDir, configFile))).toBe(false); + expect(report.mergedConfigCount).toBe(1); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + }, + ); + + it('finishes leftover lint and format configs in workspace packages', () => { + const appDir = path.join(tmpDir, 'packages', 'app'); + fs.mkdirSync(appDir, { recursive: true }); + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'root', devDependencies: { 'vite-plus': 'latest' } }), + ); + fs.writeFileSync(path.join(appDir, 'package.json'), JSON.stringify({ name: 'app' })); + fs.writeFileSync(path.join(appDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + fs.writeFileSync(path.join(appDir, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + isMonorepo: true, + packages: [{ name: 'app', path: 'packages/app' }], + }; + + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(true); + const config = fs.readFileSync(path.join(appDir, 'vite.config.ts'), 'utf8'); + expect(config).toContain('fmt:'); + expect(config).toContain('lint:'); + expect(config).toMatch(/"no-console":\s*"error"/); + expect(fs.existsSync(path.join(appDir, '.oxfmtrc.json'))).toBe(false); + expect(fs.existsSync(path.join(appDir, '.oxlintrc.json'))).toBe(false); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + }); + + it('preserves standalone configs when the tool already has an inline config', () => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'test', devDependencies: { 'vite-plus': 'latest' } }), + ); + const config = 'export default { fmt: { singleQuote: false } };\n'; + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), config); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + const workspaceInfo = makeWorkspaceInfo(tmpDir, PackageManager.npm); + + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe( + '{"singleQuote":true}\n', + ); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + }); + + it.each([ + `import fmt from './.oxfmtrc.json' with { type: 'json' }; +import lint from './.oxlintrc.json' with { type: 'json' }; +export default { fmt, lint }; +`, + `import { readFileSync } from 'node:fs'; +const fmt = JSON.parse(readFileSync(new URL('./.oxfmtrc.json', import.meta.url), 'utf8')); +const lint = JSON.parse(readFileSync(new URL('./.oxlintrc.json', import.meta.url), 'utf8')); +export default { fmt, lint }; +`, + `import config from './.oxfmtrc.json' with { type: 'json' }; +export default { define: { config: JSON.stringify(config) } }; +`, + ])('preserves config files loaded by vite.config.ts (%#)', (config) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'test', devDependencies: { 'vite-plus': 'latest' } }), + ); + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), config); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + if (config.includes('.oxlintrc.json')) { + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + } + const workspaceInfo = makeWorkspaceInfo(tmpDir, PackageManager.npm); + + for (let run = 0; run < 2; run++) { + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe( + '{"singleQuote":true}\n', + ); + if (config.includes('.oxlintrc.json')) { + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe( + '{"rules":{"no-console":"error"}}\n', + ); + } + } + }); + + it.each([ + ['.', 'packages/app'], + ['packages/shared', 'packages/app'], + ['packages/app', '.'], + ])('preserves configs in %s loaded by the Vite config in %s', (configDir, consumerDir) => { + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + packages: [ + { name: 'app', path: 'packages/app' }, + { name: 'shared', path: 'packages/shared' }, + ], + }; + for (const dir of ['.', ...workspaceInfo.packages.map((pkg) => pkg.path)]) { + fs.mkdirSync(path.join(tmpDir, dir), { recursive: true }); + fs.writeFileSync(path.join(tmpDir, dir, 'package.json'), JSON.stringify({ name: dir })); + } + const configPath = path.join(tmpDir, configDir); + const consumerPath = path.join(tmpDir, consumerDir); + const relativePath = path.relative(consumerPath, configPath).replaceAll('\\', '/'); + const config = `import fmt from './${relativePath}/.oxfmtrc.json' with { type: 'json' }; +import { readFileSync } from 'node:fs'; +const lint = JSON.parse(readFileSync(new URL('./${relativePath}/.oxlintrc.json', import.meta.url), 'utf8')); +export default { fmt, lint }; +`; + fs.writeFileSync(path.join(consumerPath, 'vite.config.mts'), config); + fs.writeFileSync(path.join(configPath, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + fs.writeFileSync(path.join(configPath, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + + for (let run = 0; run < 2; run++) { + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(consumerPath, 'vite.config.mts'), 'utf8')).toBe(config); + expect(fs.readFileSync(path.join(configPath, '.oxfmtrc.json'), 'utf8')).toBe( + '{"singleQuote":true}\n', + ); + expect(fs.readFileSync(path.join(configPath, '.oxlintrc.json'), 'utf8')).toBe( + '{"rules":{"no-console":"error"}}\n', + ); + expect(fs.existsSync(path.join(configPath, 'vite.config.ts'))).toBe(false); + } + }); + + it.each(['.', 'packages/app'])( + 'preserves configs used by scripts in %s without an inline tool config', + (packagePath) => { + const appDir = path.join(tmpDir, packagePath); + fs.mkdirSync(appDir, { recursive: true }); + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'root', devDependencies: { 'vite-plus': 'latest' } }), + ); + const prefix = packagePath === '.' ? './' : '../../'; + fs.writeFileSync( + path.join(appDir, 'package.json'), + JSON.stringify({ + name: 'app', + devDependencies: { 'vite-plus': 'latest' }, + scripts: { + fmt: `vp fmt -c ${prefix}.oxfmtrc.json src --write`, + lint: `vp lint --config=${prefix}.oxlintrc.json src`, + }, + }), + ); + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), 'export default {};\n'); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + packages: packagePath === '.' ? [] : [{ name: 'app', path: packagePath }], + }; + + for (let run = 0; run < 2; run++) { + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe( + false, + ); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe( + 'export default {};\n', + ); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe( + '{"singleQuote":true}\n', + ); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe( + '{"rules":{"no-console":"error"}}\n', + ); + } + }, + ); + + it.each([ + ['.', '.', 'script'], + ['packages/app', '.', 'script'], + ['.', 'packages/app', 'script'], + ['.', '.', 'module'], + ['packages/app', '.', 'module'], + ['.', 'packages/app', 'module'], + ])('merges independent configs when %s references %s via %s', (consumer, target, kind) => { + const appDir = path.join(tmpDir, 'packages/app'); + fs.mkdirSync(appDir, { recursive: true }); + const consumerDir = path.join(tmpDir, consumer); + const targetDir = path.join(tmpDir, target); + const independentDir = target === '.' ? appDir : tmpDir; + const relativePath = path.relative(consumerDir, targetDir).replaceAll('\\', '/'); + const prefix = relativePath ? `./${relativePath}/` : './'; + for (const dir of [tmpDir, appDir]) { + fs.writeFileSync( + path.join(dir, 'package.json'), + JSON.stringify({ + name: dir === tmpDir ? 'root' : 'app', + devDependencies: { vite: '^8.0.0', oxlint: '^1.0.0', oxfmt: '^0.40.0' }, + scripts: { + lint: + kind === 'script' && dir === consumerDir + ? `oxlint -c ${prefix}.oxlintrc.json index.js` + : 'oxlint index.js', + fmt: + kind === 'script' && dir === consumerDir + ? `oxfmt --config=${prefix}.oxfmtrc.json index.js` + : 'oxfmt index.js', + }, + }), + ); + fs.writeFileSync(path.join(dir, 'vite.config.mjs'), 'export default {};\n'); + fs.writeFileSync(path.join(dir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + fs.writeFileSync( + path.join(dir, '.oxlintrc.json'), + JSON.stringify({ rules: { [dir === targetDir ? 'no-debugger' : 'no-console']: 'error' } }), + ); + } + if (kind === 'module') { + fs.writeFileSync( + path.join(consumerDir, 'vite.config.mjs'), + `import lintRules from '${prefix}.oxlintrc.json' with { type: 'json' };\n` + + `import fmtRules from '${prefix}.oxfmtrc.json' with { type: 'json' };\n` + + 'export default { define: { rules: JSON.stringify({ lintRules, fmtRules }) } };\n', + ); + } + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.npm), + isMonorepo: true, + packages: [{ name: 'app', path: 'packages/app' }], + }; + rewriteMonorepo(workspaceInfo, true, true); + + const config = fs.readFileSync(path.join(independentDir, 'vite.config.mjs'), 'utf8'); + expect(config).toMatch(/"no-console":\s*"error"/); + expect(config).toContain('fmt:'); + expect(config).toContain('"singleQuote":true'); + for (const filename of ['.oxlintrc.json', '.oxfmtrc.json']) { + expect(fs.existsSync(path.join(independentDir, filename))).toBe(false); + expect(fs.existsSync(path.join(targetDir, filename))).toBe(true); + } + expect(readJson(path.join(appDir, 'package.json')).scripts).toMatchObject({ + lint: + kind === 'script' && consumer === 'packages/app' + ? `vp lint -c ${prefix}.oxlintrc.json index.js` + : 'vp lint index.js', + }); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(independentDir, 'vite.config.mjs'), 'utf8')).toBe(config); + }); + + it.each([ + ['script', 'vp lint --config "$LINT_CONFIG"'], + ['script', 'node check-config.js .oxlintrc.json'], + ['module', "import shared from '#shared'; export default shared;"], + ['module', "const filename = '.oxlintrc.json'; export default { define: { filename } };"], + ])('scopes uncertain %s references to their package: %s', (kind, reference) => { + const appDir = path.join(tmpDir, 'packages/app'); + fs.mkdirSync(appDir, { recursive: true }); + for (const dir of [tmpDir, appDir]) { + fs.writeFileSync( + path.join(dir, 'package.json'), + JSON.stringify({ + name: 'test', + scripts: kind === 'script' && dir === tmpDir ? { lint: reference } : {}, + }), + ); + fs.writeFileSync(path.join(dir, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + fs.writeFileSync( + path.join(dir, 'vite.config.ts'), + kind === 'module' && dir === tmpDir ? reference : 'export default {};\n', + ); + } + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + packages: [{ name: 'app', path: 'packages/app' }], + }; + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(true); + expect(fs.existsSync(path.join(tmpDir, '.oxlintrc.json'))).toBe(true); + expect(fs.existsSync(path.join(appDir, '.oxlintrc.json'))).toBe(false); + expect(fs.readFileSync(path.join(appDir, 'vite.config.ts'), 'utf8')).toMatch( + /"no-console":\s*"error"/, + ); + }); + + it.each([ + '--config "config files/lint.jsonc"', + "-c 'config files/lint.jsonc'", + '--config="config files/lint.jsonc"', + '-c="config files/lint.jsonc"', + '-c"config files/lint.jsonc"', + '"--config" "config files/lint.jsonc"', + '"--config=config files/lint.jsonc"', + ])('preserves inheritance rooted at a custom script config: %s', (args) => { + const appDir = path.join(tmpDir, 'packages/app'); + fs.mkdirSync(path.join(appDir, 'config files'), { recursive: true }); + fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify({ name: 'root' })); + fs.writeFileSync( + path.join(appDir, 'package.json'), + JSON.stringify({ name: 'app', scripts: { lint: `vp lint ${args} src` } }), + ); + const rootConfig = '{"rules":{"no-console":"error"}}\n'; + const customConfig = '// Shared lint rules\n{"extends":["../../../.oxlintrc.json"]}\n'; + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), rootConfig); + fs.writeFileSync(path.join(appDir, 'config files/lint.jsonc'), customConfig); + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), 'export default {};\n'); + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + packages: [{ name: 'app', path: 'packages/app' }], + }; + + for (let run = 0; run < 2; run++) { + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe(rootConfig); + expect(fs.readFileSync(path.join(appDir, 'config files/lint.jsonc'), 'utf8')).toBe( + customConfig, + ); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe( + 'export default {};\n', + ); + } + }); + + it.each(['vp lint --config "$LINT_CONFIG"', 'cd config && vp lint -c lint.json'])( + 'preserves configs when a script config path is uncertain: %s', + (lint) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'root', scripts: { lint } }), + ); + const config = '{"rules":{"no-console":"error"}}\n'; + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), config); + expect( + finalizeCoreMigrationForExistingVitePlus( + makeWorkspaceInfo(tmpDir, PackageManager.npm), + true, + ).oxcConfigs, + ).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe(config); + expect(fs.existsSync(path.join(tmpDir, 'vite.config.ts'))).toBe(false); + }, + ); + + it.each([ + ['./config/shared.ts', 'shared.ts'], + ['./config/shared', 'shared.ts'], + ['./config/shared.base', 'shared.base.ts'], + ['./config/shared.js', 'shared.ts'], + ['./config/shared.mjs', 'shared.mts'], + ['./config/shared.cjs', 'shared.cts'], + ['./config', 'index.ts'], + ])('preserves configs imported through the shared Vite module %s', (specifier, filename) => { + fs.mkdirSync(path.join(tmpDir, 'config')); + fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify({ name: 'root' })); + const config = `import shared from '${specifier}';\nexport default { ...shared };\n`; + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), config); + // Follow a re-export, a readFileSync path, and a cycle without evaluating code. + fs.writeFileSync( + path.join(tmpDir, 'config', filename), + "export { default } from './tools.ts';\nthrow new Error('must not execute shared config');\n", + ); + // A competing source must not hide references under another config loader. + if (specifier.endsWith('.js')) { + fs.writeFileSync(path.join(tmpDir, specifier), 'export default {};\n'); + } + fs.writeFileSync( + path.join(tmpDir, 'config/tools.ts'), + `import './${filename}'; +import { readFileSync } from 'node:fs'; +import lint from '../.oxlintrc.json' with { type: 'json' }; +const fmt = JSON.parse(readFileSync(new URL('../.oxfmtrc.json', import.meta.url), 'utf8')); +export default { fmt, lint }; +`, + ); + const lint = '{"rules":{"no-console":"error"}}\n'; + const fmt = '{"singleQuote":true}\n'; + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), lint); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), fmt); + const workspaceInfo = makeWorkspaceInfo(tmpDir, PackageManager.npm); + + for (let run = 0; run < 2; run++) { + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe(lint); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe(fmt); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + } + }); + + it.each([ + "import shared from '#shared'; export default shared;", + "import shared from /* shared config */ '#shared'; export default shared;", + "export { default } from '#shared';", + "const shared = require('#shared'); export default shared;", + "const shared = await import(/* shared config */ '#shared'); export default shared.default;", + ])('preserves configs behind a package alias: %s', (entry) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ + name: 'root', + imports: { '#shared': { import: './shared.ts', require: './unused.cjs' } }, + }), + ); + fs.writeFileSync(path.join(tmpDir, 'unused.cjs'), 'module.exports = {};\n'); + fs.writeFileSync(path.join(tmpDir, 'entry.ts'), entry); + const config = "import shared from './entry.ts';\nexport default { ...shared };\n"; + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), config); + fs.writeFileSync( + path.join(tmpDir, 'shared.ts'), + "import fmt from './.oxfmtrc.json'; import lint from './.oxlintrc.json'; export default { fmt, lint };\n", + ); + const lint = '{"rules":{"no-console":"error"}}\n'; + const fmt = '{"singleQuote":true}\n'; + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), lint); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), fmt); + + for (let run = 0; run < 2; run++) { + expect( + finalizeCoreMigrationForExistingVitePlus( + makeWorkspaceInfo(tmpDir, PackageManager.npm), + true, + ).oxcConfigs, + ).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe(lint); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe(fmt); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + } + }); + + it.each(['main', 'module', 'exports'])( + 'preserves configs behind a directory package with a %s entry', + (field) => { + fs.mkdirSync(path.join(tmpDir, 'config')); + fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"root"}\n'); + fs.writeFileSync( + path.join(tmpDir, 'config/package.json'), + JSON.stringify({ [field]: './shared.ts' }), + ); + // Finding an index is not enough: package metadata can select another file. + fs.writeFileSync(path.join(tmpDir, 'config/index.ts'), 'export default {};\n'); + fs.writeFileSync( + path.join(tmpDir, 'config/shared.ts'), + "import fmt from '../.oxfmtrc.json'; export default { fmt };\n", + ); + const config = "import shared from './config';\nexport default { ...shared };\n"; + const fmt = '{"singleQuote":true}\n'; + fs.writeFileSync(path.join(tmpDir, 'vite.config.ts'), config); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), fmt); + + for (let run = 0; run < 2; run++) { + expect( + finalizeCoreMigrationForExistingVitePlus( + makeWorkspaceInfo(tmpDir, PackageManager.npm), + true, + ).oxcConfigs, + ).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe(fmt); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + } + }, + ); + + it('still merges unreferenced configs alongside custom config arguments and local imports', () => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'root', scripts: { lint: 'vp lint --config custom.json src' } }), + ); + fs.writeFileSync(path.join(tmpDir, 'custom.json'), '{"rules":{"no-debugger":"error"}}\n'); + fs.writeFileSync(path.join(tmpDir, 'shared.ts'), 'export default { define: {} };\n'); + fs.writeFileSync( + path.join(tmpDir, 'vite.config.ts'), + "import shared from './shared';\nexport default { ...shared };\n", + ); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + + expect( + finalizeCoreMigrationForExistingVitePlus(makeWorkspaceInfo(tmpDir, PackageManager.npm), true) + .oxcConfigs, + ).toBe(true); + expect(fs.existsSync(path.join(tmpDir, '.oxfmtrc.json'))).toBe(false); + expect(fs.existsSync(path.join(tmpDir, '.oxlintrc.json'))).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, 'custom.json'), 'utf8')).toBe( + '{"rules":{"no-debugger":"error"}}\n', + ); + }); + + it('keeps an unmergeable config and reports the incomplete migration', () => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'test', devDependencies: { 'vite-plus': 'latest' } }), + ); + fs.writeFileSync( + path.join(tmpDir, 'vite.config.ts'), + 'const config = {}; export default config;\n', + ); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + const report = createMigrationReport(); + + const result = finalizeCoreMigrationForExistingVitePlus( + makeWorkspaceInfo(tmpDir, PackageManager.npm), + true, + report, + ); + expect(result.oxcConfigs).toBe(false); + expect(fs.existsSync(path.join(tmpDir, '.oxfmtrc.json'))).toBe(true); + expect(report.warnings.some((warning) => warning.includes('Failed to merge'))).toBe(true); + }); + + it('recovers workspace inheritance with shared TS configs before removing JSON sources', () => { + const appDir = path.join(tmpDir, 'packages/app'); + fs.mkdirSync(appDir, { recursive: true }); + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'root', devDependencies: { 'vite-plus': 'latest' } }), + ); + fs.writeFileSync(path.join(appDir, 'package.json'), JSON.stringify({ name: 'app' })); + const rootConfig = '{"rules":{"no-console":"error"}}\n'; + const appConfig = '{"extends":["../../.oxlintrc.json"],"rules":{"no-debugger":"error"}}\n'; + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), rootConfig); + fs.writeFileSync(path.join(appDir, '.oxlintrc.json'), appConfig); + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + packages: [{ name: 'app', path: 'packages/app' }], + }; + + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(true); + const rootViteConfig = fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8'); + const appViteConfig = fs.readFileSync(path.join(appDir, 'vite.config.ts'), 'utf8'); + expect(rootViteConfig).toContain('from "./.oxlintrc.js"'); + expect(appViteConfig).toContain('from "../../.oxlintrc.js"'); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.ts'), 'utf8')).toContain( + '"no-console": "error"', + ); + expect(fs.existsSync(path.join(tmpDir, '.oxlintrc.json'))).toBe(false); + expect(fs.existsSync(path.join(appDir, '.oxlintrc.json'))).toBe(false); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(rootViteConfig); + expect(fs.readFileSync(path.join(appDir, 'vite.config.ts'), 'utf8')).toBe(appViteConfig); + }); + + it.each(['leaf', 'cycle', 'missing'])( + 'preserves transitive lint inheritance through custom JSON files with a %s target', + (target) => { + const appDir = path.join(tmpDir, 'packages/app'); + fs.mkdirSync(path.join(appDir, 'config'), { recursive: true }); + fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify({ name: 'root' })); + fs.writeFileSync(path.join(appDir, 'package.json'), JSON.stringify({ name: 'app' })); + const rootConfig = '{"rules":{"no-console":"error"}}\n'; + const appConfig = '{"extends":["./config/lint-base.jsonc"]}\n'; + const targets = ['../../../.oxlintrc.json']; + if (target !== 'leaf') { + targets.push(target === 'cycle' ? '../.oxlintrc.json' : './missing.json'); + } + const baseConfig = `// Shared lint rules\n${JSON.stringify({ extends: targets })}\n`; + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), rootConfig); + fs.writeFileSync(path.join(appDir, '.oxlintrc.json'), appConfig); + fs.writeFileSync(path.join(appDir, 'config/lint-base.jsonc'), baseConfig); + const workspaceInfo = { + ...makeWorkspaceInfo(tmpDir, PackageManager.pnpm), + packages: [{ name: 'app', path: 'packages/app' }], + }; + + const report = createMigrationReport(); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true, report).oxcConfigs).toBe( + true, + ); + // The custom JSON file stays available, so its root dependency must remain. + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe(rootConfig); + expect(fs.readFileSync(path.join(appDir, 'config/lint-base.jsonc'), 'utf8')).toBe(baseConfig); + if (target === 'leaf') { + expect(fs.existsSync(path.join(appDir, '.oxlintrc.json'))).toBe(false); + expect(fs.readFileSync(path.join(appDir, 'vite.config.ts'), 'utf8')).toContain( + 'from "./config/lint-base.js"', + ); + expect(report.warnings).toEqual([]); + } else { + expect(fs.readFileSync(path.join(appDir, '.oxlintrc.json'), 'utf8')).toBe(appConfig); + expect(report.warnings).toEqual([expect.stringContaining('Could not migrate')]); + } + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe(rootConfig); + }, + ); + it('detects package-level legacy signals in workspaces', () => { const appDir = path.join(tmpDir, 'packages', 'app'); fs.mkdirSync(appDir, { recursive: true }); @@ -8838,6 +9441,7 @@ export default defineConfig({ scripts: false, tsconfigTypes: false, imports: true, + oxcConfigs: false, tsdownConfig: true, taskCacheConfig: false, taskCacheWarnings: [], @@ -8857,6 +9461,7 @@ export default defineConfig({ scripts: false, tsconfigTypes: false, imports: false, + oxcConfigs: false, tsdownConfig: false, taskCacheConfig: false, taskCacheWarnings: [], @@ -8896,6 +9501,7 @@ export default defineConfig({ entry: 'src/index.ts' }); scripts: false, tsconfigTypes: false, imports: true, + oxcConfigs: false, tsdownConfig: false, taskCacheConfig: false, taskCacheWarnings: [], @@ -9120,11 +9726,81 @@ export default defineConfig({ const viteConfig = fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8'); expect(viteConfig.match(/\bfmt\s*:/g)?.length).toBe(1); - // Template-authored value wins (singleQuote: true) — standalone config dropped. + // Template-authored value wins (singleQuote: true). expect(viteConfig).toContain('singleQuote: true'); expect(viteConfig).not.toContain('singleQuote: false'); - // Redundant standalone file removed. - expect(fs.existsSync(path.join(tmpDir, '.oxfmtrc.jsonc'))).toBe(false); + // The standalone file can still be used outside this config. + expect(fs.existsSync(path.join(tmpDir, '.oxfmtrc.jsonc'))).toBe(true); + }); + + it.each(['.oxlintrc.json', '.oxlintrc.jsonc'])( + 'converts unreferenced JSON extends during a fresh migration and retry (%s)', + (filename) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ + name: 'test', + scripts: { lint: 'oxlint' }, + devDependencies: { oxlint: '^1.88.0', vite: '^8.3.4' }, + }), + ); + fs.writeFileSync( + path.join(tmpDir, 'vite.config.ts'), + "import { defineConfig } from 'vite';\nexport default defineConfig({});\n", + ); + fs.mkdirSync(path.join(tmpDir, 'shared')); + const lint = '{"extends":["./shared/oxlint-config.json"]}\n'; + const shared = '{"rules":{"no-debugger":"error"}}\n'; + fs.writeFileSync(path.join(tmpDir, filename), lint); + fs.writeFileSync(path.join(tmpDir, 'shared/oxlint-config.json'), shared); + const workspaceInfo = makeWorkspaceInfo(tmpDir, PackageManager.npm); + const report = createMigrationReport(); + + rewriteStandaloneProject(tmpDir, workspaceInfo, true, true, report); + + const config = fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8'); + expect(config).toContain("from 'vite-plus'"); + expect(config).toMatch(/\blint\s*:/); + expect(config).toContain('from "./shared/oxlint-config.js"'); + expect(fs.existsSync(path.join(tmpDir, filename))).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, 'shared/oxlint-config.ts'), 'utf8')).toContain( + '"no-debugger": "error"', + ); + expect(fs.readFileSync(path.join(tmpDir, 'shared/oxlint-config.json'), 'utf8')).toBe(shared); + expect( + (readJson(path.join(tmpDir, 'package.json')).scripts as Record).lint, + ).toBe('vp lint'); + expect(report.warnings).toEqual([]); + expect(finalizeCoreMigrationForExistingVitePlus(workspaceInfo, true, report).oxcConfigs).toBe( + false, + ); + expect(fs.readFileSync(path.join(tmpDir, 'vite.config.ts'), 'utf8')).toBe(config); + expect(fs.existsSync(path.join(tmpDir, filename))).toBe(false); + expect(report.warnings).toEqual([]); + }, + ); + + it('does not inject defaults over configs retained for explicit script arguments', () => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ + name: 'test', + devDependencies: { vite: '^7.0.0' }, + scripts: { fmt: 'oxfmt -c .oxfmtrc.json src', lint: 'oxlint -c .oxlintrc.json src' }, + }), + ); + fs.writeFileSync(path.join(tmpDir, '.oxfmtrc.json'), '{"singleQuote":true}\n'); + fs.writeFileSync(path.join(tmpDir, '.oxlintrc.json'), '{"rules":{"no-console":"error"}}\n'); + + rewriteStandaloneProject(tmpDir, makeWorkspaceInfo(tmpDir, PackageManager.pnpm), true, true); + + expect(fs.existsSync(path.join(tmpDir, 'vite.config.ts'))).toBe(false); + expect(fs.readFileSync(path.join(tmpDir, '.oxfmtrc.json'), 'utf8')).toBe( + '{"singleQuote":true}\n', + ); + expect(fs.readFileSync(path.join(tmpDir, '.oxlintrc.json'), 'utf8')).toBe( + '{"rules":{"no-console":"error"}}\n', + ); }); }); diff --git a/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts b/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts index 4f083d1ef1..b533bf4bbd 100644 --- a/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts +++ b/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts @@ -7,6 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { PackageManager } from '../../types/index.ts'; import { + finalizeCoreMigrationForExistingVitePlus, injectLintTypeCheckDefaults, mergeViteConfigFiles, prepareOxlintConfigs, @@ -86,6 +87,64 @@ describe('Oxlint JSON extends migration', () => { fs.rmSync(project, { recursive: true, force: true }); }); + it.each([ + ['fresh', 'script'], + ['fresh', 'module'], + ['fresh', 'custom config'], + ['recovery', 'script'], + ['recovery', 'module'], + ['recovery', 'custom config'], + ])( + 'keeps referenced JSON after a sibling converts its inheritance (%s, %s)', + async (mode, reference) => { + createWorkspace(['a']); + const source = write('.oxlintrc.json', { + rules: { 'no-debugger': 'error' }, + options: { typeAware: false, typeCheck: false }, + }); + const original = fs.readFileSync(source, 'utf8'); + if (reference === 'module') { + write( + 'vite.config.mjs', + 'import rules from "./.oxlintrc.json" with { type: "json" }; export default { define: { rules: JSON.stringify(rules) } };', + ); + } else { + const pkg = JSON.parse(fs.readFileSync(path.join(project, 'package.json'), 'utf8')); + pkg.scripts = { + lint: `oxlint -c ${reference === 'script' ? '.oxlintrc.json' : 'custom.json'} index.js`, + }; + write('package.json', pkg); + if (reference === 'custom config') { + write('custom.json', { extends: ['./.oxlintrc.json'] }); + } + } + write('packages/a/.oxlintrc.json', { + extends: ['../../.oxlintrc.json'], + rules: { 'no-console': 'error' }, + }); + const workspace = { rootDir: project, packages: [{ name: 'a', path: 'packages/a' }] }; + const report = createMigrationReport(); + if (mode === 'fresh') { + expect(migrateWorkspace(['a']).warnings).toEqual([]); + } else { + expect(finalizeCoreMigrationForExistingVitePlus(workspace, true, report).oxcConfigs).toBe( + true, + ); + expect(report.warnings).toEqual([]); + } + expect(fs.readFileSync(source, 'utf8')).toBe(original); + expect((await loadConfig()).lint).toBeUndefined(); + expect((await loadConfig(path.join(project, 'packages/a'))).lint).toMatchObject({ + extends: [{ rules: { 'no-debugger': 'error' } }], + rules: { 'no-console': 'error' }, + }); + expect(fs.existsSync(path.join(project, 'packages/a/.oxlintrc.json'))).toBe(false); + expect(fs.existsSync(path.join(project, '.oxlintrc.ts'))).toBe(true); + expect(finalizeCoreMigrationForExistingVitePlus(workspace, true).oxcConfigs).toBe(false); + expect(fs.readFileSync(source, 'utf8')).toBe(original); + }, + ); + it('resolves nested JSONC paths relative to each declaring file and retains inheritance order', async () => { const base = write( 'shared/base.jsonc', diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index a699b6d2be..827005f01b 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -1199,6 +1199,7 @@ async function main() { coreMigrationResult.scripts || coreMigrationResult.tsconfigTypes || coreMigrationResult.imports || + coreMigrationResult.oxcConfigs || coreMigrationResult.tsdownConfig || coreMigrationResult.taskCacheConfig ) { diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index 4433d1d157..49f3989adc 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -13,6 +13,7 @@ export * from './migrator/vite-plus-bootstrap.ts'; export * from './migrator/package-json.ts'; export * from './migrator/vite-config.ts'; export * from './migrator/oxlint-config.ts'; +export * from './migrator/oxc-references.ts'; export * from './migrator/git-hooks.ts'; export * from './migrator/setup.ts'; export * from './migrator/core-finalization.ts'; diff --git a/packages/cli/src/migration/migrator/README.md b/packages/cli/src/migration/migrator/README.md index b93087cfde..dfdaa62353 100644 --- a/packages/cli/src/migration/migrator/README.md +++ b/packages/cli/src/migration/migrator/README.md @@ -31,6 +31,7 @@ Pick the file by what a function _does_, not by where it happens to be called. | `source-scan.ts` | Scanning a project's source tree for signals (browser-mode, opt-in providers, `@nuxt/test-utils`, retained upstream-vitest references). | | `vite-config.ts` | `vite.config.ts` merging, default-config injection, staged-config merge, lazy-plugin wrapping, import rewriting (`rewriteAllImports`), migrated-oxlint-config sanitization, lint-staged removal. | | `oxlint-config.ts` | Oxlint JSON inheritance graphs and shared TypeScript config generation. | +| `oxc-references.ts` | References from scripts and modules that protect Oxc JSON configs during migration. | | `eslint.ts` | ESLint → Oxlint migration, oxlint JS-plugin namespace handling, ESLint prompts/warnings. | | `prettier.ts` | Prettier → Oxfmt migration and its prompts/warnings. | | `tsup.ts` | tsup → tsdown (`vp pack`) migration via `tsdown-migrate` and its prompts/warnings. | diff --git a/packages/cli/src/migration/migrator/catalog.ts b/packages/cli/src/migration/migrator/catalog.ts index cd6b13e998..e1a986413b 100644 --- a/packages/cli/src/migration/migrator/catalog.ts +++ b/packages/cli/src/migration/migrator/catalog.ts @@ -1153,6 +1153,7 @@ export function rewriteRootWorkspacePackageJson( // When false, the root `vite-plus` edge and the direct `vite` edge are concrete. supportCatalog = true, preparedOxlintConfigs?: PreparedOxlintConfigs, + preparedReferences?: ReadonlySet, ): void { const packageJsonPath = path.join(projectPath, 'package.json'); if (!fs.existsSync(packageJsonPath)) { @@ -1293,7 +1294,14 @@ export function rewriteRootWorkspacePackageJson( undefined, undefined, catalogDependencyResolver, - packages ? { rootDir: projectPath, packages, oxlintConfigs: preparedOxlintConfigs } : undefined, + packages + ? { + rootDir: projectPath, + packages, + oxlintConfigs: preparedOxlintConfigs, + oxcReferences: preparedReferences, + } + : undefined, true, supportCatalog, ); diff --git a/packages/cli/src/migration/migrator/core-finalization.ts b/packages/cli/src/migration/migrator/core-finalization.ts index d57f2b556a..5084556a4f 100644 --- a/packages/cli/src/migration/migrator/core-finalization.ts +++ b/packages/cli/src/migration/migrator/core-finalization.ts @@ -5,11 +5,16 @@ import { rewriteScripts } from '../../../binding/index.js'; import { type WorkspacePackage } from '../../types/index.ts'; import { editJsonFile, readJsonFile } from '../../utils/json.ts'; import { rulesDir } from '../../utils/path.ts'; +import { detectConfigs } from '../detector.ts'; import { dropDeadOxlintPluginsDependency, + findReferencedOxcConfigs, hasTsconfigTypesToRewrite, mergeTsdownConfigFile, + mergeViteConfigFiles, migrateTaskCacheConfigInViteConfig, + prepareOxlintConfigs, + removeMigratedOxlintConfigs, rewriteAllImports, rewriteTsconfigTypes, } from '../migrator.ts'; @@ -84,6 +89,7 @@ export type CoreMigrationFinalizationResult = { scripts: boolean; tsconfigTypes: boolean; imports: boolean; + oxcConfigs: boolean; tsdownConfig: boolean; taskCacheConfig: boolean; // Kept out of the report so that review items alone do not make an @@ -154,6 +160,7 @@ export function finalizeCoreMigrationForExistingVitePlus( scripts: false, tsconfigTypes: false, imports: false, + oxcConfigs: false, tsdownConfig: false, taskCacheConfig: false, taskCacheWarnings: [], @@ -191,5 +198,36 @@ export function finalizeCoreMigrationForExistingVitePlus( result.taskCacheConfig; } + // A failed migration may have installed Vite+ before merging these files. + // Finish that core work without opting into unrelated first-time setup. + // Resolve every inheritance graph before any workspace removes a source. + const oxlintConfigs = prepareOxlintConfigs(projectPaths); + const referencedConfigs = findReferencedOxcConfigs(projectPaths); + for (const projectPath of projectPaths) { + const configs = detectConfigs(projectPath); + const standaloneConfigs = [configs.oxlintConfig, configs.oxfmtConfig].filter( + (config) => config !== undefined, + ); + if (standaloneConfigs.length === 0) { + continue; + } + mergeViteConfigFiles( + projectPath, + silent, + report, + workspaceInfo.packages, + workspaceInfo.rootDir, + oxlintConfigs, + referencedConfigs, + ); + if (standaloneConfigs.some((config) => !fs.existsSync(path.join(projectPath, config)))) { + result.oxcConfigs = true; + } + } + removeMigratedOxlintConfigs(oxlintConfigs); + result.oxcConfigs ||= [...oxlintConfigs.values()].some( + (migration) => !(migration instanceof Error) && migration.migrated, + ); + return result; } diff --git a/packages/cli/src/migration/migrator/orchestrators.ts b/packages/cli/src/migration/migrator/orchestrators.ts index dc4a2c5550..21b762b277 100644 --- a/packages/cli/src/migration/migrator/orchestrators.ts +++ b/packages/cli/src/migration/migrator/orchestrators.ts @@ -33,6 +33,7 @@ import { migrateTaskCacheConfigInViteConfig, pnpmSupportsWorkspaceSettings, prepareOxlintConfigs, + findReferencedOxcConfigs, supportsCatalog, projectListsRequiredVitestPeer, projectUsesVitestDirectly, @@ -402,13 +403,15 @@ export function rewriteMonorepo( workspaceInfo.downloadPackageManager.version, true, ); + const projectPaths = [ + workspaceInfo.rootDir, + ...workspaceInfo.packages.map((pkg) => path.join(workspaceInfo.rootDir, pkg.path)), + ]; const workspaceContext = { rootDir: workspaceInfo.rootDir, packages: workspaceInfo.packages, - oxlintConfigs: prepareOxlintConfigs([ - workspaceInfo.rootDir, - ...workspaceInfo.packages.map((pkg) => path.join(workspaceInfo.rootDir, pkg.path)), - ]), + oxlintConfigs: prepareOxlintConfigs(projectPaths), + oxcReferences: findReferencedOxcConfigs(projectPaths), }; // rewrite root workspace if (workspaceInfo.packageManager === PackageManager.yarn) { @@ -439,6 +442,7 @@ export function rewriteMonorepo( workspaceUsesVitest, supportCatalog, workspaceContext.oxlintConfigs, + workspaceContext.oxcReferences, ); if (workspaceInfo.packageManager === PackageManager.pnpm) { rewritePnpmWorkspaceYaml( @@ -496,6 +500,7 @@ export function rewriteMonorepo( workspaceInfo.packages, workspaceInfo.rootDir, workspaceContext.oxlintConfigs, + workspaceContext.oxcReferences, ); removeMigratedOxlintConfigs(workspaceContext.oxlintConfigs); injectLintTypeCheckDefaults(workspaceInfo.rootDir, silent, report); @@ -537,6 +542,7 @@ export function rewriteMonorepoProject( rootDir: string; packages: WorkspacePackage[]; oxlintConfigs?: PreparedOxlintConfigs; + oxcReferences?: ReadonlySet; }, deferLazyPluginWrapping = false, // Whether this workspace's edges use catalog references. Yarn catalogs require @@ -557,6 +563,7 @@ export function rewriteMonorepoProject( workspaceContext?.packages, workspaceContext?.rootDir, workspaceContext?.oxlintConfigs, + workspaceContext?.oxcReferences, ); mergeTsdownConfigFile(projectPath, silent, report); diff --git a/packages/cli/src/migration/migrator/oxc-references.ts b/packages/cli/src/migration/migrator/oxc-references.ts new file mode 100644 index 0000000000..b16cd63d29 --- /dev/null +++ b/packages/cli/src/migration/migrator/oxc-references.ts @@ -0,0 +1,196 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import { hasConfigKey } from '../../../binding/index.js'; +import { readJsonFile } from '../../utils/json.ts'; +import { detectConfigs } from '../detector.ts'; + +const CONFIG_MODULE_EXTENSIONS = ['.ts', '.mts', '.cts', '.tsx', '.js', '.mjs', '.cjs', '.jsx']; +const CONFIG_SOURCE_EXTENSIONS: Record = { + '.js': ['.ts', '.tsx'], + '.mjs': ['.mts'], + '.cjs': ['.cts'], + '.jsx': ['.tsx'], +}; + +function resolveConfigReferences(directory: string, reference: string): string[] | undefined { + // Package imports can select different modules for different loaders or + // conditions. An unresolved reference must never authorize deleting a config. + if (reference.startsWith('#')) { + return undefined; + } + if (!reference) { + return []; + } + const filename = path.resolve(directory, reference); + const extension = path.extname(filename); + const candidates = [filename]; + // Vite resolves .js imports to TypeScript sources, as well as extensionless + // imports and directory indexes. Inspect those sources before deleting JSON. + for (const suffix of CONFIG_SOURCE_EXTENSIONS[extension] ?? []) { + candidates.push(filename.slice(0, -extension.length) + suffix); + } + for (const suffix of CONFIG_MODULE_EXTENSIONS) { + candidates.push(filename + suffix, path.join(filename, `index${suffix}`)); + } + // Preserve references from all matching sources instead of relying on one + // config loader's extension precedence when multiple candidates exist. + const resolved = new Set(); + for (const candidate of candidates) { + if (fs.existsSync(candidate)) { + if (fs.statSync(candidate).isFile()) { + resolved.add(fs.realpathSync(candidate)); + } else if (fs.existsSync(path.join(candidate, 'package.json'))) { + // A directory's main/module/exports fields can supersede its index. + // Keep the configs even if another candidate above was found. + return undefined; + } + } + } + return [...resolved]; +} + +/** Find JSON files still needed by scripts, modules, or retained tool configs. */ +export function findReferencedOxcConfigs(projectPaths: string[]): Set { + const projectDirectories = [...projectPaths] + .filter((directory) => fs.existsSync(directory)) + .map((directory) => fs.realpathSync(directory)) + .toSorted((left, right) => right.length - left.length); + // A helper module belongs to its nearest workspace package. Uncertain paths + // there must not prevent unrelated packages from migrating their own configs. + const referenceDirectory = (directory: string): string => + projectDirectories.find( + (project) => directory === project || directory.startsWith(project + path.sep), + ) ?? directory; + const configReferences: { directory: string; content: string }[] = []; + const lintConfigPaths = new Set(); + const preservedConfigs = new Set(); + const modulePaths = new Set(); + const unresolvedConfigDirectories = new Set(); + for (const directory of projectDirectories) { + const packageJsonPath = path.join(directory, 'package.json'); + if (fs.existsSync(packageJsonPath)) { + const pkg = readJsonFile(packageJsonPath) as { scripts?: Record }; + for (const script of Object.values(pkg.scripts ?? {})) { + // Inspect config arguments without executing shell commands. An unknown + // path (for example an environment variable or a preceding `cd`) means + // we cannot safely decide that a standalone config is unused. + const words = (script.match(/(?:[^\s"';&|]+|"[^"]*"|'[^']*')+/g) ?? []).map((word) => + word.replace(/(["'])(.*?)\1/g, '$2'), + ); + const changesDirectory = words.includes('cd'); + for (let index = 0; index < words.length; index++) { + const word = words[index]; + let reference: string; + if (word === '--config' || word === '-c') { + reference = words[++index] ?? ''; + } else if (word.startsWith('--config=')) { + reference = word.slice('--config='.length); + } else if (word.startsWith('-c')) { + reference = word.slice(2).replace(/^=/, ''); + } else { + continue; + } + const filenames = resolveConfigReferences(directory, reference); + if (!filenames?.length || changesDirectory) { + unresolvedConfigDirectories.add(directory); + } else { + // Known references are matched by file identity below. Do not let + // their basenames also preserve a different config in this package. + words[index] = ''; + } + for (const filename of filenames ?? []) { + if (/\.jsonc?$/.test(filename)) { + preservedConfigs.add(filename); + lintConfigPaths.add(filename); + } else if (CONFIG_MODULE_EXTENSIONS.includes(path.extname(filename))) { + modulePaths.add(filename); + } else { + unresolvedConfigDirectories.add(directory); + } + } + } + configReferences.push({ directory, content: words.join(' ') }); + } + } + const projectConfigs = detectConfigs(directory); + if (projectConfigs.viteConfig) { + modulePaths.add(fs.realpathSync(path.join(directory, projectConfigs.viteConfig))); + } + } + for (const modulePath of modulePaths) { + const content = fs.readFileSync(modulePath, 'utf8'); + const directory = referenceDirectory(path.dirname(modulePath)); + // Quoted paths and package aliases cover imports, re-exports, require(), + // dynamic import(), and readFileSync/new URL calls, including comments + // between tokens. Extra matches only preserve more configs. + // Canonical paths keep cycles, including symlink cycles, finite. + const unresolvedContent = content.replace( + /(["'`])((?:\.{1,2}\/|\/|#)[^"'`\r\n]+)\1/g, + (match, _quote, reference: string) => { + const filenames = resolveConfigReferences(path.dirname(modulePath), reference); + if (!filenames) { + unresolvedConfigDirectories.add(directory); + } + for (const filename of filenames ?? []) { + if (/\.jsonc?$/.test(filename)) { + preservedConfigs.add(filename); + lintConfigPaths.add(filename); + } else if (CONFIG_MODULE_EXTENSIONS.includes(path.extname(filename))) { + modulePaths.add(filename); + } + } + return filenames?.length ? '' : match; + }, + ); + configReferences.push({ directory, content: unresolvedContent }); + } + for (const directory of projectDirectories) { + const configs = detectConfigs(directory); + for (const [filename, key] of [ + [configs.oxlintConfig, 'lint'], + [configs.oxfmtConfig, 'fmt'], + ] as const) { + if (!filename) { + continue; + } + const canonicalPath = fs.realpathSync(path.join(directory, filename)); + if (configs.viteConfig && hasConfigKey(path.join(directory, configs.viteConfig), key)) { + // Deferred graph cleanup retains this consumer's JSON dependencies. + preservedConfigs.add(canonicalPath); + } + if ( + unresolvedConfigDirectories.has(directory) || + configReferences.some( + (reference) => reference.directory === directory && reference.content.includes(filename), + ) + ) { + preservedConfigs.add(canonicalPath); + lintConfigPaths.add(canonicalPath); + } + } + } + // Set iteration also visits newly discovered targets, once each, including + // custom filenames. This follows transitive extends without looping on cycles. + for (const lintConfigPath of lintConfigPaths) { + if (!fs.existsSync(lintConfigPath)) { + continue; + } + const json = readJsonFile(lintConfigPath, true) as { extends?: unknown } | null | undefined; + if (Array.isArray(json?.extends) && json.extends.length > 0) { + // A retained JSON consumer still needs its complete inheritance chain. + preservedConfigs.add(lintConfigPath); + for (const extendedConfig of json.extends) { + if (typeof extendedConfig === 'string') { + const resolvedPath = path.resolve(path.dirname(lintConfigPath), extendedConfig); + const extendedConfigPath = fs.existsSync(resolvedPath) + ? fs.realpathSync(resolvedPath) + : resolvedPath; + preservedConfigs.add(extendedConfigPath); + lintConfigPaths.add(extendedConfigPath); + } + } + } + } + return preservedConfigs; +} diff --git a/packages/cli/src/migration/migrator/vite-config.ts b/packages/cli/src/migration/migrator/vite-config.ts index 96e6e6d46f..107de96e38 100644 --- a/packages/cli/src/migration/migrator/vite-config.ts +++ b/packages/cli/src/migration/migrator/vite-config.ts @@ -32,6 +32,7 @@ import { collectInstalledPackageNames, oxlintMigrationMarker, prepareOxlintConfigs, + findReferencedOxcConfigs, readRulesYaml, removeMigratedOxlintConfigs, resolveOxlintConfigExtends, @@ -269,11 +270,53 @@ export function mergeViteConfigFiles( // projectPath IS the workspace root). workspaceRoot?: string, preparedOxlintConfigs?: PreparedOxlintConfigs, + preparedReferences?: ReadonlySet, ): void { const configs = detectConfigs(projectPath); if (!configs.oxfmtConfig && !configs.oxlintConfig) { return; } + const rootDir = workspaceRoot ?? projectPath; + const referencedConfigs = + preparedReferences ?? + findReferencedOxcConfigs([ + ...new Set([ + rootDir, + projectPath, + ...(packages ?? []).map((pkg) => path.join(rootDir, pkg.path)), + ]), + ]); + // Check references before conversion, default injection, or source removal. + if ( + configs.oxlintConfig && + referencedConfigs.has(fs.realpathSync(path.join(projectPath, configs.oxlintConfig))) + ) { + const json = readJsonFile(path.join(projectPath, configs.oxlintConfig), true) as + | { extends?: unknown } + | null + | undefined; + if ( + configs.viteConfig && + hasConfigKey(path.join(projectPath, configs.viteConfig), 'lint') && + Array.isArray(json?.extends) && + json.extends.length > 0 + ) { + warnMigration( + `Preserved ${displayRelative(path.join(projectPath, configs.oxlintConfig))} because ${configs.viteConfig} already has a lint config. Merge its inherited rules manually.`, + report, + ); + } + configs.oxlintConfig = undefined; + } + if ( + configs.oxfmtConfig && + referencedConfigs.has(fs.realpathSync(path.join(projectPath, configs.oxfmtConfig))) + ) { + configs.oxfmtConfig = undefined; + } + if (!configs.oxfmtConfig && !configs.oxlintConfig) { + return; + } const viteConfig = ensureViteConfig(projectPath, configs, silent, report); if (configs.oxlintConfig) { mergeOxlintConfigFile( @@ -587,6 +630,12 @@ function injectConfigDefaults( report?: MigrationReport, ): void { const configs = detectConfigs(projectPath); + if ( + (configKey === 'lint' && configs.oxlintConfig) || + (configKey === 'fmt' && configs.oxfmtConfig) + ) { + return; + } if (configs.viteConfig && hasConfigKey(path.join(projectPath, configs.viteConfig), configKey)) { return; } From 1d38939055a70698f573c56d6f68f9736d8cf100 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 21 Oct 2026 00:31:15 +0800 Subject: [PATCH 2/2] fix(migrate): preserve config references during recovery --- ...ion_existing_oxc_configs_extends.global.md | 3 + ...tion_existing_oxc_configs_extends.local.md | 3 + ..._existing_oxc_configs_unreferenced_lint.md | 2 + ...ndependent_workspace_oxc_configs.global.md | 2 + ...independent_workspace_oxc_configs.local.md | 2 + ...ed_oxc_config_converted_consumer.global.md | 4 +- ...ced_oxc_config_converted_consumer.local.md | 4 +- .../migration_oxc_ecosystem_sigle.md | 7 + .../snapshots.toml | 44 ++++- .../migration_oxc_reference_retry.global.md | 172 ++++++++++++++++++ .../migration_oxc_reference_retry.local.md | 167 +++++++++++++++++ ...ion_oxlint_extends_workspace_full_setup.md | 45 +++-- ...n_oxlint_extends_workspace_new_consumer.md | 35 +++- ...eferenced_workspace_oxc_configs_extends.md | 2 + .../__tests__/oxlint-extends.spec.ts | 135 +++++++++++--- packages/cli/src/migration/bin.ts | 12 -- .../migration/migrator/core-finalization.ts | 38 +--- .../src/migration/migrator/oxc-references.ts | 65 ++++--- .../cli/src/migration/migrator/vite-config.ts | 93 ++++------ 19 files changed, 651 insertions(+), 184 deletions(-) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.local.md diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md index 3b2f42c2ea..a9e66dec92 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.global.md @@ -35,6 +35,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file vite.config.ts` ``` +// vp migrate: merged .oxlintrc.json (09d7752c6f5506f21cb975ff7f8df4f0). import oxlintConfig1 from "./lint-base.js"; export default { fmt: { @@ -55,6 +56,7 @@ export default { ## `vpt print-file lint-base.ts` ``` +// Generated by vp migrate from lint-base.json. import type { OxlintConfig } from "vite-plus/lint"; export default { @@ -99,6 +101,7 @@ This project is already using Vite+! Happy coding! ## `vpt print-file vite.config.ts` ``` +// vp migrate: merged .oxlintrc.json (09d7752c6f5506f21cb975ff7f8df4f0). import oxlintConfig1 from "./lint-base.js"; export default { fmt: { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md index 5a9687461b..c15985e257 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_extends.local.md @@ -35,6 +35,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file vite.config.ts` ``` +// vp migrate: merged .oxlintrc.json (09d7752c6f5506f21cb975ff7f8df4f0). import oxlintConfig1 from "./lint-base.js"; export default { fmt: { @@ -55,6 +56,7 @@ export default { ## `vpt print-file lint-base.ts` ``` +// Generated by vp migrate from lint-base.json. import type { OxlintConfig } from "vite-plus/lint"; export default { @@ -98,6 +100,7 @@ This project is already using Vite+! Happy coding! ## `vpt print-file vite.config.ts` ``` +// vp migrate: merged .oxlintrc.json (09d7752c6f5506f21cb975ff7f8df4f0). import oxlintConfig1 from "./lint-base.js"; export default { fmt: { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md index 52ee943b1e..0b447cabad 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_existing_oxc_configs/snapshots/migration_existing_oxc_configs_unreferenced_lint.md @@ -35,6 +35,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file vite.config.ts` ``` +// vp migrate: merged .oxlintrc.json (4f1106d1e5daf0701ef5d9536d92669d). export default { fmt: { "singleQuote": true, @@ -95,6 +96,7 @@ This project is already using Vite+! Happy coding! ## `vpt print-file vite.config.ts` ``` +// vp migrate: merged .oxlintrc.json (4f1106d1e5daf0701ef5d9536d92669d). export default { fmt: { "singleQuote": true, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md index fbd407e645..a5f2acb056 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.global.md @@ -41,6 +41,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file packages/app/vite.config.mjs` ``` +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (c22a5fff1bbaa26c1877cded5b947cf8). export default { lint: { "rules": { @@ -120,6 +121,7 @@ This project is already using Vite+! Happy coding! ## `vpt print-file packages/app/vite.config.mjs` ``` +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (c22a5fff1bbaa26c1877cded5b947cf8). export default { lint: { "rules": { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md index 7bf8a2c928..5a74d8cbb1 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_independent_workspace_oxc_configs.local.md @@ -41,6 +41,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file packages/app/vite.config.mjs` ``` +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (c22a5fff1bbaa26c1877cded5b947cf8). export default { lint: { "rules": { @@ -116,6 +117,7 @@ This project is already using Vite+! Happy coding! ## `vpt print-file packages/app/vite.config.mjs` ``` +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (c22a5fff1bbaa26c1877cded5b947cf8). export default { lint: { "rules": { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md index 0070645e89..63a2e0eb63 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.global.md @@ -32,6 +32,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file .oxlintrc.ts` ``` +// Generated by vp migrate from .oxlintrc.json. import type { OxlintConfig } from "vite-plus/lint"; export default { @@ -48,7 +49,8 @@ export default { ## `vpt print-file packages/app/vite.config.mjs` ``` -import oxlintConfig1 from "../../.oxlintrc.ts"; +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (f41297efef6313aa5180c2fdec748c48). +import oxlintConfig1 from "../../.oxlintrc.js"; export default { lint: { "extends": [oxlintConfig1], diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md index b9adc6a9cc..ea4a9f7c1e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_independent_workspace_oxc_configs/snapshots/migration_referenced_oxc_config_converted_consumer.local.md @@ -32,6 +32,7 @@ VITE+ - The Unified Toolchain for the Web ## `vpt print-file .oxlintrc.ts` ``` +// Generated by vp migrate from .oxlintrc.json. import type { OxlintConfig } from "vite-plus/lint"; export default { @@ -48,7 +49,8 @@ export default { ## `vpt print-file packages/app/vite.config.mjs` ``` -import oxlintConfig1 from "../../.oxlintrc.ts"; +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (f41297efef6313aa5180c2fdec748c48). +import oxlintConfig1 from "../../.oxlintrc.js"; export default { lint: { "extends": [oxlintConfig1], diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md index fd4804acbc..8e7a4ae0cb 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxc_ecosystem_sigle/snapshots/migration_oxc_ecosystem_sigle.md @@ -14,11 +14,14 @@ VITE+ - The Unified Toolchain for the Web vite → • 3 config updates applied • Package manager settings configured +! Warnings: + - Preserved .oxlintrc.json because vite.config.ts already has a lint config. Merge its inherited rules manually. ``` ## `vpt print-file .oxlintrc.ts` ``` +// Generated by vp migrate from .oxlintrc.json. import type { OxlintConfig } from "vite-plus/lint"; export default { @@ -45,6 +48,7 @@ apps/custom-domain/.oxlintrc.json: missing the existing Vite config must import the shared TypeScript rules ``` +// vp migrate: merged apps%2Fsigle%2F.oxlintrc.json (37ebf49fc5fb9b317632070f58d36ce2). import oxlintConfig1 from "../../.oxlintrc.js"; import { defineConfig } from 'vite-plus'; @@ -72,6 +76,7 @@ export default defineConfig({ create a Vite config for the migrated inherited rules ``` +// vp migrate: merged apps%2Fcustom-domain%2F.oxlintrc.json (1be7219bedd302f07470f23cd73129b0). import oxlintConfig1 from "../../.oxlintrc.js"; import { defineConfig } from 'vite-plus'; @@ -121,6 +126,7 @@ This project is already using Vite+! Happy coding! ## `vpt print-file apps/sigle/vite.config.ts` ``` +// vp migrate: merged apps%2Fsigle%2F.oxlintrc.json (37ebf49fc5fb9b317632070f58d36ce2). import oxlintConfig1 from "../../.oxlintrc.js"; import { defineConfig } from 'vite-plus'; @@ -146,6 +152,7 @@ export default defineConfig({ ## `vpt print-file apps/custom-domain/vite.config.ts` ``` +// vp migrate: merged apps%2Fcustom-domain%2F.oxlintrc.json (1be7219bedd302f07470f23cd73129b0). import oxlintConfig1 from "../../.oxlintrc.js"; import { defineConfig } from 'vite-plus'; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots.toml index f814be252f..f8c4b44833 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots.toml @@ -308,10 +308,10 @@ steps = [ { argv = ["vpt", "write-file", "packages/c/.oxlintrc.json", '{"extends":["../../shared/base.json"]}'], snapshot = false }, { argv = ["vpt", "write-file", "packages/c/index.js", 'console.log("hello");'], snapshot = false }, { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "index.js"], cwd = "packages/c", comment = "A new workspace uses the retained shared JSON", continue-on-failure = true }, - { argv = ["vp", "migrate", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "An upgrade does not treat shared modules as a pending migration for the new workspace" }, - { argv = ["vpt", "print-file", "packages/c/vite.config.mjs"], comment = "The unmarked workspace keeps its original Vite config" }, - { argv = ["vpt", "print-file", "packages/c/.oxlintrc.json"], comment = "Its newly authored JSON config remains intact" }, - { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "index.js"], cwd = "packages/c", comment = "The JSON config retains the same lint output", continue-on-failure = true }, + { argv = ["vp", "migrate", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "Recover the unreferenced workspace config even without a pending marker" }, + { argv = ["vpt", "print-file", "packages/c/vite.config.mjs"], comment = "The workspace imports the existing shared module" }, + { argv = ["vpt", "stat-file", "packages/c/.oxlintrc.json", "--assert", "missing"], comment = "The recovered config no longer needs its standalone JSON" }, + { argv = ["vp", "lint", "--threads=1", "index.js"], cwd = "packages/c", comment = "Plain lint retains the same diagnostic and exit status", continue-on-failure = true }, ] [[case]] @@ -323,11 +323,39 @@ steps = [ { argv = ["vp", "add", "-D", "vite-plus", "prettier@3.6.2"], snapshot = false }, { argv = ["vpt", "write-file", ".prettierrc.json", '{"singleQuote":true}'], snapshot = false }, { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "index.js"], cwd = "packages/a", comment = "The existing workspace consumer inherits the root JSON rule" }, - { argv = ["vp", "migrate", "--full", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "Prettier migration merges the root configs without migrating package configs" }, + { argv = ["vp", "migrate", "--full", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "Recover workspace configs and merge the migrated Prettier config" }, { argv = ["vpt", "print-file", "vite.config.mjs"], comment = "The root uses its shared module" }, - { argv = ["vpt", "stat-file", ".oxlintrc.json", "--assert", "file"], comment = "The root JSON remains available to untouched consumers" }, + { argv = ["vpt", "stat-file", ".oxlintrc.json", "--assert", "file"], comment = "The root JSON remains available to package b while its output conflict remains" }, { argv = ["vpt", "stat-file", ".oxlintrc.ts", "--assert", "file"] }, - { argv = ["vpt", "print-file", "packages/a/vite.config.mjs"], comment = "The package Vite config remains unchanged" }, - { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "index.js"], cwd = "packages/a", comment = "The preserved package JSON still reports the same diagnostic" }, + { argv = ["vpt", "print-file", "packages/a/vite.config.mjs"], comment = "The recovered package uses the shared module" }, + { argv = ["vpt", "stat-file", "packages/a/.oxlintrc.json", "--assert", "file"] }, + { argv = ["vp", "lint", "--threads=1", "index.js"], cwd = "packages/a", comment = "The recovered package reports the same diagnostic" }, { argv = ["vp", "lint", "--threads=1", "index.js"], comment = "The migrated root enforces the same rule" }, ] + + +[[case]] +name = "migration_oxc_reference_retry" +vp = ["global", "local"] +local-registry = true +unset-env = ["VP_SKIP_INSTALL"] +steps = [ + { argv = ["vpt", "json-edit", ".oxlintrc.json", "rules.no-debugger", '"error"'], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "Package b cannot merge while extra.ts conflicts with its config" }, + { argv = ["vpt", "json-edit", "package.json", "scripts.lint:a", '"vp lint -c packages/a/.oxlintrc.json packages/a/index.js"'], snapshot = false }, + { argv = ["vpt", "json-edit", "package.json", "scripts.lint:b", '"vp lint -c packages/b/.oxlintrc.json packages/b/index.js"'], snapshot = false }, + { argv = ["vpt", "rm", "packages/b/extra.ts"], snapshot = false }, + { argv = ["vp", "migrate", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "New script references protect both completed and pending JSON configs on retry" }, + ["vpt", "stat-file", ".oxlintrc.json", "--assert", "file"], + ["vpt", "stat-file", "packages/a/.oxlintrc.json", "--assert", "file"], + ["vpt", "stat-file", "packages/b/.oxlintrc.json", "--assert", "file"], + ["vpt", "print-file", "packages/b/vite.config.mjs"], + { argv = ["vp", "run", "lint:a"], comment = "The completed config remains usable as JSON", continue-on-failure = true }, + { argv = ["vp", "run", "lint:b"], comment = "The pending config retains its complete inheritance chain", continue-on-failure = true }, + { argv = ["vpt", "json-edit", "package.json", "scripts", '{}'], continue-on-failure = true }, + { argv = ["vp", "migrate", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"], comment = "Removing the references allows migration and deferred cleanup to finish" }, + ["vpt", "stat-file", ".oxlintrc.json", "--assert", "missing"], + ["vpt", "stat-file", "packages/a/.oxlintrc.json", "--assert", "missing"], + ["vpt", "stat-file", "packages/b/.oxlintrc.json", "--assert", "missing"], + { argv = ["vp", "lint", "--threads=1", "index.js"], cwd = "packages/b", comment = "Plain lint enforces the inherited error after recovery", continue-on-failure = true }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.global.md new file mode 100644 index 0000000000..02b66cda7f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.global.md @@ -0,0 +1,172 @@ +# migration_oxc_reference_retry + +## `vpt json-edit .oxlintrc.json rules.no-debugger '"error"'` + + +## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` + +Package b cannot merge while extra.ts conflicts with its config + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +◆ ✔ Merged .oxlintrc.json into vite.config.mjs +● Formatting code... +● Code formatted +◇ Migrated . to Vite+ +• Node pnpm +✓ Dependencies installed in +• 1 config update applied +! Warnings: + - Could not migrate packages/b/.oxlintrc.json: Error: Cannot create shared Oxlint config because /packages/b/extra.ts or /packages/b/extra.js already exists. The original config was preserved; use vp lint -c packages/b/.oxlintrc.json until it is migrated manually. +``` + +## `vpt json-edit package.json scripts.lint:a '"vp lint -c packages/a/.oxlintrc.json packages/a/index.js"'` + + +## `vpt json-edit package.json scripts.lint:b '"vp lint -c packages/b/.oxlintrc.json packages/b/index.js"'` + + +## `vpt rm packages/b/extra.ts` + + +## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` + +New script references protect both completed and pending JSON configs on retry + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +This project is already using Vite+! Happy coding! +``` + +## `vpt stat-file .oxlintrc.json --assert file` + +``` +.oxlintrc.json: file +``` + +## `vpt stat-file packages/a/.oxlintrc.json --assert file` + +``` +packages/a/.oxlintrc.json: file +``` + +## `vpt stat-file packages/b/.oxlintrc.json --assert file` + +``` +packages/b/.oxlintrc.json: file +``` + +## `vpt print-file packages/b/vite.config.mjs` + +``` +export default {}; +// vp migrate: pending .oxlintrc.json. +``` + +## `vp run lint:a` + +The completed config remains usable as JSON + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint -c packages/a/.oxlintrc.json packages/a/index.js ⊘ cache disabled + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[packages/a/index.js:1:1] + 1 │ debugger; + · ───────── + ╰──── + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:b` + +The pending config retains its complete inheritance chain + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +$ vp lint -c packages/b/.oxlintrc.json packages/b/index.js ⊘ cache disabled + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[packages/b/index.js:1:1] + 1 │ debugger; + · ───────── + ╰──── + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vpt json-edit package.json scripts {}` + +``` +``` + +## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` + +Removing the references allows migration and deferred cleanup to finish + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +● Formatting code... +● Code formatted +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite → +• 1 config update applied +``` + +## `vpt stat-file .oxlintrc.json --assert missing` + +``` +.oxlintrc.json: missing +``` + +## `vpt stat-file packages/a/.oxlintrc.json --assert missing` + +``` +packages/a/.oxlintrc.json: missing +``` + +## `vpt stat-file packages/b/.oxlintrc.json --assert missing` + +``` +packages/b/.oxlintrc.json: missing +``` + +## `cd packages/b && vp lint --threads=1 index.js` + +Plain lint enforces the inherited error after recovery + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[index.js:1:1] + 1 │ debugger; + · ───────── + ╰──── + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.local.md new file mode 100644 index 0000000000..692b340575 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxc_reference_retry.local.md @@ -0,0 +1,167 @@ +# migration_oxc_reference_retry + +## `vpt json-edit .oxlintrc.json rules.no-debugger '"error"'` + + +## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` + +Package b cannot merge while extra.ts conflicts with its config + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +◆ ✔ Merged .oxlintrc.json into vite.config.mjs +● Formatting code... +● Code formatted +◇ Migrated . to Vite+ +• Node pnpm +✓ Dependencies installed in +• 1 config update applied +! Warnings: + - Could not migrate packages/b/.oxlintrc.json: Error: Cannot create shared Oxlint config because /packages/b/extra.ts or /packages/b/extra.js already exists. The original config was preserved; use vp lint -c packages/b/.oxlintrc.json until it is migrated manually. +``` + +## `vpt json-edit package.json scripts.lint:a '"vp lint -c packages/a/.oxlintrc.json packages/a/index.js"'` + + +## `vpt json-edit package.json scripts.lint:b '"vp lint -c packages/b/.oxlintrc.json packages/b/index.js"'` + + +## `vpt rm packages/b/extra.ts` + + +## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` + +New script references protect both completed and pending JSON configs on retry + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +This project is already using Vite+! Happy coding! +``` + +## `vpt stat-file .oxlintrc.json --assert file` + +``` +.oxlintrc.json: file +``` + +## `vpt stat-file packages/a/.oxlintrc.json --assert file` + +``` +packages/a/.oxlintrc.json: file +``` + +## `vpt stat-file packages/b/.oxlintrc.json --assert file` + +``` +packages/b/.oxlintrc.json: file +``` + +## `vpt print-file packages/b/vite.config.mjs` + +``` +export default {}; +// vp migrate: pending .oxlintrc.json. +``` + +## `vp run lint:a` + +The completed config remains usable as JSON + +**Exit code:** 1 + +``` +$ vp lint -c packages/a/.oxlintrc.json packages/a/index.js ⊘ cache disabled + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[packages/a/index.js:1:1] + 1 │ debugger; + · ───────── + ╰──── + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vp run lint:b` + +The pending config retains its complete inheritance chain + +**Exit code:** 1 + +``` +$ vp lint -c packages/b/.oxlintrc.json packages/b/index.js ⊘ cache disabled + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[packages/b/index.js:1:1] + 1 │ debugger; + · ───────── + ╰──── + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` + +## `vpt json-edit package.json scripts {}` + +``` +``` + +## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` + +Removing the references allows migration and deferred cleanup to finish + +``` +VITE+ - The Unified Toolchain for the Web + +▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. +● Formatting code... +● Code formatted +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite → +• 1 config update applied +``` + +## `vpt stat-file .oxlintrc.json --assert missing` + +``` +.oxlintrc.json: missing +``` + +## `vpt stat-file packages/a/.oxlintrc.json --assert missing` + +``` +packages/a/.oxlintrc.json: missing +``` + +## `vpt stat-file packages/b/.oxlintrc.json --assert missing` + +``` +packages/b/.oxlintrc.json: missing +``` + +## `cd packages/b && vp lint --threads=1 index.js` + +Plain lint enforces the inherited error after recovery + +**Exit code:** 1 + +``` + + × eslint(no-debugger): `debugger` statement is not allowed + ╭─[index.js:1:1] + 1 │ debugger; + · ───────── + ╰──── + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_full_setup.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_full_setup.md index 8ce5b6ecc2..fb7e528d8b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_full_setup.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_full_setup.md @@ -25,7 +25,7 @@ Finished in on 1 file with rules using threads. ## `vp migrate --full --no-interactive --no-agent --no-editor --no-hooks` -Prettier migration merges the root configs without migrating package configs +Recover workspace configs and merge the migrated Prettier config ``` VITE+ - The Unified Toolchain for the Web @@ -39,9 +39,11 @@ VITE+ - The Unified Toolchain for the Web • Dependencies: vite → ✓ Dependencies installed in -• 2 config updates applied +• 3 config updates applied • Prettier migrated to Oxfmt • Package manager settings configured +! Warnings: + - Could not migrate packages/b/.oxlintrc.json: Error: Cannot create shared Oxlint config because /packages/b/extra.ts or /packages/b/extra.js already exists. The original config was preserved; use vp lint -c packages/b/.oxlintrc.json until it is migrated manually. ``` ## `vpt print-file vite.config.mjs` @@ -52,6 +54,12 @@ The root uses its shared module // vp migrate: merged .oxlintrc.json (cca48660331feaa20cf74e4f147c8e10). import oxlintConfig1 from './.oxlintrc.js'; export default { + fmt: { + singleQuote: true, + printWidth: 80, + sortPackageJson: false, + ignorePatterns: [], + }, lint: { extends: [oxlintConfig1], options: {}, @@ -60,18 +68,12 @@ export default { 'vite-plus/prefer-vite-plus-imports': 'error', }, }, - fmt: { - singleQuote: true, - printWidth: 80, - sortPackageJson: false, - ignorePatterns: [], - }, }; ``` ## `vpt stat-file .oxlintrc.json --assert file` -The root JSON remains available to untouched consumers +The root JSON remains available to package b while its output conflict remains ``` .oxlintrc.json: file @@ -85,15 +87,32 @@ The root JSON remains available to untouched consumers ## `vpt print-file packages/a/vite.config.mjs` -The package Vite config remains unchanged +The recovered package uses the shared module + +``` +// vp migrate: merged packages%2Fa%2F.oxlintrc.json (e93b0fadd405db856ff1b8847eb5f2c3). +import oxlintConfig1 from './.oxlintrc.js'; +export default { + lint: { + extends: [oxlintConfig1], + options: {}, + jsPlugins: [{ name: 'vite-plus', specifier: 'vite-plus/oxlint-plugin' }], + rules: { + 'vite-plus/prefer-vite-plus-imports': 'error', + }, + }, +}; +``` + +## `vpt stat-file packages/a/.oxlintrc.json --assert file` ``` -export default {}; +packages/a/.oxlintrc.json: file ``` -## `cd packages/a && vp lint --threads=1 -c .oxlintrc.json index.js` +## `cd packages/a && vp lint --threads=1 index.js` -The preserved package JSON still reports the same diagnostic +The recovered package reports the same diagnostic ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_new_consumer.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_new_consumer.md index 0fd8c5438f..3716ab20bb 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_new_consumer.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends_workspace/snapshots/migration_oxlint_extends_workspace_new_consumer.md @@ -64,34 +64,51 @@ Finished in on 1 file with rules using threads. ## `vp migrate --no-interactive --no-agent --no-editor --no-hooks` -An upgrade does not treat shared modules as a pending migration for the new workspace +Recover the unreferenced workspace config even without a pending marker ``` VITE+ - The Unified Toolchain for the Web ▲ No package manager is declared in package.json; using pnpm for this migration without adding a pin. Run `vp env pin` to declare it explicitly. -This project is already using Vite+! Happy coding! +● Formatting code... +● Code formatted +◇ Updated . to Vite+ +• Node pnpm +• Dependencies: + vite → +• 1 config update applied ``` ## `vpt print-file packages/c/vite.config.mjs` -The unmarked workspace keeps its original Vite config +The workspace imports the existing shared module ``` -export default {}; +// vp migrate: merged packages%2Fc%2F.oxlintrc.json (f042a7ee2bd1c045ffb01c004fc0106b). +import oxlintConfig1 from "../../shared/base.js"; +export default { + lint: { + extends: [oxlintConfig1], + options: {}, + jsPlugins: [{ name: "vite-plus", specifier: "vite-plus/oxlint-plugin" }], + rules: { + "vite-plus/prefer-vite-plus-imports": "error", + }, + }, +}; ``` -## `vpt print-file packages/c/.oxlintrc.json` +## `vpt stat-file packages/c/.oxlintrc.json --assert missing` -Its newly authored JSON config remains intact +The recovered config no longer needs its standalone JSON ``` -{"extends":["../../shared/base.json"]} +packages/c/.oxlintrc.json: missing ``` -## `cd packages/c && vp lint --threads=1 -c .oxlintrc.json index.js` +## `cd packages/c && vp lint --threads=1 index.js` -The JSON config retains the same lint output +Plain lint retains the same diagnostic and exit status **Exit code:** 1 diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md index c5ff696153..4bb80c27a0 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_referenced_workspace_oxc_configs/snapshots/migration_referenced_workspace_oxc_configs_extends.md @@ -48,6 +48,7 @@ packages/app/.oxlintrc.json: missing ## `vpt print-file packages/app/vite.config.ts` ``` +// vp migrate: merged packages%2Fapp%2F.oxlintrc.json (b0b82d2e920b90c0ee84812a3a015741). import oxlintConfig1 from "./config/lint-base.js"; import { defineConfig } from 'vite-plus'; @@ -70,6 +71,7 @@ export default defineConfig({ ## `vpt print-file packages/app/config/lint-base.ts` ``` +// Generated by vp migrate from lint-base.jsonc. import type { OxlintConfig } from "vite-plus/lint"; import oxlintConfig1 from "../../../.oxlintrc.js"; diff --git a/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts b/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts index b533bf4bbd..adadd4f1b9 100644 --- a/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts +++ b/packages/cli/src/migration/__tests__/oxlint-extends.spec.ts @@ -12,7 +12,7 @@ import { mergeViteConfigFiles, prepareOxlintConfigs, resolveOxlintConfigExtends, - retryPreservedOxlintConfigs, + finalizeOxcConfigMigration, rewriteMonorepo, } from '../migrator.ts'; import { createMigrationReport } from '../report.ts'; @@ -145,6 +145,97 @@ describe('Oxlint JSON extends migration', () => { }, ); + it.each([ + ['retry', 'pending', 'script'], + ['retry', 'pending', 'module'], + ['retry', 'completed', 'script'], + ['retry', 'completed', 'module'], + ['recovery', 'pending', 'script'], + ['recovery', 'pending', 'module'], + ['recovery', 'completed', 'script'], + ['recovery', 'completed', 'module'], + ])('protects a %s config with a %s marker and a new %s reference', (mode, state, reference) => { + createWorkspace(); + write('.oxlintrc.json', { rules: { 'no-debugger': 'error' } }); + write('packages/a/.oxlintrc.json', { extends: ['../../.oxlintrc.json'] }); + write('packages/b/.oxlintrc.json', { extends: ['../a/.oxlintrc.json'] }); + const unsupported = "export { default } from './other.mjs';"; + const pendingViteConfig = write('packages/b/vite.config.mjs', unsupported); + expect(migrateWorkspace().warnings).toHaveLength(1); + fs.writeFileSync( + pendingViteConfig, + fs.readFileSync(pendingViteConfig, 'utf8').replace(unsupported, 'export default {};'), + ); + const target = state === 'pending' ? 'b' : 'a'; + const source = path.join(project, `packages/${target}/.oxlintrc.json`); + const original = fs.readFileSync(source, 'utf8'); + const viteConfig = path.join(project, `packages/${target}/vite.config.mjs`); + const originalViteConfig = fs.readFileSync(viteConfig, 'utf8'); + if (reference === 'script') { + write('package.json', { + name: 'root', + type: 'module', + scripts: { lint: `vp lint -c packages/${target}/.oxlintrc.json` }, + }); + } else { + fs.appendFileSync( + path.join(project, 'vite.config.mjs'), + `\nimport config from './packages/${target}/.oxlintrc.json' with { type: 'json' };\n`, + ); + } + const packages = ['a', 'b'].map((name) => ({ name, path: `packages/${name}` })); + const report = createMigrationReport(); + const resume = () => + mode === 'retry' + ? finalizeOxcConfigMigration(project, true, report, packages) + : finalizeCoreMigrationForExistingVitePlus({ rootDir: project, packages }, true, report) + .oxcConfigs; + + expect(resume()).toBe(state === 'completed'); + expect(report.warnings).toEqual([]); + expect(fs.readFileSync(source, 'utf8')).toBe(original); + expect(fs.readFileSync(viteConfig, 'utf8')).toBe(originalViteConfig); + expect(fs.existsSync(path.join(project, '.oxlintrc.json'))).toBe(true); + expect(resume()).toBe(false); + }); + + it.each(['script', 'module'])( + 'keeps dependencies relative to each JSON alias referenced by a %s', + (reference) => { + createWorkspace(); + write('shared/lint.json', { extends: ['./.oxlintrc.json'] }); + for (const name of ['a', 'b']) { + write(`packages/${name}/.oxlintrc.json`, { rules: { 'no-debugger': 'error' } }); + fs.symlinkSync('../../shared/lint.json', path.join(project, `packages/${name}/alias.json`)); + } + if (reference === 'script') { + write('package.json', { + name: 'root', + type: 'module', + scripts: { + lint: 'oxlint -c packages/a/alias.json && oxlint -c packages/b/alias.json', + }, + }); + } else { + write( + 'vite.config.mjs', + `import a from './packages/a/alias.json' with { type: 'json' }; +import b from './packages/b/alias.json' with { type: 'json' }; +export default { define: { configs: JSON.stringify([a, b]) } };`, + ); + } + expect(migrateWorkspace().warnings).toEqual([]); + for (const name of ['a', 'b']) { + expect( + resolveOxlintConfigExtends(path.join(project, `packages/${name}/alias.json`)).config, + ).toMatchObject({ extends: [{ rules: { 'no-debugger': 'error' } }] }); + expect( + fs.readFileSync(path.join(project, `packages/${name}/vite.config.mjs`), 'utf8'), + ).toBe('export default {};'); + } + }, + ); + it('resolves nested JSONC paths relative to each declaring file and retains inheritance order', async () => { const base = write( 'shared/base.jsonc', @@ -551,7 +642,7 @@ describe('Oxlint JSON extends migration', () => { ); const retryReport = createMigrationReport(); expect( - retryPreservedOxlintConfigs( + finalizeOxcConfigMigration( project, true, retryReport, @@ -769,7 +860,7 @@ describe('Oxlint JSON extends migration', () => { const report = createMigrationReport(); expect( - retryPreservedOxlintConfigs( + finalizeOxcConfigMigration( project, true, report, @@ -801,43 +892,41 @@ describe('Oxlint JSON extends migration', () => { write('.oxlintrc.json', { rules: { 'no-debugger': 'error' } }); } - expect(retryPreservedOxlintConfigs(project, true)).toBe(false); + expect(finalizeOxcConfigMigration(project, true)).toBe(false); expect(fs.existsSync(path.join(project, '.oxlintrc.json'))).toBe(true); }, ); - it('does not retry unrelated JSON configs in an existing Vite+ project', () => { + it('recovers unreferenced JSON configs without a pending marker in an existing Vite+ project', () => { write('base.json', { rules: { 'no-debugger': 'error' } }); write('.oxlintrc.json', { extends: ['./base.json'] }); const report = createMigrationReport(); - expect(retryPreservedOxlintConfigs(project, true, report)).toBe(false); + expect(finalizeOxcConfigMigration(project, true, report)).toBe(true); expect(report.warnings).toEqual([]); - expect(fs.existsSync(path.join(project, '.oxlintrc.json'))).toBe(true); - expect(fs.existsSync(path.join(project, 'base.ts'))).toBe(false); - expect(fs.readFileSync(path.join(project, 'vite.config.mjs'), 'utf8')).toBe( - 'export default {};', - ); + expect(fs.existsSync(path.join(project, '.oxlintrc.json'))).toBe(false); + expect(fs.existsSync(path.join(project, 'base.ts'))).toBe(true); + expect(fs.readFileSync(path.join(project, 'vite.config.mjs'), 'utf8')).toContain('lint:'); + expect(finalizeOxcConfigMigration(project, true, report)).toBe(false); }); - it('does not retry a new workspace config that references a previously generated module', () => { + it('recovers an unmarked workspace config with a previously generated shared module', () => { write('base.json', { rules: { 'no-debugger': 'error' } }); write('.oxlintrc.json', { extends: ['./base.json'] }); mergeViteConfigFiles(project, true); createWorkspace(['a']); const source = write('packages/a/.oxlintrc.json', { extends: ['../../base.json'] }); - const original = fs.readFileSync(source, 'utf8'); const report = createMigrationReport(); expect( - retryPreservedOxlintConfigs(project, true, report, [{ name: 'a', path: 'packages/a' }]), - ).toBe(false); + finalizeOxcConfigMigration(project, true, report, [{ name: 'a', path: 'packages/a' }]), + ).toBe(true); expect(report.warnings).toEqual([]); - expect(fs.readFileSync(source, 'utf8')).toBe(original); - expect(fs.readFileSync(path.join(project, 'packages/a/vite.config.mjs'), 'utf8')).toBe( - 'export default {};', + expect(fs.existsSync(source)).toBe(false); + expect(fs.readFileSync(path.join(project, 'packages/a/vite.config.mjs'), 'utf8')).toContain( + 'lint:', ); expect(fs.existsSync(path.join(project, 'base.ts'))).toBe(true); }); @@ -853,7 +942,7 @@ describe('Oxlint JSON extends migration', () => { const report = createMigrationReport(); expect( - retryPreservedOxlintConfigs( + finalizeOxcConfigMigration( project, true, report, @@ -886,7 +975,7 @@ describe('Oxlint JSON extends migration', () => { expect(firstReport.warnings).toHaveLength(1); if (failure === 'missing source') { const pendingReport = createMigrationReport(); - expect(retryPreservedOxlintConfigs(project, true, pendingReport)).toBe(false); + expect(finalizeOxcConfigMigration(project, true, pendingReport)).toBe(false); expect(pendingReport.warnings).toHaveLength(1); write('base.json', { rules: { 'no-debugger': 'error' } }); } else { @@ -894,7 +983,7 @@ describe('Oxlint JSON extends migration', () => { } const report = createMigrationReport(); - expect(retryPreservedOxlintConfigs(project, true, report)).toBe(true); + expect(finalizeOxcConfigMigration(project, true, report)).toBe(true); expect(report.warnings).toEqual([]); expect(fs.existsSync(path.join(project, '.oxlintrc.json'))).toBe(false); @@ -914,7 +1003,7 @@ describe('Oxlint JSON extends migration', () => { const report = createMigrationReport(); expect( - retryPreservedOxlintConfigs(project, true, report, [{ name: 'a', path: 'packages/a' }]), + finalizeOxcConfigMigration(project, true, report, [{ name: 'a', path: 'packages/a' }]), ).toBe(true); expect(report.warnings).toEqual([]); const modulePath = path.join(project, '.oxlintrc.ts'); @@ -1169,7 +1258,7 @@ describe('Oxlint JSON extends migration', () => { fs.readFileSync(configPath, 'utf8').replace(viteConfig, 'export default {};'), ); const retryReport = createMigrationReport(); - expect(retryPreservedOxlintConfigs(project, true, retryReport)).toBe(true); + expect(finalizeOxcConfigMigration(project, true, retryReport)).toBe(true); expect(retryReport.warnings).toEqual([]); expect(fs.existsSync(source)).toBe(false); expect((await loadConfig()).lint!.extends![0].rules!['no-debugger']).toBe('error'); diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index 827005f01b..2098078ce2 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -77,7 +77,6 @@ import { rewriteStandaloneProject, planVitestV5Migration, refreshVitestV5Migration, - retryPreservedOxlintConfigs, prepareOxlintConfigs, removeMigratedOxlintConfigs, shouldSkipStagedMigrationForHooks, @@ -1206,17 +1205,6 @@ async function main() { didMigrate = true; } - if ( - retryPreservedOxlintConfigs( - workspaceInfoOptional.rootDir, - true, - report, - workspaceInfoOptional.packages, - ) - ) { - didMigrate = true; - } - // finalizeCore has finished; stop the spinner before the interactive setup // prompts below (collectMigrationSetupPlan / framework shims, in --full mode) // so they don't render on top of the still-running progress line. diff --git a/packages/cli/src/migration/migrator/core-finalization.ts b/packages/cli/src/migration/migrator/core-finalization.ts index 5084556a4f..231bc37982 100644 --- a/packages/cli/src/migration/migrator/core-finalization.ts +++ b/packages/cli/src/migration/migrator/core-finalization.ts @@ -5,16 +5,12 @@ import { rewriteScripts } from '../../../binding/index.js'; import { type WorkspacePackage } from '../../types/index.ts'; import { editJsonFile, readJsonFile } from '../../utils/json.ts'; import { rulesDir } from '../../utils/path.ts'; -import { detectConfigs } from '../detector.ts'; import { dropDeadOxlintPluginsDependency, - findReferencedOxcConfigs, + finalizeOxcConfigMigration, hasTsconfigTypesToRewrite, mergeTsdownConfigFile, - mergeViteConfigFiles, migrateTaskCacheConfigInViteConfig, - prepareOxlintConfigs, - removeMigratedOxlintConfigs, rewriteAllImports, rewriteTsconfigTypes, } from '../migrator.ts'; @@ -200,33 +196,11 @@ export function finalizeCoreMigrationForExistingVitePlus( // A failed migration may have installed Vite+ before merging these files. // Finish that core work without opting into unrelated first-time setup. - // Resolve every inheritance graph before any workspace removes a source. - const oxlintConfigs = prepareOxlintConfigs(projectPaths); - const referencedConfigs = findReferencedOxcConfigs(projectPaths); - for (const projectPath of projectPaths) { - const configs = detectConfigs(projectPath); - const standaloneConfigs = [configs.oxlintConfig, configs.oxfmtConfig].filter( - (config) => config !== undefined, - ); - if (standaloneConfigs.length === 0) { - continue; - } - mergeViteConfigFiles( - projectPath, - silent, - report, - workspaceInfo.packages, - workspaceInfo.rootDir, - oxlintConfigs, - referencedConfigs, - ); - if (standaloneConfigs.some((config) => !fs.existsSync(path.join(projectPath, config)))) { - result.oxcConfigs = true; - } - } - removeMigratedOxlintConfigs(oxlintConfigs); - result.oxcConfigs ||= [...oxlintConfigs.values()].some( - (migration) => !(migration instanceof Error) && migration.migrated, + result.oxcConfigs = finalizeOxcConfigMigration( + workspaceInfo.rootDir, + silent, + report, + workspaceInfo.packages, ); return result; diff --git a/packages/cli/src/migration/migrator/oxc-references.ts b/packages/cli/src/migration/migrator/oxc-references.ts index b16cd63d29..8fe08fab5e 100644 --- a/packages/cli/src/migration/migrator/oxc-references.ts +++ b/packages/cli/src/migration/migrator/oxc-references.ts @@ -1,7 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; -import { hasConfigKey } from '../../../binding/index.js'; import { readJsonFile } from '../../utils/json.ts'; import { detectConfigs } from '../detector.ts'; @@ -39,7 +38,7 @@ function resolveConfigReferences(directory: string, reference: string): string[] for (const candidate of candidates) { if (fs.existsSync(candidate)) { if (fs.statSync(candidate).isFile()) { - resolved.add(fs.realpathSync(candidate)); + resolved.add(candidate); } else if (fs.existsSync(path.join(candidate, 'package.json'))) { // A directory's main/module/exports fields can supersede its index. // Keep the configs even if another candidate above was found. @@ -65,8 +64,16 @@ export function findReferencedOxcConfigs(projectPaths: string[]): Set { const configReferences: { directory: string; content: string }[] = []; const lintConfigPaths = new Set(); const preservedConfigs = new Set(); + function preserveJsonConfig(filename: string): void { + preservedConfigs.add(fs.realpathSync(filename)); + // Match sources by identity, but resolve extends from the declaring alias. + lintConfigPaths.add( + path.join(fs.realpathSync(path.dirname(filename)), path.basename(filename)), + ); + } const modulePaths = new Set(); const unresolvedConfigDirectories = new Set(); + const migrationCandidates = new Set(); for (const directory of projectDirectories) { const packageJsonPath = path.join(directory, 'package.json'); if (fs.existsSync(packageJsonPath)) { @@ -101,10 +108,9 @@ export function findReferencedOxcConfigs(projectPaths: string[]): Set { } for (const filename of filenames ?? []) { if (/\.jsonc?$/.test(filename)) { - preservedConfigs.add(filename); - lintConfigPaths.add(filename); + preserveJsonConfig(filename); } else if (CONFIG_MODULE_EXTENSIONS.includes(path.extname(filename))) { - modulePaths.add(filename); + modulePaths.add(fs.realpathSync(filename)); } else { unresolvedConfigDirectories.add(directory); } @@ -114,12 +120,32 @@ export function findReferencedOxcConfigs(projectPaths: string[]): Set { } } const projectConfigs = detectConfigs(directory); + if (projectConfigs.oxlintConfig) { + migrationCandidates.add(path.join(directory, projectConfigs.oxlintConfig)); + } if (projectConfigs.viteConfig) { modulePaths.add(fs.realpathSync(path.join(directory, projectConfigs.viteConfig))); } } for (const modulePath of modulePaths) { - const content = fs.readFileSync(modulePath, 'utf8'); + const source = fs.readFileSync(modulePath, 'utf8'); + const provenance = source.match(/^\/\/ Generated by vp migrate from (\S+)\.\r?(?:\n|$)/); + if (provenance) { + // Custom JSON sources remain on disk after conversion. On a retry their + // workspace consumer may be gone, but their inherited sources are still needed. + try { + const filename = path.resolve(path.dirname(modulePath), decodeURIComponent(provenance[1])); + if (fs.existsSync(filename) && !migrationCandidates.has(filename)) { + preserveJsonConfig(filename); + } + } catch { + // A user-edited provenance comment is not a resolvable source reference. + } + } + const content = source + // Migration bookkeeping names a source without consuming it at runtime. + .replace(/^\/\/ vp migrate: (?:pending \S+\.|merged \S+ \([a-f0-9]{32}\)\.)\r?$/gm, '') + .replace(/^\/\/ Generated by vp migrate from \S+\.\r?$/gm, ''); const directory = referenceDirectory(path.dirname(modulePath)); // Quoted paths and package aliases cover imports, re-exports, require(), // dynamic import(), and readFileSync/new URL calls, including comments @@ -134,10 +160,9 @@ export function findReferencedOxcConfigs(projectPaths: string[]): Set { } for (const filename of filenames ?? []) { if (/\.jsonc?$/.test(filename)) { - preservedConfigs.add(filename); - lintConfigPaths.add(filename); + preserveJsonConfig(filename); } else if (CONFIG_MODULE_EXTENSIONS.includes(path.extname(filename))) { - modulePaths.add(filename); + modulePaths.add(fs.realpathSync(filename)); } } return filenames?.length ? '' : match; @@ -147,26 +172,17 @@ export function findReferencedOxcConfigs(projectPaths: string[]): Set { } for (const directory of projectDirectories) { const configs = detectConfigs(directory); - for (const [filename, key] of [ - [configs.oxlintConfig, 'lint'], - [configs.oxfmtConfig, 'fmt'], - ] as const) { + for (const filename of [configs.oxlintConfig, configs.oxfmtConfig]) { if (!filename) { continue; } - const canonicalPath = fs.realpathSync(path.join(directory, filename)); - if (configs.viteConfig && hasConfigKey(path.join(directory, configs.viteConfig), key)) { - // Deferred graph cleanup retains this consumer's JSON dependencies. - preservedConfigs.add(canonicalPath); - } if ( unresolvedConfigDirectories.has(directory) || configReferences.some( (reference) => reference.directory === directory && reference.content.includes(filename), ) ) { - preservedConfigs.add(canonicalPath); - lintConfigPaths.add(canonicalPath); + preserveJsonConfig(path.join(directory, filename)); } } } @@ -179,15 +195,12 @@ export function findReferencedOxcConfigs(projectPaths: string[]): Set { const json = readJsonFile(lintConfigPath, true) as { extends?: unknown } | null | undefined; if (Array.isArray(json?.extends) && json.extends.length > 0) { // A retained JSON consumer still needs its complete inheritance chain. - preservedConfigs.add(lintConfigPath); for (const extendedConfig of json.extends) { if (typeof extendedConfig === 'string') { const resolvedPath = path.resolve(path.dirname(lintConfigPath), extendedConfig); - const extendedConfigPath = fs.existsSync(resolvedPath) - ? fs.realpathSync(resolvedPath) - : resolvedPath; - preservedConfigs.add(extendedConfigPath); - lintConfigPaths.add(extendedConfigPath); + if (fs.existsSync(resolvedPath)) { + preserveJsonConfig(resolvedPath); + } } } } diff --git a/packages/cli/src/migration/migrator/vite-config.ts b/packages/cli/src/migration/migrator/vite-config.ts index 107de96e38..263e026db6 100644 --- a/packages/cli/src/migration/migrator/vite-config.ts +++ b/packages/cli/src/migration/migrator/vite-config.ts @@ -40,7 +40,7 @@ import { sanitizeMigratedOxlintConfig, sanitizeInheritedJsPlugins, } from '../migrator.ts'; -import { type MigrationReport } from '../report.ts'; +import { createMigrationReport, type MigrationReport } from '../report.ts'; import { type OxlintConfigMigration, type OxlintModulePlan, @@ -291,21 +291,6 @@ export function mergeViteConfigFiles( configs.oxlintConfig && referencedConfigs.has(fs.realpathSync(path.join(projectPath, configs.oxlintConfig))) ) { - const json = readJsonFile(path.join(projectPath, configs.oxlintConfig), true) as - | { extends?: unknown } - | null - | undefined; - if ( - configs.viteConfig && - hasConfigKey(path.join(projectPath, configs.viteConfig), 'lint') && - Array.isArray(json?.extends) && - json.extends.length > 0 - ) { - warnMigration( - `Preserved ${displayRelative(path.join(projectPath, configs.oxlintConfig))} because ${configs.viteConfig} already has a lint config. Merge its inherited rules manually.`, - report, - ); - } configs.oxlintConfig = undefined; } if ( @@ -340,48 +325,33 @@ function pendingOxlintMigrationMarker(filename: string): string { return `// vp migrate: pending ${encodeURIComponent(path.basename(filename))}.`; } -/** Resume partial migrations without applying lint setup to other existing Vite+ projects. */ -export function retryPreservedOxlintConfigs( +/** Finish remaining Oxc merges and deferred cleanup in an existing Vite+ workspace. */ +export function finalizeOxcConfigMigration( workspaceRoot: string, silent = false, - report?: MigrationReport, + report: MigrationReport = createMigrationReport(), packages?: WorkspacePackage[], ): boolean { - const prepared = prepareOxlintConfigs([ + const projectPaths = [ workspaceRoot, ...(packages ?? []).map((pkg) => path.join(workspaceRoot, pkg.path)), - ]); - let updated = false; - for (const [filename, migration] of prepared) { - const projectPath = path.dirname(filename); - const configs = detectConfigs(projectPath); - const existingViteConfig = configs.viteConfig && path.join(projectPath, configs.viteConfig); - const source = existingViteConfig ? fs.readFileSync(existingViteConfig, 'utf8') : ''; - const lines = source.split(/\r?\n/); - if (existingViteConfig && hasConfigKey(existingViteConfig, 'lint')) { - if (!(migration instanceof Error)) { - const marker = oxlintMigrationMarker(filename, migration.file, workspaceRoot); - migration.migrated = lines.includes(marker); - } - continue; - } - if (!lines.includes(pendingOxlintMigrationMarker(filename))) { - continue; - } - const viteConfig = ensureViteConfig(projectPath, configs, silent, report); - mergeOxlintConfigFile( + ]; + // Resolve every graph and reference before any workspace removes a source. + const prepared = prepareOxlintConfigs(projectPaths); + const referencedConfigs = findReferencedOxcConfigs(projectPaths); + const mergedConfigCount = report.mergedConfigCount; + for (const projectPath of projectPaths) { + mergeViteConfigFiles( projectPath, - viteConfig, - path.basename(filename), silent, report, packages, workspaceRoot, prepared, + referencedConfigs, ); - updated ||= !(migration instanceof Error) && migration.migrated === true; } - return removeMigratedOxlintConfigs(prepared) || updated; + return removeMigratedOxlintConfigs(prepared) || report.mergedConfigCount > mergedConfigCount; } function mergeOxlintConfigFile( @@ -432,6 +402,16 @@ function mergeOxlintConfigFile( return; } const { file } = migration; + const fullViteConfigPath = path.join(projectPath, viteConfig); + const hasLintConfig = hasConfigKey(fullViteConfigPath, 'lint'); + if (hasLintConfig && preparedOxlintConfigs) { + const marker = oxlintMigrationMarker(fullOxlintPath, file, workspaceRoot ?? projectPath); + if (fs.readFileSync(fullViteConfigPath, 'utf8').split(/\r?\n/).includes(marker)) { + // A completed merge can retain its source until another workspace is repaired. + migration.migrated = true; + return; + } + } // Keep shared rules in one module, including for the workspace that owns it. // Fields that Oxlint does not inherit must also remain in the owner's local block. const sourceConfig: OxlintConfig = structuredClone(file.config); @@ -447,12 +427,14 @@ function mergeOxlintConfigFile( }; } // Never discard an inheritance tree when a lint block already exists. - if (oxlintJson.extends?.length && hasConfigKey(path.join(projectPath, viteConfig), 'lint')) { - recordPendingMigration(); - warnMigration( - `Preserved ${displayRelative(fullOxlintPath)} because ${viteConfig} already has a lint config. Merge its inherited rules manually.`, - report, - ); + if (hasLintConfig) { + if (oxlintJson.extends?.length) { + recordPendingMigration(); + warnMigration( + `Preserved ${displayRelative(fullOxlintPath)} because ${viteConfig} already has a lint config. Merge its inherited rules manually.`, + report, + ); + } return; } const layers = oxlintConfigLayers(oxlintJson); @@ -703,15 +685,8 @@ function mergeAndRemoveJsonConfig( // AST-based check ignores comments, string-literal occurrences, and nested // keys (e.g. `plugins: [{ fmt: ... }]`). if (hasConfigKey(fullViteConfigPath, configKey)) { - if (!deferRemoval) { - fs.unlinkSync(fullJsonConfigPath); - } - if (!silent) { - prompts.log.info( - `${configKey} config already present in ${displayRelative(fullViteConfigPath)} — ${deferRemoval ? 'deferred removal of' : 'removed redundant'} ${displayRelative(fullJsonConfigPath)}`, - ); - } - return true; + // An existing block does not prove that this file has already been merged. + return false; } // Only delete the original JSON after a successful merge. Resolved lint // configs contain object extends, which JSON Oxlint configs do not accept.