From 96f91df9c6884c28538ce47becff539e6717e16d Mon Sep 17 00:00:00 2001 From: Bill Beesley Date: Tue, 7 May 2024 12:00:03 +0100 Subject: [PATCH] feat(plugins): add plugin for xo config (#621) * feat(plugins): add plugin for xo config Adds a plugin for [xo](https://github.com/xojs/xo#config). Essentially its an opinionated wrapper around eslint with a bunch of bundled rules and plugins, and the config object is just the eslint config object with some extra options. So to keep it consistent with the eslint plugin and avoid duplication I've just used the eslint helper to parse out any additional eslint plugins that have been added in the xo config. * Add xo bin --------- Co-authored-by: Lars Kappert --- .../knip/fixtures/plugins/xo/.xo-config.js | 9 +++++ .../plugins/xo/node_modules/xo/index.js | 0 .../plugins/xo/node_modules/xo/package.json | 4 +++ .../knip/fixtures/plugins/xo/package.json | 13 +++++++ .../knip/fixtures/plugins/xo/xo-config.cjs | 8 +++++ packages/knip/schema.json | 4 +++ packages/knip/src/ConfigurationValidator.ts | 1 + packages/knip/src/plugins/index.ts | 1 + packages/knip/src/plugins/xo/index.ts | 34 +++++++++++++++++++ packages/knip/src/plugins/xo/types.ts | 13 +++++++ packages/knip/test/plugins/xo.test.ts | 26 ++++++++++++++ 11 files changed, 113 insertions(+) create mode 100644 packages/knip/fixtures/plugins/xo/.xo-config.js create mode 100644 packages/knip/fixtures/plugins/xo/node_modules/xo/index.js create mode 100644 packages/knip/fixtures/plugins/xo/node_modules/xo/package.json create mode 100644 packages/knip/fixtures/plugins/xo/package.json create mode 100644 packages/knip/fixtures/plugins/xo/xo-config.cjs create mode 100644 packages/knip/src/plugins/xo/index.ts create mode 100644 packages/knip/src/plugins/xo/types.ts create mode 100644 packages/knip/test/plugins/xo.test.ts diff --git a/packages/knip/fixtures/plugins/xo/.xo-config.js b/packages/knip/fixtures/plugins/xo/.xo-config.js new file mode 100644 index 000000000..836dbfa6d --- /dev/null +++ b/packages/knip/fixtures/plugins/xo/.xo-config.js @@ -0,0 +1,9 @@ +module.exports = { + space: true, + prettier: true, + plugins: ['unused-imports'], + parserOptions: {emitDecoratorMetadata: true}, + rules: { + 'func-names': ['error', 'always'], + }, +}; diff --git a/packages/knip/fixtures/plugins/xo/node_modules/xo/index.js b/packages/knip/fixtures/plugins/xo/node_modules/xo/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/knip/fixtures/plugins/xo/node_modules/xo/package.json b/packages/knip/fixtures/plugins/xo/node_modules/xo/package.json new file mode 100644 index 000000000..0510f1374 --- /dev/null +++ b/packages/knip/fixtures/plugins/xo/node_modules/xo/package.json @@ -0,0 +1,4 @@ +{ + "name": "xo", + "bin": "index.js" +} diff --git a/packages/knip/fixtures/plugins/xo/package.json b/packages/knip/fixtures/plugins/xo/package.json new file mode 100644 index 000000000..1eed5e227 --- /dev/null +++ b/packages/knip/fixtures/plugins/xo/package.json @@ -0,0 +1,13 @@ +{ + "name": "@fixtures/xo", + "scripts": { + "test": "xo" + }, + "devDependencies": { + "xo": "^0.24.0" + }, + "xo": { + "space": true, + "plugins": ["eslint-comments"] + } +} diff --git a/packages/knip/fixtures/plugins/xo/xo-config.cjs b/packages/knip/fixtures/plugins/xo/xo-config.cjs new file mode 100644 index 000000000..2780f4402 --- /dev/null +++ b/packages/knip/fixtures/plugins/xo/xo-config.cjs @@ -0,0 +1,8 @@ +const mySharedConfig = require('glob'); + +module.exports = { + ...mySharedConfig, + rules: { + 'func-names': 'off', + }, +}; \ No newline at end of file diff --git a/packages/knip/schema.json b/packages/knip/schema.json index f732b9a8a..e57fee421 100644 --- a/packages/knip/schema.json +++ b/packages/knip/schema.json @@ -507,6 +507,10 @@ "title": "wrangler plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/wrangler/README.md)", "$ref": "#/definitions/plugin" }, + "xo": { + "title": "xo plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/xo/README.md)", + "$ref": "#/definitions/plugin" + }, "yorkie": { "title": "yorkie plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/yorkie/README.md)", "$ref": "#/definitions/plugin" diff --git a/packages/knip/src/ConfigurationValidator.ts b/packages/knip/src/ConfigurationValidator.ts index 1c201e237..a81858b7e 100644 --- a/packages/knip/src/ConfigurationValidator.ts +++ b/packages/knip/src/ConfigurationValidator.ts @@ -135,6 +135,7 @@ const pluginsSchema = z.object({ webpack: pluginSchema, wireit: pluginSchema, wrangler: pluginSchema, + xo: pluginSchema, yorkie: pluginSchema, }); diff --git a/packages/knip/src/plugins/index.ts b/packages/knip/src/plugins/index.ts index 09a31f3d5..d8c18f918 100644 --- a/packages/knip/src/plugins/index.ts +++ b/packages/knip/src/plugins/index.ts @@ -58,3 +58,4 @@ export { default as webpack } from './webpack/index.js'; export { default as wireit } from './wireit/index.js'; export { default as wrangler } from './wrangler/index.js'; export { default as yorkie } from './yorkie/index.js'; +export { default as xo } from './xo/index.js'; diff --git a/packages/knip/src/plugins/xo/index.ts b/packages/knip/src/plugins/xo/index.ts new file mode 100644 index 000000000..055f15a3f --- /dev/null +++ b/packages/knip/src/plugins/xo/index.ts @@ -0,0 +1,34 @@ +import type { EnablerPatterns } from '#p/types/config.js'; +import type { IsPluginEnabled, Plugin, ResolveConfig } from '#p/types/plugins.js'; +import { hasDependency } from '#p/util/plugin.js'; +import { getDependenciesDeep } from '../eslint/helpers.js'; +import type { XOConfig } from './types.js'; + +// link to xo docs: https://github.com/xojs/xo#config + +const title = 'xo'; + +const enablers: EnablerPatterns = ['xo']; + +const isEnabled: IsPluginEnabled = ({ dependencies, config }) => + hasDependency(dependencies, enablers) || 'xo' in config; + +const packageJsonPath = 'xo'; +const config: string[] = ['{.,}xo-config.{js,cjs,json,}', 'package.json']; + +const entry: string[] = ['{.,}xo-config.{js,cjs}']; + +const resolveConfig: ResolveConfig = async (config, options) => { + const dependencies = await getDependenciesDeep(config, options); + return [...dependencies]; +}; + +export default { + title, + enablers, + isEnabled, + packageJsonPath, + entry, + config, + resolveConfig, +} satisfies Plugin; diff --git a/packages/knip/src/plugins/xo/types.ts b/packages/knip/src/plugins/xo/types.ts new file mode 100644 index 000000000..8b89ac814 --- /dev/null +++ b/packages/knip/src/plugins/xo/types.ts @@ -0,0 +1,13 @@ +import type { ESLintConfig } from '../eslint/types.js'; + +export type XOConfig = ESLintConfig & { + envs?: string[] | undefined; + globals?: string[] | undefined; + ignores?: string[] | undefined; + nodeVersion?: string | boolean | undefined; + prettier?: boolean | undefined; + printConfig?: string | undefined; + semicolon?: boolean | undefined; + space?: boolean | number | undefined; + webpack?: boolean | object | undefined; +}; diff --git a/packages/knip/test/plugins/xo.test.ts b/packages/knip/test/plugins/xo.test.ts new file mode 100644 index 000000000..7774edbea --- /dev/null +++ b/packages/knip/test/plugins/xo.test.ts @@ -0,0 +1,26 @@ +import { test } from 'bun:test'; +import assert from 'node:assert/strict'; +import { main } from '../../src/index.js'; +import { resolve } from '../../src/util/path.js'; +import baseArguments from '../helpers/baseArguments.js'; +import baseCounters from '../helpers/baseCounters.js'; + +const cwd = resolve('fixtures/plugins/xo'); + +test('Find dependencies with the xo plugin', async () => { + const { issues, counters } = await main({ + ...baseArguments, + cwd, + }); + + assert(issues.unlisted['.xo-config.js']['eslint-plugin-unused-imports']); + assert(issues.unlisted['xo-config.cjs']['glob']); + assert(issues.unlisted['package.json']['eslint-plugin-eslint-comments']); + + assert.deepEqual(counters, { + ...baseCounters, + processed: 2, + unlisted: 3, + total: 2, + }); +});