diff --git a/packages/superdoc/package.json b/packages/superdoc/package.json index 0e5df28394..ff3e0c2f2c 100644 --- a/packages/superdoc/package.json +++ b/packages/superdoc/package.json @@ -109,7 +109,7 @@ "word-benchmark-sidecar": "node ../../devtools/word-benchmark-sidecar/server.js", "build": "vite build && pnpm run build:cdn", "build:dev": "SUPERDOC_SKIP_DTS=1 vite build", - "postbuild": "node ./scripts/ensure-types.cjs && node ./scripts/audit-bundle.cjs && node ./scripts/audit-declarations.cjs", + "postbuild": "node ./scripts/check-tsconfig-type-surface.cjs && node ./scripts/ensure-types.cjs && node ./scripts/audit-bundle.cjs && node ./scripts/audit-declarations.cjs", "audit:declarations": "node ./scripts/audit-declarations.cjs", "audit:declarations:informational": "node ./scripts/audit-declarations.cjs --informational", "check:jsdoc": "node ./scripts/check-jsdoc.cjs", diff --git a/packages/superdoc/scripts/audit-declarations.cjs b/packages/superdoc/scripts/audit-declarations.cjs index 163b1165ad..7552024b5a 100644 --- a/packages/superdoc/scripts/audit-declarations.cjs +++ b/packages/superdoc/scripts/audit-declarations.cjs @@ -36,6 +36,11 @@ const fs = require('node:fs'); const path = require('node:path'); +// SD-2864: canonical taxonomy for the published type surface. The lists +// below previously duplicated data from ensure-types.cjs; both now derive +// from the same config so the two scripts cannot drift. +const typeSurface = require('./type-surface.config.cjs'); + const distRoot = path.resolve(__dirname, '..', 'dist'); // SD-2859: strict is the default. The audit fails the build on any @@ -52,35 +57,17 @@ if (!fs.existsSync(distRoot)) { process.exit(1); } -// Packages whose public type dependencies are relocated into `superdoc`'s -// published declaration tree or explicitly guarded from falling back to an -// ambient shim. They must NEVER appear as a `declare module` block in -// `_internal-shims.d.ts` — if they do, their types collapse to `any` for -// consumers and we have a regression. Mirror of SD-2842's -// `RELOCATION_GUARD_PACKAGES` in `ensure-types.cjs`; keep the two lists in sync. -const RELOCATION_GUARD_PACKAGES = [ - '@superdoc/document-api', - '@superdoc/contracts', - '@superdoc/dom-contract', - '@superdoc/layout-bridge', - '@superdoc/layout-engine', - '@superdoc/painter-dom', - '@superdoc/pm-adapter', - '@superdoc/style-engine', - '@superdoc/common', - '@superdoc/common/list-marker-utils', -]; +// Packages that must NEVER appear as a `declare module` block in +// `_internal-shims.d.ts`. After SD-2942 the file is no longer emitted, so +// this list is a defense against stale tarballs and future re-introduction. +// Source: type-surface.config.cjs `relocationGuardPackages`. +const RELOCATION_GUARD_PACKAGES = typeSurface.relocationGuardPackages; // Specifiers that may appear as bare imports in published d.ts files even // though they are private workspace packages. Each entry has a documented -// reason; anything outside this allowlist (and outside the shim file) is a -// real leak per Rule 1. -const RULE1_ALLOWLIST = { - // Legacy public surface per the RFC. Resolves through `superdoc`'s - // published dist tree at runtime via the existing rewrite/include rules. - // Deep subpaths beyond the curated public surface are NOT allowlisted. - '@superdoc/super-editor': 'legacy public surface (RFC Decision 1)', -}; +// reason; anything outside this allowlist is a real leak per Rule 1. +// Source: type-surface.config.cjs `rule1Allowlist`. +const RULE1_ALLOWLIST = typeSurface.rule1Allowlist; function isRule1Allowed(specifier, shimmedSet) { if (RULE1_ALLOWLIST[specifier]) return true; diff --git a/packages/superdoc/scripts/check-tsconfig-type-surface.cjs b/packages/superdoc/scripts/check-tsconfig-type-surface.cjs new file mode 100644 index 0000000000..42441c1ef5 --- /dev/null +++ b/packages/superdoc/scripts/check-tsconfig-type-surface.cjs @@ -0,0 +1,68 @@ +#!/usr/bin/env node +/** + * SD-2864: enforce parity between tsconfig.json's `include` array and + * `type-surface.config.cjs`. tsconfig.json is the one consumer of the + * type-surface taxonomy that has no scripting layer (it's plain JSON), + * so we don't generate it; this check fails the build if the on-disk + * file drifts from the config in either direction. + * + * Expected shape: tsconfig.json's `include` MUST equal exactly the + * union of `baseTsconfigIncludes` (foundational sources) and + * `relocations[*].tsconfigIncludes` (per-relocation paths). No more, + * no less. + * + * Drift modes this catches: + * - A new relocation added to the config but not mirrored in + * tsconfig.json (typecheck for that source tree silently misses it). + * - A relocation removed from the config but its tsconfig.json entry + * left stale (entry would compile against source the type-surface + * no longer claims to manage; undermines single-source-of-truth). + * - Foundational base entries dropped from tsconfig.json by mistake. + * + * If foundational entries beyond the current three are needed (e.g. a + * future public package source root), add them to `baseTsconfigIncludes` + * in type-surface.config.cjs rather than carrying them only in + * tsconfig.json. + */ + +const fs = require('node:fs'); +const path = require('node:path'); + +const tsconfigPath = path.resolve(__dirname, '..', 'tsconfig.json'); +const typeSurface = require('./type-surface.config.cjs'); + +const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf8')); +const tsconfigIncludes = new Set(tsconfig.include || []); + +const relocationIncludes = typeSurface.relocations.flatMap((r) => r.tsconfigIncludes); +const expected = new Set([...typeSurface.baseTsconfigIncludes, ...relocationIncludes]); + +const missing = [...expected].filter((entry) => !tsconfigIncludes.has(entry)); +const stale = [...tsconfigIncludes].filter((entry) => !expected.has(entry)); + +let failed = false; + +if (missing.length > 0) { + failed = true; + console.error('[check-tsconfig-type-surface] tsconfig.json `include` is missing entries required by type-surface.config.cjs:'); + for (const entry of missing) { + const owner = typeSurface.relocations.find((r) => r.tsconfigIncludes.includes(entry)); + const reason = owner ? `required by ${owner.pkg}` : 'foundational base include'; + console.error(` - ${entry} (${reason})`); + } +} + +if (stale.length > 0) { + failed = true; + console.error('[check-tsconfig-type-surface] tsconfig.json `include` has entries not declared in type-surface.config.cjs:'); + for (const entry of stale) { + console.error(` - ${entry} (stale - remove from tsconfig.json or add to baseTsconfigIncludes / a relocation)`); + } +} + +if (failed) { + console.error('Update packages/superdoc/tsconfig.json or packages/superdoc/scripts/type-surface.config.cjs so the two stay in sync.'); + process.exit(1); +} + +console.log(`[check-tsconfig-type-surface] ✓ tsconfig.json mirrors ${expected.size} type-surface include paths exactly`); diff --git a/packages/superdoc/scripts/ensure-types.cjs b/packages/superdoc/scripts/ensure-types.cjs index 8ee0ed423c..8b85d0f2b3 100644 --- a/packages/superdoc/scripts/ensure-types.cjs +++ b/packages/superdoc/scripts/ensure-types.cjs @@ -3,6 +3,11 @@ const fs = require('node:fs'); const path = require('node:path'); +// SD-2864: canonical taxonomy for the published type surface. Mirrors +// vite.config.js, tsconfig.json, and audit-declarations.cjs from a single +// data file so contributors only edit one place to add a new relocation. +const typeSurface = require('./type-surface.config.cjs'); + // Verify that vite-plugin-dts generated the expected type entry points. // Path aliases are resolved by vite-plugin-dts via tsconfig.json paths. const distRoot = path.resolve(__dirname, '..', 'dist'); @@ -13,19 +18,9 @@ const repoRoot = path.resolve(__dirname, '..', '..', '..'); // `core-command-map.d.ts` is referenced via a relative import from another // emitted `.d.ts`, the consumer hits an unresolved-module error. Copy // every hand-written `.d.ts` from the source trees we publish into the -// matching dist location so those imports resolve. -// Hand-written `.d.ts` files we know are internal-only and must NOT ship -// in `superdoc`'s published dist. The copy step is opt-in via filename -// blocklist (rather than e.g. a per-file directive) so future hand-written -// declarations land in dist by default and the cost of skipping one is one -// line here. Each entry should have a comment explaining why. -const HANDWRITTEN_DTS_BLOCKLIST = new Set([ - // Ambient module declarations for internal `@superdoc/super-editor/converter/internal/...` - // subpaths. Nothing in `superdoc`'s shipped surface actually imports those subpaths, - // so the declarations would only leak the bare specifiers into published d.ts. - // Keep the file in source for super-editor's own typecheck; just don't ship it. (SD-2859) - 'converter-internal.d.ts', -]); +// matching dist location so those imports resolve. Source list: +// type-surface.config.cjs `handwrittenDtsBlocklist`. +const HANDWRITTEN_DTS_BLOCKLIST = new Set(typeSurface.handwrittenDtsBlocklist); function copyHandwrittenDtsFiles(srcDir, destDir) { let copied = 0; @@ -66,11 +61,10 @@ if (handwrittenCopiedSuperEditor > 0) { // public surface. Adding shared/ to vite-plugin-dts's `include` would shift the // common-ancestor of all source files to the repo root and reorganise the // entire dist tree, so we run tsc directly for just the files we relocate. -// Today: list-marker-utils plus its sibling layout-constants, and -// comments-types (the four Comment* types referenced via bare @superdoc/common -// imports in three internal-only dist d.ts files). Add new entries here in -// lockstep with `RELOCATION_RULES` below. -const SHARED_COMMON_DTS_TARGETS = ['list-marker-utils.ts', 'layout-constants.ts', 'comments-types.ts']; +// Source list: type-surface.config.cjs `sharedCommonDtsTargets`. Each entry +// pairs with a `relocations` rule whose distEntry points at +// `shared/common/.d.ts`. +const SHARED_COMMON_DTS_TARGETS = typeSurface.sharedCommonDtsTargets; { const { spawnSync: _spawnSync } = require('node:child_process'); const tscBin = path.join(repoRoot, 'node_modules', '.bin', 'tsc'); @@ -99,12 +93,7 @@ const SHARED_COMMON_DTS_TARGETS = ['list-marker-utils.ts', 'layout-constants.ts' console.log(`[ensure-types] ✓ Emitted ${SHARED_COMMON_DTS_TARGETS.length} shared/common declarations`); } -const requiredEntryPoints = [ - 'superdoc/src/index.d.ts', - 'superdoc/src/super-editor.d.ts', - 'super-editor/src/index.d.ts', - 'super-editor/src/types.d.ts', -]; +const requiredEntryPoints = typeSurface.requiredEntryPoints; for (const entry of requiredEntryPoints) { const fullPath = path.join(distRoot, entry); @@ -243,84 +232,20 @@ function rewriteDocApiPaths(fileContent, filePath) { }); } -// SD-2842: relocate workspace packages whose types appear on the -// public surface. Same idea as the document-api rewrite above: emit -// their declarations into superdoc's dist (via vite-plugin-dts include) -// and redirect bare specifiers in emitted .d.ts files to relative -// paths the consumer can resolve. -// -// SD-2893 note for pm-adapter: only specific type subpaths are -// relocated (see vite.config.js include list). Do not add a broad -// `@superdoc/pm-adapter` rule unless the barrel declaration is also -// emitted; otherwise a bare specifier would rewrite to a missing -// relative path and evade the audit gate. -const RELOCATION_RULES = [ - { pkg: '@superdoc/contracts', distEntry: 'layout-engine/contracts/src/index.d.ts', matchSubpaths: true }, - { pkg: '@superdoc/dom-contract', distEntry: 'layout-engine/dom-contract/src/index.d.ts', matchSubpaths: true }, - { pkg: '@superdoc/layout-bridge', distEntry: 'layout-engine/layout-bridge/src/index.d.ts', matchSubpaths: true }, - { pkg: '@superdoc/layout-engine', distEntry: 'layout-engine/layout-engine/src/index.d.ts', matchSubpaths: true }, - { pkg: '@superdoc/painter-dom', distEntry: 'layout-engine/painters/dom/src/index.d.ts', matchSubpaths: true }, - { - pkg: '@superdoc/pm-adapter/converter-context.js', - distEntry: 'layout-engine/pm-adapter/src/converter-context.d.ts', - matchSubpaths: false, - }, - { - pkg: '@superdoc/pm-adapter/sections/types.js', - distEntry: 'layout-engine/pm-adapter/src/sections/types.d.ts', - matchSubpaths: false, - }, - // SD-2893: list-marker-utils is the only @superdoc/common subpath publicly - // reachable today (via painter-dom). Relocate just this file so the bare - // @superdoc/common shim does not capture it; the parent @superdoc/common - // package and other subpaths stay shimmed until separately drained. - { - pkg: '@superdoc/common/list-marker-utils', - distEntry: 'shared/common/list-marker-utils.d.ts', - matchSubpaths: false, - }, - // SD-2893: only the /ooxml subpath of style-engine is publicly reachable. - // Relocate just this subpath plus its sibling cascade.ts dependency - // (see vite.config.js include list). The bare @superdoc/style-engine is - // guarded but unrewritten; if a future bare-barrel leak appears the audit - // gate fails rather than producing a missing relative path. - { - pkg: '@superdoc/style-engine/ooxml', - distEntry: 'layout-engine/style-engine/src/ooxml/index.d.ts', - matchSubpaths: false, - }, - // SD-2893: bare @superdoc/common appears in three internal-only dist d.ts - // files for the four Comment* types (Comment, CommentContent, CommentJSON, - // CommentThreadingProfile). Point the bare specifier at comments-types.d.ts - // (emitted via SHARED_COMMON_DTS_TARGETS) so the rewrite resolves to a real - // file. matchSubpaths: false because only the bare specifier is referenced; - // any future @superdoc/common/ import would not be auto- - // rewritten, falling through to the audit gate. The runtime-value imports - // from the main entry (DOCX, PDF, HTML, getFileObject, compareVersions, - // BlankDOCX) are still handled by the inline-replacement step above. - { - pkg: '@superdoc/common', - distEntry: 'shared/common/comments-types.d.ts', - matchSubpaths: false, - }, -]; - -// Guard packages that must never fall back to `_internal-shims.d.ts`. -// `@superdoc/pm-adapter` is guarded as a root package even though only -// two exact subpaths are relocated today; a future bare-barrel leak should -// fail the build rather than ship as `any`. -const RELOCATION_GUARD_PACKAGES = [ - '@superdoc/document-api', - '@superdoc/contracts', - '@superdoc/dom-contract', - '@superdoc/layout-bridge', - '@superdoc/layout-engine', - '@superdoc/painter-dom', - '@superdoc/pm-adapter', - '@superdoc/style-engine', - '@superdoc/common', - '@superdoc/common/list-marker-utils', -]; +// SD-2842 / SD-2864: relocate workspace packages whose types appear on the +// public surface. Each rule redirects bare/subpath specifiers in emitted +// .d.ts files to a relative path inside dist. The canonical list lives in +// type-surface.config.cjs; this script picks the fields it needs. +const RELOCATION_RULES = typeSurface.relocations.map(({ pkg, distEntry, matchSubpaths }) => ({ + pkg, + distEntry, + matchSubpaths, +})); + +// Guard packages that must never appear as a `declare module` block in +// `_internal-shims.d.ts`. SD-2942 removed the shim emit; this list is +// kept as defense against stale tarballs and future re-introduction. +const RELOCATION_GUARD_PACKAGES = typeSurface.relocationGuardPackages; function isRelocatedSpecifier(mod) { return RELOCATION_RULES.some((rule) => @@ -356,11 +281,9 @@ const RELOCATION_REWRITERS = RELOCATION_RULES.map((rule) => ({ // Any root specifier added here should also be listed in // RELOCATION_GUARD_PACKAGES so it cannot fall back to an ambient `any` -// shim after we intentionally skip shim generation. -const UNSHIMMED_PRIVATE_SPECIFIERS = new Set([ - '@superdoc/pm-adapter', - '@superdoc/style-engine', -]); +// shim after we intentionally skip shim generation. List source: +// type-surface.config.cjs (`unshimmedPrivateSpecifiers`). +const UNSHIMMED_PRIVATE_SPECIFIERS = new Set(typeSurface.unshimmedPrivateSpecifiers); function shouldSkipWorkspaceShim(mod) { return ( diff --git a/packages/superdoc/scripts/type-surface.config.cjs b/packages/superdoc/scripts/type-surface.config.cjs new file mode 100644 index 0000000000..b671cb336e --- /dev/null +++ b/packages/superdoc/scripts/type-surface.config.cjs @@ -0,0 +1,235 @@ +/** + * SD-2864: single source of truth for the published superdoc type surface. + * + * The same taxonomy was previously duplicated across four files: + * - packages/superdoc/scripts/ensure-types.cjs + * - packages/superdoc/scripts/audit-declarations.cjs + * - packages/superdoc/vite.config.js + * - packages/superdoc/tsconfig.json + * + * Adding a new public-surface relocation required coordinated edits to all + * four. PR #3144 (pm-adapter) and several SD-2893 stack PRs each shipped a + * regression caused by drift between these lists. This config consolidates + * the canonical data so each consumer derives what it needs from a single + * place. + * + * Shape: + * - `requiredEntryPoints`: dist d.ts paths that must exist after build. + * - `handwrittenDtsBlocklist`: filenames in source that must NOT be + * copied into dist (internal-only ambient declarations). + * - `relocations`: workspace packages whose types appear on the public + * surface. Each entry pairs the rewriter rule with the source paths + * vite-plugin-dts and tsconfig.json need to include for the + * declarations to be emitted into superdoc/dist. + * - `sharedCommonDtsTargets`: filenames in `shared/common/` that the + * postbuild step compiles via tsc. Used for relocations whose source + * lives outside `packages/` and would otherwise shift the + * vite-plugin-dts common-ancestor. + * - `relocationGuardPackages`: packages that must never appear as a + * `declare module` block in `_internal-shims.d.ts` (audit Rule 3). + * SD-2942 removed the shim mechanism; this list is kept as defense + * against stale tarballs and future re-introduction. + * - `unshimmedPrivateSpecifiers`: bare specifiers that, if a future + * mechanism re-introduces shim generation, must not be auto-shimmed. + * They should fail audit Rule 1 instead. + * - `rule1Allowlist`: bare `@superdoc/*` specifiers permitted in + * published d.ts. Currently only the legacy public super-editor + * surface per RFC Decision 1. + * + * Adding a new relocation: append one entry to `relocations` with the + * package specifier, the dist target the rewriter should point at, and + * the source-include patterns vite + tsconfig need. Every consumer picks + * up the new entry without further edits. + */ + +const requiredEntryPoints = [ + 'superdoc/src/index.d.ts', + 'superdoc/src/super-editor.d.ts', + 'super-editor/src/index.d.ts', + 'super-editor/src/types.d.ts', +]; + +/** + * Foundational source roots tsconfig.json must include but `relocations` + * does not own. These are the public-package sources themselves + * (`superdoc/src`, `super-editor/src`, `document-api/src`), distinct from + * the workspace-internal packages relocated via `relocations`. The + * tsconfig parity check expects exactly this base set plus the union of + * `relocations[*].tsconfigIncludes`. + */ +const baseTsconfigIncludes = ['src', '../super-editor/src', '../document-api/src']; + +const handwrittenDtsBlocklist = [ + // Ambient module declarations for internal `@superdoc/super-editor/converter/internal/...` + // subpaths. Nothing in superdoc's shipped surface imports those subpaths, + // so the declarations would only leak the bare specifiers into published d.ts. + // Keep the file in source for super-editor's own typecheck; just don't ship it. (SD-2859) + 'converter-internal.d.ts', +]; + +/** + * Each relocation describes a private workspace package whose types appear + * on the public surface, plus the source patterns needed to emit those + * declarations into superdoc/dist. + * + * - `pkg`: the bare specifier consumers' d.ts files reference. + * - `distEntry`: the file path (relative to dist root) that the rewriter + * redirects bare specifiers to. Must be emitted by either vite-plugin-dts + * (full-glob include) or the postbuild tsc step (sharedCommonDtsTargets). + * - `matchSubpaths`: when true, the rewriter also rewrites + * `pkg/` → `distEntry` with `/index.js` swapped for `/`. + * When false, only the exact `pkg` specifier is rewritten; subpaths + * fall through to audit Rule 1 (used for narrow relocations like + * pm-adapter where only specific subpaths are emitted). + * - `viteIncludes`: glob/file patterns added to vite-plugin-dts's + * `include` array. Multiple entries are allowed when the relocation + * needs sibling files (e.g. style-engine/ooxml depends on cascade.ts). + * - `tsconfigIncludes`: parallel paths added to tsconfig.json's + * `include` array. The check-tsconfig script verifies parity. + */ +const relocations = [ + { + pkg: '@superdoc/contracts', + distEntry: 'layout-engine/contracts/src/index.d.ts', + matchSubpaths: true, + viteIncludes: ['../layout-engine/contracts/src/**/*'], + tsconfigIncludes: ['../layout-engine/contracts/src'], + }, + { + pkg: '@superdoc/dom-contract', + distEntry: 'layout-engine/dom-contract/src/index.d.ts', + matchSubpaths: true, + viteIncludes: ['../layout-engine/dom-contract/src/**/*'], + tsconfigIncludes: ['../layout-engine/dom-contract/src'], + }, + { + pkg: '@superdoc/layout-bridge', + distEntry: 'layout-engine/layout-bridge/src/index.d.ts', + matchSubpaths: true, + viteIncludes: ['../layout-engine/layout-bridge/src/**/*'], + tsconfigIncludes: ['../layout-engine/layout-bridge/src'], + }, + { + pkg: '@superdoc/layout-engine', + distEntry: 'layout-engine/layout-engine/src/index.d.ts', + matchSubpaths: true, + viteIncludes: ['../layout-engine/layout-engine/src/**/*'], + tsconfigIncludes: ['../layout-engine/layout-engine/src'], + }, + { + pkg: '@superdoc/painter-dom', + distEntry: 'layout-engine/painters/dom/src/index.d.ts', + matchSubpaths: true, + viteIncludes: ['../layout-engine/painters/dom/src/**/*'], + tsconfigIncludes: ['../layout-engine/painters/dom/src'], + }, + // pm-adapter: subpath-only. The full barrel pulls in @superdoc/style-engine + // and other internal packages that would re-expand the shim list. + { + pkg: '@superdoc/pm-adapter/converter-context.js', + distEntry: 'layout-engine/pm-adapter/src/converter-context.d.ts', + matchSubpaths: false, + viteIncludes: ['../layout-engine/pm-adapter/src/converter-context.ts'], + tsconfigIncludes: ['../layout-engine/pm-adapter/src/converter-context.ts'], + }, + { + pkg: '@superdoc/pm-adapter/sections/types.js', + distEntry: 'layout-engine/pm-adapter/src/sections/types.d.ts', + matchSubpaths: false, + viteIncludes: ['../layout-engine/pm-adapter/src/sections/types.ts'], + tsconfigIncludes: ['../layout-engine/pm-adapter/src/sections/types.ts'], + }, + // style-engine/ooxml: subpath-only. Includes the ooxml subtree plus the + // sibling cascade.ts dependency it imports. + { + pkg: '@superdoc/style-engine/ooxml', + distEntry: 'layout-engine/style-engine/src/ooxml/index.d.ts', + matchSubpaths: false, + viteIncludes: [ + '../layout-engine/style-engine/src/ooxml/**/*', + '../layout-engine/style-engine/src/cascade.ts', + ], + tsconfigIncludes: [ + '../layout-engine/style-engine/src/ooxml', + '../layout-engine/style-engine/src/cascade.ts', + ], + }, + // common/list-marker-utils and common (bare): emitted via tsc-postbuild + // (see sharedCommonDtsTargets) because the source lives in shared/, which + // would shift the vite-plugin-dts common-ancestor if added to vite include. + // Empty viteIncludes/tsconfigIncludes are deliberate: ensure-types.cjs's + // tsc-postbuild step handles emit; no vite/tsconfig participation needed. + { + pkg: '@superdoc/common/list-marker-utils', + distEntry: 'shared/common/list-marker-utils.d.ts', + matchSubpaths: false, + viteIncludes: [], // emitted via sharedCommonDtsTargets tsc-postbuild + tsconfigIncludes: [], + }, + { + pkg: '@superdoc/common', + distEntry: 'shared/common/comments-types.d.ts', + matchSubpaths: false, + viteIncludes: [], // emitted via sharedCommonDtsTargets tsc-postbuild + tsconfigIncludes: [], + }, +]; + +/** + * Filenames in `shared/common/` that the postbuild tsc step compiles into + * `dist/shared/common/`. Each filename pairs with a `relocations` entry + * whose `distEntry` lives at `shared/common/.d.ts`. + */ +const sharedCommonDtsTargets = [ + 'list-marker-utils.ts', + 'layout-constants.ts', // dependency of list-marker-utils + 'comments-types.ts', +]; + +/** + * Packages that must NEVER appear as a `declare module` entry in + * `_internal-shims.d.ts`. After SD-2942 the shim file is no longer emitted, + * so this list is a defense against stale tarballs and future + * re-introduction. Mirrored automatically by ensure-types and audit. + */ +const relocationGuardPackages = [ + '@superdoc/document-api', + '@superdoc/contracts', + '@superdoc/dom-contract', + '@superdoc/layout-bridge', + '@superdoc/layout-engine', + '@superdoc/painter-dom', + '@superdoc/pm-adapter', + '@superdoc/style-engine', + '@superdoc/common', + '@superdoc/common/list-marker-utils', +]; + +/** + * Bare specifiers that any future shim-generation mechanism must NOT + * shim. They should fail audit Rule 1 instead. Used today only as + * forward-compat documentation; the SD-2942 removal made shim + * generation a no-op. + */ +const unshimmedPrivateSpecifiers = ['@superdoc/pm-adapter', '@superdoc/style-engine']; + +/** + * Bare `@superdoc/*` specifiers permitted in published d.ts beyond the + * relocation rules. Currently only the legacy public super-editor surface + * per RFC Decision 1; consumers resolve it through the `superdoc/super-editor` + * subpath export at runtime. + */ +const rule1Allowlist = { + '@superdoc/super-editor': 'legacy public surface (RFC Decision 1)', +}; + +module.exports = { + requiredEntryPoints, + handwrittenDtsBlocklist, + baseTsconfigIncludes, + relocations, + sharedCommonDtsTargets, + relocationGuardPackages, + unshimmedPrivateSpecifiers, + rule1Allowlist, +}; diff --git a/packages/superdoc/vite.config.js b/packages/superdoc/vite.config.js index 177fd6b22a..7f197a12ed 100644 --- a/packages/superdoc/vite.config.js +++ b/packages/superdoc/vite.config.js @@ -13,6 +13,12 @@ import vue from '@vitejs/plugin-vue' import { version } from './package.json'; import sourceResolve from '../../vite.sourceResolve'; +// SD-2864: derive the dts include list from the canonical type-surface +// config so vite, ensure-types, audit, and the tsconfig parity check +// share one source of truth for relocations. +const cjsRequire = createRequire(import.meta.url); +const typeSurface = cjsRequire('./scripts/type-surface.config.cjs'); + // WORKAROUND: rolldown doesn't support trailing-slash imports (e.g. 'punycode/') // which Node.js treats as "resolve the package entry point". node-stdlib-browser's // url polyfill uses `import from 'punycode/'` and rolldown tries to open the @@ -113,39 +119,16 @@ export default defineConfig(({ mode, command }) => { const plugins = [ vue(), !skipDts && dts({ - // SD-2815: include `../document-api/src/**/*` so the doc-api - // types re-exported from `superdoc/ui` (CommentInfo, Receipt, - // SelectionInfo, TextTarget, etc.) emit real declarations into - // the published dist instead of falling through to the - // `_internal-shims.d.ts` `any` fallback that ensure-types.cjs - // generates for every unshipped `@superdoc/*` package. Without - // this, packed consumers see `any` for those public types and - // the new re-export surface adds no actual checking. + // Foundational sources (superdoc, super-editor, document-api) are + // always included; relocation patterns come from the canonical + // type-surface config (SD-2864). Each `relocations` entry pairs the + // ensure-types rewriter rule with the vite include patterns so the + // two cannot drift. include: [ 'src/**/*', '../super-editor/src/**/*', '../document-api/src/**/*', - // SD-2842: relocate workspace packages whose types appear on the - // public surface so they emit into superdoc's dist and the - // rewrite step in ensure-types can redirect bare specifiers to - // local relative paths. Same pattern as @superdoc/document-api. - '../layout-engine/contracts/src/**/*', - '../layout-engine/dom-contract/src/**/*', - '../layout-engine/layout-bridge/src/**/*', - '../layout-engine/layout-engine/src/**/*', - '../layout-engine/painters/dom/src/**/*', - // SD-2893: pm-adapter is included file-by-file (not via `src/**/*`) - // because the full barrel pulls in @superdoc/style-engine and other - // internal packages that would re-expand the shim list. Only the - // type subpaths reachable from the public surface are relocated. - '../layout-engine/pm-adapter/src/converter-context.ts', - '../layout-engine/pm-adapter/src/sections/types.ts', - // SD-2893: only the /ooxml subpath of style-engine is publicly - // reachable today. Include the ooxml subtree plus the cascade.ts - // sibling it depends on. The full src/**/* glob pulls the broader - // project graph through contracts project references. - '../layout-engine/style-engine/src/ooxml/**/*', - '../layout-engine/style-engine/src/cascade.ts', + ...typeSurface.relocations.flatMap((r) => r.viteIncludes), ], outDir: 'dist', // vite-plugin-dts still gathers diagnostics for this mixed JS/Vue source