@effected/workspaces 0.3.1 → 0.4.1
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/README.md +29 -3
- package/WorkspaceDiscovery.js +103 -0
- package/WorkspacesSync.js +16 -8
- package/index.d.ts +82 -22
- package/node-sync.d.ts +3 -1
- package/node-sync.js +4 -2
- package/package.json +14 -12
- package/tsdoc-metadata.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@effected/workspaces)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
8
|
Monorepo workspace tooling for [Effect](https://effect.website) v4: find the workspace root, enumerate its packages, walk the dependency graph, detect the package manager, resolve pnpm catalogs, read the lockfile and work out which packages a git range touches. Every capability is a service you provide at the edge and swap in tests. Works with npm, pnpm, yarn Berry and bun.
|
|
9
9
|
|
|
@@ -151,7 +151,7 @@ const options = {
|
|
|
151
151
|
path, // node:path satisfies SyncPath verbatim
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
-
const root = findWorkspaceRootSync(options);
|
|
154
|
+
const root = findWorkspaceRootSync(process.cwd(), options);
|
|
155
155
|
const packages = root === null ? [] : getWorkspacePackagesSync(root, options);
|
|
156
156
|
// root: the workspace root path, or null when none is found above the cwd
|
|
157
157
|
// packages: the discovered workspace packages, empty when there is no root
|
|
@@ -180,12 +180,38 @@ const program = Effect.gen(function* () {
|
|
|
180
180
|
|
|
181
181
|
`WorkspaceRootNotFoundError`, `WorkspaceDiscoveryError`, `WorkspacePatternError`, `PackageNotFoundError`, `WorkspaceManifestError`, `PackageManagerDetectionError`, `CatalogAssemblyError`, `LockfileReadError`, `CyclicDependencyError` and `ChangeDetectionError` each name one thing that can actually go wrong, and each method's error channel is narrowed to the ones it can produce. `CatalogAssemblyError` is defined in `@effected/npm`, beside the resolver contract that names it in its channel — import it from there. Change detection additionally surfaces `@effected/git`'s typed git errors, such as `NotARepositoryError`.
|
|
182
182
|
|
|
183
|
+
## Testing
|
|
184
|
+
|
|
185
|
+
Every service here can be replaced with `Layer.succeed` and a hand-built value, and `WorkspaceDiscovery` ships that pattern ready-made: `WorkspaceDiscovery.layerTest(overrides)` provides an in-memory double where a test stubs only the methods it exercises. The defaults model an empty workspace, and the derived methods run over the effective `listPackages`, so stubbing that one method keeps `getPackage`, `importerMap` and `resolveFile` answering consistently:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { WorkspaceDiscovery, WorkspacePackage } from "@effected/workspaces";
|
|
189
|
+
import { Effect } from "effect";
|
|
190
|
+
|
|
191
|
+
// Bind to a const — layers memoize by reference.
|
|
192
|
+
const TestDiscovery = WorkspaceDiscovery.layerTest({
|
|
193
|
+
listPackages: () =>
|
|
194
|
+
Effect.succeed([
|
|
195
|
+
WorkspacePackage.make({
|
|
196
|
+
name: "@my-org/utils",
|
|
197
|
+
version: "1.0.0",
|
|
198
|
+
path: "/repo/packages/utils",
|
|
199
|
+
packageJsonPath: "/repo/packages/utils/package.json",
|
|
200
|
+
relativePath: "packages/utils",
|
|
201
|
+
}),
|
|
202
|
+
]),
|
|
203
|
+
});
|
|
204
|
+
// program.pipe(Effect.provide(TestDiscovery))
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
A name miss in the derived `getPackage` fails with the service's own typed `PackageNotFoundError`, exactly as the live implementation does. Two deliberate edges: `info()` has no honest default (a fabricated root path would leak into consumer path logic), so it dies with an explanatory defect unless stubbed, and the derived file-ownership methods assume POSIX paths, so pass your own `resolveFile` for win32 fixtures. `WorkspaceDiscovery.makeTest(overrides)` returns the bare service shape when you want the double without a layer.
|
|
208
|
+
|
|
183
209
|
## Features
|
|
184
210
|
|
|
185
211
|
- `Workspaces.layer` / `Workspaces.layerWithGit` / `Workspaces.resolvers` — the composite layers, split on requirements rather than feature flags: a filesystem, a filesystem plus a subprocess, and the two `@effected/npm` resolver contracts.
|
|
186
212
|
- `Workspaces.resolverLayer` / `Workspaces.resolveManifest` — the one-call manifest-resolution path: a fresh, unmemoized layer per call so root discovery follows your cwd, and one-shot resolution of a whole `Manifest` against the real workspace.
|
|
187
213
|
- `WorkspaceRoot` — root discovery from a `cwd`, over `WORKSPACE_MARKERS`.
|
|
188
|
-
- `WorkspaceDiscovery` — package enumeration with a bounded descent for segment-crossing `packages/**` patterns,
|
|
214
|
+
- `WorkspaceDiscovery` — package enumeration with a bounded descent for segment-crossing `packages/**` patterns, per-package lookup and the `makeTest` / `layerTest` in-memory test doubles.
|
|
189
215
|
- `WorkspacePackage` — a deliberately tolerant manifest model, so one member with an odd version cannot fail discovery for the whole repo. `manifestRecord` keeps the as-read `package.json` for tolerant access to fields outside the typed slice without a second read; `WorkspacePackage.manifest(pkg)` re-reads and is the opt-in bridge to `@effected/package-json`'s strict `Package`.
|
|
190
216
|
- `DependencyGraph` — a value class over discovered packages: `levels()` for parallel build tiers, the flattened topological order, and `CyclicDependencyError` when there isn't one.
|
|
191
217
|
- `PackageManagerDetector` — npm, pnpm, yarn or bun from lockfiles and the `packageManager` field.
|
package/WorkspaceDiscovery.js
CHANGED
|
@@ -304,6 +304,109 @@ var WorkspaceDiscovery = class WorkspaceDiscovery extends Context.Service()("@ef
|
|
|
304
304
|
*/
|
|
305
305
|
static layer = (options) => Layer.effect(WorkspaceDiscovery, WorkspaceDiscovery.make(options));
|
|
306
306
|
/**
|
|
307
|
+
* An in-memory test double of the service shape, with every method
|
|
308
|
+
* defaulted so a test stubs only what it exercises.
|
|
309
|
+
*
|
|
310
|
+
* @remarks
|
|
311
|
+
* The defaults model an **empty workspace**, and the derived methods run
|
|
312
|
+
* over the *effective* `listPackages` — the override when one is supplied —
|
|
313
|
+
* so stubbing only `listPackages` yields a consistent double:
|
|
314
|
+
*
|
|
315
|
+
* - `listPackages` — succeeds with `[]`.
|
|
316
|
+
* - `importerMap` — derived: the packages keyed by `relativePath`.
|
|
317
|
+
* - `getPackage` — derived: a name lookup that fails with the service's own
|
|
318
|
+
* typed {@link PackageNotFoundError} on a miss, exactly as the live
|
|
319
|
+
* implementation does.
|
|
320
|
+
* - `resolveFile` / `resolveFiles` — derived: longest-prefix ownership over
|
|
321
|
+
* `pkg.path`, POSIX-terminated (`"/"`); supply a win32 double explicitly
|
|
322
|
+
* if your fixture paths are win32.
|
|
323
|
+
* - `refresh` — a no-op (`Effect.void`); there is nothing memoized to drop.
|
|
324
|
+
* - `info` — **dies** with an explanatory defect. No honest default exists
|
|
325
|
+
* (a fabricated root path would leak into consumer path logic), so an
|
|
326
|
+
* unstubbed `info()` call is a test-wiring mistake and fails loudly as a
|
|
327
|
+
* defect rather than succeeding with a lie or failing with a dishonest
|
|
328
|
+
* typed error.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* import { WorkspaceDiscovery, WorkspacePackage } from "@effected/workspaces";
|
|
333
|
+
* import { Effect } from "effect";
|
|
334
|
+
*
|
|
335
|
+
* const double = WorkspaceDiscovery.makeTest({
|
|
336
|
+
* listPackages: () =>
|
|
337
|
+
* Effect.succeed([
|
|
338
|
+
* WorkspacePackage.make({
|
|
339
|
+
* name: "@my-org/utils",
|
|
340
|
+
* version: "1.0.0",
|
|
341
|
+
* path: "/repo/packages/utils",
|
|
342
|
+
* packageJsonPath: "/repo/packages/utils/package.json",
|
|
343
|
+
* relativePath: "packages/utils",
|
|
344
|
+
* }),
|
|
345
|
+
* ]),
|
|
346
|
+
* });
|
|
347
|
+
* // `getPackage`, `importerMap`, `resolveFile(s)` now answer consistently.
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
static makeTest = (overrides = {}) => {
|
|
351
|
+
const listPackages = overrides.listPackages ?? (() => Effect.succeed([]));
|
|
352
|
+
const ownerOf = (filePath, all) => {
|
|
353
|
+
let best;
|
|
354
|
+
let bestLength = 0;
|
|
355
|
+
for (const pkg of all) {
|
|
356
|
+
const prefix = pkg.path.endsWith("/") ? pkg.path : `${pkg.path}/`;
|
|
357
|
+
if (filePath.startsWith(prefix) && prefix.length > bestLength) {
|
|
358
|
+
best = pkg;
|
|
359
|
+
bestLength = prefix.length;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return Option.fromUndefinedOr(best);
|
|
363
|
+
};
|
|
364
|
+
return {
|
|
365
|
+
listPackages,
|
|
366
|
+
info: () => Effect.die(/* @__PURE__ */ new Error("WorkspaceDiscovery.makeTest: info() was called but not stubbed — no honest default WorkspaceInfo exists for a test double; pass an `info` override.")),
|
|
367
|
+
importerMap: () => Effect.map(listPackages(), (all) => new Map(all.map((pkg) => [pkg.relativePath, pkg]))),
|
|
368
|
+
getPackage: (name) => Effect.flatMap(listPackages(), (all) => {
|
|
369
|
+
const found = all.find((pkg) => pkg.name === name);
|
|
370
|
+
return found !== void 0 ? Effect.succeed(found) : Effect.fail(new PackageNotFoundError({
|
|
371
|
+
name,
|
|
372
|
+
available: all.map((pkg) => pkg.name)
|
|
373
|
+
}));
|
|
374
|
+
}),
|
|
375
|
+
resolveFile: (filePath) => Effect.map(listPackages(), (all) => ownerOf(filePath, all)),
|
|
376
|
+
resolveFiles: (filePaths) => Effect.map(listPackages(), (all) => {
|
|
377
|
+
const seen = /* @__PURE__ */ new Map();
|
|
378
|
+
for (const filePath of filePaths) {
|
|
379
|
+
const owner = ownerOf(filePath, all);
|
|
380
|
+
if (Option.isSome(owner)) seen.set(owner.value.name, owner.value);
|
|
381
|
+
}
|
|
382
|
+
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
383
|
+
}),
|
|
384
|
+
refresh: () => Effect.void,
|
|
385
|
+
...overrides
|
|
386
|
+
};
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* The test layer: {@link WorkspaceDiscovery.makeTest} behind
|
|
390
|
+
* `Layer.succeed`, so a suite provides only the methods it exercises.
|
|
391
|
+
*
|
|
392
|
+
* @remarks
|
|
393
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
394
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
395
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```ts
|
|
399
|
+
* import { WorkspaceDiscovery } from "@effected/workspaces";
|
|
400
|
+
* import { Effect } from "effect";
|
|
401
|
+
*
|
|
402
|
+
* const TestDiscovery = WorkspaceDiscovery.layerTest({
|
|
403
|
+
* listPackages: () => Effect.succeed([]),
|
|
404
|
+
* });
|
|
405
|
+
* // program.pipe(Effect.provide(TestDiscovery))
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
static layerTest = (overrides = {}) => Layer.succeed(WorkspaceDiscovery, WorkspaceDiscovery.makeTest(overrides));
|
|
409
|
+
/**
|
|
307
410
|
* The real implementation of `@effected/npm`'s `WorkspaceResolver` contract
|
|
308
411
|
* — the one `@effected/package-json` declares but cannot fill.
|
|
309
412
|
*
|
package/WorkspacesSync.js
CHANGED
|
@@ -38,20 +38,28 @@ const isDirectory = (fileSystem, dir) => {
|
|
|
38
38
|
/** Whether `dir` holds a `package.json`. */
|
|
39
39
|
const isPackage = (options, dir) => options.fileSystem.exists(options.path.join(dir, "package.json"));
|
|
40
40
|
/**
|
|
41
|
-
* The nearest workspace root at or above `
|
|
41
|
+
* The nearest workspace root at or above `cwd`, or `null`.
|
|
42
42
|
*
|
|
43
43
|
* @remarks
|
|
44
44
|
* **Synchronous.** The Effect surface is `WorkspaceRoot`; reach for this one
|
|
45
45
|
* only where you genuinely cannot run an Effect — a Vitest config being the
|
|
46
46
|
* motivating case. The file and path operations are the caller's
|
|
47
|
-
* ({@link
|
|
47
|
+
* ({@link WorkspacesSyncOptions}); this module imports no `node:*` and
|
|
48
48
|
* assumes no posix.
|
|
49
49
|
*
|
|
50
|
+
* The signature is path-first, options second — the same shape as
|
|
51
|
+
* {@link getWorkspacePackagesSync} and the rest of the kit's sync facades
|
|
52
|
+
* (`TsconfigLoaderSync.load(configPath, options)`). `cwd` is required and
|
|
53
|
+
* positional: the earlier options-bag form defaulted it to an ambient
|
|
54
|
+
* `process.cwd()` read, which both broke the symmetry with its sibling and
|
|
55
|
+
* was the module's one platform assumption.
|
|
56
|
+
*
|
|
50
57
|
* Markers match the async service exactly: a `pnpm-workspace.yaml`, or a
|
|
51
58
|
* `package.json` carrying a `workspaces` field.
|
|
52
59
|
*
|
|
53
|
-
* @param
|
|
54
|
-
*
|
|
60
|
+
* @param cwd - Where to start the ascent (typically `process.cwd()`),
|
|
61
|
+
* matching the Effect layers' `{ cwd }` option.
|
|
62
|
+
* @param options - The consumer-supplied file and path operations.
|
|
55
63
|
*
|
|
56
64
|
* @example
|
|
57
65
|
* ```ts
|
|
@@ -59,7 +67,7 @@ const isPackage = (options, dir) => options.fileSystem.exists(options.path.join(
|
|
|
59
67
|
* import * as path from "node:path";
|
|
60
68
|
* import { findWorkspaceRootSync } from "@effected/workspaces";
|
|
61
69
|
*
|
|
62
|
-
* const root = findWorkspaceRootSync({
|
|
70
|
+
* const root = findWorkspaceRootSync(process.cwd(), {
|
|
63
71
|
* fileSystem: {
|
|
64
72
|
* exists: existsSync,
|
|
65
73
|
* readFile: (p) => readFileSync(p, "utf8"),
|
|
@@ -72,9 +80,9 @@ const isPackage = (options, dir) => options.fileSystem.exists(options.path.join(
|
|
|
72
80
|
*
|
|
73
81
|
* @public
|
|
74
82
|
*/
|
|
75
|
-
const findWorkspaceRootSync = (options) => {
|
|
83
|
+
const findWorkspaceRootSync = (cwd, options) => {
|
|
76
84
|
const { fileSystem, path } = options;
|
|
77
|
-
let current = path.resolve(
|
|
85
|
+
let current = path.resolve(cwd);
|
|
78
86
|
for (let depth = 0; depth < 32 * 8; depth++) {
|
|
79
87
|
if (fileSystem.exists(path.join(current, "pnpm-workspace.yaml"))) return current;
|
|
80
88
|
const manifest = readJson(fileSystem, path.join(current, "package.json"));
|
|
@@ -173,7 +181,7 @@ const readPackageSync = (options, directory, relativePath) => {
|
|
|
173
181
|
* },
|
|
174
182
|
* path,
|
|
175
183
|
* };
|
|
176
|
-
* const root = findWorkspaceRootSync(ops);
|
|
184
|
+
* const root = findWorkspaceRootSync(process.cwd(), ops);
|
|
177
185
|
* const packages = root === null ? [] : getWorkspacePackagesSync(root, ops);
|
|
178
186
|
* ```
|
|
179
187
|
*
|
package/index.d.ts
CHANGED
|
@@ -440,6 +440,72 @@ declare class WorkspaceDiscovery extends WorkspaceDiscovery_base {
|
|
|
440
440
|
* rather than calling `layer(...)` at each composition site.
|
|
441
441
|
*/
|
|
442
442
|
static readonly layer: (options?: WorkspaceDiscoveryOptions) => Layer.Layer<WorkspaceDiscovery, never, WorkspaceRoot | FileSystem.FileSystem | Path.Path>;
|
|
443
|
+
/**
|
|
444
|
+
* An in-memory test double of the service shape, with every method
|
|
445
|
+
* defaulted so a test stubs only what it exercises.
|
|
446
|
+
*
|
|
447
|
+
* @remarks
|
|
448
|
+
* The defaults model an **empty workspace**, and the derived methods run
|
|
449
|
+
* over the *effective* `listPackages` — the override when one is supplied —
|
|
450
|
+
* so stubbing only `listPackages` yields a consistent double:
|
|
451
|
+
*
|
|
452
|
+
* - `listPackages` — succeeds with `[]`.
|
|
453
|
+
* - `importerMap` — derived: the packages keyed by `relativePath`.
|
|
454
|
+
* - `getPackage` — derived: a name lookup that fails with the service's own
|
|
455
|
+
* typed {@link PackageNotFoundError} on a miss, exactly as the live
|
|
456
|
+
* implementation does.
|
|
457
|
+
* - `resolveFile` / `resolveFiles` — derived: longest-prefix ownership over
|
|
458
|
+
* `pkg.path`, POSIX-terminated (`"/"`); supply a win32 double explicitly
|
|
459
|
+
* if your fixture paths are win32.
|
|
460
|
+
* - `refresh` — a no-op (`Effect.void`); there is nothing memoized to drop.
|
|
461
|
+
* - `info` — **dies** with an explanatory defect. No honest default exists
|
|
462
|
+
* (a fabricated root path would leak into consumer path logic), so an
|
|
463
|
+
* unstubbed `info()` call is a test-wiring mistake and fails loudly as a
|
|
464
|
+
* defect rather than succeeding with a lie or failing with a dishonest
|
|
465
|
+
* typed error.
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```ts
|
|
469
|
+
* import { WorkspaceDiscovery, WorkspacePackage } from "@effected/workspaces";
|
|
470
|
+
* import { Effect } from "effect";
|
|
471
|
+
*
|
|
472
|
+
* const double = WorkspaceDiscovery.makeTest({
|
|
473
|
+
* listPackages: () =>
|
|
474
|
+
* Effect.succeed([
|
|
475
|
+
* WorkspacePackage.make({
|
|
476
|
+
* name: "@my-org/utils",
|
|
477
|
+
* version: "1.0.0",
|
|
478
|
+
* path: "/repo/packages/utils",
|
|
479
|
+
* packageJsonPath: "/repo/packages/utils/package.json",
|
|
480
|
+
* relativePath: "packages/utils",
|
|
481
|
+
* }),
|
|
482
|
+
* ]),
|
|
483
|
+
* });
|
|
484
|
+
* // `getPackage`, `importerMap`, `resolveFile(s)` now answer consistently.
|
|
485
|
+
* ```
|
|
486
|
+
*/
|
|
487
|
+
static readonly makeTest: (overrides?: Partial<WorkspaceDiscoveryShape>) => WorkspaceDiscoveryShape;
|
|
488
|
+
/**
|
|
489
|
+
* The test layer: {@link WorkspaceDiscovery.makeTest} behind
|
|
490
|
+
* `Layer.succeed`, so a suite provides only the methods it exercises.
|
|
491
|
+
*
|
|
492
|
+
* @remarks
|
|
493
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
494
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
495
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```ts
|
|
499
|
+
* import { WorkspaceDiscovery } from "@effected/workspaces";
|
|
500
|
+
* import { Effect } from "effect";
|
|
501
|
+
*
|
|
502
|
+
* const TestDiscovery = WorkspaceDiscovery.layerTest({
|
|
503
|
+
* listPackages: () => Effect.succeed([]),
|
|
504
|
+
* });
|
|
505
|
+
* // program.pipe(Effect.provide(TestDiscovery))
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
static readonly layerTest: (overrides?: Partial<WorkspaceDiscoveryShape>) => Layer.Layer<WorkspaceDiscovery>;
|
|
443
509
|
/**
|
|
444
510
|
* The real implementation of `@effected/npm`'s `WorkspaceResolver` contract
|
|
445
511
|
* — the one `@effected/package-json` declares but cannot fill.
|
|
@@ -1608,34 +1674,28 @@ interface WorkspacesSyncOptions {
|
|
|
1608
1674
|
readonly path: SyncPath;
|
|
1609
1675
|
}
|
|
1610
1676
|
/**
|
|
1611
|
-
*
|
|
1612
|
-
* operations plus where to start the ascent.
|
|
1613
|
-
*
|
|
1614
|
-
* @public
|
|
1615
|
-
*/
|
|
1616
|
-
interface FindWorkspaceRootSyncOptions extends WorkspacesSyncOptions {
|
|
1617
|
-
/**
|
|
1618
|
-
* Where to start the ascent, matching the Effect layers' `{ cwd }` option.
|
|
1619
|
-
*
|
|
1620
|
-
* @defaultValue `process.cwd()`, read lazily per call.
|
|
1621
|
-
*/
|
|
1622
|
-
readonly cwd?: string;
|
|
1623
|
-
}
|
|
1624
|
-
/**
|
|
1625
|
-
* The nearest workspace root at or above `options.cwd`, or `null`.
|
|
1677
|
+
* The nearest workspace root at or above `cwd`, or `null`.
|
|
1626
1678
|
*
|
|
1627
1679
|
* @remarks
|
|
1628
1680
|
* **Synchronous.** The Effect surface is `WorkspaceRoot`; reach for this one
|
|
1629
1681
|
* only where you genuinely cannot run an Effect — a Vitest config being the
|
|
1630
1682
|
* motivating case. The file and path operations are the caller's
|
|
1631
|
-
* ({@link
|
|
1683
|
+
* ({@link WorkspacesSyncOptions}); this module imports no `node:*` and
|
|
1632
1684
|
* assumes no posix.
|
|
1633
1685
|
*
|
|
1686
|
+
* The signature is path-first, options second — the same shape as
|
|
1687
|
+
* {@link getWorkspacePackagesSync} and the rest of the kit's sync facades
|
|
1688
|
+
* (`TsconfigLoaderSync.load(configPath, options)`). `cwd` is required and
|
|
1689
|
+
* positional: the earlier options-bag form defaulted it to an ambient
|
|
1690
|
+
* `process.cwd()` read, which both broke the symmetry with its sibling and
|
|
1691
|
+
* was the module's one platform assumption.
|
|
1692
|
+
*
|
|
1634
1693
|
* Markers match the async service exactly: a `pnpm-workspace.yaml`, or a
|
|
1635
1694
|
* `package.json` carrying a `workspaces` field.
|
|
1636
1695
|
*
|
|
1637
|
-
* @param
|
|
1638
|
-
*
|
|
1696
|
+
* @param cwd - Where to start the ascent (typically `process.cwd()`),
|
|
1697
|
+
* matching the Effect layers' `{ cwd }` option.
|
|
1698
|
+
* @param options - The consumer-supplied file and path operations.
|
|
1639
1699
|
*
|
|
1640
1700
|
* @example
|
|
1641
1701
|
* ```ts
|
|
@@ -1643,7 +1703,7 @@ interface FindWorkspaceRootSyncOptions extends WorkspacesSyncOptions {
|
|
|
1643
1703
|
* import * as path from "node:path";
|
|
1644
1704
|
* import { findWorkspaceRootSync } from "@effected/workspaces";
|
|
1645
1705
|
*
|
|
1646
|
-
* const root = findWorkspaceRootSync({
|
|
1706
|
+
* const root = findWorkspaceRootSync(process.cwd(), {
|
|
1647
1707
|
* fileSystem: {
|
|
1648
1708
|
* exists: existsSync,
|
|
1649
1709
|
* readFile: (p) => readFileSync(p, "utf8"),
|
|
@@ -1656,7 +1716,7 @@ interface FindWorkspaceRootSyncOptions extends WorkspacesSyncOptions {
|
|
|
1656
1716
|
*
|
|
1657
1717
|
* @public
|
|
1658
1718
|
*/
|
|
1659
|
-
declare const findWorkspaceRootSync: (options:
|
|
1719
|
+
declare const findWorkspaceRootSync: (cwd: string, options: WorkspacesSyncOptions) => string | null;
|
|
1660
1720
|
/**
|
|
1661
1721
|
* Options for {@link getWorkspacePackagesSync}: the required consumer-supplied
|
|
1662
1722
|
* operations plus the traversal bound.
|
|
@@ -1714,7 +1774,7 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
|
|
|
1714
1774
|
* },
|
|
1715
1775
|
* path,
|
|
1716
1776
|
* };
|
|
1717
|
-
* const root = findWorkspaceRootSync(ops);
|
|
1777
|
+
* const root = findWorkspaceRootSync(process.cwd(), ops);
|
|
1718
1778
|
* const packages = root === null ? [] : getWorkspacePackagesSync(root, ops);
|
|
1719
1779
|
* ```
|
|
1720
1780
|
*
|
|
@@ -1722,5 +1782,5 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
|
|
|
1722
1782
|
*/
|
|
1723
1783
|
declare const getWorkspacePackagesSync: (root: string, options: GetWorkspacePackagesSyncOptions) => ReadonlyArray<WorkspacePackage>;
|
|
1724
1784
|
//#endregion
|
|
1725
|
-
export { type CatalogAssemblyFailure, CatalogSet, ChangeDetectionError, type ChangeDetectionFailure, ChangeDetectionOptions, ChangeDetector, type ChangeDetectorShape, ConfigDependencyHooks, type ConfigDependencyHooksShape, CyclicDependencyError, type DependencyDiff, DependencyGraph, DetectedPackageManager, type
|
|
1785
|
+
export { type CatalogAssemblyFailure, CatalogSet, ChangeDetectionError, type ChangeDetectionFailure, ChangeDetectionOptions, ChangeDetector, type ChangeDetectorShape, ConfigDependencyHooks, type ConfigDependencyHooksShape, CyclicDependencyError, type DependencyDiff, DependencyGraph, DetectedPackageManager, type GetWorkspacePackagesSyncOptions, LockfileReadError, type LockfileReadFailure, LockfileReader, type LockfileReaderOptions, type LockfileReaderShape, PackageManagerDetectionError, type PackageManagerDetectionFailure, PackageManagerDetector, PackageManagerName, PackageNotFoundError, PackageStateSnapshot, PublishConfig, PublishTarget, PublishabilityDetector, type PublishabilityDetectorShape, type SyncFileSystem, type SyncPath, WORKSPACE_MARKERS, WorkspaceCatalogs, type WorkspaceCatalogsOptions, type WorkspaceCatalogsShape, WorkspaceDiscovery, WorkspaceDiscoveryError, type WorkspaceDiscoveryFailure, type WorkspaceDiscoveryOptions, type WorkspaceDiscoveryShape, WorkspaceInfo, type WorkspaceLookupFailure, WorkspaceManifestError, WorkspacePackage, WorkspacePatternError, WorkspaceRoot, WorkspaceRootNotFoundError, type WorkspaceSnapshotAtFailure, type WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshots, type WorkspaceSnapshotsOptions, type WorkspaceSnapshotsShape, WorkspaceStateSnapshot, Workspaces, type WorkspacesOptions, type WorkspacesServices, type WorkspacesSyncOptions, findWorkspaceRootSync, getWorkspacePackagesSync };
|
|
1726
1786
|
//# sourceMappingURL=index.d.ts.map
|
package/node-sync.d.ts
CHANGED
|
@@ -106,7 +106,9 @@ declare const nodePath: SyncPath;
|
|
|
106
106
|
/**
|
|
107
107
|
* The complete Node-bound options bag for `findWorkspaceRootSync` and
|
|
108
108
|
* `getWorkspacePackagesSync` — {@link nodeFileSystem} plus {@link nodePath}.
|
|
109
|
-
*
|
|
109
|
+
* Both helpers take their path positionally, so this bag usually passes
|
|
110
|
+
* through verbatim; spread it only to add `getWorkspacePackagesSync`'s
|
|
111
|
+
* traversal extras: `{ ...nodeSyncOps, maxDepth }`.
|
|
110
112
|
*
|
|
111
113
|
* @public
|
|
112
114
|
*/
|
package/node-sync.js
CHANGED
|
@@ -21,7 +21,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
|
21
21
|
* import { findWorkspaceRootSync, getWorkspacePackagesSync } from "@effected/workspaces";
|
|
22
22
|
* import { nodeSyncOps } from "@effected/workspaces/node-sync";
|
|
23
23
|
*
|
|
24
|
-
* const root = findWorkspaceRootSync(nodeSyncOps);
|
|
24
|
+
* const root = findWorkspaceRootSync(process.cwd(), nodeSyncOps);
|
|
25
25
|
* const packages = root === null ? [] : getWorkspacePackagesSync(root, nodeSyncOps);
|
|
26
26
|
* ```
|
|
27
27
|
*
|
|
@@ -54,7 +54,9 @@ const nodePath = path;
|
|
|
54
54
|
/**
|
|
55
55
|
* The complete Node-bound options bag for `findWorkspaceRootSync` and
|
|
56
56
|
* `getWorkspacePackagesSync` — {@link nodeFileSystem} plus {@link nodePath}.
|
|
57
|
-
*
|
|
57
|
+
* Both helpers take their path positionally, so this bag usually passes
|
|
58
|
+
* through verbatim; spread it only to add `getWorkspacePackagesSync`'s
|
|
59
|
+
* traversal extras: `{ ...nodeSyncOps, maxDepth }`.
|
|
58
60
|
*
|
|
59
61
|
* @public
|
|
60
62
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/workspaces",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Monorepo workspace tooling as Effect services — root discovery, package enumeration, the dependency graph, package-manager detection, pnpm catalog resolution, lockfile IO and git-based change detection.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,30 +35,32 @@
|
|
|
35
35
|
"exports": {
|
|
36
36
|
".": {
|
|
37
37
|
"types": "./index.d.ts",
|
|
38
|
-
"import": "./index.js"
|
|
38
|
+
"import": "./index.js",
|
|
39
|
+
"default": "./index.js"
|
|
39
40
|
},
|
|
40
41
|
"./node-sync": {
|
|
41
42
|
"types": "./node-sync.d.ts",
|
|
42
|
-
"import": "./node-sync.js"
|
|
43
|
+
"import": "./node-sync.js",
|
|
44
|
+
"default": "./node-sync.js"
|
|
43
45
|
},
|
|
44
46
|
"./package.json": "./package.json"
|
|
45
47
|
},
|
|
46
48
|
"dependencies": {
|
|
47
|
-
"@effected/git": "0.4.
|
|
48
|
-
"@effected/glob": "0.1.
|
|
49
|
-
"@effected/lockfiles": "0.1.
|
|
50
|
-
"@effected/npm": "0.2.
|
|
51
|
-
"@effected/package-json": "0.3.
|
|
52
|
-
"@effected/semver": "0.1.
|
|
53
|
-
"@effected/walker": "0.2.
|
|
54
|
-
"@effected/yaml": "0.
|
|
49
|
+
"@effected/git": "0.4.1",
|
|
50
|
+
"@effected/glob": "0.1.2",
|
|
51
|
+
"@effected/lockfiles": "0.1.5",
|
|
52
|
+
"@effected/npm": "0.2.1",
|
|
53
|
+
"@effected/package-json": "0.3.1",
|
|
54
|
+
"@effected/semver": "0.1.1",
|
|
55
|
+
"@effected/walker": "0.2.2",
|
|
56
|
+
"@effected/yaml": "0.4.0",
|
|
55
57
|
"@pnpm/catalogs.config": "^1100.0.0",
|
|
56
58
|
"@pnpm/catalogs.protocol-parser": "^1100.0.0",
|
|
57
59
|
"@pnpm/catalogs.resolver": "^1100.0.0",
|
|
58
60
|
"@pnpm/catalogs.types": "^1100.0.0"
|
|
59
61
|
},
|
|
60
62
|
"peerDependencies": {
|
|
61
|
-
"effect": "4.0.0-beta.
|
|
63
|
+
"effect": "4.0.0-beta.99"
|
|
62
64
|
},
|
|
63
65
|
"engines": {
|
|
64
66
|
"node": ">=24.11.0"
|