@effected/workspaces 0.8.0 → 0.9.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/PackageManagerName.js +88 -2
- package/Publishability.js +63 -4
- package/README.md +3 -1
- package/ReleaseTag.js +260 -0
- package/VersioningStrategy.js +122 -0
- package/WorkspaceDiscovery.js +4 -1
- package/WorkspaceSnapshots.js +8 -1
- package/Workspaces.js +227 -148
- package/index.d.ts +704 -15
- package/index.js +3 -1
- package/package.json +7 -6
package/Workspaces.js
CHANGED
|
@@ -3,183 +3,262 @@ import { WorkspaceDiscovery } from "./WorkspaceDiscovery.js";
|
|
|
3
3
|
import { ChangeDetector } from "./ChangeDetector.js";
|
|
4
4
|
import { PackageManagerDetector } from "./PackageManagerName.js";
|
|
5
5
|
import { LockfileReader } from "./LockfileReader.js";
|
|
6
|
-
import { PublishabilityDetector } from "./Publishability.js";
|
|
7
6
|
import { WorkspaceCatalogs } from "./WorkspaceCatalogs.js";
|
|
8
7
|
import { WorkspaceSnapshots } from "./WorkspaceSnapshots.js";
|
|
9
8
|
import { Git } from "@effected/git";
|
|
10
|
-
import { Effect, Layer } from "effect";
|
|
9
|
+
import { Effect, Layer, Option } from "effect";
|
|
10
|
+
import { ExecContext, LocalExec, LocalExecError } from "@effected/commands";
|
|
11
11
|
|
|
12
12
|
//#region src/Workspaces.ts
|
|
13
|
-
/**
|
|
14
|
-
* Every service that needs only a filesystem: root, package-manager detection,
|
|
15
|
-
* discovery, lockfile reading, catalogs and publishability.
|
|
16
|
-
*
|
|
17
|
-
* @remarks
|
|
18
|
-
* Requires core `FileSystem` and `Path`, which the consumer provides at the
|
|
19
|
-
* edge (`@effect/platform-node`, `@effect/platform-bun`, or a test's
|
|
20
|
-
* `FileSystem.layerNoop`).
|
|
21
|
-
*
|
|
22
|
-
* **Bind the result to a `const`.** This is a parameterized factory and layers
|
|
23
|
-
* memoize by reference, so calling it twice builds everything twice.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { Workspaces } from "@effected/workspaces";
|
|
28
|
-
* import { Layer } from "effect";
|
|
29
|
-
*
|
|
30
|
-
* const WorkspacesLayer = Workspaces.layer();
|
|
31
|
-
* const AppLayer = Layer.provide(WorkspacesLayer, PlatformLayer);
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* @public
|
|
35
|
-
*/
|
|
36
13
|
const compose = (options, catalogsFactory) => {
|
|
37
14
|
const roots = WorkspaceRoot.layer;
|
|
38
15
|
const detector = PackageManagerDetector.layer;
|
|
39
16
|
const discovery = WorkspaceDiscovery.layer(options).pipe(Layer.provide(roots));
|
|
40
17
|
const lockfiles = LockfileReader.layer(options).pipe(Layer.provide(roots), Layer.provide(detector), Layer.provide(discovery));
|
|
41
18
|
const catalogs = catalogsFactory(options).pipe(Layer.provide(roots), Layer.provide(lockfiles));
|
|
42
|
-
return Layer.mergeAll(roots, detector, discovery, lockfiles, catalogs
|
|
19
|
+
return Layer.mergeAll(roots, detector, discovery, lockfiles, catalogs);
|
|
43
20
|
};
|
|
44
21
|
const layer = (options) => compose(options, WorkspaceCatalogs.layer);
|
|
45
|
-
/**
|
|
46
|
-
* The git-free composite plus {@link ChangeDetector} and
|
|
47
|
-
* {@link WorkspaceSnapshots}, over `@effected/git`'s `Git` service.
|
|
48
|
-
*
|
|
49
|
-
* @remarks
|
|
50
|
-
* The extra requirement is core's `ChildProcessSpawner` (behind `Git`), which
|
|
51
|
-
* is why it is a separate layer rather than a flag: a consumer that never
|
|
52
|
-
* detects changes or reads at a ref should not have to be able to spawn a
|
|
53
|
-
* subprocess. The consumer provides `ChildProcessSpawner` once at the edge
|
|
54
|
-
* (`@effect/platform-node`'s `NodeServices.layer`); a test provides
|
|
55
|
-
* `Layer.succeed(Git, …)` and needs no repository on disk.
|
|
56
|
-
*
|
|
57
|
-
* @public
|
|
58
|
-
*/
|
|
59
22
|
const layerWithGit = (options) => {
|
|
60
23
|
const core = layer(options);
|
|
61
24
|
const git = Git.layer;
|
|
62
25
|
return Layer.mergeAll(core, git, ChangeDetector.layer.pipe(Layer.provide(git), Layer.provide(core)), WorkspaceSnapshots.layer(options).pipe(Layer.provide(git), Layer.provide(core)));
|
|
63
26
|
};
|
|
64
|
-
/**
|
|
65
|
-
* The two `@effected/npm` resolver contracts, implemented for real.
|
|
66
|
-
*
|
|
67
|
-
* @remarks
|
|
68
|
-
* Provide this alongside `@effected/package-json`'s `Package.resolve` and a
|
|
69
|
-
* manifest's `catalog:` and `workspace:` specifiers resolve against the actual
|
|
70
|
-
* workspace instead of the no-op layers' `Option.none()`.
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* ```ts
|
|
74
|
-
* import { Package } from "@effected/package-json";
|
|
75
|
-
* import { Workspaces } from "@effected/workspaces";
|
|
76
|
-
* import { Layer } from "effect";
|
|
77
|
-
*
|
|
78
|
-
* const WorkspacesLayer = Workspaces.layer();
|
|
79
|
-
* const Resolvers = Workspaces.resolvers.pipe(Layer.provide(WorkspacesLayer));
|
|
80
|
-
* ```
|
|
81
|
-
*
|
|
82
|
-
* @public
|
|
83
|
-
*/
|
|
84
27
|
const resolvers = Layer.mergeAll(WorkspaceCatalogs.catalogResolver, WorkspaceDiscovery.workspaceResolver);
|
|
85
|
-
/**
|
|
86
|
-
* The git-free composite, but with catalog assembly that **replays config
|
|
87
|
-
* dependency `pnpmfile.cjs` hooks** — {@link WorkspaceCatalogs.layerWithConfigDependencies}
|
|
88
|
-
* in place of the default no-op catalogs layer.
|
|
89
|
-
*
|
|
90
|
-
* @remarks
|
|
91
|
-
* Identical requirement set to {@link Workspaces.layer}; the only difference is
|
|
92
|
-
* that config-dependency code is executed in process. Opt in deliberately — the
|
|
93
|
-
* default {@link Workspaces.layer} never executes config-dependency code.
|
|
94
|
-
*
|
|
95
|
-
* **Bind the result to a `const`.**
|
|
96
|
-
*
|
|
97
|
-
* @public
|
|
98
|
-
*/
|
|
99
28
|
const layerWithConfigDependencies = (options) => compose(options, WorkspaceCatalogs.layerWithConfigDependencies);
|
|
100
|
-
/**
|
|
101
|
-
* The one-call resolver factory: {@link Workspaces.resolvers} pre-wired over
|
|
102
|
-
* {@link Workspaces.layerWithConfigDependencies}, so the two `@effected/npm`
|
|
103
|
-
* contracts (`CatalogResolver`, `WorkspaceResolver`) need only a platform
|
|
104
|
-
* (`FileSystem` + `Path`) from the consumer.
|
|
105
|
-
*
|
|
106
|
-
* @remarks
|
|
107
|
-
* This is deliberately a **parameterized layer function, and the fresh layer
|
|
108
|
-
* per call is the feature**: layers memoize by reference, so each call mints
|
|
109
|
-
* an unmemoized layer whose root discovery re-runs — including a per-call
|
|
110
|
-
* `process.cwd()` read when `options.cwd` is omitted. A build tool that
|
|
111
|
-
* changes directory between manifests gets a correct re-discovery each time
|
|
112
|
-
* precisely because nothing is shared across calls. When you *want* sharing,
|
|
113
|
-
* bind one call's result to a `const` and provide that; the memoization rule
|
|
114
|
-
* is unchanged, this factory just refuses to hide it.
|
|
115
|
-
*
|
|
116
|
-
* Catalog assembly replays config-dependency `pnpmfile` hooks (the
|
|
117
|
-
* `layerWithConfigDependencies` path) — the semantics a real pnpm install
|
|
118
|
-
* has. Compose {@link Workspaces.resolvers} with {@link Workspaces.layer}
|
|
119
|
-
* yourself if config-dependency code must not run in process.
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* ```ts
|
|
123
|
-
* import { Workspaces } from "@effected/workspaces";
|
|
124
|
-
* import { Effect } from "effect";
|
|
125
|
-
*
|
|
126
|
-
* const program = doSomethingWithResolvers.pipe(
|
|
127
|
-
* Effect.provide(Workspaces.resolverLayer()),
|
|
128
|
-
* );
|
|
129
|
-
* ```
|
|
130
|
-
*
|
|
131
|
-
* @public
|
|
132
|
-
*/
|
|
133
29
|
const resolverLayer = (options) => resolvers.pipe(Layer.provide(layerWithConfigDependencies(options)));
|
|
134
|
-
/**
|
|
135
|
-
* Resolve every `catalog:` and `workspace:` specifier in one `Manifest`
|
|
136
|
-
* against the real workspace, in one call — the 90% path. Decode stays at the
|
|
137
|
-
* consumer's edge: build the `Manifest` with `Manifest.decode` (from
|
|
138
|
-
* `@effected/npm`), hand it here, and get a new `Manifest` back with concrete
|
|
139
|
-
* ranges; `toRecord()` returns to the wire shape.
|
|
140
|
-
*
|
|
141
|
-
* @remarks
|
|
142
|
-
* Composes `manifest.resolve()` with a fresh {@link Workspaces.resolverLayer}
|
|
143
|
-
* per call, so the workspace root is re-discovered from `options.cwd` (or the
|
|
144
|
-
* current `process.cwd()`) on every invocation. Consumers processing many
|
|
145
|
-
* manifests should check `manifest.needsResolution` first and skip the call
|
|
146
|
-
* entirely when no dependency field carries a `catalog:`/`workspace:`
|
|
147
|
-
* specifier — that predicate is pure and avoids catalog assembly altogether.
|
|
148
|
-
*
|
|
149
|
-
* A specifier the workspace cannot answer fails typed as
|
|
150
|
-
* `UnresolvedDependencyError`; assembly and mechanism failures surface as
|
|
151
|
-
* `CatalogAssemblyError` / `DependencyResolutionError`.
|
|
152
|
-
*
|
|
153
|
-
* @example
|
|
154
|
-
* ```ts
|
|
155
|
-
* import { Manifest } from "@effected/npm";
|
|
156
|
-
* import { Workspaces } from "@effected/workspaces";
|
|
157
|
-
* import { Effect } from "effect";
|
|
158
|
-
*
|
|
159
|
-
* const program = Effect.gen(function* () {
|
|
160
|
-
* const manifest = yield* Manifest.decode({ dependencies: { effect: "catalog:" } });
|
|
161
|
-
* const resolved = manifest.needsResolution ? yield* Workspaces.resolveManifest(manifest) : manifest;
|
|
162
|
-
* return resolved.toRecord();
|
|
163
|
-
* });
|
|
164
|
-
* ```
|
|
165
|
-
*
|
|
166
|
-
* @public
|
|
167
|
-
*/
|
|
168
30
|
const resolveManifest = Effect.fn("Workspaces.resolveManifest")(function* (manifest, options) {
|
|
169
31
|
return yield* manifest.resolve().pipe(Effect.provide(resolverLayer(options)));
|
|
170
32
|
});
|
|
33
|
+
const localExecLayer = (options) => Layer.effect(LocalExec, Effect.gen(function* () {
|
|
34
|
+
const roots = yield* WorkspaceRoot;
|
|
35
|
+
const detector = yield* PackageManagerDetector;
|
|
36
|
+
return { context: Effect.gen(function* () {
|
|
37
|
+
const cwd = options?.cwd ?? globalThis.process?.cwd?.() ?? "/";
|
|
38
|
+
const root = yield* roots.find(cwd).pipe(Effect.asSome, Effect.orElseSucceed(Option.none));
|
|
39
|
+
if (Option.isNone(root)) return Option.none();
|
|
40
|
+
const detected = yield* detector.detect(root.value).pipe(Effect.asSome, Effect.catchTag("PackageManagerDetectionError", () => Effect.succeed(Option.none())), Effect.mapError((cause) => new LocalExecError({
|
|
41
|
+
directory: root.value,
|
|
42
|
+
cause
|
|
43
|
+
})));
|
|
44
|
+
if (Option.isNone(detected)) return Option.none();
|
|
45
|
+
const { prefix, dlxPrefix, scriptPrefix } = LocalExec.prefixes(detected.value.name);
|
|
46
|
+
return Option.some(ExecContext.make({
|
|
47
|
+
label: detected.value.name,
|
|
48
|
+
prefix,
|
|
49
|
+
dlxPrefix,
|
|
50
|
+
scriptPrefix,
|
|
51
|
+
directory: root.value
|
|
52
|
+
}));
|
|
53
|
+
}) };
|
|
54
|
+
}));
|
|
171
55
|
/**
|
|
172
56
|
* The composite layers.
|
|
173
57
|
*
|
|
174
58
|
* @public
|
|
175
59
|
*/
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
60
|
+
var Workspaces = class {
|
|
61
|
+
constructor() {}
|
|
62
|
+
/**
|
|
63
|
+
* Every service that needs only a filesystem: root, package-manager
|
|
64
|
+
* detection, discovery, lockfile reading and catalogs.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* Requires core `FileSystem` and `Path`, which the consumer provides at the
|
|
68
|
+
* edge (`@effect/platform-node`, `@effect/platform-bun`, or a test's
|
|
69
|
+
* `FileSystem.layerNoop`).
|
|
70
|
+
*
|
|
71
|
+
* **`PublishabilityDetector` is neither provided nor required here.** The
|
|
72
|
+
* composite used to bake in npm semantics, which a naively-ordered override
|
|
73
|
+
* silently lost to; now it supplies no default, and — because nothing inside
|
|
74
|
+
* the composite asks a publishability question — it does not require one in
|
|
75
|
+
* `R` either. The requirement surfaces in the `R` of each operation that
|
|
76
|
+
* asks (`VersioningStrategy.detect`, e.g.), so a program that asks and never
|
|
77
|
+
* wires a detector fails to compile at that operation, and a program that
|
|
78
|
+
* never asks never supplies a publish policy. Wire one explicitly where
|
|
79
|
+
* needed: `Layer.mergeAll(Workspaces.layer(), PublishabilityDetector.layerNpm)`.
|
|
80
|
+
*
|
|
81
|
+
* **Bind the result to a `const`.** This is a parameterized factory and
|
|
82
|
+
* layers memoize by reference, so calling it twice builds everything twice.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
87
|
+
* import { Layer } from "effect";
|
|
88
|
+
*
|
|
89
|
+
* const WorkspacesLayer = Workspaces.layer();
|
|
90
|
+
* const AppLayer = Layer.provide(WorkspacesLayer, PlatformLayer);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
static layer = layer;
|
|
94
|
+
/**
|
|
95
|
+
* The git-free composite, but with catalog assembly that **replays config
|
|
96
|
+
* dependency `pnpmfile.cjs` hooks** —
|
|
97
|
+
* {@link WorkspaceCatalogs.layerWithConfigDependencies} in place of the
|
|
98
|
+
* default no-op catalogs layer.
|
|
99
|
+
*
|
|
100
|
+
* @remarks
|
|
101
|
+
* Identical requirement set to {@link Workspaces.layer}; the only
|
|
102
|
+
* difference is that config-dependency code is executed in process. Opt in
|
|
103
|
+
* deliberately — the default {@link Workspaces.layer} never executes
|
|
104
|
+
* config-dependency code.
|
|
105
|
+
*
|
|
106
|
+
* **Bind the result to a `const`.**
|
|
107
|
+
*/
|
|
108
|
+
static layerWithConfigDependencies = layerWithConfigDependencies;
|
|
109
|
+
/**
|
|
110
|
+
* The git-free composite plus {@link ChangeDetector} and
|
|
111
|
+
* {@link WorkspaceSnapshots}, over `@effected/git`'s `Git` service.
|
|
112
|
+
*
|
|
113
|
+
* @remarks
|
|
114
|
+
* The extra requirement is core's `ChildProcessSpawner` (behind `Git`),
|
|
115
|
+
* which is why it is a separate layer rather than a flag: a consumer that
|
|
116
|
+
* never detects changes or reads at a ref should not have to be able to
|
|
117
|
+
* spawn a subprocess. The consumer provides `ChildProcessSpawner` once at
|
|
118
|
+
* the edge (`@effect/platform-node`'s `NodeServices.layer`); a test
|
|
119
|
+
* provides `Layer.succeed(Git, …)` and needs no repository on disk.
|
|
120
|
+
*/
|
|
121
|
+
static layerWithGit = layerWithGit;
|
|
122
|
+
/**
|
|
123
|
+
* This package's implementation of `@effected/commands`' `LocalExec`
|
|
124
|
+
* contract: how to run a project-local binary here.
|
|
125
|
+
*
|
|
126
|
+
* @remarks
|
|
127
|
+
* **An inverted contract, the `@effected/npm` `CatalogResolver`
|
|
128
|
+
* precedent.** Tool discovery needs package-manager detection and
|
|
129
|
+
* workspace-root resolution, both of which live here — but a direct edge
|
|
130
|
+
* from `@effected/commands` to this package would make that boundary-tier
|
|
131
|
+
* package integrated, and through the planned `npm` → `commands` edge
|
|
132
|
+
* would drag `npm`, `lockfiles` (pure!) and `package-json` up a tier with
|
|
133
|
+
* it. So `commands` declares the narrow contract and we ship the layer.
|
|
134
|
+
*
|
|
135
|
+
* **The argv knowledge is not duplicated.** `LocalExec.prefixes(name)` is
|
|
136
|
+
* the one home of the four managers' `exec`/`dlx`/script-runner prefixes;
|
|
137
|
+
* this layer
|
|
138
|
+
* detects *which* manager owns the directory and asks `commands` what that
|
|
139
|
+
* manager's argv looks like. Neither package reimplements the other's
|
|
140
|
+
* half.
|
|
141
|
+
*
|
|
142
|
+
* **`None` is success.** Outside any workspace — and inside one whose
|
|
143
|
+
* manager cannot be identified — the answer is `Option.none()`: "there is
|
|
144
|
+
* no project-local way to run tools here" is an ordinary fact, not an
|
|
145
|
+
* exceptional one, and a consumer running in a bare directory should not
|
|
146
|
+
* have to catch an error to learn it. The contract's typed
|
|
147
|
+
* `LocalExecError` is reserved for **mechanism** failure — a manifest that
|
|
148
|
+
* exists but cannot be read or parsed, which means something is broken
|
|
149
|
+
* rather than absent. That is npm's resolver convention, adopted
|
|
150
|
+
* verbatim.
|
|
151
|
+
*
|
|
152
|
+
* `directory` is the resolved **workspace root**, not the caller's cwd: a
|
|
153
|
+
* project-local launcher has to run where the workspace is.
|
|
154
|
+
*
|
|
155
|
+
* A consumer with no monorepo never needs this layer, and therefore never
|
|
156
|
+
* installs this package — `LocalExec.layerNone` and `LocalExec.layerFor`
|
|
157
|
+
* are one-liners in `@effected/commands`.
|
|
158
|
+
*
|
|
159
|
+
* **Bind the result to a `const`** — a parameterized layer factory mints a
|
|
160
|
+
* fresh reference per call and layers memoize by reference.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* import { ToolDiscovery } from "@effected/commands";
|
|
165
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
166
|
+
* import { Layer } from "effect";
|
|
167
|
+
*
|
|
168
|
+
* const AppLayer = ToolDiscovery.layer.pipe(
|
|
169
|
+
* Layer.provide(Workspaces.localExecLayer()),
|
|
170
|
+
* Layer.provide(Workspaces.layer()),
|
|
171
|
+
* Layer.provide(NodeServices.layer),
|
|
172
|
+
* );
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
static localExecLayer = localExecLayer;
|
|
176
|
+
/**
|
|
177
|
+
* Resolve every `catalog:` and `workspace:` specifier in one `Manifest`
|
|
178
|
+
* against the real workspace, in one call — the 90% path. Decode stays at
|
|
179
|
+
* the consumer's edge: build the `Manifest` with `Manifest.decode` (from
|
|
180
|
+
* `@effected/npm`), hand it here, and get a new `Manifest` back with
|
|
181
|
+
* concrete ranges; `toRecord()` returns to the wire shape.
|
|
182
|
+
*
|
|
183
|
+
* @remarks
|
|
184
|
+
* Composes `manifest.resolve()` with a fresh {@link Workspaces.resolverLayer}
|
|
185
|
+
* per call, so the workspace root is re-discovered from `options.cwd` (or
|
|
186
|
+
* the current `process.cwd()`) on every invocation. Consumers processing
|
|
187
|
+
* many manifests should check `manifest.needsResolution` first and skip
|
|
188
|
+
* the call entirely when no dependency field carries a
|
|
189
|
+
* `catalog:`/`workspace:` specifier — that predicate is pure and avoids
|
|
190
|
+
* catalog assembly altogether.
|
|
191
|
+
*
|
|
192
|
+
* A specifier the workspace cannot answer fails typed as
|
|
193
|
+
* `UnresolvedDependencyError`; assembly and mechanism failures surface as
|
|
194
|
+
* `CatalogAssemblyError` / `DependencyResolutionError`.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* import { Manifest } from "@effected/npm";
|
|
199
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
200
|
+
* import { Effect } from "effect";
|
|
201
|
+
*
|
|
202
|
+
* const program = Effect.gen(function* () {
|
|
203
|
+
* const manifest = yield* Manifest.decode({ dependencies: { effect: "catalog:" } });
|
|
204
|
+
* const resolved = manifest.needsResolution ? yield* Workspaces.resolveManifest(manifest) : manifest;
|
|
205
|
+
* return resolved.toRecord();
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
static resolveManifest = resolveManifest;
|
|
210
|
+
/**
|
|
211
|
+
* The one-call resolver factory: {@link Workspaces.resolvers} pre-wired
|
|
212
|
+
* over {@link Workspaces.layerWithConfigDependencies}, so the two
|
|
213
|
+
* `@effected/npm` contracts (`CatalogResolver`, `WorkspaceResolver`) need
|
|
214
|
+
* only a platform (`FileSystem` + `Path`) from the consumer.
|
|
215
|
+
*
|
|
216
|
+
* @remarks
|
|
217
|
+
* This is deliberately a **parameterized layer function, and the fresh
|
|
218
|
+
* layer per call is the feature**: layers memoize by reference, so each
|
|
219
|
+
* call mints an unmemoized layer whose root discovery re-runs — including
|
|
220
|
+
* a per-call `process.cwd()` read when `options.cwd` is omitted. A build
|
|
221
|
+
* tool that changes directory between manifests gets a correct
|
|
222
|
+
* re-discovery each time precisely because nothing is shared across
|
|
223
|
+
* calls. When you *want* sharing, bind one call's result to a `const` and
|
|
224
|
+
* provide that; the memoization rule is unchanged, this factory just
|
|
225
|
+
* refuses to hide it.
|
|
226
|
+
*
|
|
227
|
+
* Catalog assembly replays config-dependency `pnpmfile` hooks (the
|
|
228
|
+
* `layerWithConfigDependencies` path) — the semantics a real pnpm install
|
|
229
|
+
* has. Compose {@link Workspaces.resolvers} with {@link Workspaces.layer}
|
|
230
|
+
* yourself if config-dependency code must not run in process.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
235
|
+
* import { Effect } from "effect";
|
|
236
|
+
*
|
|
237
|
+
* const program = doSomethingWithResolvers.pipe(
|
|
238
|
+
* Effect.provide(Workspaces.resolverLayer()),
|
|
239
|
+
* );
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
static resolverLayer = resolverLayer;
|
|
243
|
+
/**
|
|
244
|
+
* The two `@effected/npm` resolver contracts, implemented for real.
|
|
245
|
+
*
|
|
246
|
+
* @remarks
|
|
247
|
+
* Provide this alongside `@effected/package-json`'s `Package.resolve` and
|
|
248
|
+
* a manifest's `catalog:` and `workspace:` specifiers resolve against the
|
|
249
|
+
* actual workspace instead of the no-op layers' `Option.none()`.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* import { Package } from "@effected/package-json";
|
|
254
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
255
|
+
* import { Layer } from "effect";
|
|
256
|
+
*
|
|
257
|
+
* const WorkspacesLayer = Workspaces.layer();
|
|
258
|
+
* const Resolvers = Workspaces.resolvers.pipe(Layer.provide(WorkspacesLayer));
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
static resolvers = resolvers;
|
|
183
262
|
};
|
|
184
263
|
|
|
185
264
|
//#endregion
|