@savvy-web/silk 0.4.1 → 0.5.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/changesets-changelog.cjs +9955 -9539
- package/changesets-changelog.d.cts +113 -122
- package/changesets-changelog.d.ts +113 -122
- package/changesets-markdownlint.cjs +9955 -9539
- package/changesets-markdownlint.d.cts +113 -122
- package/changesets-markdownlint.d.ts +113 -122
- package/package.json +11 -10
- package/packages/silk-effects/dist/dev/pkg/changesets/index.cjs +8 -0
- package/packages/silk-effects/dist/dev/pkg/changesets/index.js +10 -2
- package/packages/silk-effects/dist/dev/pkg/changesets/services/branch-analyzer.cjs +61 -0
- package/packages/silk-effects/dist/dev/pkg/changesets/services/branch-analyzer.js +61 -3
- package/packages/silk-effects/dist/dev/pkg/changesets/services/config-inspector.cjs +131 -6
- package/packages/silk-effects/dist/dev/pkg/changesets/services/config-inspector.js +125 -5
- package/packages/silk-effects/dist/dev/pkg/services/ChangesetConfig.cjs +78 -0
- package/packages/silk-effects/dist/dev/pkg/services/ChangesetConfig.js +78 -0
- package/packages/silk-effects/dist/dev/pkg/services/SilkPublishability.cjs +194 -0
- package/packages/silk-effects/dist/dev/pkg/services/SilkPublishability.js +193 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const require_ChangesetConfigReader = require('./ChangesetConfigReader.cjs');
|
|
2
|
+
let effect = require("effect");
|
|
3
|
+
|
|
4
|
+
//#region ../silk-effects/dist/dev/pkg/services/ChangesetConfig.js
|
|
5
|
+
/**
|
|
6
|
+
* Accessor service over a workspace root's `.changeset/config.json`.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Reads through {@link ChangesetConfigReader} (FileSystem-based) with a per-root cache.
|
|
10
|
+
* Every accessor is total (error channel `never`): a missing or unreadable config collapses
|
|
11
|
+
* to `mode: "none"` and empty/false defaults.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.4.0
|
|
14
|
+
*/
|
|
15
|
+
var ChangesetConfig = class extends effect.Context.Tag("@savvy-web/silk-effects/ChangesetConfig")() {
|
|
16
|
+
/**
|
|
17
|
+
* The one ignore matcher: exact name match, or `@scope/*` wildcard.
|
|
18
|
+
*
|
|
19
|
+
* `"@scope/*"` matches `"@scope/anything"` (prefix kept includes the trailing slash),
|
|
20
|
+
* but not the bare scope `"@scope"`.
|
|
21
|
+
*/
|
|
22
|
+
static matches(name, pattern) {
|
|
23
|
+
if (pattern.endsWith("/*")) {
|
|
24
|
+
const prefix = pattern.slice(0, -1);
|
|
25
|
+
return name.startsWith(prefix);
|
|
26
|
+
}
|
|
27
|
+
return name === pattern;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const isSilk = (cfg) => "_isSilk" in cfg && cfg._isSilk === true;
|
|
31
|
+
/**
|
|
32
|
+
* Live {@link ChangesetConfig} reading via {@link ChangesetConfigReader}, cached per root.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* Requires `ChangesetConfigReader` (which requires `FileSystem`). Provide
|
|
36
|
+
* `ChangesetConfigReaderLive` + a platform layer (`NodeContext.layer`).
|
|
37
|
+
*
|
|
38
|
+
* @since 0.4.0
|
|
39
|
+
*/
|
|
40
|
+
const ChangesetConfigLive = effect.Layer.effect(ChangesetConfig, effect.Effect.gen(function* () {
|
|
41
|
+
const reader = yield* require_ChangesetConfigReader.ChangesetConfigReader;
|
|
42
|
+
const cache = /* @__PURE__ */ new Map();
|
|
43
|
+
const read = (root) => effect.Effect.gen(function* () {
|
|
44
|
+
const hit = cache.get(root);
|
|
45
|
+
if (hit !== void 0) return hit;
|
|
46
|
+
const result = yield* reader.read(root).pipe(effect.Effect.option);
|
|
47
|
+
cache.set(root, result);
|
|
48
|
+
return result;
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
mode: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
52
|
+
onNone: () => "none",
|
|
53
|
+
onSome: (cfg) => isSilk(cfg) ? "silk" : "vanilla"
|
|
54
|
+
}))),
|
|
55
|
+
versionPrivate: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
56
|
+
onNone: () => false,
|
|
57
|
+
onSome: (cfg) => {
|
|
58
|
+
const pp = cfg.privatePackages;
|
|
59
|
+
return pp !== void 0 && pp !== false && pp.version === true;
|
|
60
|
+
}
|
|
61
|
+
}))),
|
|
62
|
+
ignorePatterns: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
63
|
+
onNone: () => [],
|
|
64
|
+
onSome: (cfg) => cfg.ignore ?? []
|
|
65
|
+
}))),
|
|
66
|
+
isIgnored: (name, root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
67
|
+
onNone: () => false,
|
|
68
|
+
onSome: (cfg) => (cfg.ignore ?? []).some((p) => ChangesetConfig.matches(name, p))
|
|
69
|
+
}))),
|
|
70
|
+
fixed: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
71
|
+
onNone: () => [],
|
|
72
|
+
onSome: (cfg) => cfg.fixed ?? []
|
|
73
|
+
})))
|
|
74
|
+
};
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
exports.ChangesetConfig = ChangesetConfig;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ChangesetConfigReader } from "./ChangesetConfigReader.js";
|
|
2
|
+
import { Context, Effect, Layer, Option } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region ../silk-effects/dist/dev/pkg/services/ChangesetConfig.js
|
|
5
|
+
/**
|
|
6
|
+
* Accessor service over a workspace root's `.changeset/config.json`.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Reads through {@link ChangesetConfigReader} (FileSystem-based) with a per-root cache.
|
|
10
|
+
* Every accessor is total (error channel `never`): a missing or unreadable config collapses
|
|
11
|
+
* to `mode: "none"` and empty/false defaults.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.4.0
|
|
14
|
+
*/
|
|
15
|
+
var ChangesetConfig = class extends Context.Tag("@savvy-web/silk-effects/ChangesetConfig")() {
|
|
16
|
+
/**
|
|
17
|
+
* The one ignore matcher: exact name match, or `@scope/*` wildcard.
|
|
18
|
+
*
|
|
19
|
+
* `"@scope/*"` matches `"@scope/anything"` (prefix kept includes the trailing slash),
|
|
20
|
+
* but not the bare scope `"@scope"`.
|
|
21
|
+
*/
|
|
22
|
+
static matches(name, pattern) {
|
|
23
|
+
if (pattern.endsWith("/*")) {
|
|
24
|
+
const prefix = pattern.slice(0, -1);
|
|
25
|
+
return name.startsWith(prefix);
|
|
26
|
+
}
|
|
27
|
+
return name === pattern;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const isSilk = (cfg) => "_isSilk" in cfg && cfg._isSilk === true;
|
|
31
|
+
/**
|
|
32
|
+
* Live {@link ChangesetConfig} reading via {@link ChangesetConfigReader}, cached per root.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* Requires `ChangesetConfigReader` (which requires `FileSystem`). Provide
|
|
36
|
+
* `ChangesetConfigReaderLive` + a platform layer (`NodeContext.layer`).
|
|
37
|
+
*
|
|
38
|
+
* @since 0.4.0
|
|
39
|
+
*/
|
|
40
|
+
const ChangesetConfigLive = Layer.effect(ChangesetConfig, Effect.gen(function* () {
|
|
41
|
+
const reader = yield* ChangesetConfigReader;
|
|
42
|
+
const cache = /* @__PURE__ */ new Map();
|
|
43
|
+
const read = (root) => Effect.gen(function* () {
|
|
44
|
+
const hit = cache.get(root);
|
|
45
|
+
if (hit !== void 0) return hit;
|
|
46
|
+
const result = yield* reader.read(root).pipe(Effect.option);
|
|
47
|
+
cache.set(root, result);
|
|
48
|
+
return result;
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
mode: (root) => read(root).pipe(Effect.map(Option.match({
|
|
52
|
+
onNone: () => "none",
|
|
53
|
+
onSome: (cfg) => isSilk(cfg) ? "silk" : "vanilla"
|
|
54
|
+
}))),
|
|
55
|
+
versionPrivate: (root) => read(root).pipe(Effect.map(Option.match({
|
|
56
|
+
onNone: () => false,
|
|
57
|
+
onSome: (cfg) => {
|
|
58
|
+
const pp = cfg.privatePackages;
|
|
59
|
+
return pp !== void 0 && pp !== false && pp.version === true;
|
|
60
|
+
}
|
|
61
|
+
}))),
|
|
62
|
+
ignorePatterns: (root) => read(root).pipe(Effect.map(Option.match({
|
|
63
|
+
onNone: () => [],
|
|
64
|
+
onSome: (cfg) => cfg.ignore ?? []
|
|
65
|
+
}))),
|
|
66
|
+
isIgnored: (name, root) => read(root).pipe(Effect.map(Option.match({
|
|
67
|
+
onNone: () => false,
|
|
68
|
+
onSome: (cfg) => (cfg.ignore ?? []).some((p) => ChangesetConfig.matches(name, p))
|
|
69
|
+
}))),
|
|
70
|
+
fixed: (root) => read(root).pipe(Effect.map(Option.match({
|
|
71
|
+
onNone: () => [],
|
|
72
|
+
onSome: (cfg) => cfg.fixed ?? []
|
|
73
|
+
})))
|
|
74
|
+
};
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
export { ChangesetConfig };
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const require_ChangesetConfig = require('./ChangesetConfig.cjs');
|
|
2
|
+
const require_index = require('../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.cjs');
|
|
3
|
+
let effect = require("effect");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let _effect_platform = require("@effect/platform");
|
|
6
|
+
|
|
7
|
+
//#region ../silk-effects/dist/dev/pkg/services/SilkPublishability.js
|
|
8
|
+
const NPM_DEFAULT = "https://registry.npmjs.org/";
|
|
9
|
+
/**
|
|
10
|
+
* Default registry endpoints for the well-known target keys. Kept in sync with the
|
|
11
|
+
* bundler's `resolveTargets` so the pre-build fallback matches what the binding will carry
|
|
12
|
+
* (no trailing slash).
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_REGISTRIES = {
|
|
15
|
+
npm: "https://registry.npmjs.org",
|
|
16
|
+
github: "https://npm.pkg.github.com"
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Silk publishability rules over `workspaces-effect`'s {@link PublishTarget}.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* In silk mode `private: true` is the norm on workspace `package.json`; publishability is
|
|
23
|
+
* derived from `publishConfig`, with the `private` flag consulted only as a last-resort
|
|
24
|
+
* default. All helpers are static so a consumer sees the full rule surface in one place.
|
|
25
|
+
*
|
|
26
|
+
* @since 0.4.0
|
|
27
|
+
*/
|
|
28
|
+
var SilkPublishability = class {
|
|
29
|
+
/**
|
|
30
|
+
* Apply silk publishability rules to a raw `package.json` and the bundler's resolved
|
|
31
|
+
* target binding. Targets-first precedence:
|
|
32
|
+
*
|
|
33
|
+
* - A non-empty `publishConfig.targets` map (the bundler's Record-map form) makes the
|
|
34
|
+
* package publishable regardless of `private`. With a `binding` (post-prod-build), one
|
|
35
|
+
* {@link PublishTarget} is emitted per resolved registry target, its `directory` set to
|
|
36
|
+
* the bound group's `dist/prod/<group>/pkg` dir. Without a binding (pre-build), one
|
|
37
|
+
* placeholder target is emitted per declared key so publishability and target counts
|
|
38
|
+
* are correct; the directory is best-effort and unused until the build writes the
|
|
39
|
+
* binding.
|
|
40
|
+
* - Else `publishConfig.access` → one target at `publishConfig.directory`.
|
|
41
|
+
* - Else `private !== true` → one default public target.
|
|
42
|
+
* - Else `[]`.
|
|
43
|
+
*
|
|
44
|
+
* @param pkgName - The package's name (the base name for `true`/empty-object targets).
|
|
45
|
+
* @param raw - The raw `package.json` (silk reads the unschematized `publishConfig`).
|
|
46
|
+
* @param binding - The parsed `dist/prod/targets.json` binding, or `null` when the prod
|
|
47
|
+
* build has not run yet. See {@link readTargetsBinding}.
|
|
48
|
+
*/
|
|
49
|
+
static detect(pkgName, raw, binding) {
|
|
50
|
+
const pc = raw.publishConfig;
|
|
51
|
+
const targets = pc?.targets;
|
|
52
|
+
if (targets && typeof targets === "object" && !Array.isArray(targets) && Object.keys(targets).length > 0) {
|
|
53
|
+
const access = pc?.access ?? "public";
|
|
54
|
+
if (binding) {
|
|
55
|
+
const dirByGroup = new Map(binding.groups.map((g) => [g.id, g.dir]));
|
|
56
|
+
return binding.targets.map((t) => new require_index.PublishTarget({
|
|
57
|
+
name: t.name,
|
|
58
|
+
registry: t.registry,
|
|
59
|
+
directory: dirByGroup.get(t.group) ?? `dist/prod/${t.group}/pkg`,
|
|
60
|
+
access
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
return Object.keys(targets).map((id) => new require_index.PublishTarget({
|
|
64
|
+
name: pkgName,
|
|
65
|
+
registry: DEFAULT_REGISTRIES[id] ?? pc?.registry ?? NPM_DEFAULT,
|
|
66
|
+
directory: `dist/prod/${id}/pkg`,
|
|
67
|
+
access
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
if (pc && (pc.access === "public" || pc.access === "restricted")) return [new require_index.PublishTarget({
|
|
71
|
+
name: pkgName,
|
|
72
|
+
registry: pc.registry ?? NPM_DEFAULT,
|
|
73
|
+
directory: pc.directory ?? ".",
|
|
74
|
+
access: pc.access
|
|
75
|
+
})];
|
|
76
|
+
if (raw.private !== true) return [new require_index.PublishTarget({
|
|
77
|
+
name: pkgName,
|
|
78
|
+
registry: pc?.registry ?? NPM_DEFAULT,
|
|
79
|
+
directory: pc?.directory ?? ".",
|
|
80
|
+
access: pc?.access ?? "public"
|
|
81
|
+
})];
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve a package's publish targets via {@link PublishabilityDetector}, then drop any
|
|
86
|
+
* whose built `directory` package.json is `private: true`. Returned targets keep the
|
|
87
|
+
* detector's original (possibly package-relative) `directory`.
|
|
88
|
+
*/
|
|
89
|
+
static resolveTargets(pkg, root) {
|
|
90
|
+
return effect.Effect.gen(function* () {
|
|
91
|
+
const detector = yield* require_index.PublishabilityDetector;
|
|
92
|
+
const fs = yield* _effect_platform.FileSystem.FileSystem;
|
|
93
|
+
const targets = yield* detector.detect(pkg, root);
|
|
94
|
+
const kept = [];
|
|
95
|
+
for (const t of targets) if (!(yield* isTargetPrivate(fs, (0, node_path.isAbsolute)(t.directory) ? t.directory : (0, node_path.join)(pkg.path, t.directory)))) kept.push(t);
|
|
96
|
+
return kept;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The publishable, non-ignored packages, resolved through the single
|
|
101
|
+
* {@link PublishabilityDetector} (which already honors changeset ignore in adaptive mode).
|
|
102
|
+
*/
|
|
103
|
+
static listPublishable(root) {
|
|
104
|
+
return effect.Effect.gen(function* () {
|
|
105
|
+
const discovery = yield* require_index.WorkspaceDiscovery;
|
|
106
|
+
const detector = yield* require_index.PublishabilityDetector;
|
|
107
|
+
const packages = yield* discovery.listPackages().pipe(effect.Effect.orDie);
|
|
108
|
+
const out = [];
|
|
109
|
+
for (const pkg of packages) {
|
|
110
|
+
const targets = yield* detector.detect(pkg, root);
|
|
111
|
+
if (targets.length > 0) out.push({
|
|
112
|
+
name: pkg.name,
|
|
113
|
+
version: pkg.version,
|
|
114
|
+
path: pkg.path,
|
|
115
|
+
targetCount: targets.length
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
|
|
123
|
+
const isTargetPrivate = (fs, targetDir) => fs.readFileString((0, node_path.join)(targetDir, "package.json")).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
|
|
124
|
+
try: () => JSON.parse(content).private === true,
|
|
125
|
+
catch: () => /* @__PURE__ */ new Error("invalid package.json")
|
|
126
|
+
})), effect.Effect.orElseSucceed(() => false));
|
|
127
|
+
/** Read a raw `package.json` from disk via FileSystem; missing/unreadable/malformed → null. */
|
|
128
|
+
const readRaw = (fs, packageJsonPath) => fs.readFileString(packageJsonPath).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
|
|
129
|
+
try: () => JSON.parse(content),
|
|
130
|
+
catch: () => /* @__PURE__ */ new Error("invalid package.json")
|
|
131
|
+
})), effect.Effect.orElseSucceed(() => null));
|
|
132
|
+
/**
|
|
133
|
+
* Read the bundler's `<pkgPath>/dist/prod/targets.json` binding via FileSystem.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* Returns `null` when the file is missing/unreadable/malformed — i.e. before the prod build
|
|
137
|
+
* has run. {@link SilkPublishability.detect} falls back to declared-key placeholders in that
|
|
138
|
+
* case. Used by the silk {@link PublishabilityDetector} layers and the workspace analyzer.
|
|
139
|
+
*
|
|
140
|
+
* @param fs - The FileSystem service.
|
|
141
|
+
* @param pkgPath - Absolute path to the package directory.
|
|
142
|
+
* @since 1.0.0
|
|
143
|
+
*/
|
|
144
|
+
const readTargetsBinding = (fs, pkgPath) => fs.readFileString((0, node_path.join)(pkgPath, "dist", "prod", "targets.json")).pipe(effect.Effect.flatMap((content) => effect.Effect.try({
|
|
145
|
+
try: () => JSON.parse(content),
|
|
146
|
+
catch: () => /* @__PURE__ */ new Error("invalid targets.json")
|
|
147
|
+
})), effect.Effect.orElseSucceed(() => null));
|
|
148
|
+
/**
|
|
149
|
+
* Override of `workspaces-effect`'s {@link PublishabilityDetector} Tag with pure silk rules.
|
|
150
|
+
*
|
|
151
|
+
* @remarks Requires `FileSystem` (captured at layer build); `detect` reads the raw
|
|
152
|
+
* `package.json` from `pkg.packageJsonPath` and applies {@link SilkPublishability.detect}.
|
|
153
|
+
*
|
|
154
|
+
* @since 0.4.0
|
|
155
|
+
*/
|
|
156
|
+
const SilkPublishabilityDetectorLive = effect.Layer.effect(require_index.PublishabilityDetector, effect.Effect.gen(function* () {
|
|
157
|
+
const fs = yield* _effect_platform.FileSystem.FileSystem;
|
|
158
|
+
return { detect: (pkg, _root) => effect.Effect.gen(function* () {
|
|
159
|
+
const raw = yield* readRaw(fs, pkg.packageJsonPath);
|
|
160
|
+
if (!raw) return [];
|
|
161
|
+
const binding = yield* readTargetsBinding(fs, pkg.path);
|
|
162
|
+
return SilkPublishability.detect(pkg.name, raw, binding);
|
|
163
|
+
}) };
|
|
164
|
+
}));
|
|
165
|
+
/**
|
|
166
|
+
* Ignore-aware override of {@link PublishabilityDetector}. `detect` short-circuits to `[]`
|
|
167
|
+
* for changeset-ignored packages, then dispatches on {@link ChangesetConfig.mode}:
|
|
168
|
+
* `none` → `[]`; `silk` → {@link SilkPublishability.detect}; `vanilla` → the library default.
|
|
169
|
+
*
|
|
170
|
+
* @remarks Requires `FileSystem` + {@link ChangesetConfig} at build.
|
|
171
|
+
*
|
|
172
|
+
* @since 0.4.0
|
|
173
|
+
*/
|
|
174
|
+
const PublishabilityDetectorAdaptiveLive = effect.Layer.effect(require_index.PublishabilityDetector, effect.Effect.gen(function* () {
|
|
175
|
+
const fs = yield* _effect_platform.FileSystem.FileSystem;
|
|
176
|
+
const config = yield* require_ChangesetConfig.ChangesetConfig;
|
|
177
|
+
const vanilla = yield* effect.Effect.provide(require_index.PublishabilityDetector, require_index.PublishabilityDetectorLive);
|
|
178
|
+
return { detect: (pkg, root) => effect.Effect.gen(function* () {
|
|
179
|
+
if (yield* config.isIgnored(pkg.name, root)) return [];
|
|
180
|
+
const mode = yield* config.mode(root);
|
|
181
|
+
if (mode === "none") return [];
|
|
182
|
+
if (mode === "silk") {
|
|
183
|
+
const raw = yield* readRaw(fs, pkg.packageJsonPath);
|
|
184
|
+
if (!raw) return [];
|
|
185
|
+
const binding = yield* readTargetsBinding(fs, pkg.path);
|
|
186
|
+
return SilkPublishability.detect(pkg.name, raw, binding);
|
|
187
|
+
}
|
|
188
|
+
return yield* vanilla.detect(pkg, root);
|
|
189
|
+
}) };
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
exports.SilkPublishability = SilkPublishability;
|
|
194
|
+
exports.readTargetsBinding = readTargetsBinding;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { ChangesetConfig } from "./ChangesetConfig.js";
|
|
2
|
+
import { PublishTarget, PublishabilityDetector, PublishabilityDetectorLive, WorkspaceDiscovery } from "../../../../../../node_modules/.pnpm/workspaces-effect@1.2.0_@effect_platform@0.96.1_effect@3.21.3__effect@3.21.3/node_modules/workspaces-effect/index.js";
|
|
3
|
+
import { Effect, Layer } from "effect";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import { FileSystem } from "@effect/platform";
|
|
6
|
+
|
|
7
|
+
//#region ../silk-effects/dist/dev/pkg/services/SilkPublishability.js
|
|
8
|
+
const NPM_DEFAULT = "https://registry.npmjs.org/";
|
|
9
|
+
/**
|
|
10
|
+
* Default registry endpoints for the well-known target keys. Kept in sync with the
|
|
11
|
+
* bundler's `resolveTargets` so the pre-build fallback matches what the binding will carry
|
|
12
|
+
* (no trailing slash).
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_REGISTRIES = {
|
|
15
|
+
npm: "https://registry.npmjs.org",
|
|
16
|
+
github: "https://npm.pkg.github.com"
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Silk publishability rules over `workspaces-effect`'s {@link PublishTarget}.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* In silk mode `private: true` is the norm on workspace `package.json`; publishability is
|
|
23
|
+
* derived from `publishConfig`, with the `private` flag consulted only as a last-resort
|
|
24
|
+
* default. All helpers are static so a consumer sees the full rule surface in one place.
|
|
25
|
+
*
|
|
26
|
+
* @since 0.4.0
|
|
27
|
+
*/
|
|
28
|
+
var SilkPublishability = class {
|
|
29
|
+
/**
|
|
30
|
+
* Apply silk publishability rules to a raw `package.json` and the bundler's resolved
|
|
31
|
+
* target binding. Targets-first precedence:
|
|
32
|
+
*
|
|
33
|
+
* - A non-empty `publishConfig.targets` map (the bundler's Record-map form) makes the
|
|
34
|
+
* package publishable regardless of `private`. With a `binding` (post-prod-build), one
|
|
35
|
+
* {@link PublishTarget} is emitted per resolved registry target, its `directory` set to
|
|
36
|
+
* the bound group's `dist/prod/<group>/pkg` dir. Without a binding (pre-build), one
|
|
37
|
+
* placeholder target is emitted per declared key so publishability and target counts
|
|
38
|
+
* are correct; the directory is best-effort and unused until the build writes the
|
|
39
|
+
* binding.
|
|
40
|
+
* - Else `publishConfig.access` → one target at `publishConfig.directory`.
|
|
41
|
+
* - Else `private !== true` → one default public target.
|
|
42
|
+
* - Else `[]`.
|
|
43
|
+
*
|
|
44
|
+
* @param pkgName - The package's name (the base name for `true`/empty-object targets).
|
|
45
|
+
* @param raw - The raw `package.json` (silk reads the unschematized `publishConfig`).
|
|
46
|
+
* @param binding - The parsed `dist/prod/targets.json` binding, or `null` when the prod
|
|
47
|
+
* build has not run yet. See {@link readTargetsBinding}.
|
|
48
|
+
*/
|
|
49
|
+
static detect(pkgName, raw, binding) {
|
|
50
|
+
const pc = raw.publishConfig;
|
|
51
|
+
const targets = pc?.targets;
|
|
52
|
+
if (targets && typeof targets === "object" && !Array.isArray(targets) && Object.keys(targets).length > 0) {
|
|
53
|
+
const access = pc?.access ?? "public";
|
|
54
|
+
if (binding) {
|
|
55
|
+
const dirByGroup = new Map(binding.groups.map((g) => [g.id, g.dir]));
|
|
56
|
+
return binding.targets.map((t) => new PublishTarget({
|
|
57
|
+
name: t.name,
|
|
58
|
+
registry: t.registry,
|
|
59
|
+
directory: dirByGroup.get(t.group) ?? `dist/prod/${t.group}/pkg`,
|
|
60
|
+
access
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
return Object.keys(targets).map((id) => new PublishTarget({
|
|
64
|
+
name: pkgName,
|
|
65
|
+
registry: DEFAULT_REGISTRIES[id] ?? pc?.registry ?? NPM_DEFAULT,
|
|
66
|
+
directory: `dist/prod/${id}/pkg`,
|
|
67
|
+
access
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
if (pc && (pc.access === "public" || pc.access === "restricted")) return [new PublishTarget({
|
|
71
|
+
name: pkgName,
|
|
72
|
+
registry: pc.registry ?? NPM_DEFAULT,
|
|
73
|
+
directory: pc.directory ?? ".",
|
|
74
|
+
access: pc.access
|
|
75
|
+
})];
|
|
76
|
+
if (raw.private !== true) return [new PublishTarget({
|
|
77
|
+
name: pkgName,
|
|
78
|
+
registry: pc?.registry ?? NPM_DEFAULT,
|
|
79
|
+
directory: pc?.directory ?? ".",
|
|
80
|
+
access: pc?.access ?? "public"
|
|
81
|
+
})];
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve a package's publish targets via {@link PublishabilityDetector}, then drop any
|
|
86
|
+
* whose built `directory` package.json is `private: true`. Returned targets keep the
|
|
87
|
+
* detector's original (possibly package-relative) `directory`.
|
|
88
|
+
*/
|
|
89
|
+
static resolveTargets(pkg, root) {
|
|
90
|
+
return Effect.gen(function* () {
|
|
91
|
+
const detector = yield* PublishabilityDetector;
|
|
92
|
+
const fs = yield* FileSystem.FileSystem;
|
|
93
|
+
const targets = yield* detector.detect(pkg, root);
|
|
94
|
+
const kept = [];
|
|
95
|
+
for (const t of targets) if (!(yield* isTargetPrivate(fs, isAbsolute(t.directory) ? t.directory : join(pkg.path, t.directory)))) kept.push(t);
|
|
96
|
+
return kept;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The publishable, non-ignored packages, resolved through the single
|
|
101
|
+
* {@link PublishabilityDetector} (which already honors changeset ignore in adaptive mode).
|
|
102
|
+
*/
|
|
103
|
+
static listPublishable(root) {
|
|
104
|
+
return Effect.gen(function* () {
|
|
105
|
+
const discovery = yield* WorkspaceDiscovery;
|
|
106
|
+
const detector = yield* PublishabilityDetector;
|
|
107
|
+
const packages = yield* discovery.listPackages().pipe(Effect.orDie);
|
|
108
|
+
const out = [];
|
|
109
|
+
for (const pkg of packages) {
|
|
110
|
+
const targets = yield* detector.detect(pkg, root);
|
|
111
|
+
if (targets.length > 0) out.push({
|
|
112
|
+
name: pkg.name,
|
|
113
|
+
version: pkg.version,
|
|
114
|
+
path: pkg.path,
|
|
115
|
+
targetCount: targets.length
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
|
|
123
|
+
const isTargetPrivate = (fs, targetDir) => fs.readFileString(join(targetDir, "package.json")).pipe(Effect.flatMap((content) => Effect.try({
|
|
124
|
+
try: () => JSON.parse(content).private === true,
|
|
125
|
+
catch: () => /* @__PURE__ */ new Error("invalid package.json")
|
|
126
|
+
})), Effect.orElseSucceed(() => false));
|
|
127
|
+
/** Read a raw `package.json` from disk via FileSystem; missing/unreadable/malformed → null. */
|
|
128
|
+
const readRaw = (fs, packageJsonPath) => fs.readFileString(packageJsonPath).pipe(Effect.flatMap((content) => Effect.try({
|
|
129
|
+
try: () => JSON.parse(content),
|
|
130
|
+
catch: () => /* @__PURE__ */ new Error("invalid package.json")
|
|
131
|
+
})), Effect.orElseSucceed(() => null));
|
|
132
|
+
/**
|
|
133
|
+
* Read the bundler's `<pkgPath>/dist/prod/targets.json` binding via FileSystem.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* Returns `null` when the file is missing/unreadable/malformed — i.e. before the prod build
|
|
137
|
+
* has run. {@link SilkPublishability.detect} falls back to declared-key placeholders in that
|
|
138
|
+
* case. Used by the silk {@link PublishabilityDetector} layers and the workspace analyzer.
|
|
139
|
+
*
|
|
140
|
+
* @param fs - The FileSystem service.
|
|
141
|
+
* @param pkgPath - Absolute path to the package directory.
|
|
142
|
+
* @since 1.0.0
|
|
143
|
+
*/
|
|
144
|
+
const readTargetsBinding = (fs, pkgPath) => fs.readFileString(join(pkgPath, "dist", "prod", "targets.json")).pipe(Effect.flatMap((content) => Effect.try({
|
|
145
|
+
try: () => JSON.parse(content),
|
|
146
|
+
catch: () => /* @__PURE__ */ new Error("invalid targets.json")
|
|
147
|
+
})), Effect.orElseSucceed(() => null));
|
|
148
|
+
/**
|
|
149
|
+
* Override of `workspaces-effect`'s {@link PublishabilityDetector} Tag with pure silk rules.
|
|
150
|
+
*
|
|
151
|
+
* @remarks Requires `FileSystem` (captured at layer build); `detect` reads the raw
|
|
152
|
+
* `package.json` from `pkg.packageJsonPath` and applies {@link SilkPublishability.detect}.
|
|
153
|
+
*
|
|
154
|
+
* @since 0.4.0
|
|
155
|
+
*/
|
|
156
|
+
const SilkPublishabilityDetectorLive = Layer.effect(PublishabilityDetector, Effect.gen(function* () {
|
|
157
|
+
const fs = yield* FileSystem.FileSystem;
|
|
158
|
+
return { detect: (pkg, _root) => Effect.gen(function* () {
|
|
159
|
+
const raw = yield* readRaw(fs, pkg.packageJsonPath);
|
|
160
|
+
if (!raw) return [];
|
|
161
|
+
const binding = yield* readTargetsBinding(fs, pkg.path);
|
|
162
|
+
return SilkPublishability.detect(pkg.name, raw, binding);
|
|
163
|
+
}) };
|
|
164
|
+
}));
|
|
165
|
+
/**
|
|
166
|
+
* Ignore-aware override of {@link PublishabilityDetector}. `detect` short-circuits to `[]`
|
|
167
|
+
* for changeset-ignored packages, then dispatches on {@link ChangesetConfig.mode}:
|
|
168
|
+
* `none` → `[]`; `silk` → {@link SilkPublishability.detect}; `vanilla` → the library default.
|
|
169
|
+
*
|
|
170
|
+
* @remarks Requires `FileSystem` + {@link ChangesetConfig} at build.
|
|
171
|
+
*
|
|
172
|
+
* @since 0.4.0
|
|
173
|
+
*/
|
|
174
|
+
const PublishabilityDetectorAdaptiveLive = Layer.effect(PublishabilityDetector, Effect.gen(function* () {
|
|
175
|
+
const fs = yield* FileSystem.FileSystem;
|
|
176
|
+
const config = yield* ChangesetConfig;
|
|
177
|
+
const vanilla = yield* Effect.provide(PublishabilityDetector, PublishabilityDetectorLive);
|
|
178
|
+
return { detect: (pkg, root) => Effect.gen(function* () {
|
|
179
|
+
if (yield* config.isIgnored(pkg.name, root)) return [];
|
|
180
|
+
const mode = yield* config.mode(root);
|
|
181
|
+
if (mode === "none") return [];
|
|
182
|
+
if (mode === "silk") {
|
|
183
|
+
const raw = yield* readRaw(fs, pkg.packageJsonPath);
|
|
184
|
+
if (!raw) return [];
|
|
185
|
+
const binding = yield* readTargetsBinding(fs, pkg.path);
|
|
186
|
+
return SilkPublishability.detect(pkg.name, raw, binding);
|
|
187
|
+
}
|
|
188
|
+
return yield* vanilla.detect(pkg, root);
|
|
189
|
+
}) };
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
export { SilkPublishability, readTargetsBinding };
|