diff --git a/packages/knip/fixtures/plugins/ladle/package.json b/packages/knip/fixtures/plugins/ladle/package.json new file mode 100644 index 000000000..53b97f541 --- /dev/null +++ b/packages/knip/fixtures/plugins/ladle/package.json @@ -0,0 +1,18 @@ +{ + "name": "@fixtures/ladle", + "scripts": { + "dev": "ladle serve --port 4123", + "build": "ladle build" + }, + "dependencies": { + "@ladle/react": "*", + "react": "*", + "react-dom": "*" + }, + "devDependencies": { + "@types/node": "*", + "@types/react": "*", + "@types/react-dom": "*", + "typescript": "*" + } +} diff --git a/packages/knip/schema.json b/packages/knip/schema.json index a827d44a7..22eeb2127 100644 --- a/packages/knip/schema.json +++ b/packages/knip/schema.json @@ -356,6 +356,10 @@ "title": "Jest plugin configuration (https://knip.dev/reference/plugins/jest)", "$ref": "#/definitions/plugin" }, + "ladle": { + "title": "ladle plugin configuration (https://knip.dev/reference/plugins/ladle)", + "$ref": "#/definitions/plugin" + }, "lefthook": { "title": "lefthook plugin configuration (https://knip.dev/reference/plugins/lefthook)", "$ref": "#/definitions/plugin" diff --git a/packages/knip/src/ConfigurationValidator.ts b/packages/knip/src/ConfigurationValidator.ts index 95956a060..c3f4500d6 100644 --- a/packages/knip/src/ConfigurationValidator.ts +++ b/packages/knip/src/ConfigurationValidator.ts @@ -96,6 +96,7 @@ const pluginsSchema = z.object({ 'graphql-codegen': pluginSchema, husky: pluginSchema, jest: pluginSchema, + ladle: pluginSchema, lefthook: pluginSchema, 'lint-staged': pluginSchema, linthtml: pluginSchema, diff --git a/packages/knip/src/plugins/index.ts b/packages/knip/src/plugins/index.ts index 32ac5129a..b62cb4ff8 100644 --- a/packages/knip/src/plugins/index.ts +++ b/packages/knip/src/plugins/index.ts @@ -17,6 +17,7 @@ export { default as githubActions } from './github-actions/index.js'; export { default as graphqlCodegen } from './graphql-codegen/index.js'; export { default as husky } from './husky/index.js'; export { default as jest } from './jest/index.js'; +export { default as ladle } from './ladle/index.js'; export { default as lefthook } from './lefthook/index.js'; export { default as linthtml } from './linthtml/index.js'; export { default as lintStaged } from './lint-staged/index.js'; diff --git a/packages/knip/src/plugins/ladle/index.ts b/packages/knip/src/plugins/ladle/index.ts new file mode 100644 index 000000000..e7c6ca5d5 --- /dev/null +++ b/packages/knip/src/plugins/ladle/index.ts @@ -0,0 +1,40 @@ +import type { EnablerPatterns } from '#p/types/config.js'; +import type { IsPluginEnabled, Plugin, ResolveEntryPaths } from '#p/types/plugins.js'; +import { hasDependency } from '#p/util/plugin.js'; +import type { LadleConfig } from './types.js'; +import { toEntryPattern } from '../../util/protocols.js'; + +// https://ladle.dev/docs/config + +const title = 'ladle'; + +const enablers: EnablerPatterns = [/^@ladle\//]; + +const isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers); + +const config = ['.ladle/config.{mjs,js,ts}']; + +const stories: string[] = ['**/*.@(stories.@(mdx|js|jsx|mjs|ts|tsx))']; +const restEntry: string[] = ['.ladle/components.{js,jsx,ts,tsx}', 'head.html']; +const entry: string[] = [...restEntry, ...stories]; + +const project = ['.ladle/**/*.{js,jsx,ts,tsx}']; + +const resolveEntryPaths: ResolveEntryPaths = async localConfig => { + const localStories = typeof localConfig.stories === 'string' ? [localConfig.stories] : localConfig.stories; + const localViteConfig = localConfig.viteConfig ? [localConfig.viteConfig] : []; + + const patterns = [...restEntry, ...(localStories ?? stories), ...localViteConfig]; + + return patterns.map(toEntryPattern); +}; + +export default { + title, + enablers, + isEnabled, + config, + entry, + project, + resolveEntryPaths, +} satisfies Plugin; diff --git a/packages/knip/src/plugins/ladle/types.ts b/packages/knip/src/plugins/ladle/types.ts new file mode 100644 index 000000000..7e2f2487d --- /dev/null +++ b/packages/knip/src/plugins/ladle/types.ts @@ -0,0 +1,4 @@ +export type LadleConfig = { + stories?: string | string[]; + viteConfig?: string; +}; diff --git a/packages/knip/test/plugins/ladle.test.ts b/packages/knip/test/plugins/ladle.test.ts new file mode 100644 index 000000000..2242a0a97 --- /dev/null +++ b/packages/knip/test/plugins/ladle.test.ts @@ -0,0 +1,31 @@ +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/ladle'); + +test('Find dependencies with the ladle plugin', async () => { + const { issues, counters } = await main({ + ...baseArguments, + cwd, + }); + + assert(issues.dependencies['package.json']['@ladle/react']); + assert(issues.dependencies['package.json']['react']); + assert(issues.dependencies['package.json']['react-dom']); + assert(issues.devDependencies['package.json']['@types/react']); + assert(issues.devDependencies['package.json']['@types/react-dom']); + assert(issues.binaries['package.json']['ladle']); + + assert.deepEqual(counters, { + ...baseCounters, + binaries: 1, + dependencies: 3, + devDependencies: 2, + processed: 0, + total: 0, + }); +});