refactor(cli)!: namespace the profile and bundle manifests under dsh.profile and dsh.bundle
A profile manifest and a bundle manifest are different kinds and shared one flat `dsh` section: `dsh.plugins` listed bundles (not plugins) and `dsh.patch` declared a bundle's layer. Each kind now names its role — a bundle declares `dsh.bundle.patch`, a profile declares `dsh.profile.bundles` — so a package.json states which role it plays and the list name matches its contents. `DEFAULT_PROFILE_PLUGINS` becomes `DEFAULT_PROFILE_BUNDLES`, and `DshManifestSection` splits into `DshBundleManifest`/`DshProfileManifest`. Pre-release: no compatibility shim; turtle-ui moved with it (bd5ff10).
This commit is contained in:
36 files changed
+203
-156
No files matched your search
+23
-22
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* `dsh plugin --profile <name> <args...>` — profile plugin management as a
|
||||
* thin pnpm forwarder: initialize the profile on first use, run
|
||||
* `pnpm <args...>` in the profile directory, then reconcile the `dsh.plugins`
|
||||
* bundle-layer list against the installed state (a dependency resolving to a
|
||||
* package that declares `dsh.patch` joins the layer stack; a removed or
|
||||
* patch-less dependency leaves it). Reconciling by installed state, not by
|
||||
* dependency diff, means `update` activates a package that gained its
|
||||
* `dsh.patch` in a newer version.
|
||||
* `pnpm <args...>` in the profile directory, then reconcile the
|
||||
* `dsh.profile.bundles` layer list against the installed state (a dependency
|
||||
* resolving to a package that declares `dsh.bundle` joins the layer stack; a
|
||||
* removed or bundle-less dependency leaves it). Reconciling by installed
|
||||
* state, not by dependency diff, means `update` activates a package that
|
||||
* gained its `dsh.bundle` declaration in a newer version.
|
||||
* @module @deepseek-ai/dsh/plugin
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,7 @@ import { spawnSync } from 'node:child_process'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
import {
|
||||
DEFAULT_PROFILE_PLUGINS,
|
||||
DEFAULT_PROFILE_BUNDLES,
|
||||
initProfile,
|
||||
PROFILE_TEMPLATES,
|
||||
readProfileManifest,
|
||||
@@ -31,7 +31,7 @@ const NAME = 'dsh'
|
||||
* Whether a resolved dependency exports a profile patch, i.e. is a bundle.
|
||||
* @param packageName - the dependency's package name.
|
||||
* @param profileDir - the profile directory (resolution anchor).
|
||||
* @returns true when the package manifest declares `dsh.patch`.
|
||||
* @returns true when the package manifest declares `dsh.bundle`.
|
||||
*/
|
||||
function exportsPatch(packageName: string, profileDir: string): boolean {
|
||||
let dir: string
|
||||
@@ -41,25 +41,26 @@ function exportsPatch(packageName: string, profileDir: string): boolean {
|
||||
return false // pnpm reported success yet the package is unresolvable — treat as plain
|
||||
}
|
||||
const manifest = readProfileManifest(NAME, dir)
|
||||
return manifest.dsh?.patch !== undefined
|
||||
return manifest.dsh?.bundle?.patch !== undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile `dsh.plugins` against the installed state: pnpm has already
|
||||
* written the real installed names (so a git/path/tarball/alias spec on the
|
||||
* command line reconciles by its true package name) and materialized the
|
||||
* packages. A dependency that resolves to a `dsh.patch`-declaring package
|
||||
* joins the layer stack (appended in dependency order); a dependency-listed
|
||||
* name that no longer does — removed, or the installed version dropped the
|
||||
* declaration — leaves it. In-box bundles from the profile template are not
|
||||
* dependencies and are never touched. Warns once per newly-added patch-less
|
||||
* dependency (a plain library is fine; the warning is orientation).
|
||||
* Reconcile `dsh.profile.bundles` against the installed state: pnpm has
|
||||
* already written the real installed names (so a git/path/tarball/alias spec
|
||||
* on the command line reconciles by its true package name) and materialized
|
||||
* the packages. A dependency that resolves to a `dsh.bundle`-declaring
|
||||
* package joins the layer stack (appended in dependency order); a
|
||||
* dependency-listed name that no longer does — removed, or the installed
|
||||
* version dropped the declaration — leaves it. In-box bundles from the
|
||||
* profile template are not dependencies and are never touched. Warns once
|
||||
* per newly-added bundle-less dependency (a plain library is fine; the
|
||||
* warning is orientation).
|
||||
*/
|
||||
function reconcilePlugins(before: ProfileManifest, profileDir: string): void {
|
||||
const after = readProfileManifest(NAME, profileDir)
|
||||
const beforeDeps = new Set(Object.keys(before.dependencies ?? {}))
|
||||
const dependencies = Object.keys(after.dependencies ?? {})
|
||||
const plugins = after.dsh?.plugins ?? []
|
||||
const plugins = after.dsh?.profile?.bundles ?? []
|
||||
let changed = false
|
||||
for (const packageName of dependencies) {
|
||||
const isBundle = exportsPatch(packageName, profileDir)
|
||||
@@ -68,7 +69,7 @@ function reconcilePlugins(before: ProfileManifest, profileDir: string): void {
|
||||
changed = true
|
||||
} else if (!isBundle && !beforeDeps.has(packageName)) {
|
||||
process.stderr.write(
|
||||
`${NAME}: warning: ${packageName} declares no dsh.patch — installed as a plain dependency, not a profile layer `
|
||||
`${NAME}: warning: ${packageName} declares no dsh.bundle — installed as a plain dependency, not a profile layer `
|
||||
+ '(a later update that gains one activates it automatically)\n',
|
||||
)
|
||||
}
|
||||
@@ -85,7 +86,7 @@ function reconcilePlugins(before: ProfileManifest, profileDir: string): void {
|
||||
}
|
||||
}
|
||||
if (!changed) return
|
||||
after.dsh = { ...after.dsh, plugins }
|
||||
after.dsh = { ...after.dsh, profile: { ...after.dsh?.profile, bundles: plugins } }
|
||||
writeProfileManifest(profileDir, after)
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ function anchorPathSpec(argument: string, cwd: string): string {
|
||||
export function runPlugin(profile: string, args: readonly string[]): number {
|
||||
const dir = resolveProfileDir(profile)
|
||||
if (!existsSync(join(dir, 'package.json'))) {
|
||||
initProfile(dir, PROFILE_TEMPLATES[profile] ?? DEFAULT_PROFILE_PLUGINS)
|
||||
initProfile(dir, PROFILE_TEMPLATES[profile] ?? DEFAULT_PROFILE_BUNDLES)
|
||||
process.stderr.write(`${NAME}: initialized profile ${profile} at ${dir}\n`)
|
||||
}
|
||||
const before = readProfileManifest(NAME, dir)
|
||||
|
||||
Reference in New Issue
Block a user