@dbx-tools/projen 0.6.39 → 0.6.41
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/index.ts +2 -2
- package/package.json +25 -23
- package/src/bun-app.ts +164 -0
- package/src/packages.ts +6 -4
- package/src/pnpm-workspace.ts +73 -12
- package/src/project.ts +94 -48
- package/src/release.ts +114 -38
- package/src/tags.ts +28 -14
- package/tasks/bump.ts +41 -87
- package/tasks/publish.ts +156 -0
- package/src/vite.ts +0 -99
package/tasks/bump.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env -S
|
|
1
|
+
#!/usr/bin/env -S bun
|
|
2
2
|
/**
|
|
3
3
|
* `projen bump` - synth, compute the next release version, then (by default)
|
|
4
4
|
* commit, tag, and push it. Pushing the tag is what triggers the release
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* - the local `package.json` version,
|
|
12
12
|
* then incremented by `--level` (patch | minor | major; default patch).
|
|
13
13
|
*
|
|
14
|
-
* `--sibling <dir>:<tagPrefix>` (repeatable) releases
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* `--sibling <dir>:<tagPrefix>` (repeatable) releases an in-repo project that
|
|
15
|
+
* publishes on its OWN tag namespace (e.g. `projen/`, tagged `projen-v*`) at the
|
|
16
|
+
* SAME version as the root, in the same run: its manifest version is stamped, its
|
|
17
17
|
* `<tagPrefix><version>` tag is cut and pushed (triggering its own workflow), and
|
|
18
18
|
* it is included in the local-registry publish. Taking the base version from
|
|
19
19
|
* every prefix at once is what keeps the two in lockstep: the engine sat at
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
*
|
|
33
33
|
* `--local-registry <value>` publishes the just-tagged version to a LOCAL
|
|
34
34
|
* registry (e.g. a verdaccio) right after the git tag is pushed - so a local
|
|
35
|
-
* `
|
|
35
|
+
* `bun run bump` both fires the GitHub release (public npm) and populates your
|
|
36
36
|
* local registry. Values:
|
|
37
37
|
* - `auto` (default): publish only when `npm config get registry` is a
|
|
38
38
|
* loopback host (`localhost` / `127.0.0.0/8` / `::1`); otherwise skip.
|
|
@@ -44,15 +44,10 @@ import { log, net } from "@dbx-tools/shared-core";
|
|
|
44
44
|
import { Command, Option } from "commander";
|
|
45
45
|
import { chmodSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
46
46
|
import { resolve } from "node:path";
|
|
47
|
+
import { fileURLToPath } from "node:url";
|
|
47
48
|
|
|
48
49
|
const logger = log.logger("projen:bump");
|
|
49
50
|
const LEVELS = ["patch", "minor", "major"] as const;
|
|
50
|
-
const DEPENDENCY_FIELDS = [
|
|
51
|
-
"dependencies",
|
|
52
|
-
"devDependencies",
|
|
53
|
-
"optionalDependencies",
|
|
54
|
-
"peerDependencies",
|
|
55
|
-
] as const;
|
|
56
51
|
type Level = (typeof LEVELS)[number];
|
|
57
52
|
|
|
58
53
|
/** A standalone in-repo project released alongside the root, on its own tag prefix. */
|
|
@@ -126,39 +121,30 @@ function readPackageVersion(pkgPath: string): [number, number, number] {
|
|
|
126
121
|
}
|
|
127
122
|
|
|
128
123
|
/**
|
|
129
|
-
* Write `version` into a manifest projen owns
|
|
130
|
-
*
|
|
131
|
-
*
|
|
124
|
+
* Write ONLY the `version` field into a manifest projen owns (read-only, so
|
|
125
|
+
* bracketed by a chmod that restores the mode). Used for the ROOT and the
|
|
126
|
+
* standalone `projen/` sibling so the committed release marks the version.
|
|
127
|
+
*
|
|
128
|
+
* It deliberately does NOT rewrite `@scope/*` dep ranges. `projen/` is a workspace
|
|
129
|
+
* member with `workspace:*` sibling deps; baking a `^version` for the just-bumped
|
|
130
|
+
* (not-yet-published) version into the COMMITTED manifest breaks the release
|
|
131
|
+
* workflow's initial `bun install`, which checks out that commit before anything
|
|
132
|
+
* is published. Sibling versions are resolved transiently at publish time instead:
|
|
133
|
+
* `projen-release` runs `tasks/publish.ts --stamp-only` (set versions + refresh
|
|
134
|
+
* lockfile), then `bun publish` in `projen/` strips `workspace:*` to those versions.
|
|
132
135
|
*/
|
|
133
|
-
function writeManifestVersion(pkgPath: string, version: string
|
|
136
|
+
function writeManifestVersion(pkgPath: string, version: string): void {
|
|
134
137
|
const { mode } = statSync(pkgPath);
|
|
135
138
|
chmodSync(pkgPath, mode | 0o200);
|
|
136
139
|
try {
|
|
137
140
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>;
|
|
138
141
|
pkg.version = version;
|
|
139
|
-
if (dependencyScope) {
|
|
140
|
-
for (const field of DEPENDENCY_FIELDS) {
|
|
141
|
-
const dependencies = pkg[field];
|
|
142
|
-
if (!dependencies || typeof dependencies !== "object") continue;
|
|
143
|
-
for (const name of Object.keys(dependencies)) {
|
|
144
|
-
if (name.startsWith(dependencyScope)) {
|
|
145
|
-
(dependencies as Record<string, string>)[name] = `^${version}`;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
142
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
151
143
|
} finally {
|
|
152
144
|
chmodSync(pkgPath, mode);
|
|
153
145
|
}
|
|
154
146
|
}
|
|
155
147
|
|
|
156
|
-
/** Scoped package prefix (`@scope/`) shared by the root's release packages. */
|
|
157
|
-
function releaseDependencyScope(pkgPath: string): string | undefined {
|
|
158
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { name?: string };
|
|
159
|
-
return /^@[^/]+\//.exec(pkg.name ?? "")?.[0];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
148
|
/**
|
|
163
149
|
* Resolve the `--local-registry` value to a registry URL to publish to, or
|
|
164
150
|
* `undefined` to skip. `false` skips; a URL is used as-is; `auto` uses the
|
|
@@ -217,7 +203,6 @@ program
|
|
|
217
203
|
}) => {
|
|
218
204
|
const pkgPath = resolve(process.cwd(), "package.json");
|
|
219
205
|
if (!existsSync(pkgPath)) throw new Error(`no package.json in ${process.cwd()}`);
|
|
220
|
-
const dependencyScope = releaseDependencyScope(pkgPath);
|
|
221
206
|
|
|
222
207
|
const siblings = opts.sibling.map((s) => ({ ...s, pkgPath: resolve(s.dir, "package.json") }));
|
|
223
208
|
for (const s of siblings) {
|
|
@@ -228,7 +213,7 @@ program
|
|
|
228
213
|
// manifests, workspace file, tasks, ...) rather than a stale one.
|
|
229
214
|
if (opts.synth) {
|
|
230
215
|
logger.info("synthesizing (projen)");
|
|
231
|
-
exec.spawnSync("
|
|
216
|
+
exec.spawnSync("bun", [".projenrc.ts"], {
|
|
232
217
|
cwd: process.cwd(),
|
|
233
218
|
stdout: "inherit",
|
|
234
219
|
stderr: "inherit",
|
|
@@ -265,7 +250,7 @@ program
|
|
|
265
250
|
|
|
266
251
|
if (opts.version) {
|
|
267
252
|
writeManifestVersion(pkgPath, version);
|
|
268
|
-
for (const s of siblings) writeManifestVersion(s.pkgPath, version
|
|
253
|
+
for (const s of siblings) writeManifestVersion(s.pkgPath, version);
|
|
269
254
|
const also = siblings.length ? ` (and ${siblings.map((s) => s.dir).join(", ")})` : "";
|
|
270
255
|
logger.info(`wrote version ${version} to package.json${also}`);
|
|
271
256
|
}
|
|
@@ -304,57 +289,26 @@ program
|
|
|
304
289
|
}
|
|
305
290
|
if (publishToLocalRegistry) {
|
|
306
291
|
logger.info(`publishing ${version} to local registry ${localRegistry}`);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
"npm",
|
|
328
|
-
"version",
|
|
329
|
-
version,
|
|
330
|
-
"--no-git-tag-version",
|
|
331
|
-
"--allow-same-version",
|
|
332
|
-
]);
|
|
333
|
-
// Provenance is opt-in (see `.projenrc.ts`): the generated
|
|
334
|
-
// `publishConfig` omits it, so local (verdaccio) publishes never try to
|
|
335
|
-
// attest. CI turns it on with `npm_config_provenance=true`.
|
|
336
|
-
runInRepo("pnpm", [
|
|
337
|
-
"-r",
|
|
338
|
-
"publish",
|
|
339
|
-
"--registry",
|
|
340
|
-
localRegistry,
|
|
341
|
-
"--no-git-checks",
|
|
342
|
-
"--access",
|
|
343
|
-
"public",
|
|
344
|
-
]);
|
|
345
|
-
// `pnpm -r` cannot see a sibling (not a workspace member), so publish each
|
|
346
|
-
// one on its own. Skipping this is what left a local registry serving a
|
|
347
|
-
// current CLI against a months-old engine.
|
|
348
|
-
for (const s of siblings) {
|
|
349
|
-
runIn(s.dir, "pnpm", [
|
|
350
|
-
"publish",
|
|
351
|
-
"--registry",
|
|
352
|
-
localRegistry,
|
|
353
|
-
"--no-git-checks",
|
|
354
|
-
"--access",
|
|
355
|
-
"public",
|
|
356
|
-
]);
|
|
357
|
-
}
|
|
292
|
+
// Mirror the CI `release` workflow via the shared publish task: it sets
|
|
293
|
+
// the release version on every workspace member (`bun pm pkg set`; they
|
|
294
|
+
// keep `0.0.0` on disk, projen-owned, so it unlocks each briefly), then
|
|
295
|
+
// `bun publish`es each non-private one - and bun natively strips the
|
|
296
|
+
// `workspace:`/`catalog:` protocols in the packed tarball, resolving each
|
|
297
|
+
// to the version just set. The next `projen` synth restores the manifests
|
|
298
|
+
// to `0.0.0`; the release version lives in the git tag. Provenance is off
|
|
299
|
+
// for a local registry (no OIDC), so `NPM_CONFIG_PROVENANCE` is unset.
|
|
300
|
+
// `publish.ts` is this task's SIBLING in the engine's `tasks/` dir; resolve
|
|
301
|
+
// it off `import.meta.url` (works whether the engine is source-linked in-repo
|
|
302
|
+
// or installed under node_modules) rather than a repo-relative `tasks/...`
|
|
303
|
+
// that only exists inside `projen/`.
|
|
304
|
+
const publishScript = fileURLToPath(new URL("./publish.ts", import.meta.url));
|
|
305
|
+
exec.spawnSync("bun", [publishScript, version, "--registry", localRegistry], {
|
|
306
|
+
cwd: process.cwd(),
|
|
307
|
+
stdout: "inherit",
|
|
308
|
+
stderr: "inherit",
|
|
309
|
+
stdin: "ignore",
|
|
310
|
+
check: true,
|
|
311
|
+
});
|
|
358
312
|
logger.success(`published ${version} to ${localRegistry}`);
|
|
359
313
|
}
|
|
360
314
|
|
|
@@ -364,7 +318,7 @@ program
|
|
|
364
318
|
// manifest finish the bump in lockstep.
|
|
365
319
|
if (opts.version) {
|
|
366
320
|
writeManifestVersion(pkgPath, version);
|
|
367
|
-
for (const s of siblings) writeManifestVersion(s.pkgPath, version
|
|
321
|
+
for (const s of siblings) writeManifestVersion(s.pkgPath, version);
|
|
368
322
|
logger.info(`synchronized release manifests at ${version}`);
|
|
369
323
|
}
|
|
370
324
|
},
|
package/tasks/publish.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun
|
|
2
|
+
/**
|
|
3
|
+
* `bun tasks/publish.ts <version> [--registry <url>] [--exclude <dir>] [--dry-run]`
|
|
4
|
+
* - set a release version on every workspace member and publish each non-private
|
|
5
|
+
* one with `bun publish`.
|
|
6
|
+
*
|
|
7
|
+
* Bun has no `pnpm -r publish`, so this loop is the recursive-publish stand-in.
|
|
8
|
+
* It leans on native bun for everything bun already does:
|
|
9
|
+
*
|
|
10
|
+
* - **version stamping** is `bun pm pkg set version=<version>` per member (bun's
|
|
11
|
+
* native package.json editor - idempotent, edits only that dir's manifest, no
|
|
12
|
+
* git side effects; `bun pm version` is for bumping and errors on an unchanged
|
|
13
|
+
* version, so `pkg set` is the right tool for an exact release value);
|
|
14
|
+
* - **`workspace:` / `catalog:` rewriting** is NOT done here - `bun publish`
|
|
15
|
+
* strips both protocols in the PACKED tarball, resolving `workspace:*` to the
|
|
16
|
+
* sibling's version and `catalog:` to the root catalog entry. (Verified: a
|
|
17
|
+
* packed manifest shows `"@scope/x": "<version>"` and the real catalog range,
|
|
18
|
+
* while the on-disk manifest keeps the protocols.) Setting each member's
|
|
19
|
+
* version first is the only prerequisite, so a sibling resolves the release
|
|
20
|
+
* version rather than the disk default of `0.0.0`;
|
|
21
|
+
* - **`publishConfig` substitution** (compiled `lib/` entry points) and the
|
|
22
|
+
* `prepack` (compile) run are `bun publish`'s own pack behavior.
|
|
23
|
+
*
|
|
24
|
+
* `--dry-run` forwards to `bun publish`: it packs + validates (running prepack)
|
|
25
|
+
* but uploads nothing, so the `release` workflow is testable end-to-end via a
|
|
26
|
+
* `workflow_dispatch` run without anything reaching npm. `--registry` targets a
|
|
27
|
+
* non-default registry (a local verdaccio); `--exclude <dir>` (repeatable,
|
|
28
|
+
* repo-relative) skips a member that releases on its OWN tag namespace (e.g.
|
|
29
|
+
* `projen`, published by `projen-release`, not the main `release`).
|
|
30
|
+
*
|
|
31
|
+
* The disk manifests normally carry `version: 0.0.0` (projen owns them, read-only);
|
|
32
|
+
* this unlocks each only long enough to set the version + publish. The next
|
|
33
|
+
* `projen` synth restores them - the release version lives in the git tag.
|
|
34
|
+
*/
|
|
35
|
+
import { chmodSync, existsSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
36
|
+
import { dirname, join, resolve } from "node:path";
|
|
37
|
+
import { exec } from "@dbx-tools/core";
|
|
38
|
+
import { log } from "@dbx-tools/shared-core";
|
|
39
|
+
import { parse } from "yaml";
|
|
40
|
+
|
|
41
|
+
const logger = log.logger("dbx-tools:publish");
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Workspace member dirs (absolute), read from the root `pnpm-workspace.yaml` - the
|
|
45
|
+
* file the engine keeps for the Databricks Apps pnpm deploy, which also lists every
|
|
46
|
+
* bun workspace member. (`bun pm ls` reports installed deps, not the member globs,
|
|
47
|
+
* so the manifest list is the source of truth.)
|
|
48
|
+
*/
|
|
49
|
+
function workspaceMembers(root: string): string[] {
|
|
50
|
+
const file = join(root, "pnpm-workspace.yaml");
|
|
51
|
+
if (!existsSync(file)) return [];
|
|
52
|
+
const doc = parse(readFileSync(file, "utf8")) as { packages?: string[] } | null;
|
|
53
|
+
return (doc?.packages ?? []).map((m) => resolve(root, m));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Spawn `command` in `cwd` with `PATH` overridden, failing the task on non-zero. */
|
|
57
|
+
function run(cwd: string, command: string, args: string[], path: string): void {
|
|
58
|
+
exec.spawnSync(command, args, {
|
|
59
|
+
cwd,
|
|
60
|
+
stdout: "inherit",
|
|
61
|
+
stderr: "inherit",
|
|
62
|
+
stdin: "ignore",
|
|
63
|
+
check: true,
|
|
64
|
+
env: { ...process.env, PATH: path },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Make a projen-readonly manifest writable so `bun pm pkg set` / `bun publish` can edit it. */
|
|
69
|
+
function unlockManifest(pkgPath: string): void {
|
|
70
|
+
chmodSync(pkgPath, statSync(pkgPath).mode | 0o200);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* PATH with the workspace-root `node_modules/.bin` prepended. `bun publish` runs
|
|
75
|
+
* each package's `prepack` (compile) through projen's dax shell, which resolves
|
|
76
|
+
* `tsc` off PATH - but under the hoisted linker `tsc` lives ONLY in the root
|
|
77
|
+
* `.bin`, not a per-package one, so without this the compile fails with
|
|
78
|
+
* `dax: tsc: command not found`. (In CI `bun install` already puts it there; this
|
|
79
|
+
* makes the task self-sufficient when invoked directly too.)
|
|
80
|
+
*/
|
|
81
|
+
function enrichedPath(root: string): string {
|
|
82
|
+
const binDir = join(root, "node_modules", ".bin");
|
|
83
|
+
const current = process.env.PATH ?? "";
|
|
84
|
+
return current.split(":").includes(binDir) ? current : `${binDir}:${current}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const [version, ...rest] = process.argv.slice(2);
|
|
88
|
+
if (!version) {
|
|
89
|
+
logger.error(
|
|
90
|
+
"usage: bun tasks/publish.ts <version> [--registry <url>] [--exclude <dir>] [--dry-run]",
|
|
91
|
+
);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const registryIdx = rest.indexOf("--registry");
|
|
95
|
+
const registry = registryIdx >= 0 ? rest[registryIdx + 1] : undefined;
|
|
96
|
+
const dryRun = rest.includes("--dry-run");
|
|
97
|
+
// `--stamp-only`: set versions + refresh the lockfile, then STOP (no publish).
|
|
98
|
+
// The standalone `projen-release` uses this to version-stamp the workspace so its
|
|
99
|
+
// own `bun publish` (run separately, in `projen/`) resolves `workspace:*` siblings
|
|
100
|
+
// to the release version; publishing every member here would double-publish them.
|
|
101
|
+
const stampOnly = rest.includes("--stamp-only");
|
|
102
|
+
const excluded = new Set(
|
|
103
|
+
rest.reduce<string[]>((acc, arg, i) => (arg === "--exclude" ? [...acc, rest[i + 1]] : acc), []),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const root = process.cwd();
|
|
107
|
+
const path = enrichedPath(root);
|
|
108
|
+
const members = workspaceMembers(root)
|
|
109
|
+
.filter((dir) => existsSync(join(dir, "package.json")))
|
|
110
|
+
.filter((dir) => !excluded.has(resolve(root, dir).replace(`${resolve(root)}/`, "")));
|
|
111
|
+
|
|
112
|
+
// Set the version on EVERY member first (native `bun pm pkg set`), so a sibling
|
|
113
|
+
// published later has its `workspace:*` dep resolved to the release version - not
|
|
114
|
+
// the disk default of 0.0.0 - by `bun publish`'s own protocol rewriting.
|
|
115
|
+
logger.info(`setting ${version} across ${members.length} members`);
|
|
116
|
+
for (const dir of members) {
|
|
117
|
+
unlockManifest(join(dir, "package.json"));
|
|
118
|
+
run(dir, "bun", ["pm", "pkg", "set", `version=${version}`], path);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Refresh the lockfile so `bun publish` resolves each `workspace:*` to the version
|
|
122
|
+
// just set. `bun publish`/`pm pack` reads the workspace version from the LOCKFILE,
|
|
123
|
+
// not the live manifest, and a plain `bun install` (even `--force`) does NOT
|
|
124
|
+
// re-resolve it after only a version-field change - deleting the lockfile first
|
|
125
|
+
// does. Without this every `workspace:*` dep would publish as the stale `0.0.0`.
|
|
126
|
+
const lockfile = join(root, "bun.lock");
|
|
127
|
+
if (existsSync(lockfile)) rmSync(lockfile);
|
|
128
|
+
logger.info("refreshing lockfile so workspace deps resolve to the release version");
|
|
129
|
+
run(root, "bun", ["install"], path);
|
|
130
|
+
|
|
131
|
+
if (stampOnly) {
|
|
132
|
+
logger.success(`stamped ${members.length} members @ ${version} (no publish)`);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const publishArgs = [
|
|
137
|
+
"--access",
|
|
138
|
+
"public",
|
|
139
|
+
...(registry ? ["--registry", registry] : []),
|
|
140
|
+
...(dryRun ? ["--dry-run"] : []),
|
|
141
|
+
];
|
|
142
|
+
let published = 0;
|
|
143
|
+
for (const dir of members) {
|
|
144
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
145
|
+
name?: string;
|
|
146
|
+
private?: boolean;
|
|
147
|
+
};
|
|
148
|
+
if (pkg.private) {
|
|
149
|
+
logger.info(`skip private ${pkg.name ?? dirname(dir)}`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
logger.info(`${dryRun ? "dry-run publishing" : "publishing"} ${pkg.name} @ ${version}`);
|
|
153
|
+
run(dir, "bun", ["publish", ...publishArgs], path);
|
|
154
|
+
published += 1;
|
|
155
|
+
}
|
|
156
|
+
logger.success(`${dryRun ? "dry-run: packed" : "published"} ${published} packages @ ${version}`);
|
package/src/vite.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `vite.config.ts` as a first-class projen file component.
|
|
3
|
-
*
|
|
4
|
-
* {@link ViteConfigFile} extends projen's `TextFile` and emits a generated,
|
|
5
|
-
* read-only Vite config: the React plugin plus a runtime OVERRIDE chain. At Vite
|
|
6
|
-
* startup the generated config looks for each unmanaged override module sitting
|
|
7
|
-
* beside it (see {@link DEFAULT_VITE_OVERRIDES}) and, when present, merges that
|
|
8
|
-
* module's default export over the generated config with Vite's `mergeConfig` - in
|
|
9
|
-
* listed order, so later files win and absent ones are skipped. A package thus
|
|
10
|
-
* tweaks Vite WITHOUT editing the projen-owned file.
|
|
11
|
-
*
|
|
12
|
-
* An override may be written in TypeScript or JavaScript. The generated config
|
|
13
|
-
* reaches it through a dynamic `import()` of a runtime-computed URL, which Vite's
|
|
14
|
-
* config bundling cannot inline and so leaves for Node to execute - and Node
|
|
15
|
-
* strips types from a `.ts` file outside `node_modules` on its own. Being a
|
|
16
|
-
* package-ROOT file (not under `src/`), neither the generated `vite.config.ts`
|
|
17
|
-
* nor the override is in the package's `tsconfig` `include`, so their `node:*`
|
|
18
|
-
* usage never trips the `ui` package's `compile` under the DOM-only tsconfig.
|
|
19
|
-
*/
|
|
20
|
-
import { type Project, TextFile } from "projen";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Default unmanaged override modules, merged over the generated config in order
|
|
24
|
-
* (later wins, absent files skipped). Both extensions are accepted so an existing
|
|
25
|
-
* JavaScript override keeps working; the TypeScript one is listed last so it wins
|
|
26
|
-
* where a package is mid-migration and still has both.
|
|
27
|
-
*
|
|
28
|
-
* Exported because ESLint has to ignore them: an override is a package-ROOT file
|
|
29
|
-
* outside any `src/**` tsconfig include, so the type-aware parser cannot resolve
|
|
30
|
-
* it to a project - the same reason the generated `vite.config.ts` is ignored.
|
|
31
|
-
*/
|
|
32
|
-
export const DEFAULT_VITE_OVERRIDES = ["vite.config.override.js", "vite.config.override.ts"];
|
|
33
|
-
|
|
34
|
-
/** Render the generated `vite.config.ts` source with the override chain inlined. */
|
|
35
|
-
function renderViteConfig(overridePaths: string[]): string {
|
|
36
|
-
const overrides = overridePaths.map((path) => ` ${JSON.stringify(path)},`).join("\n");
|
|
37
|
-
return String.raw`
|
|
38
|
-
import { existsSync } from "node:fs";
|
|
39
|
-
import react from "@vitejs/plugin-react";
|
|
40
|
-
import {
|
|
41
|
-
defineConfig,
|
|
42
|
-
mergeConfig,
|
|
43
|
-
type ConfigEnv,
|
|
44
|
-
type UserConfig,
|
|
45
|
-
type UserConfigExport,
|
|
46
|
-
} from "vite";
|
|
47
|
-
|
|
48
|
-
// Unmanaged override modules (relative to this file), merged over the generated
|
|
49
|
-
// config in order - later wins, absent files are skipped.
|
|
50
|
-
const OVERRIDE_FILES = [
|
|
51
|
-
${overrides}
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
async function resolveConfig(
|
|
55
|
-
config: UserConfigExport,
|
|
56
|
-
env: ConfigEnv,
|
|
57
|
-
): Promise<UserConfig> {
|
|
58
|
-
if (typeof config === "function") {
|
|
59
|
-
return await config(env);
|
|
60
|
-
}
|
|
61
|
-
return await config;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export default defineConfig(async (configEnv: ConfigEnv) => {
|
|
65
|
-
let config: UserConfig = {
|
|
66
|
-
plugins: [react()],
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
for (const file of OVERRIDE_FILES) {
|
|
70
|
-
const overrideUrl = new URL(file, import.meta.url);
|
|
71
|
-
if (!existsSync(overrideUrl)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const overrideModule = await import(overrideUrl.href);
|
|
75
|
-
const override = await resolveConfig(
|
|
76
|
-
overrideModule.default as UserConfigExport,
|
|
77
|
-
configEnv,
|
|
78
|
-
);
|
|
79
|
-
config = mergeConfig(config, override);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return config;
|
|
83
|
-
});
|
|
84
|
-
`.trimStart();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* A projen-owned, read-only `vite.config.ts` (React + the runtime override merge
|
|
89
|
-
* chain described in the module docstring).
|
|
90
|
-
*/
|
|
91
|
-
export class ViteConfigFile extends TextFile {
|
|
92
|
-
constructor(project: Project) {
|
|
93
|
-
super(project, "vite.config.ts", {
|
|
94
|
-
marker: true,
|
|
95
|
-
readonly: true,
|
|
96
|
-
lines: renderViteConfig(DEFAULT_VITE_OVERRIDES).split("\n"),
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|