@williamthorsen/tsconfig 0.6.0 → 0.7.0

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.
@@ -1,6 +1,6 @@
1
- /** @noformat — @generated. Do not edit. Compiled by rdy. */
1
+ /** @noformat -- @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.33.0";
3
+ export const __readyupVersion = "0.35.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/default.ts
@@ -71,6 +71,46 @@ function toStringArray(value) {
71
71
  return value.filter((entry) => typeof entry === "string");
72
72
  }
73
73
 
74
+ // src/readiness/findEscapingPaths.ts
75
+ import { dirname, join, relative } from "node:path/posix";
76
+ var CONFIG_DIR_TEMPLATE = "${configDir}";
77
+ var FIELDS = ["exclude", "files", "include"];
78
+ var ROOTED_PATH = /^(?:\/|[A-Za-z]:)/;
79
+ function findEscapingPaths(entries) {
80
+ const judged = entries[0];
81
+ if (judged === void 0) return [];
82
+ const judgedDir = dirname(judged.path);
83
+ return FIELDS.flatMap((field) => findEscapingPathsInField(entries, field, judgedDir));
84
+ }
85
+ function findDeclaringEntry(entries, field) {
86
+ for (const entry of entries) {
87
+ const declared = entry.config[field];
88
+ if (declared) return entry;
89
+ }
90
+ return void 0;
91
+ }
92
+ function findEscapingPathsInField(entries, field, judgedDir) {
93
+ const declaring = findDeclaringEntry(entries, field);
94
+ if (declaring === void 0) return [];
95
+ const declared = declaring.config[field];
96
+ if (!Array.isArray(declared)) return [];
97
+ const declaredDir = dirname(declaring.path);
98
+ return declared.filter((value) => typeof value === "string").flatMap((value) => {
99
+ const outward = findOutwardPath(value, declaredDir, judgedDir);
100
+ return outward === void 0 ? [] : [{ declaredIn: declaring.path, field, path: outward }];
101
+ });
102
+ }
103
+ function findOutwardPath(value, declaredDir, judgedDir) {
104
+ const normalized = value.split("\\").join("/");
105
+ if (ROOTED_PATH.test(normalized)) return normalized;
106
+ const resolved = startsWithConfigDir(normalized) ? join(judgedDir, normalized.slice(CONFIG_DIR_TEMPLATE.length)) : join(declaredDir, normalized);
107
+ const fromJudged = relative(judgedDir, resolved);
108
+ return fromJudged === ".." || fromJudged.startsWith("../") ? fromJudged : void 0;
109
+ }
110
+ function startsWithConfigDir(value) {
111
+ return value.slice(0, CONFIG_DIR_TEMPLATE.length).toLowerCase() === CONFIG_DIR_TEMPLATE.toLowerCase();
112
+ }
113
+
74
114
  // src/readiness/findRedundantOptions.ts
75
115
  import { isDeepStrictEqual } from "node:util";
76
116
  function findRedundantOptions(entries, baseIndex) {
@@ -96,6 +136,11 @@ function collectDependencyDeclaredKeys(entries, baseIndex) {
96
136
  return keys;
97
137
  }
98
138
 
139
+ // src/readiness/listSearchDirs.ts
140
+ function listSearchDirs(workspaceDirs) {
141
+ return [.../* @__PURE__ */ new Set([".", ...workspaceDirs])];
142
+ }
143
+
99
144
  // src/readiness/node-floor.ts
100
145
  var NODE_MAJOR = /^v?(\d+)(?:[.\s]|$)/;
101
146
  var NODE_MAJOR_CEILING = 99;
@@ -135,7 +180,7 @@ var cache = {
135
180
  chains: /* @__PURE__ */ new Map()
136
181
  };
137
182
  var default_default = defineRdyKit({
138
- description: `Adoption and alignment checks for a project consuming ${PACKAGE_NAME}`,
183
+ description: `Adoption, alignment, and hygiene checks for a project consuming ${PACKAGE_NAME}`,
139
184
  defaultSeverity: "warn",
140
185
  checklists: [
141
186
  {
@@ -172,6 +217,17 @@ var default_default = defineRdyKit({
172
217
  fix: "Raise engines.node to a major implementing the base's ES year: below it, code that typechecks fails at runtime"
173
218
  }
174
219
  ]
220
+ },
221
+ {
222
+ name: "hygiene",
223
+ checks: [
224
+ {
225
+ name: "No tsconfig's include, exclude, or files names a path outside its own directory",
226
+ skip: skipUnlessSomeTsconfigWasFound,
227
+ check: noEscapingPaths,
228
+ fix: "Declare the field named above in the config that owns the directory, listing every path it needs, since a local declaration replaces the inherited one rather than merging and discards the default exclude of node_modules, bower_components, jspm_packages, and outDir; or prefix each path in the config being extended with ${configDir}, which resolves to the consuming config's directory"
229
+ }
230
+ ]
175
231
  }
176
232
  ]
177
233
  });
@@ -188,6 +244,10 @@ function classifyTsconfig(path) {
188
244
  function dedupe(values) {
189
245
  return [...new Set(values)];
190
246
  }
247
+ function describeEscape(configPath, escaping) {
248
+ const source = escaping.declaredIn === configPath ? "" : `, from ${escaping.declaredIn}`;
249
+ return `${escaping.field}: ${escaping.path}${source}`;
250
+ }
191
251
  function everyEsYearMatchesTheBase() {
192
252
  const baseEsYear = readBaseEsYear();
193
253
  if (baseEsYear === void 0) return true;
@@ -256,12 +316,29 @@ function listPathsOfKind(classified, kind) {
256
316
  return classified.filter((adoption) => adoption.kind === kind).map((adoption) => adoption.path);
257
317
  }
258
318
  function listTsconfigSearchDirs() {
259
- return discoverWorkspaces().map((workspace) => workspace.dir);
319
+ return listSearchDirs(discoverWorkspaces().map((workspace) => workspace.dir));
260
320
  }
261
321
  function nodeFloorSupportsBaseEsYear() {
262
322
  const verdict = judgeNodeFloor();
263
323
  return verdict.kind === "skip" ? true : { ok: verdict.kind === "pass", detail: verdict.detail };
264
324
  }
325
+ function noEscapingPaths() {
326
+ const judged = findTsconfigs().flatMap((path) => {
327
+ const chain = readChain(path);
328
+ return chain === void 0 ? [] : [{ escapes: findEscapingPaths(chain.entries), path }];
329
+ });
330
+ const offenders = judged.filter((judgedConfig) => judgedConfig.escapes.length > 0);
331
+ const progress = {
332
+ type: "fraction",
333
+ passedCount: judged.length - offenders.length,
334
+ count: judged.length
335
+ };
336
+ if (offenders.length === 0) return { ok: true, progress };
337
+ const details = offenders.flatMap(
338
+ (offender) => offender.escapes.map((escaping) => `${offender.path} (${describeEscape(offender.path, escaping)})`)
339
+ );
340
+ return { ok: false, detail: details.join(", "), progress };
341
+ }
265
342
  function noRedundantOptions() {
266
343
  const offenders = listAdoptedTsconfigs().flatMap(
267
344
  (adoption) => findRedundantOptions(adoption.chain.entries, adoption.baseIndex).map(
@@ -302,6 +379,9 @@ function skipUnlessSomeTsconfigIsAccountable() {
302
379
  if (classified.length === 0) return "no workspace tsconfig was found";
303
380
  return classified.some((adoption) => adoption.kind !== "external-base") ? false : "every workspace tsconfig extends a base belonging to another package";
304
381
  }
382
+ function skipUnlessSomeTsconfigWasFound() {
383
+ return findTsconfigs().length === 0 ? "no workspace tsconfig was found" : false;
384
+ }
305
385
  export {
306
386
  default_default as default
307
387
  };
@@ -4,13 +4,14 @@
4
4
  {
5
5
  "checklists": [
6
6
  "adoption",
7
- "alignment"
7
+ "alignment",
8
+ "hygiene"
8
9
  ],
9
- "description": "Adoption and alignment checks for a project consuming @williamthorsen/tsconfig",
10
+ "description": "Adoption, alignment, and hygiene checks for a project consuming @williamthorsen/tsconfig",
10
11
  "esbuildVersion": "0.28.2",
11
12
  "inputs": [
12
13
  {
13
- "hash": "8988b5dc",
14
+ "hash": "f4ec4ffd",
14
15
  "kind": "module",
15
16
  "path": "kits/default.ts"
16
17
  },
@@ -29,11 +30,21 @@
29
30
  "kind": "module",
30
31
  "path": "../src/readiness/es-year.ts"
31
32
  },
33
+ {
34
+ "hash": "bfbb0002",
35
+ "kind": "module",
36
+ "path": "../src/readiness/findEscapingPaths.ts"
37
+ },
32
38
  {
33
39
  "hash": "acd6ba2b",
34
40
  "kind": "module",
35
41
  "path": "../src/readiness/findRedundantOptions.ts"
36
42
  },
43
+ {
44
+ "hash": "30178e5c",
45
+ "kind": "module",
46
+ "path": "../src/readiness/listSearchDirs.ts"
47
+ },
37
48
  {
38
49
  "hash": "31b0fbf5",
39
50
  "kind": "module",
@@ -42,10 +53,10 @@
42
53
  ],
43
54
  "name": "default",
44
55
  "path": "kits/default.js",
45
- "readyupVersion": "0.33.0",
56
+ "readyupVersion": "0.35.0",
46
57
  "source": "kits/default.ts",
47
- "sourceHash": "8988b5dc",
48
- "targetHash": "eac2f4f5"
58
+ "sourceHash": "f4ec4ffd",
59
+ "targetHash": "2bcc6e9b"
49
60
  }
50
61
  ]
51
62
  }
package/README.md CHANGED
@@ -3,17 +3,13 @@
3
3
  Shared TypeScript base config for Node-only projects. Inlines the settings of [`@tsconfig/strictest`](https://www.npmjs.com/package/@tsconfig/strictest) and adds the Node and build options it leaves out, so a consuming repo declares only what is genuinely its own.
4
4
 
5
5
  <!-- section:release-notes -->
6
- ## Release notes — v0.6.0 (2026-08-26)
6
+ ## Release notes — v0.7.0 (2026-08-30)
7
7
 
8
8
  ### 🎉 Features
9
9
 
10
- - 🚨 **Breaking:** Declare readyup as an optional peer of both kit-shipping packages (#179)
10
+ - Report an include, exclude, or files path outside a tsconfig's directory (#197)
11
11
 
12
- Declares `readyup` as an optional peer dependency of `@williamthorsen/tsconfig` and `@williamthorsen/eslint-config-typescript`, with a `>=0.33.0` floor. Both packages ship readiness kits that run against the consumer's own readyup, and below 0.33.0 that readyup omits the repo root from a monorepo's workspace list, so the kits check every member package and leave the root unchecked.
13
-
14
- Both kits now take readyup's workspace list directly, dropping the root literal each carried to compensate for the older behavior.
15
-
16
- Migration: A consumer running `rdy` against either kit upgrades `readyup` to 0.33.0 or later.
12
+ Adds a `hygiene` checklist to the `@williamthorsen/tsconfig` readiness kit, reporting at `warn` a workspace tsconfig whose effective `include`, `exclude`, or `files` names a path outside the directory holding it, whether that config declared the field itself or inherited it from a base.
17
13
  <!-- /section:release-notes -->
18
14
 
19
15
  ## Installation
@@ -102,12 +98,13 @@ export default defineRdyConfig({
102
98
  });
103
99
  ```
104
100
 
105
- The kit needs readyup 0.33.0 or later, declared as an optional peer. Below that, readyup does not report the repo root among a monorepo's workspaces, so the kit checks every member package and leaves the root unchecked.
101
+ The `readyup >=0.33.0` optional peer names the version the kit is developed and tested against. Below 0.33.0, readyup does not report the repo root among a monorepo's workspaces, so the kit supplies the root itself and sweeps every member package alongside it.
106
102
 
107
- The kit walks each workspace tsconfig's `extends` chain and reports four things:
103
+ The kit walks each workspace tsconfig's `extends` chain and reports five things:
108
104
 
109
105
  - **Adoption**: a tsconfig reaching neither this base nor a framework base. A tsconfig naming this base through a specifier that does not resolve is reported separately, as declared but not installed.
110
106
  - **Re-declaration**: an option restated with the base's own value. A key that a framework base in the same chain also declares is exempt, since restating it is how a config wins against that base.
107
+ - **Escaping paths**: an `include`, `exclude`, or `files` path resolving outside the directory that holds the tsconfig. A config that is extended hands these fields down resolved against its own directory, so a package inheriting them is governed by paths pointing outside itself; the remedy is a complete local declaration, or `${configDir}` in the config being extended.
111
108
  - **ES year**: a `target` or `lib` declaring a year other than the base's, read from the base you extend rather than from a constant compiled into the kit.
112
109
  - **Node floor**: an `engines.node` below the major that implements that ES year. This is the one failure `tsc` cannot surface on its own, which is why it alone is an error.
113
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/tsconfig",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Shared TypeScript base config for Node-only projects",
5
5
  "keywords": [
6
6
  "tsconfig",
@@ -31,7 +31,7 @@
31
31
  "devDependencies": {
32
32
  "@tsconfig/strictest": "2.0.8",
33
33
  "esbuild": "0.28.2",
34
- "readyup": "0.33.0"
34
+ "readyup": "0.35.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "readyup": ">=0.33.0"