dt-clean 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [v1.1.1](https://github.com/ljharb/dt-clean/compare/v1.1.0...v1.1.1) - 2026-06-21
9
+
10
+ ### Commits
11
+
12
+ - [Fix] `--setup`: never duplicate `dt-clean`, and converge on the preferred hook [`bc32122`](https://github.com/ljharb/dt-clean/commit/bc321229b54decfd6000e8d58d202604c36af206)
13
+ - [Dev Deps] add `dt-clean` (self-reference) [`d1a6cdf`](https://github.com/ljharb/dt-clean/commit/d1a6cdf7de2c29d1a17858bf8febf4bd586253f6)
14
+
8
15
  ## [v1.1.0](https://github.com/ljharb/dt-clean/compare/v1.0.1...v1.1.0) - 2026-06-21
9
16
 
10
17
  ### Commits
package/README.md CHANGED
@@ -65,9 +65,10 @@ npx dt-clean --setup
65
65
 
66
66
  - if you have no `dependencies` script, it adds `"dependencies": "dt-clean --auto"`;
67
67
  - if you already have one, it adds `dt-clean --auto` to a free `postdependencies` (or `predependencies`) hook instead, so your existing script is never touched - and if every hook is taken, it appends `&& dt-clean --auto` to your `dependencies` script rather than clobbering it;
68
- - if `dt-clean --auto` is already wired in, it does nothing.
68
+ - if it already added `dt-clean --auto` to a `post`/`pre` hook and the preferred `dependencies` slot later frees up, re-running moves it back to the most-preferred available hook;
69
+ - if `dt-clean --auto` is already in the best available hook, it does nothing; and if some other `dt-clean` invocation (or a customized one you wrote) is already present, it leaves that alone rather than adding a duplicate.
69
70
 
70
- It only ever adds this one invocation and leaves the rest of your `package.json` (and its formatting) alone, so it's safe to re-run. The result is simply the equivalent of:
71
+ It only ever manages this one invocation and leaves the rest of your `package.json` (and its formatting) alone, so it's safe to re-run - repeated runs converge on the same result. That result is simply the equivalent of:
71
72
 
72
73
  ```json
73
74
  {
package/bin.mjs CHANGED
@@ -67,6 +67,8 @@ if (setup) {
67
67
  const { action, script } = await setupScripts(cwd);
68
68
  console.log({
69
69
  chained: `Appended \`dt-clean --auto\` to the existing \`${script}\` script in \`package.json\`.`,
70
+ exists: `\`${script}\` already invokes \`dt-clean\` (without \`--auto\`); leaving it unchanged - add \`--auto\` there yourself for install-time cleanup.`,
71
+ moved: `Moved \`dt-clean --auto\` to the preferred \`${script}\` script in \`package.json\`.`,
70
72
  present: `\`${script}\` already runs \`dt-clean --auto\`; nothing to do.`,
71
73
  set: `Added \`dt-clean --auto\` to the \`${script}\` script in \`package.json\`.`,
72
74
  }[action]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dt-clean",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Ensures the only DefinitelyTyped (`@types`) packages you have installed are the ones you need",
5
5
  "bin": "./bin.mjs",
6
6
  "exports": {
@@ -51,6 +51,7 @@
51
51
  "@types/semver": "^7.7.1",
52
52
  "auto-changelog": "^2.6.0",
53
53
  "c8": "^11.0.0",
54
+ "dt-clean": "file:./",
54
55
  "eslint": "^10.5.0",
55
56
  "esmock": "^2.7.6",
56
57
  "globals": "^17.6.0",
package/setup.d.ts CHANGED
@@ -4,7 +4,7 @@ declare namespace setup {
4
4
  type DependencyHook = 'predependencies' | 'dependencies' | 'postdependencies';
5
5
 
6
6
  type SetupResult = {
7
- action: 'present' | 'set' | 'chained';
7
+ action: 'present' | 'exists' | 'set' | 'moved' | 'chained';
8
8
  script: DependencyHook;
9
9
  };
10
10
  }
package/setup.mjs CHANGED
@@ -21,6 +21,11 @@ function hasAuto(script) {
21
21
  return typeof script === 'string' && (/\bdt-clean\b[^&|;]*--auto\b/).test(script);
22
22
  }
23
23
 
24
+ /** @param {string | undefined} script */
25
+ function hasDtClean(script) {
26
+ return typeof script === 'string' && (/\bdt-clean\b/).test(script);
27
+ }
28
+
24
29
  /** @type {import('./setup.d.ts')} */
25
30
  export default async function setup(cwd) {
26
31
  const packageJSONpath = join(cwd, 'package.json');
@@ -32,18 +37,38 @@ export default async function setup(cwd) {
32
37
  /** @type {NonNullable<PackageJSON['scripts']>} */
33
38
  const scripts = { ...pkg.scripts };
34
39
 
35
- const present = HOOKS.find((hook) => hasAuto(scripts[hook]));
36
- if (present) {
37
- return { action: 'present', script: present };
40
+ // a standalone `dt-clean --auto` we wrote ourselves, which we may therefore safely relocate.
41
+ const owned = HOOKS.find((hook) => scripts[hook] === AUTO);
42
+
43
+ if (!owned) {
44
+ // a `dt-clean --auto` we didn't write (chained or customized): leave it exactly as-is.
45
+ const wired = HOOKS.find((hook) => hasAuto(scripts[hook]));
46
+ if (wired) {
47
+ return { action: 'present', script: wired };
48
+ }
49
+ // some other `dt-clean` invocation: don't add a second one.
50
+ const existing = HOOKS.find((hook) => hasDtClean(scripts[hook]));
51
+ if (existing) {
52
+ return { action: 'exists', script: existing };
53
+ }
38
54
  }
39
55
 
40
- const free = HOOKS.find((hook) => !scripts[hook]);
56
+ // the most-preferred hook our invocation should occupy: free, or already holding it.
57
+ const target = HOOKS.find((hook) => !scripts[hook] || scripts[hook] === AUTO);
58
+
59
+ if (owned && owned === target) {
60
+ return { action: 'present', script: owned };
61
+ }
41
62
 
42
63
  /** @type {SetupResult} */
43
64
  let result;
44
- if (free) {
45
- scripts[free] = AUTO;
46
- result = { action: 'set', script: free };
65
+ if (target) {
66
+ if (owned) {
67
+ // a more-preferred hook is now free: relocate to it.
68
+ delete scripts[owned];
69
+ }
70
+ scripts[target] = AUTO;
71
+ result = { action: owned ? 'moved' : 'set', script: target };
47
72
  } else {
48
73
  // every hook is occupied, so chain onto `dependencies` rather than clobber anything.
49
74
  scripts.dependencies = `${scripts.dependencies} && ${AUTO}`;