@paleo/workspace 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/helpers.js CHANGED
@@ -81,8 +81,8 @@ export function copyAndPatchFile(ctx, relPath, source, patchFn, label, force, op
81
81
  else {
82
82
  if (!existsSync(source.path)) {
83
83
  if (!optional) {
84
- console.error(`Error: source ${source.path} not found. Bootstrap the main worktree first ` +
85
- "(`workspace setup`), provide a `source`, or mark the entry as optional.");
84
+ console.error(`Error: config source ${source.path} not found. Bootstrap it first ` +
85
+ "(`workspace setup`, or commit the template), or mark the entry as optional.");
86
86
  process.exit(1);
87
87
  }
88
88
  ctx.log(`Warning: source ${source.path} not found, skipping (optional).`);
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { runWorkspace } from "./workspace.js";
2
2
  export { defaultWorktreeDirName } from "./worktree.js";
3
3
  export type { WorktreeDirNameFn } from "./worktree.js";
4
- export type { WorkspaceConfig, SetupContext, FinalizeResult, SummaryContext, PatchContext, ConfigFileEntry, ConfigFileSource, ConfigFileSourceSpec, PurgeContext, } from "./workspace.js";
4
+ export type { WorkspaceConfig, SetupContext, FinalizeResult, SummaryContext, PatchContext, ConfigFileEntry, ConfigFileSource, MainWorktreeConfigFileSource, NewWorktreeConfigFileSource, ContentConfigFileSource, PurgeContext, } from "./workspace.js";
5
5
  export { runDevServer } from "./dev-server.js";
6
6
  export type { DevServerConfig, DevServerSummaryContext, ServerDescriptor, ServerContext, SpawnServer, CallbackServer, } from "./dev-server.js";
7
7
  export type { ResolvedSlot } from "./slots.js";
@@ -1,3 +1,4 @@
1
+ import { type ResolvedFileSource } from "./helpers.js";
1
2
  import { type SlotStatus } from "./slots.js";
2
3
  import { type WorktreeDirNameFn } from "./worktree.js";
3
4
  /** Configuration accepted by {@link runWorkspace}. */
@@ -32,7 +33,7 @@ export interface WorkspaceConfig {
32
33
  * Holds the setup log and dev-server logs.
33
34
  */
34
35
  runtimeDir: string;
35
- /** Config files copied from the main worktree and patched per slot. */
36
+ /** Gitignored files seeded into each worktree (from main, a committed template, or content) and patched per slot. */
36
37
  configFiles: ConfigFileEntry[];
37
38
  /**
38
39
  * Runs before `configFiles` are copied. Use this to bootstrap source files the kernel expects
@@ -132,41 +133,37 @@ export interface PurgeContext {
132
133
  extra?: unknown;
133
134
  verbose: boolean;
134
135
  }
135
- /** A `{ path }` (relative to the main worktree) or `{ content }` (verbatim) initial source. */
136
- export type ConfigFileSourceSpec = {
137
- path: string;
138
- } | {
139
- content: string;
140
- };
141
- /**
142
- * Overrides where a {@link ConfigFileEntry}'s initial content comes from. Defaults to reading
143
- * `entry.path` from the main worktree.
144
- *
145
- * - `{ path }` — read this path (relative to the main worktree) instead of `entry.path`,
146
- * e.g. seed from a committed example: `{ path: "packages/api/.env.local.example" }`.
147
- * - `{ content }` — use this string as the initial content verbatim.
148
- * - a callback returning either of the above, resolved per worktree with the same
149
- * {@link PatchContext} the `patch` callback receives.
150
- */
151
- export type ConfigFileSource = ConfigFileSourceSpec | ((ctx: PatchContext) => ConfigFileSourceSpec);
152
- /** One config file seeded from its source (the main worktree by default) and patched per slot. */
136
+ /** One config file seeded from its source and patched per slot. */
153
137
  export interface ConfigFileEntry {
154
138
  /** Path relative to the worktree root. Written to the current worktree. */
155
139
  path: string;
140
+ /** Where the initial content comes from. */
141
+ source: ConfigFileSource;
142
+ /** Rewrites the source content per slot. Omit to copy the content verbatim. */
143
+ patch?: (content: string, ctx: PatchContext) => string;
156
144
  /**
157
- * Overrides the initial content's source. Defaults to reading `path` from the main worktree.
158
- * Use this to seed from a committed example or supplied content instead. See {@link ConfigFileSource}.
159
- */
160
- source?: ConfigFileSource;
161
- /** Returns the patched content given the source content and the slot's ports. */
162
- patch: (content: string, ctx: PatchContext) => string;
163
- /**
164
- * When `true`, a missing `{ path }` source logs a warning and skips the entry.
165
- * Default: required (missing source aborts setup). Bootstrap the main worktree first via
166
- * `workspace setup`, or seed sources in `preSetup`. Ignored for `{ content }` sources.
145
+ * When `true`, a missing source file logs a warning and skips the entry instead of aborting.
146
+ * Applies to `mainWorktree` and `newWorktree` sources; ignored for `content`.
167
147
  */
168
148
  optional?: boolean;
169
149
  }
150
+ /** Where a {@link ConfigFileEntry}'s initial content comes from. */
151
+ export type ConfigFileSource = MainWorktreeConfigFileSource | NewWorktreeConfigFileSource | ContentConfigFileSource;
152
+ /** Copies the gitignored file at the entry's `path` from the main worktree. */
153
+ export interface MainWorktreeConfigFileSource {
154
+ kind: "mainWorktree";
155
+ }
156
+ /** Copies a committed template from the new worktree's own checkout. */
157
+ export interface NewWorktreeConfigFileSource {
158
+ kind: "newWorktree";
159
+ /** Path of the template, relative to the worktree root (e.g. a committed `.example` file). */
160
+ path: string;
161
+ }
162
+ /** Uses the given content verbatim. The function form may be async. */
163
+ export interface ContentConfigFileSource {
164
+ kind: "content";
165
+ content: string | (() => string | Promise<string>);
166
+ }
170
167
  /** Context passed to {@link ConfigFileEntry.patch}. */
171
168
  export interface PatchContext {
172
169
  slot: number;
@@ -175,3 +172,4 @@ export interface PatchContext {
175
172
  currentWorktree: string;
176
173
  }
177
174
  export declare function runWorkspace(config: WorkspaceConfig): Promise<void>;
175
+ export declare function resolveConfigSource(entry: ConfigFileEntry, ctx: PatchContext): Promise<ResolvedFileSource>;
package/dist/workspace.js CHANGED
@@ -148,7 +148,7 @@ async function runSetup(command, ctx, run, config, registryDir) {
148
148
  }
149
149
  linkSharedDirectories(setupCtx, config.sharedDirs, verboseLog);
150
150
  linkWorkspaceRegistry(setupCtx, config.runtimeDir, verboseLog);
151
- generateConfigFiles(setupCtx, config.configFiles, slot, ports, command.force, verboseLog);
151
+ await generateConfigFiles(setupCtx, config.configFiles, slot, ports, command.force, verboseLog);
152
152
  teeLog(config.printSummary({
153
153
  slot,
154
154
  branch,
@@ -701,7 +701,7 @@ function linkWorkspaceRegistry(ctx, runtimeDir, log, opts) {
701
701
  symlinkSync(relative(runtimeRoot, mainDir), link);
702
702
  log("Created workspace-registry symlink → main worktree.");
703
703
  }
704
- function generateConfigFiles(ctx, entries, slot, ports, force, log) {
704
+ async function generateConfigFiles(ctx, entries, slot, ports, force, log) {
705
705
  for (const entry of entries) {
706
706
  const patchCtx = {
707
707
  slot,
@@ -709,17 +709,26 @@ function generateConfigFiles(ctx, entries, slot, ports, force, log) {
709
709
  mainWorktree: ctx.mainWorktree,
710
710
  currentWorktree: ctx.currentWorktree,
711
711
  };
712
- copyAndPatchFile({ currentWorktree: ctx.currentWorktree, log }, entry.path, resolveConfigSource(entry, patchCtx), (content) => entry.patch(content, patchCtx), entry.path, force, entry.optional ?? false);
712
+ const { patch } = entry;
713
+ const patchFn = patch
714
+ ? (content) => patch(content, patchCtx)
715
+ : (content) => content;
716
+ copyAndPatchFile({ currentWorktree: ctx.currentWorktree, log }, entry.path, await resolveConfigSource(entry, patchCtx), patchFn, entry.path, force, entry.optional ?? false);
717
+ }
718
+ }
719
+ export async function resolveConfigSource(entry, ctx) {
720
+ const { source } = entry;
721
+ switch (source.kind) {
722
+ case "mainWorktree":
723
+ return { path: join(ctx.mainWorktree, entry.path) };
724
+ case "newWorktree":
725
+ return { path: join(ctx.currentWorktree, source.path) };
726
+ case "content":
727
+ return {
728
+ content: typeof source.content === "function" ? await source.content() : source.content,
729
+ };
713
730
  }
714
731
  }
715
- function resolveConfigSource(entry, ctx) {
716
- const spec = typeof entry.source === "function" ? entry.source(ctx) : entry.source;
717
- if (spec === undefined)
718
- return { path: join(ctx.mainWorktree, entry.path) };
719
- if ("content" in spec)
720
- return { content: spec.content };
721
- return { path: join(ctx.mainWorktree, spec.path) };
722
- }
723
732
  function resolveRemoveTarget(command, ctx, registry) {
724
733
  if (command.branch === undefined) {
725
734
  if (ctx.isMainWorktree) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paleo/workspace",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Run multiple git-worktree dev environments side by side.",
5
5
  "keywords": [
6
6
  "workspace",