Repository: webpro/knip
Pull request #78 (2023-03-12 07:10:17)

feat: add semantic-release plugin
This PR is based of #82 and thus relies on that PR's changes.

https://github.com/semantic-release/semantic-release


Patch:
diff --git a/README.md b/README.md
index 74c9d222..f48239cd 100644
--- a/README.md
+++ b/README.md
@@ -297,6 +297,7 @@ Knip contains a growing list of plugins:
 - [Remark][36]
 - [Remix][37]
 - [Rollup][38]
+- [Semantic Release][38]
 - [Sentry][39]
 - [Storybook][40]
 - [Stryker][41]
@@ -644,6 +645,7 @@ Special thanks to the wonderful people who have contributed to this project:
 [36]: ./src/plugins/remark
 [37]: ./src/plugins/remix
 [38]: ./src/plugins/rollup
+[38]: ./src/plugins/semantic-release
 [39]: ./src/plugins/sentry
 [40]: ./src/plugins/storybook
 [41]: ./src/plugins/stryker
diff --git a/schema.json b/schema.json
index a98e78f1..10896e50 100644
--- a/schema.json
+++ b/schema.json
@@ -268,6 +268,10 @@
           "title": "Rollup plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/rollup/README.md)",
           "$ref": "#/definitions/plugin"
         },
+        "semantic-release": {
+          "title": "semantic-release plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/semantic-release/README.md)",
+          "$ref": "#/definitions/plugin"
+        },
         "sentry": {
           "title": "Sentry plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/sentry/README.md)",
           "$ref": "#/definitions/plugin"
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
index 5998b82d..67ba616f 100644
--- a/src/plugins/index.ts
+++ b/src/plugins/index.ts
@@ -24,6 +24,7 @@ export * as releaseIt from './release-it/index.js';
 export * as remark from './remark/index.js';
 export * as remix from './remix/index.js';
 export * as rollup from './rollup/index.js';
+export * as semanticRelease from './semantic-release/index.js';
 export * as sentry from './sentry/index.js';
 export * as storybook from './storybook/index.js';
 export * as stryker from './stryker/index.js';
diff --git a/src/plugins/semantic-release/README.md b/src/plugins/semantic-release/README.md
new file mode 100644
index 00000000..6ef08ab1
--- /dev/null
+++ b/src/plugins/semantic-release/README.md
@@ -0,0 +1,22 @@
+# Semantic Release
+
+## Enabled
+
+This plugin is enabled when any of the following package names and/or regular expressions has a match in `dependencies`
+or `devDependencies`:
+
+- `semantic-release`
+
+## Default configuration
+
+```json
+{
+  "semantic-release": {
+    "config": [".releaserc", ".releaserc.{yaml,yml,json,js,cjs}", "release.config.{js,cjs}", "package.json"]
+  }
+}
+```
+
+Also see [Knip plugins][1] for more information about plugins.
+
+[1]: https://github.com/webpro/knip/blob/main/README.md#plugins
diff --git a/src/plugins/semantic-release/index.ts b/src/plugins/semantic-release/index.ts
new file mode 100644
index 00000000..65968c07
--- /dev/null
+++ b/src/plugins/semantic-release/index.ts
@@ -0,0 +1,29 @@
+import { _load } from '../../util/loader.js';
+import { timerify } from '../../util/performance.js';
+import { hasDependency } from '../../util/plugin.js';
+import type { PluginConfig } from './types.js';
+import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';
+
+// https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration-file
+
+export const NAME = 'Semantic Release';
+
+/** @public */
+export const ENABLERS = ['semantic-release'];
+
+export const isEnabled: IsPluginEnabledCallback = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
+
+export const CONFIG_FILE_PATTERNS = [
+  '.releaserc',
+  '.releaserc.{yaml,yml,json,js,cjs}',
+  'release.config.{js,cjs}',
+  'package.json',
+];
+
+const findPluginDependencies: GenericPluginCallback = async (configFilePath, { manifest }) => {
+  const config: PluginConfig = configFilePath.endsWith('package.json') ? manifest.release : await _load(configFilePath);
+  const plugins = config?.plugins ?? [];
+  return plugins.map(plugin => (Array.isArray(plugin) ? plugin[0] : plugin));
+};
+
+export const findDependencies = timerify(findPluginDependencies);
diff --git a/src/plugins/semantic-release/types.ts b/src/plugins/semantic-release/types.ts
new file mode 100644
index 00000000..b8dc75c9
--- /dev/null
+++ b/src/plugins/semantic-release/types.ts
@@ -0,0 +1,3 @@
+export type PluginConfig = {
+  plugins?: (string | [string, Record<string, unknown>])[];
+};


Test patch:
diff --git a/test/fixtures/plugins/semantic-release/.releaserc b/test/fixtures/plugins/semantic-release/.releaserc
new file mode 100644
index 00000000..93e07e8a
--- /dev/null
+++ b/test/fixtures/plugins/semantic-release/.releaserc
@@ -0,0 +1,53 @@
+branches:
+  - main
+  - name: next
+    channel: next
+    prerelease: next
+plugins:
+  - - '@semantic-release/commit-analyzer'
+    - preset: angular
+      parserOpts:
+        headerPattern: '^(\w*)(?:\((.*)\))?!?: (.*)$'
+        breakingHeaderPattern: '^(\w*)(?:\((.*)\))?!: (.*)$'
+      releaseRules:
+        - breaking: true
+          release: major
+        - revert: true
+          release: patch
+        - type: feat
+          release: minor
+        - type: fix
+          release: patch
+        - type: perf
+          release: patch
+        - type: build
+          scope: deps
+          release: patch
+        - type: build
+          scope: peer-deps
+          release: patch
+        - type: build
+          scope: release-patch
+          release: patch
+        - type: build
+          scope: release-minor
+          release: minor
+        - type: build
+          scope: release-major
+          release: major
+  - - '@semantic-release/release-notes-generator'
+    - preset: angular
+      parserOpts:
+        headerPattern: '^(\w*)(?:\((.*)\))?!?: (.*)$'
+        breakingHeaderPattern: '^(\w*)(?:\((.*)\))?!: (.*)$'
+  - - '@semantic-release/changelog'
+    - changelogTitle: '# Changelog
+
+        All notable changes to this project will be documented in this file. Dates are displayed in UTC.'
+  - - '@semantic-release/git'
+    - assets:
+        - CHANGELOG.md
+  - '@semantic-release/npm'
+  - - '@semantic-release/github'
+    - releasedLabels:
+        - 'Status: Released'
diff --git a/test/fixtures/plugins/semantic-release/package.json b/test/fixtures/plugins/semantic-release/package.json
new file mode 100644
index 00000000..452afe2b
--- /dev/null
+++ b/test/fixtures/plugins/semantic-release/package.json
@@ -0,0 +1,22 @@
+{
+  "release": {
+    "branches": [
+      "main"
+    ],
+    "plugins": [
+      "@semantic-release/commit-analyzer",
+      "@semantic-release/release-notes-generator",
+      "@semantic-release/changelog",
+      "@semantic-release/git",
+      "@semantic-release/npm",
+      [
+        "@semantic-release/github",
+        {
+          "releasedLabels": [
+            "Status: Released"
+          ]
+        }
+      ]
+    ]
+  }
+}
diff --git a/test/tests/plugins/semantic-release.test.ts b/test/tests/plugins/semantic-release.test.ts
new file mode 100644
index 00000000..72dc21ab
--- /dev/null
+++ b/test/tests/plugins/semantic-release.test.ts
@@ -0,0 +1,34 @@
+import assert from 'node:assert/strict';
+import path from 'node:path';
+import test from 'node:test';
+import * as semanticRelease from '../../../src/plugins/semantic-release/index.js';
+import { getManifest } from '../../helpers/index.js';
+
+const cwd = path.resolve('test/fixtures/plugins/semantic-release');
+const manifest = getManifest(cwd);
+
+test('Find dependencies in semantic-release package.json configuration (json)', async () => {
+  const configFilePath = path.join(cwd, 'package.json');
+  const dependencies = await semanticRelease.findDependencies(configFilePath, { manifest });
+  assert.deepEqual(dependencies, [
+    '@semantic-release/commit-analyzer',
+    '@semantic-release/release-notes-generator',
+    '@semantic-release/changelog',
+    '@semantic-release/git',
+    '@semantic-release/npm',
+    '@semantic-release/github',
+  ]);
+});
+
+test('Find dependencies in semantic-release .releaserc configuration (yaml)', async () => {
+  const configFilePath = path.join(cwd, '.releaserc');
+  const dependencies = await semanticRelease.findDependencies(configFilePath, {});
+  assert.deepEqual(dependencies, [
+    '@semantic-release/commit-analyzer',
+    '@semantic-release/release-notes-generator',
+    '@semantic-release/changelog',
+    '@semantic-release/git',
+    '@semantic-release/npm',
+    '@semantic-release/github',
+  ]);
+});