fix(sdk): address remaining tooling review findings
This commit is contained in:
@@ -135,9 +135,11 @@ export class CordisYamlFile extends ProjectFile {
|
||||
}
|
||||
|
||||
/** Add one new top-level entry, rejecting duplicate ids. */
|
||||
addEntry(entry: CordisConfigEntry): void {
|
||||
addEntry(entry: CordisConfigEntry, commentedExample?: string): void {
|
||||
if (this.entryNode(entry.id)) throw new Error(`Cordis config entry already exists: ${entry.id}`)
|
||||
this.sequence().items.push(this.document.createNode(entry))
|
||||
const node = this.document.createNode(entry)
|
||||
if (commentedExample) node.comment = commentedExample.split('\n').map(line => ` ${line}`).join('\n')
|
||||
this.sequence().items.push(node)
|
||||
}
|
||||
|
||||
/** Remove an entry by id and report whether it existed. */
|
||||
|
||||
@@ -4,73 +4,93 @@
|
||||
* @module @deepseek-ai/dsh-helper/documents/pnpm-workspace-file
|
||||
*/
|
||||
|
||||
import { parse, stringify } from 'yaml'
|
||||
import {
|
||||
isMap, isScalar, isSeq, parseDocument,
|
||||
type Document, type Scalar, type YAMLMap, type YAMLSeq,
|
||||
} from 'yaml'
|
||||
import { ProjectFile, withTrailingNewline } from './project-file.ts'
|
||||
|
||||
function parseYaml(text: string): Document.Parsed {
|
||||
const document = parseDocument(text, { keepSourceTokens: true, prettyErrors: true })
|
||||
if (document.errors.length > 0) {
|
||||
throw new Error(`invalid pnpm-workspace.yaml: ${document.errors.map(error => error.message).join('; ')}`)
|
||||
}
|
||||
if (!isMap(document.contents)) throw new Error('pnpm-workspace.yaml root must be an object')
|
||||
return document
|
||||
}
|
||||
|
||||
/** Generated pnpm-workspace.yaml model. */
|
||||
export class PnpmWorkspaceFile extends ProjectFile {
|
||||
private readonly packages = new Set<string>()
|
||||
private autoInstallPeers = true
|
||||
private readonly document: Document.Parsed
|
||||
|
||||
private constructor(originalText?: string) {
|
||||
private constructor(document: Document.Parsed, originalText?: string) {
|
||||
super('pnpm-workspace.yaml', originalText)
|
||||
this.document = document
|
||||
}
|
||||
|
||||
/** Create a pnpm workspace document. */
|
||||
static create(): PnpmWorkspaceFile {
|
||||
return new PnpmWorkspaceFile()
|
||||
}
|
||||
|
||||
/** Parse the workspace fields the SDK owns. */
|
||||
static parse(text: string): PnpmWorkspaceFile {
|
||||
const value: unknown = parse(text)
|
||||
if (value === null || Array.isArray(value) || typeof value !== 'object') {
|
||||
throw new Error('pnpm-workspace.yaml root must be an object')
|
||||
}
|
||||
const input = value as Record<string, unknown>
|
||||
if (!Array.isArray(input.packages) || input.packages.some(item => typeof item !== 'string')) {
|
||||
throw new Error('pnpm-workspace.yaml packages must be an array of strings')
|
||||
}
|
||||
const document = new PnpmWorkspaceFile(text)
|
||||
for (const pattern of input.packages) document.packages.add(pattern as string)
|
||||
if (input.autoInstallPeers !== undefined && typeof input.autoInstallPeers !== 'boolean') {
|
||||
throw new Error('pnpm-workspace.yaml autoInstallPeers must be boolean')
|
||||
}
|
||||
document.autoInstallPeers = input.autoInstallPeers !== false
|
||||
const document = new PnpmWorkspaceFile(parseYaml('{}\n'))
|
||||
document.mapping().set('packages', document.document.createNode([]))
|
||||
document.mapping().set('allowBuilds', document.document.createNode({ esbuild: true }))
|
||||
return document
|
||||
}
|
||||
|
||||
/** Clone workspace globs and peer-install policy. */
|
||||
/** Parse the workspace fields the SDK owns while retaining all other YAML. */
|
||||
static parse(text: string): PnpmWorkspaceFile {
|
||||
const document = new PnpmWorkspaceFile(parseYaml(text), text)
|
||||
document.packageSequence()
|
||||
const autoInstallPeers = document.mapping().get('autoInstallPeers')
|
||||
if (autoInstallPeers !== undefined && typeof autoInstallPeers !== 'boolean') {
|
||||
throw new Error('pnpm-workspace.yaml autoInstallPeers must be boolean')
|
||||
}
|
||||
return document
|
||||
}
|
||||
|
||||
/** Clone the complete comment-preserving workspace document. */
|
||||
override clone(): PnpmWorkspaceFile {
|
||||
const clone = new PnpmWorkspaceFile(this.originalText)
|
||||
for (const pattern of this.packages) clone.packages.add(pattern)
|
||||
clone.autoInstallPeers = this.autoInstallPeers
|
||||
return clone
|
||||
return new PnpmWorkspaceFile(parseYaml(this.serialize()), this.originalText)
|
||||
}
|
||||
|
||||
/** Add one package workspace glob. */
|
||||
addPackage(pattern: string): void {
|
||||
this.packages.add(pattern)
|
||||
const packages = this.packageSequence()
|
||||
if (packages.items.some(item => item.value === pattern)) return
|
||||
packages.add(this.document.createNode(pattern))
|
||||
}
|
||||
|
||||
/** Disable registry peer auto-installation for live-link projects. */
|
||||
disableAutoInstallPeers(): void {
|
||||
this.autoInstallPeers = false
|
||||
this.mapping().set('autoInstallPeers', false)
|
||||
}
|
||||
|
||||
/** Validate workspace globs. */
|
||||
override validate(): void {
|
||||
for (const pattern of this.packages) {
|
||||
for (const pattern of this.packageValues()) {
|
||||
if (pattern.trim().length === 0) throw new Error('pnpm workspace pattern must not be empty')
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialize pnpm's workspace and peer policy. */
|
||||
/** Serialize the workspace while retaining unknown settings and comments. */
|
||||
override serialize(): string {
|
||||
return withTrailingNewline(stringify({
|
||||
packages: [...this.packages].sort(),
|
||||
...this.autoInstallPeers ? {} : { autoInstallPeers: false },
|
||||
allowBuilds: { esbuild: true },
|
||||
}, { lineWidth: 0 }))
|
||||
return withTrailingNewline(this.document.toString({ lineWidth: 0 }))
|
||||
}
|
||||
|
||||
private mapping(): YAMLMap {
|
||||
/* v8 ignore next -- parseYaml and create both establish a mapping root */
|
||||
if (!isMap(this.document.contents)) throw new Error('pnpm-workspace.yaml root must be an object')
|
||||
return this.document.contents
|
||||
}
|
||||
|
||||
private packageSequence(): YAMLSeq<Scalar<string>> {
|
||||
const packages = this.mapping().get('packages', true)
|
||||
if (!isSeq(packages) || packages.items.some(item => !isScalar(item) || typeof item.value !== 'string')) {
|
||||
throw new Error('pnpm-workspace.yaml packages must be an array of strings')
|
||||
}
|
||||
return packages as YAMLSeq<Scalar<string>>
|
||||
}
|
||||
|
||||
private packageValues(): string[] {
|
||||
return this.packageSequence().items.map(item => item.value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user