Skip to content

Support XO v1+#1819

Merged
webpro merged 3 commits into
webpro-nl:mainfrom
patrik-csak:xo
Jun 27, 2026
Merged

Support XO v1+#1819
webpro merged 3 commits into
webpro-nl:mainfrom
patrik-csak:xo

Conversation

@patrik-csak

@patrik-csak patrik-csak commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Update XO plugin to support XO v1+
    • Update config and entry to support XO v1+ config files
    • Update types
    • Determine ESLint config type (flat or deprecated) based on installed XO version
  • Rename xo fixture to xo-0 and create new xo fixture for XO v1+
    • Translate fixtures/plugins/xo-0/.xo-config.js to fixtures/plugins/xo/xo.config.js
      • Create mock eslint-plugin-unused-imports because in eslint flat config, plugins can't be referenced by string, they must be imported
    • Translate fixtures/plugins/xo-0/xo.config.cjs to fixtures/plugins/xo/xo.config.ts
  • Rename mock config glob to my-shared-config for clarity

XO config history

xo version supported configs change from previous
v0 package.json, .xo-config.{js,cjs,json}, xo.config.{js,cjs}
v1 package.json, xo.config.{js,cjs,mjs,ts,cts,mts} require flat config, drop .xo-config.*
v2 package.json, xo.config.{js,mjs,ts,mts} drop xo.config.{cjs,cts}
v3 " none

Copilot AI review requested due to automatic review settings June 25, 2026 19:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds XO v1+ (flat-config era) support to Knip’s XO plugin by expanding supported config filenames, distinguishing XO v0 vs v1+ behavior in tests/fixtures, and allowing ESLint config input extraction to be overridden for non-eslint.config.* flat configs.

Changes:

  • Extend XO plugin config/entry globs to include xo.config.{mjs,ts,cts,mts} and introduce XO-major-based flat/deprecated handling.
  • Add separate fixture + test coverage for XO v0 (.xo-config.* / deprecated config shape) vs XO v1+ (flat xo.config.*).
  • Update ESLint helper getInputs to accept an optional “flat config override” flag (used by the XO plugin).

Reviewed changes

Copilot reviewed 10 out of 19 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/knip/src/plugins/xo/index.ts Adds XO major-version detection and treats xo.config.* as flat-config inputs.
packages/knip/src/plugins/xo/types.ts Broadens XO config typing to support both v0 deprecated configs and v1+ flat config items/arrays.
packages/knip/src/plugins/eslint/helpers.ts Adds isFlatConfigOverride param to route non-eslint.config.* configs through flat/deprecated extraction.
packages/knip/test/plugins/xo.test.ts Updates XO plugin test to assert unlisted deps from flat xo.config.{js,ts} fixtures.
packages/knip/test/plugins/xo-0.test.ts Adds XO v0-specific test asserting deprecated .xo-config.* and package.json plugin resolution.
packages/knip/fixtures/plugins/xo/package.json Updates fixture to XO ^3.0.2 and removes deprecated plugins field usage.
packages/knip/fixtures/plugins/xo/xo.config.js Adds flat XO config file importing eslint-plugin-unused-imports (to be detected as unlisted).
packages/knip/fixtures/plugins/xo/xo.config.ts Adds flat XO config file importing my-shared-config (to be detected as unlisted).
packages/knip/fixtures/plugins/xo/node_modules/my-shared-config/package.json Adds dummy resolvable package for my-shared-config fixture import.
packages/knip/fixtures/plugins/xo/node_modules/my-shared-config/index.js Provides the module content for my-shared-config.
packages/knip/fixtures/plugins/xo/node_modules/eslint-plugin-unused-imports/package.json Adds dummy resolvable package for eslint-plugin-unused-imports fixture import.
packages/knip/fixtures/plugins/xo/node_modules/eslint-plugin-unused-imports/index.js Provides the module content for eslint-plugin-unused-imports.
packages/knip/fixtures/plugins/xo-0/package.json Adds XO v0 fixture manifest (XO ^0.60.0) with deprecated package.json config.
packages/knip/fixtures/plugins/xo-0/.xo-config.js Adds XO v0 deprecated .xo-config.js fixture that references unused-imports.
packages/knip/fixtures/plugins/xo-0/xo.config.cjs Adjusts XO v0 xo.config.cjs fixture to require my-shared-config.
packages/knip/fixtures/plugins/xo-0/node_modules/xo/package.json Fixes dummy package name to xo for the XO v0 fixture environment.
packages/knip/fixtures/plugins/xo-0/node_modules/my-shared-config/package.json Adds dummy resolvable package for the XO v0 fixture require.

Comment thread packages/knip/src/plugins/xo/index.ts Outdated
@patrik-csak
patrik-csak marked this pull request as draft June 25, 2026 19:31

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed from packages/knip/fixtures/plugins/xo/package.json

Comment on lines -11 to -13
"plugins": [
"eslint-comments"
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied from packages/knip/fixtures/plugins/xo-0/xo.config.js and converted to flat config

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied from packages/knip/fixtures/plugins/xo-0/xo.config.ts and converted to flat config

Comment on lines +13 to +21
options: PluginOptions,
isFlatConfigOverride?: boolean
): (Input | ConfigInput)[] => {
const { configFileName } = options;

if (extname(configFileName) === '.json' || !isFlatConfig(configFileName)) {
const isFlatConfigFile =
isFlatConfigOverride ?? (extname(configFileName) !== '.json' && isFlatConfig(configFileName));

if (!isFlatConfigFile) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a quick and dirty way to allow the XO plugin to determine whether the config is flat or deprecated. I didn’t spend much time on it, assuming that it would need to be discussed.

Comment thread packages/knip/src/plugins/xo/index.ts Outdated

const resolveConfig: ResolveConfig<XOConfig> = async (config, options) => {
const inputs = getInputs(config, options);
const xoVersion = options.manifest.getMajor('xo') ?? 3;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Falling back to the latest version to match behavior of the react email plugin

const dep = (manifest.getMajor('react-email') ?? 6) >= 6 ? '@react-email/ui' : '@react-email/preview-server';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed from packages/knip/test/plugins/xo.test.ts

Comment on lines +14 to 16
assert(issues.unlisted['xo.config.js']['eslint-plugin-unused-imports']);
assert(issues.unlisted['xo.config.ts']['my-shared-config']);

@patrik-csak patrik-csak Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • eslint-plugin-unused-imports changes from unresolved to unlisted because it must be imported in flat config as opposed to referenced by string in deprecated config

    + import unusedImports from 'eslint-plugin-unused-imports';- plugins: ['unused-imports'],
    + plugins: {
    +   'unused-imports': unusedImports,
    + },
  • Renamed glob to my-shared-config

  • Removed package.json unresolved plugin check because XO v1+ only supports serializable values in package.json.xo, so there's nothing we can add to package.json.xo to exercise Knip

@patrik-csak
patrik-csak marked this pull request as ready for review June 25, 2026 20:29
@webpro

webpro commented Jun 26, 2026

Copy link
Copy Markdown
Member

Thanks for the PR! Would you mind if I add a commit and you can review that? I think we could simplify a bit here.

@patrik-csak

Copy link
Copy Markdown
Contributor Author

Not at all!

xo@1+ uses ESLint flat config: plugins are referenced via `import`, so
they're already detected as entry-file imports. Resolve those configs from
the AST (reusing the ESLint flat-config resolver) and skip loading them,
gated on the installed xo major. xo@0 keeps the loaded eslintrc path.

- Mirrors the ESLint plugin (isLoadConfig=false + resolveFromAST for flat)
- Avoids spurious "Error loading xo.config.*" when a config spreads real
  plugins (executing them can throw, e.g. on an uninstalled peer)
- Drops the getInputs override and the unused flat config type
@webpro

webpro commented Jun 26, 2026

Copy link
Copy Markdown
Member

There you go, let me know what you think. Imho it doesn't have to be all that perfect across each and every version of xo, as long as we grab entry points and dependencies properly.

@pkg-pr-new

pkg-pr-new Bot commented Jun 26, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/knip@1819
npm i https://pkg.pr.new/@knip/language-server@1819
npm i https://pkg.pr.new/@knip/mcp@1819

commit: 613d09e

@patrik-csak

Copy link
Copy Markdown
Contributor Author

Looks good to me

@webpro
webpro merged commit 1dffe36 into webpro-nl:main Jun 27, 2026
30 checks passed
@webpro

webpro commented Jun 27, 2026

Copy link
Copy Markdown
Member

🚀 This pull request is included in v6.22.0. See Release 6.22.0 for release notes.

Using Knip in a commercial project? Please consider becoming a sponsor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants