@fairgarden/distribution 0.1.0-0.canary.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.
Files changed (82) hide show
  1. package/Readme.md +97 -0
  2. package/dist/add-module.d.ts +62 -0
  3. package/dist/add-module.d.ts.map +1 -0
  4. package/dist/add-module.js +279 -0
  5. package/dist/add-module.js.map +1 -0
  6. package/dist/calver.d.ts +65 -0
  7. package/dist/calver.d.ts.map +1 -0
  8. package/dist/calver.js +115 -0
  9. package/dist/calver.js.map +1 -0
  10. package/dist/canary.d.ts +58 -0
  11. package/dist/canary.d.ts.map +1 -0
  12. package/dist/canary.js +130 -0
  13. package/dist/canary.js.map +1 -0
  14. package/dist/changelog.d.ts +111 -0
  15. package/dist/changelog.d.ts.map +1 -0
  16. package/dist/changelog.js +285 -0
  17. package/dist/changelog.js.map +1 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +1076 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/config-edit.d.ts +18 -0
  23. package/dist/config-edit.d.ts.map +1 -0
  24. package/dist/config-edit.js +166 -0
  25. package/dist/config-edit.js.map +1 -0
  26. package/dist/extends.d.ts +60 -0
  27. package/dist/extends.d.ts.map +1 -0
  28. package/dist/extends.js +173 -0
  29. package/dist/extends.js.map +1 -0
  30. package/dist/extract.d.ts +31 -0
  31. package/dist/extract.d.ts.map +1 -0
  32. package/dist/extract.js +177 -0
  33. package/dist/extract.js.map +1 -0
  34. package/dist/forks.d.ts +116 -0
  35. package/dist/forks.d.ts.map +1 -0
  36. package/dist/forks.js +357 -0
  37. package/dist/forks.js.map +1 -0
  38. package/dist/git-url.d.ts +19 -0
  39. package/dist/git-url.d.ts.map +1 -0
  40. package/dist/git-url.js +55 -0
  41. package/dist/git-url.js.map +1 -0
  42. package/dist/index.d.ts +21 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +11 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/inherit.d.ts +18 -0
  47. package/dist/inherit.d.ts.map +1 -0
  48. package/dist/inherit.js +84 -0
  49. package/dist/inherit.js.map +1 -0
  50. package/dist/manifest.d.ts +43 -0
  51. package/dist/manifest.d.ts.map +1 -0
  52. package/dist/manifest.js +64 -0
  53. package/dist/manifest.js.map +1 -0
  54. package/dist/overrides.d.ts +32 -0
  55. package/dist/overrides.d.ts.map +1 -0
  56. package/dist/overrides.js +81 -0
  57. package/dist/overrides.js.map +1 -0
  58. package/dist/policy.d.ts +86 -0
  59. package/dist/policy.d.ts.map +1 -0
  60. package/dist/policy.js +269 -0
  61. package/dist/policy.js.map +1 -0
  62. package/dist/readme.d.ts +60 -0
  63. package/dist/readme.d.ts.map +1 -0
  64. package/dist/readme.js +304 -0
  65. package/dist/readme.js.map +1 -0
  66. package/dist/release.d.ts +164 -0
  67. package/dist/release.d.ts.map +1 -0
  68. package/dist/release.js +454 -0
  69. package/dist/release.js.map +1 -0
  70. package/dist/scaffold.d.ts +45 -0
  71. package/dist/scaffold.d.ts.map +1 -0
  72. package/dist/scaffold.js +492 -0
  73. package/dist/scaffold.js.map +1 -0
  74. package/dist/submodules.d.ts +144 -0
  75. package/dist/submodules.d.ts.map +1 -0
  76. package/dist/submodules.js +315 -0
  77. package/dist/submodules.js.map +1 -0
  78. package/dist/workflows.d.ts +102 -0
  79. package/dist/workflows.d.ts.map +1 -0
  80. package/dist/workflows.js +710 -0
  81. package/dist/workflows.js.map +1 -0
  82. package/package.json +54 -0
package/Readme.md ADDED
@@ -0,0 +1,97 @@
1
+ # FairGarden Distribution
2
+
3
+ <!-- fg:version -->
4
+
5
+ Version **0.1.0-alpha.2**
6
+
7
+ <!-- /fg:version -->
8
+
9
+ <!-- fg:releasing -->
10
+
11
+ ## Releasing
12
+
13
+ This module releases on its own. `0.1.0-alpha.2` is what main is working towards,
14
+ not what is published — the version here is always the next one.
15
+
16
+ 1. **Publish it.** Run the *Publish* workflow from the Actions tab, picking the
17
+ dist tag. It refuses if that version is already on npm.
18
+ 2. **Move it on.** `pnpm release` — opens a pull request bumping this branch
19
+ to `0.1.0-alpha.3`, or `pnpm release --id rc` to change
20
+ identifier. A prerelease gets no maintenance branch; there is no released
21
+ line behind it yet.
22
+
23
+ Every push to main publishes `@fairgarden/distribution@canary`. A canary is not a release and
24
+ carries no promise; it is there so main can be tried without a checkout.
25
+
26
+ <!-- /fg:releasing -->
27
+
28
+ Ship a set of versioned modules together.
29
+
30
+ End users do not care about semver — they want to know how old their copy is,
31
+ and `2024.12.01` says that at a glance where `1.2.1` does not. Developers do
32
+ care, because semver says how much work an upgrade will be. A distribution uses
33
+ each where it belongs: the distribution is versioned by date, the modules inside
34
+ it by semver.
35
+
36
+ ```
37
+ @fairgarden/core 2024.12.01
38
+ @fairgarden/id 1.2.3
39
+ @fairgarden/design 1.5.6
40
+ @fairgarden/membership 1.0.0
41
+ ```
42
+
43
+ Modules are git submodules, so the commit is the pin and the version is whatever
44
+ the module's own `package.json` says. Nothing in the distribution repository
45
+ restates it.
46
+
47
+ ## Documentation
48
+
49
+ The docs are a site in this repository. Run them with:
50
+
51
+ ```bash
52
+ pnpm --filter @fairgarden/distribution-docs dev
53
+ ```
54
+
55
+ - **Versioning** — why two schemes, where a version comes from, and LTS branches
56
+ - **Modules** — apps, packages, and how they resolve
57
+ - **Extending** — one distribution built on another
58
+ - **Deploying** — one deployment, or many
59
+ - **Running it** — dev modes, and a hostname per app
60
+ - **Growing a module** — building in place, then extracting
61
+ - **Policies** — the organization's rules, on every module's own, built once
62
+ - **Releasing a module** — semver, maintenance branches and dist-tags
63
+ - **Submodule urls** — what a build host can actually clone
64
+ - **Commands** and **Functions** — the `fg-dist` CLI and its API
65
+
66
+ ## Install
67
+
68
+ ```bash
69
+ pnpm add -D @fairgarden/distribution
70
+ ```
71
+
72
+ Or scaffold without installing anything:
73
+
74
+ ```bash
75
+ pnpx @fairgarden/distribution init distribution acme --name @acme/core
76
+ ```
77
+
78
+ ```bash
79
+ fg-dist init <kind> [dir] # scaffold a distribution, monolith or module
80
+ fg-dist add-module <url> # add a module repository as a submodule
81
+ fg-dist extract <path> # turn a directory here into its own repository
82
+ fg-dist sync # what has moved, and whether the floor holds
83
+ fg-dist bump [name...] # take the newest non-major version
84
+ fg-dist check # fail when this ships older than what it extends
85
+ fg-dist use-https # rewrite ssh submodule urls, and check they are public
86
+ fg-dist readme # write the module versions into the readmes
87
+ fg-dist overrides # resolve modules from the tree, not the registry
88
+ fg-dist workflows # give every module a publishing workflow
89
+ fg-dist policy build # the organization's policy, on every module's own
90
+ fg-dist policy use # give this service its copy, in its build
91
+ fg-dist canary # stamp a canary version, for CI
92
+ fg-dist release --minor # open the next version on main, and a branch behind it
93
+ fg-dist prerelease --major # start the next line on a branch, leaving main alone
94
+ ```
95
+
96
+ Mounting a distribution's apps into one Next deployment is a separate concern,
97
+ handled by `@fairgarden/monolith`.
@@ -0,0 +1,62 @@
1
+ /** `git@host:owner/name.git` or `https://host/owner/name(.git)` -> `name`. */
2
+ export declare const nameFromUrl: (url: string) => string;
3
+ export interface AddModuleOptions {
4
+ /** Where the submodule goes, relative to the repository. Defaults to `apps/<name>`. */
5
+ at?: string;
6
+ /** Mount name, which becomes the URL segment. Defaults to the repository name. */
7
+ mount?: string;
8
+ /** The monolith whose package.json gains the dependency. Found when omitted. */
9
+ monolith?: string;
10
+ /**
11
+ * Record the URL as given instead of rewriting it to HTTPS. Only do this
12
+ * when nothing but a developer's machine will ever clone the submodule.
13
+ */
14
+ ssh?: boolean;
15
+ /** Note the module in the distribution's changelog. On by default. */
16
+ changelog?: boolean;
17
+ }
18
+ /**
19
+ * The app that composes the modules, or undefined when there is none.
20
+ *
21
+ * A distribution too complex to serve from one deployment has no monolith at
22
+ * all; each app under `apps/` is deployed on its own.
23
+ */
24
+ export declare const findMonolith: (root: string, from: string) => Promise<string | undefined>;
25
+ export interface AddModuleResult {
26
+ /** Whether the workspace overrides were updated to include this module. */
27
+ overrides: boolean;
28
+ /** Why they were not, when the workspace could not be edited. */
29
+ overridesError: string | undefined;
30
+ mount: string;
31
+ relativePath: string;
32
+ packageName: string | undefined;
33
+ version: string | undefined;
34
+ /** Whether the monolith's package.json was changed. */
35
+ linked: boolean;
36
+ /** The monolith package.json that was, or should be, updated. */
37
+ monolithPackageJson: string | undefined;
38
+ /** The config the mount was written into. */
39
+ monolithConfig: string | undefined;
40
+ /** Whether the mount was added; false when it was already declared. */
41
+ mounted: boolean;
42
+ /** Why the config could not be edited, when it could not be. */
43
+ mountError: string | undefined;
44
+ /** The URL recorded in .gitmodules, which may differ from the one given. */
45
+ url: string;
46
+ /** Whether that URL was rewritten from SSH. */
47
+ rewritten: boolean;
48
+ /** Whether the module has routes, and so is mounted rather than depended on. */
49
+ isApp: boolean;
50
+ /** Whether turbo.json was told the monolith compiles this app from its source. */
51
+ turbo: boolean;
52
+ }
53
+ /**
54
+ * Add a module repository as a submodule and wire it into the monolith.
55
+ *
56
+ * The submodule is what pins the version, and the dependency is what makes the
57
+ * module resolvable by package name. The mount itself is declared in the
58
+ * monolith's Next config, which is left alone — rewriting someone's config is
59
+ * not something a command should do quietly.
60
+ */
61
+ export declare const addModule: (cwd: string, url: string, options?: AddModuleOptions) => Promise<AddModuleResult>;
62
+ //# sourceMappingURL=add-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-module.d.ts","sourceRoot":"","sources":["../src/add-module.ts"],"names":[],"mappings":"AAiCA,8EAA8E;AAC9E,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAGzC,CAAA;AAED,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAgBD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,MAAM,MAAM,KACX,OAAO,CAAC,MAAM,GAAG,SAAS,CAiB5B,CAAA;AAED,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,6CAA6C;IAC7C,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAA;IAChB,gEAAgE;IAChE,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAA;IAClB,gFAAgF;IAChF,KAAK,EAAE,OAAO,CAAA;IACd,kFAAkF;IAClF,KAAK,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,UAAS,gBAAqB,KAC7B,OAAO,CAAC,eAAe,CAmHzB,CAAA"}
@@ -0,0 +1,279 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { readFile, writeFile, readdir, stat, mkdir } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { writeOverrides } from "./overrides.js";
5
+ import { repositoryRoot } from "./submodules.js";
6
+ import { readPackageName } from "./scaffold.js";
7
+ import { addMount, ConfigEditError, declaresMonolith } from "./config-edit.js";
8
+ import { toHttps } from "./git-url.js";
9
+ import { noteInDistribution, repositoryLink } from "./changelog.js";
10
+ const CONFIG_FILES = [
11
+ 'next.config.ts',
12
+ 'next.config.mts',
13
+ 'next.config.js',
14
+ 'next.config.mjs',
15
+ 'next.config.cjs',
16
+ ];
17
+ const git = (cwd, args) => {
18
+ try {
19
+ return execFileSync('git', args, {
20
+ cwd,
21
+ encoding: 'utf8',
22
+ stdio: ['ignore', 'pipe', 'pipe'],
23
+ }).trim();
24
+ }
25
+ catch (cause) {
26
+ const stderr = cause instanceof Error && 'stderr' in cause ? String(cause.stderr).trim() : '';
27
+ throw new Error(`git ${args[0]} failed${stderr ? `: ${stderr}` : ''}`, { cause });
28
+ }
29
+ };
30
+ /** `git@host:owner/name.git` or `https://host/owner/name(.git)` -> `name`. */
31
+ export const nameFromUrl = (url) => {
32
+ const last = url.replace(/\.git$/, '').split(/[/:]/).pop() ?? '';
33
+ return last || 'module';
34
+ };
35
+ /**
36
+ * Whether a directory holds the app that composes modules.
37
+ *
38
+ * Identified by its Next config calling `withMonolith`. A module depends on
39
+ * this package as well, so the dependency alone would match every module.
40
+ */
41
+ const composesModules = async (dir) => {
42
+ for (const file of CONFIG_FILES) {
43
+ const source = await readFile(path.join(dir, file), 'utf8').catch(() => undefined);
44
+ if (source !== undefined)
45
+ return declaresMonolith(source);
46
+ }
47
+ return false;
48
+ };
49
+ /**
50
+ * The app that composes the modules, or undefined when there is none.
51
+ *
52
+ * A distribution too complex to serve from one deployment has no monolith at
53
+ * all; each app under `apps/` is deployed on its own.
54
+ */
55
+ export const findMonolith = async (root, from) => {
56
+ if (await composesModules(from))
57
+ return from;
58
+ const candidates = [path.join(root, 'apps', 'monolith')];
59
+ for (const parent of ['apps', 'packages']) {
60
+ const entries = await readdir(path.join(root, parent), {
61
+ withFileTypes: true,
62
+ }).catch(() => []);
63
+ for (const entry of entries) {
64
+ if (entry.isDirectory())
65
+ candidates.push(path.join(root, parent, entry.name));
66
+ }
67
+ }
68
+ for (const candidate of candidates) {
69
+ if (await composesModules(candidate))
70
+ return candidate;
71
+ }
72
+ return undefined;
73
+ };
74
+ /**
75
+ * Add a module repository as a submodule and wire it into the monolith.
76
+ *
77
+ * The submodule is what pins the version, and the dependency is what makes the
78
+ * module resolvable by package name. The mount itself is declared in the
79
+ * monolith's Next config, which is left alone — rewriting someone's config is
80
+ * not something a command should do quietly.
81
+ */
82
+ export const addModule = async (cwd, url, options = {}) => {
83
+ const root = repositoryRoot(cwd);
84
+ if (!root)
85
+ throw new Error(`${cwd} is not inside a git repository.`);
86
+ const name = nameFromUrl(url);
87
+ // Where it lands is decided after cloning, since only the contents say
88
+ // whether this is an app to deploy or a package apps depend on.
89
+ const relativePath = options.at ?? path.join('apps', name);
90
+ const full = path.join(root, relativePath);
91
+ // .gitmodules is committed, so this URL is what every later clone uses.
92
+ const recorded = options.ssh ? url : toHttps(url);
93
+ const rewritten = recorded !== url;
94
+ git(root, ['submodule', 'add', recorded, relativePath]);
95
+ git(root, ['submodule', 'update', '--init', '--recursive', relativePath]);
96
+ let placedAt = relativePath;
97
+ let placedFull = full;
98
+ // A distribution is apps and the packages they share. An app has routes; a
99
+ // package does not, and belongs beside the others rather than under apps/.
100
+ const isApp = await hasRoutes(full);
101
+ if (!options.at && !isApp) {
102
+ const packagePath = path.join('packages', name);
103
+ try {
104
+ await mkdir(path.join(root, 'packages'), { recursive: true });
105
+ git(root, ['mv', relativePath, packagePath]);
106
+ placedAt = packagePath;
107
+ placedFull = path.join(root, packagePath);
108
+ }
109
+ catch {
110
+ // Leaving it under apps/ is wrong but harmless; say nothing and carry on.
111
+ }
112
+ }
113
+ const packageName = await readPackageName(placedFull);
114
+ const version = await readVersion(placedFull);
115
+ const monolithRoot = options.monolith
116
+ ? path.resolve(cwd, options.monolith)
117
+ : await findMonolith(root, cwd);
118
+ const monolithPackageJson = monolithRoot
119
+ ? path.join(monolithRoot, 'package.json')
120
+ : undefined;
121
+ // Linked through the workspace, so the version is irrelevant here — only
122
+ // the name is. Guarding on the version left a module with none mounted but
123
+ // never depended on.
124
+ let linked = false;
125
+ if (packageName && monolithPackageJson && monolithRoot !== root) {
126
+ linked = await addDependency(monolithPackageJson, packageName);
127
+ }
128
+ const mount = options.mount ?? name;
129
+ // Only an app is mounted at a path. A package is depended on, not served.
130
+ // A mount is written into the config as a bare string, which is read back as
131
+ // a package name. Without one there is nothing valid to write, so say so
132
+ // rather than record a path that will not resolve.
133
+ const { config, mounted, error } = monolithRoot && isApp
134
+ ? packageName
135
+ ? await declareMount(monolithRoot, mount, packageName)
136
+ : {
137
+ config: undefined,
138
+ mounted: false,
139
+ error: 'It has no package name, so there is nothing to mount it by. ' +
140
+ 'Give it one, or mount it by path yourself.',
141
+ }
142
+ : { config: undefined, mounted: false, error: undefined };
143
+ const turbo = monolithRoot && isApp && packageName ? await compileFromSource(root, packageName) : false;
144
+ // The distribution resolves its own modules from the tree, not from their
145
+ // declared ranges — which stop matching as soon as a submodule is bumped
146
+ // past them.
147
+ // The repository, not where the command was run: adding a module from
148
+ // apps/monolith is a supported path, and `cwd` there is not the root.
149
+ //
150
+ // Last, and after everything that matters. The submodule is added and the
151
+ // mount is written by now, so a workspace this cannot edit — a sibling with
152
+ // no checkout, an `overrides:` key someone else wrote — is something to
153
+ // report, not something to fail the whole command over.
154
+ if (options.changelog !== false && packageName) {
155
+ noteInDistribution(root, `\`${packageName}\`${version ? ` ${version}` : ''} added, from ${repositoryLink(recorded)}`);
156
+ }
157
+ const overrides = await writeOverrides(repositoryRoot(cwd) ?? cwd).catch((error) => ({
158
+ changed: false,
159
+ error: error instanceof Error ? error.message : String(error),
160
+ }));
161
+ return {
162
+ overrides: overrides.changed,
163
+ overridesError: 'error' in overrides ? overrides.error : undefined,
164
+ mount,
165
+ relativePath: placedAt,
166
+ packageName,
167
+ version,
168
+ linked,
169
+ monolithPackageJson,
170
+ monolithConfig: config,
171
+ mounted,
172
+ mountError: error,
173
+ url: recorded,
174
+ rewritten,
175
+ isApp,
176
+ turbo,
177
+ };
178
+ };
179
+ const BUILD_LIBS = 'build:libs';
180
+ /**
181
+ * Have the monolith's build wait for this app's libraries rather than for the
182
+ * app's own build: the monolith compiles it from source, so building it on its
183
+ * own as well would only take time. Only where turbo.json has the
184
+ * `build:libs` task this relies on.
185
+ */
186
+ const compileFromSource = async (root, packageName) => {
187
+ const file = path.join(root, 'turbo.json');
188
+ let turbo;
189
+ try {
190
+ turbo = JSON.parse(await readFile(file, 'utf8'));
191
+ }
192
+ catch {
193
+ return false;
194
+ }
195
+ const key = `${packageName}#${BUILD_LIBS}`;
196
+ if (!turbo.tasks?.[BUILD_LIBS] || turbo.tasks[key])
197
+ return false;
198
+ turbo.tasks[key] = { dependsOn: [`^${BUILD_LIBS}`] };
199
+ await writeFile(file, `${JSON.stringify(turbo, null, 2)}\n`);
200
+ return true;
201
+ };
202
+ /**
203
+ * Write the mount into the monolith's Next config.
204
+ *
205
+ * Reports rather than throws when the config is shaped in a way it cannot
206
+ * edit — the submodule is already added by then, and failing the whole command
207
+ * over a line someone can paste would be worse than saying so.
208
+ */
209
+ const declareMount = async (monolithRoot, mount, module) => {
210
+ for (const file of CONFIG_FILES) {
211
+ const full = path.join(monolithRoot, file);
212
+ let source;
213
+ try {
214
+ source = await readFile(full, 'utf8');
215
+ }
216
+ catch {
217
+ continue;
218
+ }
219
+ try {
220
+ const result = addMount(source, mount, module);
221
+ if (result.changed)
222
+ await writeFile(full, result.source);
223
+ return { config: full, mounted: result.changed, error: undefined };
224
+ }
225
+ catch (cause) {
226
+ return {
227
+ config: full,
228
+ mounted: false,
229
+ error: cause instanceof ConfigEditError
230
+ ? cause.message
231
+ : `The config could not be edited: ${String(cause)}`,
232
+ };
233
+ }
234
+ }
235
+ return { config: undefined, mounted: false, error: 'No Next config was found.' };
236
+ };
237
+ const isDirectory = async (candidate) => (await stat(candidate).catch(() => undefined))?.isDirectory() === true;
238
+ /** Whether a checkout is an app, meaning it has routes Next would serve. */
239
+ const hasRoutes = async (root) => {
240
+ for (const dir of ['app', 'pages', path.join('src', 'app'), path.join('src', 'pages')]) {
241
+ if (await isDirectory(path.join(root, dir)))
242
+ return true;
243
+ }
244
+ return false;
245
+ };
246
+ const readVersion = async (root) => {
247
+ try {
248
+ const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
249
+ return typeof pkg.version === 'string' ? pkg.version : undefined;
250
+ }
251
+ catch {
252
+ return undefined;
253
+ }
254
+ };
255
+ /**
256
+ * Depend on the module through the workspace, not on a version.
257
+ *
258
+ * Nothing in a distribution repository pins a module version: the submodule
259
+ * commit is the pin, and the version is whatever the package.json inside that
260
+ * checkout says. `workspace:*` is how that is spelled, and it keeps the
261
+ * repository from carrying a second, stale copy of the same fact.
262
+ */
263
+ const addDependency = async (packageJsonPath, name) => {
264
+ let pkg;
265
+ try {
266
+ pkg = JSON.parse(await readFile(packageJsonPath, 'utf8'));
267
+ }
268
+ catch {
269
+ return false;
270
+ }
271
+ const dependencies = { ...(pkg.dependencies ?? {}) };
272
+ if (dependencies[name] === 'workspace:*')
273
+ return false;
274
+ dependencies[name] = 'workspace:*';
275
+ pkg.dependencies = Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
276
+ await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
277
+ return true;
278
+ };
279
+ //# sourceMappingURL=add-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-module.js","sourceRoot":"","sources":["../src/add-module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * A distribution's version: `YY.MM.NN`, and a prerelease stage on the way.
3
+ *
4
+ * `26.09.01-alpha.0` is the first release of September 2026, at its first
5
+ * alpha. A release goes through its stages — alpha, beta, then stable — and the
6
+ * next release that month is `26.09.02`. The month is when it is published, so
7
+ * a new month starts again at `.01`, stage counter at 0, whatever was
8
+ * unfinished before: a distribution still in alpha publishes `alpha.0`,
9
+ * `alpha.1`, … until the month turns.
10
+ *
11
+ * End users read the month: `26.03` against `26.09` says six months, where
12
+ * `1.1.1` against `1.2.1` says nothing. The day is not in it — it says nothing
13
+ * a reader needs — and neither is `20`.
14
+ *
15
+ * npm takes semver only, which allows no leading zeros, so the registry spells
16
+ * it `26.9.1-alpha.0`: the same version, which sorts the same way. The
17
+ * distribution — its manifest, its tags — keeps the padded form.
18
+ */
19
+ export interface CalendarVersion {
20
+ /** Two digits: 26 for 2026. */
21
+ year: number;
22
+ month: number;
23
+ /** Which release of the month, from 1. */
24
+ release: number;
25
+ /** The stage it is at, until it is stable. */
26
+ prerelease: {
27
+ id: string;
28
+ number: number;
29
+ } | undefined;
30
+ }
31
+ export declare const parseCalendarVersion: (version: string) => CalendarVersion | undefined;
32
+ export declare const formatCalendarVersion: (version: CalendarVersion) => string;
33
+ /** Throw unless `version` is a distribution's; the message says what one looks like. */
34
+ export declare const assertCalendarVersion: (version: string) => CalendarVersion;
35
+ /** How npm spells it: the same version, without the zeros semver does not allow. */
36
+ export declare const npmVersion: (version: string) => string;
37
+ /** The year and month a release is published in, which the version names. UTC, as CI is. */
38
+ export declare const monthOf: (on?: Date) => {
39
+ year: number;
40
+ month: number;
41
+ };
42
+ /** The first release of the month: `26.09.01-alpha.0`, or with `false`, stable. */
43
+ export declare const firstRelease: (on?: Date, id?: string | false) => string;
44
+ export interface NextOptions {
45
+ /** Whether `current` has been published already. */
46
+ published: boolean;
47
+ on?: Date;
48
+ /** Move to this stage — alpha, beta, rc. */
49
+ id?: string;
50
+ /** Leave the prerelease stages: this release is stable. */
51
+ stable?: boolean;
52
+ }
53
+ /**
54
+ * The version a distribution publishes next.
55
+ *
56
+ * Published, it moves on: the next attempt at the same stage, the next stage
57
+ * when one is asked for, stable with `stable`, or — once stable — the month's
58
+ * next release. Not yet published, it stays what it is, unless a stage is
59
+ * asked for. Either way, a version from a month already over becomes this
60
+ * month's first release, at the stage it was at or the one asked for.
61
+ */
62
+ export declare const nextCalendarVersion: (current: string, options: NextOptions) => string;
63
+ /** Whether `version` names a month before the one it is now. */
64
+ export declare const isFromPastMonth: (version: string, on?: Date) => boolean;
65
+ //# sourceMappingURL=calver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calver.d.ts","sourceRoot":"","sources":["../src/calver.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAA;CACvD;AAID,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,eAAe,GAAG,SAYxE,CAAA;AAID,eAAO,MAAM,qBAAqB,GAAI,SAAS,eAAe,KAAG,MAEqB,CAAA;AAEtF,wFAAwF;AACxF,eAAO,MAAM,qBAAqB,GAAI,SAAS,MAAM,KAAG,eASvD,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,KAAG,MAI5C,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,OAAO,GAAI,KAAI,IAAiB,KAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAG3E,CAAA;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,GAAI,KAAI,IAAiB,EAAE,KAAI,MAAM,GAAG,KAAe,KAAG,MACoB,CAAA;AAEvG,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAA;IAClB,EAAE,CAAC,EAAE,IAAI,CAAA;IACT,4CAA4C;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAKD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,EAAE,SAAS,WAAW,KAAG,MAqD3E,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,EAAE,KAAI,IAAiB,KAAG,OAKxE,CAAA"}
package/dist/calver.js ADDED
@@ -0,0 +1,115 @@
1
+ import semver from 'semver';
2
+ const PATTERN = /^(\d{2})\.(\d{2})\.(\d{2,})(?:-([a-z]+)\.(\d+))?$/;
3
+ export const parseCalendarVersion = (version) => {
4
+ const match = PATTERN.exec(version);
5
+ if (!match)
6
+ return undefined;
7
+ const [, year, month, release, id, number] = match;
8
+ const parsed = {
9
+ year: Number(year),
10
+ month: Number(month),
11
+ release: Number(release),
12
+ prerelease: id ? { id, number: Number(number) } : undefined,
13
+ };
14
+ if (parsed.month < 1 || parsed.month > 12 || parsed.release < 1)
15
+ return undefined;
16
+ return parsed;
17
+ };
18
+ const pad = (value) => String(value).padStart(2, '0');
19
+ export const formatCalendarVersion = (version) => `${pad(version.year)}.${pad(version.month)}.${pad(version.release)}` +
20
+ (version.prerelease ? `-${version.prerelease.id}.${version.prerelease.number}` : '');
21
+ /** Throw unless `version` is a distribution's; the message says what one looks like. */
22
+ export const assertCalendarVersion = (version) => {
23
+ const parsed = parseCalendarVersion(version);
24
+ if (!parsed) {
25
+ throw new Error(`${version} is not a distribution version. One is YY.MM.NN — the year, the month, ` +
26
+ 'and which release of the month — with a stage until it is stable: 26.09.01-alpha.0.');
27
+ }
28
+ return parsed;
29
+ };
30
+ /** How npm spells it: the same version, without the zeros semver does not allow. */
31
+ export const npmVersion = (version) => {
32
+ const cleaned = semver.clean(version, { loose: true });
33
+ if (!cleaned)
34
+ throw new Error(`${version} is not a version npm can publish.`);
35
+ return cleaned;
36
+ };
37
+ /** The year and month a release is published in, which the version names. UTC, as CI is. */
38
+ export const monthOf = (on = new Date()) => ({
39
+ year: on.getUTCFullYear() % 100,
40
+ month: on.getUTCMonth() + 1,
41
+ });
42
+ /** The first release of the month: `26.09.01-alpha.0`, or with `false`, stable. */
43
+ export const firstRelease = (on = new Date(), id = 'alpha') => formatCalendarVersion({ ...monthOf(on), release: 1, prerelease: id ? { id, number: 0 } : undefined });
44
+ const compareStages = (a, b) => semver.compare(`1.0.0-${a}.0`, `1.0.0-${b}.0`);
45
+ /**
46
+ * The version a distribution publishes next.
47
+ *
48
+ * Published, it moves on: the next attempt at the same stage, the next stage
49
+ * when one is asked for, stable with `stable`, or — once stable — the month's
50
+ * next release. Not yet published, it stays what it is, unless a stage is
51
+ * asked for. Either way, a version from a month already over becomes this
52
+ * month's first release, at the stage it was at or the one asked for.
53
+ */
54
+ export const nextCalendarVersion = (current, options) => {
55
+ const version = assertCalendarVersion(current);
56
+ const { published, id, stable = false } = options;
57
+ const now = monthOf(options.on);
58
+ if (id && stable)
59
+ throw new Error('Ask for a stage or for stable, not both.');
60
+ const newMonth = now.year > version.year || (now.year === version.year && now.month > version.month);
61
+ if (!newMonth && (now.year !== version.year || now.month !== version.month)) {
62
+ throw new Error(`${current} is in a month still to come; it cannot be moved on from yet.`);
63
+ }
64
+ const stageAfter = stable ? undefined : (id ?? version.prerelease?.id);
65
+ if (newMonth) {
66
+ return formatCalendarVersion({
67
+ ...now,
68
+ release: 1,
69
+ prerelease: stageAfter ? { id: stageAfter, number: 0 } : undefined,
70
+ });
71
+ }
72
+ const same = { ...version };
73
+ const pre = version.prerelease;
74
+ // Moving to another stage of the same release: only ever forward.
75
+ const toStage = (next) => {
76
+ if (pre && compareStages(next, pre.id) < 0) {
77
+ throw new Error(`${current} is at ${pre.id} already, which is past ${next}.`);
78
+ }
79
+ if (!pre && !published) {
80
+ // An unpublished stable release can still be taken back to a stage.
81
+ return formatCalendarVersion({ ...same, prerelease: { id: next, number: 0 } });
82
+ }
83
+ if (!pre) {
84
+ return formatCalendarVersion({ ...same, release: same.release + 1, prerelease: { id: next, number: 0 } });
85
+ }
86
+ return formatCalendarVersion({ ...same, prerelease: { id: next, number: 0 } });
87
+ };
88
+ if (!published) {
89
+ if (stable)
90
+ return formatCalendarVersion({ ...same, prerelease: undefined });
91
+ if (id && id !== pre?.id)
92
+ return toStage(id);
93
+ return current;
94
+ }
95
+ if (stable) {
96
+ if (!pre)
97
+ return formatCalendarVersion({ ...same, release: same.release + 1 });
98
+ return formatCalendarVersion({ ...same, prerelease: undefined });
99
+ }
100
+ if (id && id !== pre?.id)
101
+ return toStage(id);
102
+ if (pre)
103
+ return formatCalendarVersion({ ...same, prerelease: { id: pre.id, number: pre.number + 1 } });
104
+ // Stable, and published: the month's next release.
105
+ return formatCalendarVersion({ ...same, release: same.release + 1 });
106
+ };
107
+ /** Whether `version` names a month before the one it is now. */
108
+ export const isFromPastMonth = (version, on = new Date()) => {
109
+ const parsed = parseCalendarVersion(version);
110
+ if (!parsed)
111
+ return false;
112
+ const now = monthOf(on);
113
+ return now.year > parsed.year || (now.year === parsed.year && now.month > parsed.month);
114
+ };
115
+ //# sourceMappingURL=calver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calver.js","sourceRoot":"","sources":["../src/calver.ts"],"names":[],"mappings":"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"}