fix(sdk): address project tooling review findings

This commit is contained in:
imccyu
2026-07-15 21:06:01 +08:00
parent 42b07a7022
commit e150bc446d
18 changed files with 241 additions and 47 deletions
@@ -6,15 +6,41 @@
import { featureId } from '../../ids.ts'
import type { ProjectProfile } from '../../project/types.ts'
import {
createAppPackageScripts,
createAppProjectArtifacts,
createProjectTemplateContext,
} from '../../templates/project-template.ts'
import {
FeatureOption,
ExclusiveOptionFeature,
} from '../feature.ts'
import { ProjectContribution } from '../resources.ts'
import { npmCordisConfigEntry, optionalString, requiredString } from './helpers.ts'
import { ProjectContribution, type ProjectResource } from '../resources.ts'
import {
npmCordisConfigEntry,
optionalString,
ownedTextFile,
packageScript,
requiredString,
} from './helpers.ts'
const ID = featureId('app')
function appProjectResources(
profile: ProjectProfile,
runInterface: 'acp' | 'stdio' | 'embed',
): readonly ProjectResource[] {
const context = createProjectTemplateContext(profile, runInterface)
const scripts = createAppPackageScripts(context)
return [
...createAppProjectArtifacts(context).map(document => (
ownedTextFile(ID, document.relativePath, document.serialize())
)),
packageScript(ID, 'dev', scripts.dev),
packageScript(ID, 'start', scripts.start),
]
}
class AppOption extends FeatureOption {
override readonly id: 'acp' | 'stdio' | 'embed'
override readonly label: string
@@ -45,6 +71,7 @@ class AppOption extends FeatureOption {
switch (this.id) {
case 'acp':
return new ProjectContribution([
...appProjectResources(profile, this.id),
...npmCordisConfigEntry(ID, {
id: 'user-interaction',
name: '@deepseek-ai/dsh-user-interaction',
@@ -57,6 +84,7 @@ class AppOption extends FeatureOption {
])
case 'stdio':
return new ProjectContribution([
...appProjectResources(profile, this.id),
...npmCordisConfigEntry(ID, {
id: 'user-interaction',
name: '@deepseek-ai/dsh-user-interaction',
@@ -74,7 +102,7 @@ class AppOption extends FeatureOption {
]),
])
case 'embed':
return new ProjectContribution([])
return new ProjectContribution(appProjectResources(profile, this.id))
}
}
}
@@ -12,6 +12,7 @@ import type {
EnvironmentResource,
OwnedFileResource,
NpmDependencyResource,
PackageScriptResource,
} from '../resources.ts'
/** Create a runtime NPM dependency resource. */
@@ -24,6 +25,17 @@ function npmDependency(_owner: string, name: string): NpmDependencyResource {
}
}
/** Create a feature-owned package script that is replaceable only while unchanged. */
export function packageScript(_owner: string, name: string, command: string): PackageScriptResource {
return {
kind: 'package-script',
key: resourceKey(`package-script:${name}`),
name,
command,
removeOnlyWhenUnchanged: true,
}
}
/** Create a Cordis config entry resource with explicitly owned config keys. */
export function cordisConfigEntry(
_owner: string,
@@ -285,6 +285,11 @@ export abstract class Feature {
diagnostics.push(`missing package.json ${resource.section} entry ${resource.name}`)
}
break
case 'package-script':
if (!manifest.scripts?.[resource.name]) {
diagnostics.push(`missing package.json script ${resource.name}`)
}
break
case 'owned-file':
if (!project.hasDocument(resource.document.relativePath)) diagnostics.push(`missing owned file ${resource.document.relativePath}`)
break
@@ -16,6 +16,15 @@ export interface NpmDependencyResource {
section: 'dependencies' | 'devDependencies'
}
/** Feature-owned package script. */
export interface PackageScriptResource {
kind: 'package-script'
key: ResourceKey
name: string
command: string
removeOnlyWhenUnchanged: boolean
}
/** Owned Cordis config entry plus the config keys safe to update in place. */
export interface CordisConfigEntryResource {
kind: 'cordis-config-entry'
@@ -46,6 +55,7 @@ export interface OwnedFileResource {
/** Any resource a feature can add to a project. */
export type ProjectResource =
| NpmDependencyResource
| PackageScriptResource
| CordisConfigEntryResource
| EnvironmentResource
| OwnedFileResource