diff --git a/.changeset/fields-plugin-editor-dist-test-declarations-4006.md b/.changeset/fields-plugin-editor-dist-test-declarations-4006.md new file mode 100644 index 000000000..8cea0f113 --- /dev/null +++ b/.changeset/fields-plugin-editor-dist-test-declarations-4006.md @@ -0,0 +1,12 @@ +--- +'@object-ui/fields': patch +'@object-ui/plugin-editor': patch +--- + +`@object-ui/fields` and `@object-ui/plugin-editor` stop publishing their test declarations + +Both packages' build tsconfigs set `include: ["src"]` with no test exclude, so every test file entered the declaration program and its `.d.ts` was written into `dist/`. Both are published (`private` is false, `files` contains `dist`), so those declarations shipped: 85 from `@object-ui/fields` and one from `@object-ui/plugin-editor`. Adding the test exclude the other twenty-odd packages already use removes them. + +Nothing else about either artifact moves. Measured by building each package both ways from a cleared `dist/`, then diffing the file lists: `@object-ui/fields` goes from 163 files to 78 and `@object-ui/plugin-editor` from 6 to 5, every one of the 86 disappearances is a `*.test.d.ts`, no file appears, and all 83 surviving files are byte-identical by sha256 — including each package's entry `dist/index.d.ts`. The entry type surface is therefore unchanged and no import can break; this is the tarball shedding files nothing resolved. + +The type coverage those files were a side effect of did not go with them. Because the build program read the tests, these two packages counted as "tests type-checked" in `scripts/check-type-check-coverage.mjs` — a correct verdict reached through an emit nobody wanted. Excluding the tests alone would have silently dropped 86 test files out of every `tsc` program, so the same change adds a `tsconfig.test.json` per package, chained from each package's `type-check` script, and the coverage gate stays at 41 of 41 packages compiling their tests with zero declared debt on both sides of the change. diff --git a/packages/fields/package.json b/packages/fields/package.json index cdf74e08c..4d99a56ed 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -26,7 +26,7 @@ "scripts": { "build": "tsc && vite build && node scripts/build-css.mjs", "clean": "rm -rf dist", - "type-check": "tsc --noEmit", + "type-check": "tsc --noEmit && tsc -p tsconfig.test.json", "test": "vitest run", "lint": "eslint ." }, diff --git a/packages/fields/tsconfig.json b/packages/fields/tsconfig.json index 6a44fb0fd..8e8c26971 100644 --- a/packages/fields/tsconfig.json +++ b/packages/fields/tsconfig.json @@ -2,8 +2,24 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", + // `..` (i.e. `packages/`), NOT `src` — load-bearing, measured in + // objectui#4006. This config inherits the root tsconfig's `paths`, which map + // `@object-ui/core` / `@object-ui/types` to their sibling `packages/*/src` + // trees, so those sources are real program inputs. Narrowing `rootDir` to + // `src` (or dropping it, which defaults it to the common source directory) + // turns all 122 of them into TS6059 "not under rootDir" — and it does so + // even under `--noEmit`, because TS6059 is a program-level verdict. It does + // not affect what is emitted: this config inherits the root's + // `noEmit: true`, so `tsc` here only CHECKS; `dist` is written by + // vite-plugin-dts, which overrides `rootDir` to `src` and clears `paths`. "rootDir": "..", "jsx": "react-jsx" }, - "include": ["src"] + "include": ["src"], + // Tests are excluded from the BUILD program so they stop being emitted into + // the published `dist` (objectui#4006 — 85 `*.test.d.ts` shipped from here). + // Their type coverage did not go away with them: it moved to the + // `tsconfig.test.json` chained off this package's `type-check` script, which + // is what scripts/check-type-check-coverage.mjs verifies. + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"] } diff --git a/packages/fields/tsconfig.test.json b/packages/fields/tsconfig.test.json new file mode 100644 index 000000000..383c6c6bf --- /dev/null +++ b/packages/fields/tsconfig.test.json @@ -0,0 +1,62 @@ +{ + // Type-checks this package's TESTS, which `tsconfig.json` now excludes. + // See `packages/plugin-dashboard/tsconfig.test.json` for the template and + // `packages/types/tsconfig.test.json` for why the exclusion would otherwise + // be a hole: a test nothing compiles can assert a contract the compiler never + // checked and then read as evidence that the contract holds (objectui#3009). + // + // The hole is NEW here, and that is the whole point of objectui#4006. Until + // this file existed these 85 tests were checked — but only as a SIDE EFFECT + // of the build config having no test exclude at all, which is also why 85 + // `*.test.d.ts` were emitted into the published `dist/`. Removing the emit + // therefore had to move the coverage in the same commit, not after it: + // scripts/check-type-check-coverage.mjs fails 5c the moment the exclude + // lands alone (measured — it names all 85 files). + // + // Chained from this package's `type-check` script, which is what the CI + // `Type Check` job runs; the coverage gate enforces the chaining — a config + // nothing runs is the objectui#3009 failure itself. + "extends": "../../tsconfig.json", + "compilerOptions": { + // A checking project, never an emitting one. The build config inherits the + // root's `noEmit: true` as well (this package emits `dist` from + // vite-plugin-dts, not from `tsc`), but stating it here is what the gate + // requires and what keeps this project from ever growing an output. + "noEmit": true, + "composite": false, + "declaration": false, + "jsx": "react-jsx", + // Deliberately NOT raised to ES2022. Measured: no test file in this package + // reaches for `Array.prototype.at` or any other ES2021+ builtin, so the + // tests are held to the same lib the shipped SOURCE targets. + "lib": ["ES2020", "DOM", "DOM.Iterable"], + // + // `types` is deliberately NOT named, unlike plugin-dashboard's. Naming it + // switches off automatic `@types/*` inclusion, and this package's build + // program does not name it either — so leaving it unset is what reproduces + // the exact program these tests were checked by until now, which is the + // property objectui#4006 had to preserve while moving the coverage. + // Measured: no test file here uses `global`, and the seven suites that use + // `@testing-library/jest-dom` matchers `import '@testing-library/jest-dom'` + // explicitly, so its global augmentation reaches the whole program through + // that import (plugin-list's lesson). + // + // `paths` drops the root tsconfig's source-tree mappings so `@object-ui/*` + // and `@objectstack/spec/*` resolve through each workspace dependency's + // built `.d.ts` rather than pulling sibling package sources in as program + // inputs. `type-check` dependsOn `^build` (turbo.json), so those `.d.ts` + // exist by the time this runs. This is the one place the project is + // deliberately STRICTER than the program it replaces: the build config + // keeps the root `paths` (and the `rootDir: ".."` that lets sibling sources + // sit inside the root), so until now these tests were checked against + // sibling SOURCE. Checking them against the built declarations is what the + // published contract actually is, and it is what every other chained test + // project in this repo does. Measured: zero new errors either way. + "paths": {} + }, + // Only the test files. Note there is no ambient `*.d.ts` under `src/` in this + // package today — if one is ever added it must be NAMED here, because being + // imported is not enough to make an ambient declaration a program input, and + // nothing imports one (plugin-map's lesson, objectui#4270). + "include": ["src/**/*.test.ts", "src/**/*.test.tsx"] +} diff --git a/packages/plugin-editor/package.json b/packages/plugin-editor/package.json index d986f21aa..2b5cfa90e 100644 --- a/packages/plugin-editor/package.json +++ b/packages/plugin-editor/package.json @@ -27,7 +27,7 @@ "build": "vite build", "test": "vitest run", "test:watch": "vitest", - "type-check": "tsc --noEmit", + "type-check": "tsc --noEmit && tsc -p tsconfig.test.json", "lint": "eslint ." }, "dependencies": { diff --git a/packages/plugin-editor/tsconfig.json b/packages/plugin-editor/tsconfig.json index 9ffe21aef..2b3802a5b 100644 --- a/packages/plugin-editor/tsconfig.json +++ b/packages/plugin-editor/tsconfig.json @@ -13,5 +13,14 @@ "composite": true, "skipLibCheck": true }, - "include": ["src"] + "include": ["src"], + // Tests are excluded from the BUILD program so they stop being emitted into + // the published `dist` (objectui#4006 — `dist/index.test.d.ts` shipped from + // here). The emitter is vite-plugin-dts, whose own `include: ['src']` in + // `vite.config.ts` does NOT displace this `exclude`: measured, the exclude + // alone removes `dist/index.test.d.ts`, so no dts-plugin `exclude` is needed. + // Their type coverage did not go away with them: it moved to the + // `tsconfig.test.json` chained off this package's `type-check` script, which + // is what scripts/check-type-check-coverage.mjs verifies. + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"] } diff --git a/packages/plugin-editor/tsconfig.test.json b/packages/plugin-editor/tsconfig.test.json new file mode 100644 index 000000000..b9e41af3a --- /dev/null +++ b/packages/plugin-editor/tsconfig.test.json @@ -0,0 +1,45 @@ +{ + // Type-checks this package's TESTS, which `tsconfig.json` now excludes. + // Template: `packages/plugin-dashboard/tsconfig.test.json` (objectui#4530); + // rationale: `packages/types/tsconfig.test.json`. A test nothing compiles can + // assert a contract the compiler never checked and then read as evidence that + // the contract holds (objectui#3009). + // + // As in `@object-ui/fields`, the hole is new and deliberate: until + // objectui#4006 this suite was checked only as a side effect of the build + // config having no test exclude, which is also how `dist/index.test.d.ts` + // came to be published. The exclude and this project are one change — + // scripts/check-type-check-coverage.mjs fails 5c on the exclude alone + // (measured). + // + // Chained from this package's `type-check` script, which is what the CI + // `Type Check` job runs; the coverage gate enforces the chaining. + "extends": "../../tsconfig.json", + "compilerOptions": { + // A checking project, never an emitting one. This matters more here than in + // most packages: this package's BUILD config deliberately sets + // `noEmit: false` with `declaration` and `composite`, so those three must be + // turned back off rather than inherited — this project extends the ROOT + // config, not the build one, but stating them keeps that true if the + // `extends` target ever changes. + "noEmit": true, + "composite": false, + "declaration": false, + "jsx": "react-jsx", + // Measured: the single suite compiles clean against the root's ES2020 + // baseline, so the test is held to the same lib the shipped SOURCE targets. + "lib": ["ES2020", "DOM", "DOM.Iterable"], + // `paths` drops the root tsconfig's source-tree mappings so `@object-ui/*` + // resolves through each workspace dependency's built `.d.ts` rather than + // pulling sibling package sources in as program inputs. `type-check` + // dependsOn `^build` (turbo.json), so those `.d.ts` exist by the time this + // runs. The suite imports exactly one workspace specifier, + // `@object-ui/core`, plus its own `./index`. + "paths": {} + }, + // Only the test files. There is no ambient `*.d.ts` under `src/` in this + // package today — if one is ever added it must be NAMED here, because being + // imported is not enough to make an ambient declaration a program input + // (plugin-map's lesson, objectui#4270). + "include": ["src/**/*.test.ts", "src/**/*.test.tsx"] +}