Skip to content
源码版本47f9438 (dsh@0.1)

Loading and Discovery

Plugin loading happens in three layers:

  1. Loader (vendor/loader) service manages the entry tree + Node's built-in ModuleLoader (--expose-internals) for ESM/CJS imports
  2. Include plugin parses cordis.patch.yml (including !!js expressions)
  3. dsh plugin CLI reconciles pnpm package installation with the bundle layer

The Loader hooks onto the internal/plugin / internal/config / internal/update events, automatically handling fiber.entry binding, config interpolation, and reload write-back.

Loader Service

class Loader extends EntryTree at vendor/loader/src/index.ts:65-100:

  • internal = ModuleLoader.fromInternal() — get Node's internal ESM loader
  • builtins dict for builtin plugins
  • On construction it registers three internal/* hooks: config interpolation, update write-back, and plugin lifecycle logging

unwrapExports: Unifying Three Shapes

unwrapExports(exports) at vendor/loader/src/index.ts:192-199 handles the dual interop of default / __esModule, unifying ESM / CJS / default-export shapes into a single plugin object.

Node's Built-in ModuleLoader

dsh uses --expose-internals to get at Node's internal ESM loader — this is the prerequisite for fine-grained control over module loading, including HMR cache cleanup.

V1 vs V2

vendor/loader/src/internal.ts:55-102 defines the ModuleLoaderV1 (Node 22/23) and ModuleLoaderV2 (Node 24+) interfaces, with field differences clearly annotated:
  • getModuleJobForImportgetOrCreateModuleJob
  • resolve becomes private
  • etc.

ModuleLoader.fromInternal() at vendor/loader/src/internal.ts:120-132: Node major ≥ 24 takes v2, ≥ 22 takes v1, obtaining the cascaded loader of internal/modules/esm/loader via --expose-internals or node-addon-require-builtin.

dsh plugin CLI: pnpm Reconciliation

Is It a Bundle Layer?

exportsPatch(packageName, profileDir) at apps/cli/src/plugin.ts:36-45:

  1. Resolves the bundle dir.
  2. Reads the manifest.
  3. Checks whether dsh.bundle.patch exists — i.e. "is it a bundle layer".

reconcilePlugins

reconcilePlugins(before, profileDir) at apps/cli/src/plugin.ts:59-91:

After pnpm finishes, diffs the before/after dependencies:

  • A new dependency that is a bundle → push into dsh.profile.bundles
  • A removed dependency, or a new version that no longer declares a bundle → splice out

Path Anchoring

anchorPathSpec(argument, cwd) at apps/cli/src/plugin.ts:104-112: rewrites . / .. / file: / link: relative specs against the caller's cwd, to avoid self-reference inside the profile directory.

The Entry Structure of patch.yml

packages/bundle/base/cordis.patch.yml:15-45 is the base bundle's insert list; each line:
yaml
- id: <stable>          # stable identity
  name: '<pkg or path>'  # module locator
  config: ...            # optional
  disabled: !!js ...     # optional; the only interpolated metadata field

Key Mechanisms

1. entry id Is a Stable Identity

A Loader entry's id is a stable identity: when the config file changes the loader diffs by id and only mounts / unmounts / reconfigures the changed lines; an entry without an id gets a fresh id on every read and is treated as "delete + add".

2. disabled: !!js Routing

disabled: !!js <expr> is the only interpolated metadata field; it is evaluated against the loader context on every mount decision, so platform / environment differences (e.g. process.platform === 'win32') are routed here.

3. internal/config Hook for Interpolation

The Loader internal/config hook does the interpolation: the config of tree carriers (Group / Include) stays literal, while the config of leaf entries goes through interpolate(ctx, config) to resolve !!js.

4. Loader.Intercept.await

Loader.Intercept.await keeps plugins that depend on the loader pending while the entry tree still has unfinished work — preventing plugins from seeing an incomplete registry early in boot.

5. Profile Is a Real pnpm Workspace

dsh plugin add is pnpm add; when pnpm ≥ 10 blocks the prepare script of a git dependency, the CLI prompts the user to add allowBuilds. Plugin management fully reuses pnpm's dependency graph.

Unofficial community learning site. Content based on the MIT-licensed deepseek-ai/deepseek-harness source. · Privacy · Terms · About