mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
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:
@@ -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":')
|
||||
})
|
||||
})
|
||||
@@ -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.')
|
||||
}
|
||||
}
|
||||
@@ -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 }),
|
||||
|
||||
Reference in New Issue
Block a user