perf(infra): map each workspace package to an explicit path alias

tsconfig.base.json resolved @deepseek-ai/dsh-* through 49 candidate
globs and @deepseek-ai/dsh-*/invariant through 45, one per package
group. Resolution tries candidates in order, so a package late in the
list paid for every earlier miss — and under the dsh source launch each
miss is an ERR_MODULE_NOT_FOUND that Node decorates with a full
CommonJS resolution walk. A boot profile attributed 934.6 ms, 35% of
startup, to that decoration path across 60,942 failed resolutions. The
cost landed hardest on packages/util/*, which sits at position 44 of 49
and holds the leaf utilities nearly every plugin imports.

gen-tsconfig-paths writes one explicit alias per package into a marked
region and both group wildcards are gone; verify-tsconfig-paths reports
drift and runs in the ci-static lane. Booting the headless profile from
source drops from ~2,157 ms to ~1,055 ms with --help output unchanged.

All 1,022 dsh specifiers in repository sources resolve to the same
target as before, except seven /invariant specifiers in the lsp,
terminal, and runtime-diagnostics groups that the deleted wildcard
never listed: those reached built lib/types instead of src, against the
rule that static gates resolve through paths to src on a clean tree.
This commit is contained in:
Yichen Jiang
2026-08-27 17:26:52 +08:00
parent c6e1914f2d
commit 12c161e1a7
8 changed files with 690 additions and 104 deletions
+69
View File
@@ -0,0 +1,69 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { collectPackageAliases, renderAliases, writeRegion } from './gen-tsconfig-paths.ts'
const root = resolve(import.meta.dirname, '..')
describe('generated tsconfig package aliases', () => {
it('maps each package to its own source directory', () => {
const aliases = collectPackageAliases()
expect(aliases.length).toBeGreaterThan(100)
const session = aliases.find(alias => alias.specifier === '@deepseek-ai/dsh-session')
expect(session).toEqual({
specifier: '@deepseek-ai/dsh-session',
source: './packages/core/session/src',
hasInvariant: true,
})
// Sorted, so a package added anywhere lands in a stable spot in the diff.
expect([...aliases].sort((a, b) => a.specifier.localeCompare(b.specifier))).toEqual(aliases)
// Only packages named after their directory: the rest carry hand-written
// aliases, because the removed wildcards could never have resolved them.
expect(aliases.some(alias => alias.specifier === '@deepseek-ai/dsh-typert-protocol')).toBe(false)
})
it('yields to a hand-written alias and closes without a trailing comma', () => {
const aliases = [
{ specifier: '@deepseek-ai/dsh-a', source: './packages/g/a/src', hasInvariant: true },
{ specifier: '@deepseek-ai/dsh-b', source: './packages/g/b/src', hasInvariant: false },
]
const body = renderAliases(aliases, new Set(['@deepseek-ai/dsh-a']))
// The hand-written bare alias is skipped; its /invariant sibling is not.
expect(body).toBe([
' "@deepseek-ai/dsh-a/invariant": ["./packages/g/a/src/invariant.ts"]',
' "@deepseek-ai/dsh-b": ["./packages/g/b/src"]',
].join(',\n'))
expect(body.endsWith(',')).toBe(false)
})
it('replaces only the marked region', () => {
const text = [
'{ "before": 1,',
' // BEGIN generated package aliases — pnpm run gen-tsconfig-paths',
' "stale": ["gone"]',
' // END generated package aliases',
' "after": 2 }',
].join('\n')
const next = writeRegion(text, ' "fresh": ["kept"]')
expect(next).toContain('{ "before": 1,')
expect(next).toContain(' "after": 2 }')
expect(next).toContain('"fresh": ["kept"]')
expect(next).not.toContain('stale')
})
it('refuses a config without the region markers', () => {
expect(() => writeRegion('{}', '')).toThrow('missing the generated-region markers')
})
it('leaves no wildcard that probes every package group', () => {
const config = readFileSync(resolve(root, 'tsconfig.base.json'), 'utf8')
// These two listed one candidate per group, so resolving a package late in
// the list cost a filesystem probe — and under tsx a decorated module
// error — for every group before it.
expect(config).not.toContain('"@deepseek-ai/dsh-*":')
expect(config).not.toContain('"@deepseek-ai/dsh-*/invariant":')
})
})
+171
View File
@@ -0,0 +1,171 @@
/**
* Expand the workspace path aliases that a wildcard would otherwise resolve by
* probing every package group in turn.
*
* `tsconfig.base.json` is the resolution facade for the whole repository, and
* two of its aliases used one key per *group* rather than per package:
* `@deepseek-ai/dsh-*` listed 49 candidate globs and `@deepseek-ai/dsh-*\/invariant`
* listed 45. TypeScript and tsx try those candidates in order, so a specifier
* whose package sits late in the list pays for every earlier miss. Under tsx's
* ESM hook each miss is an `ERR_MODULE_NOT_FOUND` that Node decorates with a
* full CommonJS resolution walk, which dominated source-launch boot.
*
* This generator writes one explicit entry per package into a marked region of
* `tsconfig.base.json`, leaving every hand-written alias and comment outside
* that region untouched. `--check` reports drift instead of writing, so a new
* package that needs an alias fails a gate rather than silently resolving
* through a fallback that no longer exists.
*
* @module scripts/gen-tsconfig-paths
*/
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = fileURLToPath(new URL('..', import.meta.url))
const CONFIG = join(ROOT, 'tsconfig.base.json')
const BEGIN = ' // BEGIN generated package aliases — pnpm run gen-tsconfig-paths'
const END = ' // END generated package aliases'
/** Package-name prefix the expanded aliases cover. */
const PREFIX = '@deepseek-ai/dsh-'
/** One workspace package the generated region maps. */
interface PackageAlias {
/** Bare specifier, e.g. `@deepseek-ai/dsh-session`. */
readonly specifier: string
/** Repository-relative source directory, e.g. `./packages/session/session/src`. */
readonly source: string
/** Whether the package carries `src/invariant.ts`, which earns a second alias. */
readonly hasInvariant: boolean
}
/**
* Read a workspace manifest's declared name.
* @param manifest - absolute path to a `package.json`.
* @returns The declared name, or undefined when the file is absent or nameless.
*/
function packageName(manifest: string): string | undefined {
let parsed: unknown
try {
parsed = JSON.parse(readFileSync(manifest, 'utf8'))
} catch (_absentOrUnreadableManifest) {
return undefined
}
if (typeof parsed !== 'object' || parsed === null) return undefined
const name: unknown = (parsed as { name?: unknown }).name
return typeof name === 'string' ? name : undefined
}
/**
* Collect every package the removed wildcards could resolve.
*
* A wildcard substituted the specifier's suffix into `packages/<group>/<suffix>/src`,
* so it only ever resolved a package whose declared name is exactly
* `@deepseek-ai/dsh-<directory>`. Packages named after something other than
* their directory already carry a hand-written alias and are skipped here.
*
* @returns Aliases sorted by specifier.
* @throws When two package directories claim one specifier, which the removed
* wildcards resolved by group order and an explicit map cannot express.
*/
export function collectPackageAliases(): PackageAlias[] {
const packages = join(ROOT, 'packages')
const bySpecifier = new Map<string, PackageAlias & { directory: string }>()
for (const group of readdirSync(packages).sort()) {
const groupDir = join(packages, group)
if (!statSync(groupDir).isDirectory()) continue
for (const directory of readdirSync(groupDir).sort()) {
const packageDir = join(groupDir, directory)
const name = packageName(join(packageDir, 'package.json'))
if (name === undefined || name !== `${PREFIX}${directory}`) continue
if (!existsSync(join(packageDir, 'src'))) continue
const previous = bySpecifier.get(name)
if (previous !== undefined) {
throw new Error(
`gen-tsconfig-paths: ${name} is claimed by packages/${previous.directory} and packages/${group}/${directory}; `
+ 'an explicit alias cannot express the group-order tiebreak the wildcard used.',
)
}
bySpecifier.set(name, {
specifier: name,
source: `./packages/${group}/${directory}/src`,
hasInvariant: existsSync(join(packageDir, 'src', 'invariant.ts')),
directory: `${group}/${directory}`,
})
}
}
return [...bySpecifier.values()]
.map(({ specifier, source, hasInvariant }) => ({ specifier, source, hasInvariant }))
.sort((left, right) => left.specifier.localeCompare(right.specifier))
}
/**
* Render the generated region's alias lines.
* @param aliases - packages to map, in emission order.
* @param handWritten - specifiers already mapped outside the region; a duplicate key would shadow one silently.
* @returns The region body, one JSON member per line.
*/
export function renderAliases(aliases: readonly PackageAlias[], handWritten: ReadonlySet<string>): string {
const lines: string[] = []
for (const alias of aliases) {
if (!handWritten.has(alias.specifier)) {
lines.push(` ${JSON.stringify(alias.specifier)}: [${JSON.stringify(alias.source)}]`)
}
const invariant = `${alias.specifier}/invariant`
if (alias.hasInvariant && !handWritten.has(invariant)) {
lines.push(` ${JSON.stringify(invariant)}: [${JSON.stringify(`${alias.source}/invariant.ts`)}]`)
}
}
// The region closes `paths`, so the last member carries no trailing comma.
return lines.join(',\n')
}
/**
* Replace the generated region of a config's text.
* @param text - current `tsconfig.base.json` contents.
* @param body - rendered alias lines.
* @returns The updated contents.
* @throws When the markers are missing or out of order.
*/
export function writeRegion(text: string, body: string): string {
const begin = text.indexOf(BEGIN)
const end = text.indexOf(END)
if (begin < 0 || end < begin) {
throw new Error(`gen-tsconfig-paths: ${CONFIG} is missing the generated-region markers.`)
}
return `${text.slice(0, begin)}${BEGIN}\n${body}\n${END}${text.slice(end + END.length)}`
}
/**
* Parse the config's `paths` keys, ignoring the generated region.
* @param text - current `tsconfig.base.json` contents.
* @returns Specifiers mapped by hand.
*/
function handWrittenSpecifiers(text: string): Set<string> {
const begin = text.indexOf(BEGIN)
const end = text.indexOf(END)
const outside = begin < 0 || end < begin ? text : text.slice(0, begin) + text.slice(end)
const keys = new Set<string>()
for (const match of outside.matchAll(/^\s*"(@deepseek-ai\/[^"]+)":/gm)) {
const key = match[1]
if (key !== undefined) keys.add(key)
}
return keys
}
if (import.meta.url === `file://${process.argv[1] ?? ''}`) {
const check = process.argv.includes('--check')
const current = readFileSync(CONFIG, 'utf8')
const next = writeRegion(current, renderAliases(collectPackageAliases(), handWrittenSpecifiers(current)))
if (current === next) {
console.log('gen-tsconfig-paths: tsconfig.base.json package aliases are current.')
} else if (check) {
console.error('gen-tsconfig-paths: tsconfig.base.json is stale; run `pnpm run gen-tsconfig-paths`.')
process.exitCode = 1
} else {
writeFileSync(CONFIG, next)
console.log('gen-tsconfig-paths: rewrote tsconfig.base.json package aliases.')
}
}
+1
View File
@@ -720,6 +720,7 @@ function docSyncLeafGates(options: {
pnpmScript('doc-refs', 'verify-doc-refs', { label: 'doc refs', quick: true }),
pnpmScript('subsystem-pages', 'verify-subsystem-pages', { label: 'subsystem pages' }),
pnpmScript('package-paths', 'verify-package-paths', { label: 'package paths' }),
pnpmScript('tsconfig-paths', 'verify-tsconfig-paths', { label: 'tsconfig paths' }),
pnpmScript('config-source-ownership', 'verify-config-source-ownership', { label: 'config source ownership' }),
pnpmScript('package-readme-model-experience', 'verify-package-readme-model-experience', { label: 'package README model experience', quick: true }),
pnpmScript('agent-note-classification', 'verify-agent-note-classification', { label: 'agent note classification', quick: true }),