@cosmicdrift/kumiko-dev-server 0.304.0 → 0.305.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.305.0",
|
|
4
4
|
"description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding, codegen. Its compose-stacks/env-schema subpaths are consumed at prod boot too — see @cosmicdrift/kumiko-server-runtime for the prod runner itself.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"kumiko-upgrade": "./bin/kumiko-upgrade.ts"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
63
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
64
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
65
|
-
"@cosmicdrift/kumiko-server-runtime": "0.
|
|
62
|
+
"@cosmicdrift/kumiko-bundled-features": "0.305.0",
|
|
63
|
+
"@cosmicdrift/kumiko-framework": "0.305.0",
|
|
64
|
+
"@cosmicdrift/kumiko-headless": "0.305.0",
|
|
65
|
+
"@cosmicdrift/kumiko-server-runtime": "0.305.0",
|
|
66
66
|
"ts-morph": "^28.0.0"
|
|
67
67
|
},
|
|
68
68
|
"publishConfig": {
|
|
@@ -17,7 +17,11 @@ import {
|
|
|
17
17
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
18
18
|
import { TestUsers } from "@cosmicdrift/kumiko-framework/stack";
|
|
19
19
|
import { z } from "zod";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createKumikoServer,
|
|
22
|
+
type KumikoServerHandle,
|
|
23
|
+
STYLESHEET_WATCH_ENV,
|
|
24
|
+
} from "../create-kumiko-server";
|
|
21
25
|
|
|
22
26
|
const TENANT_ID = "00000000-0000-4000-8000-000000000001" as TenantId;
|
|
23
27
|
|
|
@@ -471,6 +475,45 @@ describe("createKumikoServer — stylesheet tailwind failure (graceful)", () =>
|
|
|
471
475
|
});
|
|
472
476
|
});
|
|
473
477
|
|
|
478
|
+
describe("createKumikoServer — stylesheet watch disabled via env", () => {
|
|
479
|
+
test("KUMIKO_DEV_STYLESHEET_WATCH=0 serves the built CSS and never spawns a --watch process", async () => {
|
|
480
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "kumiko-css-no-watch-"));
|
|
481
|
+
const entryCss = join(tmpDir, "styles.css");
|
|
482
|
+
// Plain CSS, no `@import "tailwindcss"` — the marker property survives
|
|
483
|
+
// the Tailwind CLI/lightningcss pass unchanged, unlike a color value
|
|
484
|
+
// which lightningcss may renormalize.
|
|
485
|
+
writeFileSync(entryCss, ".kumiko-probe { --probe-marker: 42; }\n");
|
|
486
|
+
const previousWatchEnv = process.env[STYLESHEET_WATCH_ENV];
|
|
487
|
+
process.env[STYLESHEET_WATCH_ENV] = "0";
|
|
488
|
+
try {
|
|
489
|
+
handle = await createKumikoServer({
|
|
490
|
+
features: [probeFeature],
|
|
491
|
+
port: 0,
|
|
492
|
+
installSignalHandlers: false,
|
|
493
|
+
stylesheet: entryCss,
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
const cssRes = await handle.fetch(new Request("http://localhost/styles.css"));
|
|
497
|
+
expect(cssRes.status).toBe(200);
|
|
498
|
+
const css = await cssRes.text();
|
|
499
|
+
expect(css).toContain("--probe-marker: 42");
|
|
500
|
+
|
|
501
|
+
// -ww: unwrapped, full argv — a truncated args column would make the
|
|
502
|
+
// negative assert below pass vacuously. The pid check proves ps
|
|
503
|
+
// actually returned live process rows on this machine/CI runner.
|
|
504
|
+
const ps = Bun.spawnSync(["ps", "-A", "-ww", "-o", "pid=,args="]);
|
|
505
|
+
const psOutput = ps.stdout.toString();
|
|
506
|
+
const lines = psOutput.split("\n");
|
|
507
|
+
expect(lines.some((line) => line.includes(String(process.pid)))).toBe(true);
|
|
508
|
+
expect(lines.some((line) => line.includes(entryCss) && line.includes("--watch"))).toBe(false);
|
|
509
|
+
} finally {
|
|
510
|
+
if (previousWatchEnv === undefined) delete process.env[STYLESHEET_WATCH_ENV];
|
|
511
|
+
else process.env[STYLESHEET_WATCH_ENV] = previousWatchEnv;
|
|
512
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
474
517
|
describe("createKumikoServer — real Bun.build (buildClient)", () => {
|
|
475
518
|
test("clientEntry without _buildBundle produces a JS bundle via Bun.build", async () => {
|
|
476
519
|
const tmpDir = mkdtempSync(join(tmpdir(), "kumiko-real-build-"));
|
|
@@ -11,7 +11,11 @@ import { describe, expect, test } from "bun:test";
|
|
|
11
11
|
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
resolveStylesheet,
|
|
16
|
+
resolveStylesheetWatch,
|
|
17
|
+
STYLESHEET_WATCH_ENV,
|
|
18
|
+
} from "../create-kumiko-server";
|
|
15
19
|
|
|
16
20
|
describe("resolveStylesheet", () => {
|
|
17
21
|
test("string → resolved absolute path", () => {
|
|
@@ -108,3 +112,21 @@ describe("resolveStylesheet", () => {
|
|
|
108
112
|
}
|
|
109
113
|
});
|
|
110
114
|
});
|
|
115
|
+
|
|
116
|
+
describe("resolveStylesheetWatch", () => {
|
|
117
|
+
test("an explicit option wins over the env", () => {
|
|
118
|
+
expect(resolveStylesheetWatch({ stylesheetWatch: false }, {})).toBe(false);
|
|
119
|
+
expect(resolveStylesheetWatch({ stylesheetWatch: true }, { [STYLESHEET_WATCH_ENV]: "0" })).toBe(
|
|
120
|
+
true,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("no option, env=0 → false", () => {
|
|
125
|
+
expect(resolveStylesheetWatch({}, { [STYLESHEET_WATCH_ENV]: "0" })).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("no option, no or other env value → true", () => {
|
|
129
|
+
expect(resolveStylesheetWatch({}, {})).toBe(true);
|
|
130
|
+
expect(resolveStylesheetWatch({}, { [STYLESHEET_WATCH_ENV]: "1" })).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
});
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.305.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "createKumikoServer/runDevApp gain stylesheetWatch (env KUMIKO_DEV_STYLESHEET_WATCH=0) to build Tailwind CSS once without a --watch process",
|
|
6
|
+
"detail": "`defineAppE2eConfig` sets `KUMIKO_DEV_STYLESHEET_WATCH=0`, so the dev-server it boots builds CSS once and stops instead of keeping a Tailwind `--watch` process alive. Tailwind v4's watcher subscribes to the whole app cwd recursively with no gitignore filter, so every Playwright artifact write under `test-results/` previously counted as a rebuild trigger."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.301.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -162,18 +162,24 @@ export type CreateKumikoServerOptions = {
|
|
|
162
162
|
readonly js: string;
|
|
163
163
|
readonly map: string;
|
|
164
164
|
}>;
|
|
165
|
-
/** Absolute path to the CSS entry (
|
|
166
|
-
* @import "tailwindcss").
|
|
167
|
-
*
|
|
168
|
-
*
|
|
165
|
+
/** Absolute path to the CSS entry (typically styles.css with
|
|
166
|
+
* @import "tailwindcss"). The dev-server builds it with the Tailwind
|
|
167
|
+
* CLI and serves the result at /styles.css; `stylesheetWatch` decides
|
|
168
|
+
* whether the CLI keeps running with `--watch` afterwards.
|
|
169
169
|
*
|
|
170
|
-
*
|
|
171
|
-
* `@cosmicdrift/kumiko-renderer-web/styles.css
|
|
172
|
-
*
|
|
173
|
-
* ../../packages/renderer-web/src/styles.css hardcoden.
|
|
170
|
+
* When `undefined` and a client entry is set, the app's own
|
|
171
|
+
* src/styles.css wins, otherwise `@cosmicdrift/kumiko-renderer-web/styles.css`
|
|
172
|
+
* resolves via package exports, so no sample hardcodes a monorepo-relative path.
|
|
174
173
|
*
|
|
175
|
-
* `stylesheet: false`
|
|
174
|
+
* `stylesheet: false` disables the CSS pipeline. */
|
|
176
175
|
readonly stylesheet?: string | false;
|
|
176
|
+
/** `true`: the Tailwind CLI keeps running with `--watch` after the
|
|
177
|
+
* initial build. `false`: CSS is built once at boot, no watcher.
|
|
178
|
+
* When `undefined`, env `KUMIKO_DEV_STYLESHEET_WATCH=0` means `false`,
|
|
179
|
+
* anything else `true`. `defineAppE2eConfig` sets that env: Tailwind v4
|
|
180
|
+
* subscribes to the whole app cwd without a gitignore filter, so every
|
|
181
|
+
* Playwright artifact write under test-results/ would trigger a rebuild. */
|
|
182
|
+
readonly stylesheetWatch?: boolean;
|
|
177
183
|
/** Optional HTML template served at `GET /`. The dev-server injects
|
|
178
184
|
* a `<script type="module" src="/client.js">` and a reload-listener
|
|
179
185
|
* snippet into `</body>` if those aren't already there. Defaults to a
|
|
@@ -259,6 +265,10 @@ export type KumikoServerHandle = {
|
|
|
259
265
|
const CSRF_COOKIE = "kumiko_csrf";
|
|
260
266
|
const AUTH_COOKIE = "kumiko_auth";
|
|
261
267
|
|
|
268
|
+
// Duplicated in packages/testing/src/e2e/constants.ts: the Playwright config
|
|
269
|
+
// runs under Node and cannot import this module.
|
|
270
|
+
export const STYLESHEET_WATCH_ENV = "KUMIKO_DEV_STYLESHEET_WATCH";
|
|
271
|
+
|
|
262
272
|
// Reload snippet injected into every page-load so the browser
|
|
263
273
|
// subscribes to /_reload without the HTML needing to hard-code it.
|
|
264
274
|
//
|
|
@@ -661,12 +671,24 @@ export function resolveStylesheet(options: CreateKumikoServerOptions): string |
|
|
|
661
671
|
}
|
|
662
672
|
}
|
|
663
673
|
|
|
664
|
-
//
|
|
665
|
-
//
|
|
666
|
-
//
|
|
667
|
-
//
|
|
668
|
-
|
|
674
|
+
// The E2E template can only reach an app's own server entry through its env,
|
|
675
|
+
// so the env has to count whenever the option is left undefined.
|
|
676
|
+
//
|
|
677
|
+
// @internal — exported for unit tests only, not re-exported from the index.
|
|
678
|
+
export function resolveStylesheetWatch(
|
|
679
|
+
options: Pick<CreateKumikoServerOptions, "stylesheetWatch">,
|
|
680
|
+
env: Readonly<Record<string, string | undefined>> = process.env,
|
|
681
|
+
): boolean {
|
|
682
|
+
if (options.stylesheetWatch !== undefined) return options.stylesheetWatch;
|
|
683
|
+
return env[STYLESHEET_WATCH_ENV] !== "0";
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Non-fatal by design (returns undefined): if the CLI can't be resolved or
|
|
687
|
+
// the initial build fails (flaky network, missing dependency), the server
|
|
688
|
+
// boots without CSS instead of dying.
|
|
689
|
+
async function startTailwindPipeline(
|
|
669
690
|
entryCss: string,
|
|
691
|
+
{ watch }: { readonly watch: boolean },
|
|
670
692
|
): Promise<{ outPath: string; kill: () => void } | undefined> {
|
|
671
693
|
const bunResolver = hasBun
|
|
672
694
|
? (globalThis as { Bun: { resolveSync: (id: string, from: string) => string } }).Bun
|
|
@@ -681,7 +703,11 @@ async function startTailwindWatcher(
|
|
|
681
703
|
}
|
|
682
704
|
const outDir = mkdtempSync(join(tmpdir(), "kumiko-tw-"));
|
|
683
705
|
const outPath = join(outDir, "styles.css");
|
|
684
|
-
logInfo(
|
|
706
|
+
logInfo(
|
|
707
|
+
watch
|
|
708
|
+
? `[kumiko-server] tailwind watcher → ${outPath}`
|
|
709
|
+
: `[kumiko-server] tailwind one-shot build → ${outPath}`,
|
|
710
|
+
);
|
|
685
711
|
const bunPath = process.argv[0] ?? "bun";
|
|
686
712
|
// Initial-Build blockend, damit der erste /styles.css-Request kein
|
|
687
713
|
// 404 bekommt. Dann den watcher im Hintergrund mit unref() — sonst
|
|
@@ -701,6 +727,9 @@ async function startTailwindWatcher(
|
|
|
701
727
|
logError("[kumiko-server] tailwind one-shot-build fehlgeschlagen — booting ohne CSS:", err);
|
|
702
728
|
return undefined;
|
|
703
729
|
}
|
|
730
|
+
if (!watch) {
|
|
731
|
+
return { outPath, kill: () => {} };
|
|
732
|
+
}
|
|
704
733
|
const watcher = spawn(bunPath, ["run", cliPath, "-i", entryCss, "-o", outPath, "--watch"], {
|
|
705
734
|
stdio: "inherit",
|
|
706
735
|
});
|
|
@@ -775,21 +804,20 @@ export async function createKumikoServer(
|
|
|
775
804
|
}
|
|
776
805
|
|
|
777
806
|
// --- Tailwind stylesheet (optional) ---
|
|
778
|
-
// Tailwind
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
// und der Browser-Reload kommt eh nur nach Bundle-Rebuild.
|
|
807
|
+
// The Tailwind CLI writes to a temp file that every /styles.css request
|
|
808
|
+
// reads fresh: no in-memory signalling needed, and the browser only
|
|
809
|
+
// reloads after a bundle rebuild anyway.
|
|
782
810
|
//
|
|
783
|
-
//
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
// einen absoluten Pfad — funktioniert sowohl im Monorepo (Workspace-
|
|
787
|
-
// Link) als auch in einer installierten Fremd-App (node_modules).
|
|
811
|
+
// Without an explicit `stylesheet`, the default resolves through the
|
|
812
|
+
// package exports of `@cosmicdrift/kumiko-renderer-web`, which works both
|
|
813
|
+
// for a workspace link and for an installed app (node_modules).
|
|
788
814
|
let stylesheetPath: string | undefined;
|
|
789
815
|
let killTailwind: (() => void) | undefined;
|
|
790
816
|
const resolvedStylesheet = resolveStylesheet(options);
|
|
791
817
|
if (resolvedStylesheet !== undefined) {
|
|
792
|
-
const handle = await
|
|
818
|
+
const handle = await startTailwindPipeline(resolvedStylesheet, {
|
|
819
|
+
watch: resolveStylesheetWatch(options),
|
|
820
|
+
});
|
|
793
821
|
if (handle !== undefined) {
|
|
794
822
|
stylesheetPath = handle.outPath;
|
|
795
823
|
killTailwind = handle.kill;
|
package/src/index.ts
CHANGED
package/src/run-dev-app.ts
CHANGED
|
@@ -196,6 +196,8 @@ export type RunDevAppOptions = {
|
|
|
196
196
|
/** CSS-Entry. Default: package-export `@cosmicdrift/kumiko-renderer-web/styles.css`
|
|
197
197
|
* wenn ein client-Entry gesetzt ist. `false` deaktiviert die CSS-Pipeline. */
|
|
198
198
|
readonly stylesheet?: string | false;
|
|
199
|
+
/** Tailwind `--watch` process on/off, see `CreateKumikoServerOptions.stylesheetWatch`. */
|
|
200
|
+
readonly stylesheetWatch?: boolean;
|
|
199
201
|
/** Eigenes HTML-Template; sonst minimal-Default (#root + client.js).
|
|
200
202
|
* Im Multi-Entry-Mode ist es das Fallback-Template, wenn ein einzelner
|
|
201
203
|
* Entry kein eigenes htmlPath setzt. */
|
|
@@ -479,6 +481,7 @@ export async function runDevApp(options: RunDevAppOptions): Promise<KumikoServer
|
|
|
479
481
|
...(options.hostDispatch !== undefined && { hostDispatch: options.hostDispatch }),
|
|
480
482
|
...(options.resolvePageHead !== undefined && { resolvePageHead: options.resolvePageHead }),
|
|
481
483
|
...(options.stylesheet !== undefined && { stylesheet: options.stylesheet }),
|
|
484
|
+
...(options.stylesheetWatch !== undefined && { stylesheetWatch: options.stylesheetWatch }),
|
|
482
485
|
...(options.htmlPath !== undefined && { htmlPath: options.htmlPath }),
|
|
483
486
|
...(options.port !== undefined && { port: options.port }),
|
|
484
487
|
...(options.watchDirs !== undefined && { watchDirs: options.watchDirs }),
|