plgg-bundle 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/bin/appAliasHook.mjs +89 -0
- package/bin/hook.mjs +94 -0
- package/bin/plgg-bundle.mjs +16 -0
- package/dist/Dev/fixtures/devPoc.spec.d.ts +2 -0
- package/dist/Dev/fixtures/devPoc.spec.d.ts.map +1 -0
- package/dist/Dev/fixtures/devPoc.spec.js +145 -0
- package/dist/Dev/fixtures/devPoc.spec.js.map +1 -0
- package/dist/Dev/model/Fetch.d.ts +20 -0
- package/dist/Dev/model/Fetch.d.ts.map +1 -0
- package/dist/Dev/model/Fetch.js +8 -0
- package/dist/Dev/model/Fetch.js.map +1 -0
- package/dist/Dev/model/ModuleGraph.d.ts +27 -0
- package/dist/Dev/model/ModuleGraph.d.ts.map +1 -0
- package/dist/Dev/model/ModuleGraph.js +2 -0
- package/dist/Dev/model/ModuleGraph.js.map +1 -0
- package/dist/Dev/model/Protocol.d.ts +22 -0
- package/dist/Dev/model/Protocol.d.ts.map +1 -0
- package/dist/Dev/model/Protocol.js +31 -0
- package/dist/Dev/model/Protocol.js.map +1 -0
- package/dist/Dev/node/devServer.d.ts +21 -0
- package/dist/Dev/node/devServer.d.ts.map +1 -0
- package/dist/Dev/node/devServer.js +298 -0
- package/dist/Dev/node/devServer.js.map +1 -0
- package/dist/Dev/node/httpAdapter.d.ts +19 -0
- package/dist/Dev/node/httpAdapter.d.ts.map +1 -0
- package/dist/Dev/node/httpAdapter.js +76 -0
- package/dist/Dev/node/httpAdapter.js.map +1 -0
- package/dist/Dev/node/scanGraph.d.ts +21 -0
- package/dist/Dev/node/scanGraph.d.ts.map +1 -0
- package/dist/Dev/node/scanGraph.js +145 -0
- package/dist/Dev/node/scanGraph.js.map +1 -0
- package/dist/Dev/node/watch.d.ts +10 -0
- package/dist/Dev/node/watch.d.ts.map +1 -0
- package/dist/Dev/node/watch.js +44 -0
- package/dist/Dev/node/watch.js.map +1 -0
- package/dist/Dev/usecase/allowedHost.d.ts +16 -0
- package/dist/Dev/usecase/allowedHost.d.ts.map +1 -0
- package/dist/Dev/usecase/allowedHost.js +33 -0
- package/dist/Dev/usecase/allowedHost.js.map +1 -0
- package/dist/Dev/usecase/allowedHost.spec.d.ts +2 -0
- package/dist/Dev/usecase/allowedHost.spec.d.ts.map +1 -0
- package/dist/Dev/usecase/allowedHost.spec.js +13 -0
- package/dist/Dev/usecase/allowedHost.spec.js.map +1 -0
- package/dist/Dev/usecase/buildGraph.d.ts +12 -0
- package/dist/Dev/usecase/buildGraph.d.ts.map +1 -0
- package/dist/Dev/usecase/buildGraph.js +24 -0
- package/dist/Dev/usecase/buildGraph.js.map +1 -0
- package/dist/Dev/usecase/buildGraph.spec.d.ts +2 -0
- package/dist/Dev/usecase/buildGraph.spec.d.ts.map +1 -0
- package/dist/Dev/usecase/buildGraph.spec.js +16 -0
- package/dist/Dev/usecase/buildGraph.spec.js.map +1 -0
- package/dist/Dev/usecase/decorateDevHtml.d.ts +12 -0
- package/dist/Dev/usecase/decorateDevHtml.d.ts.map +1 -0
- package/dist/Dev/usecase/decorateDevHtml.js +15 -0
- package/dist/Dev/usecase/decorateDevHtml.js.map +1 -0
- package/dist/Dev/usecase/decorateDevHtml.spec.d.ts +2 -0
- package/dist/Dev/usecase/decorateDevHtml.spec.d.ts.map +1 -0
- package/dist/Dev/usecase/decorateDevHtml.spec.js +19 -0
- package/dist/Dev/usecase/decorateDevHtml.spec.js.map +1 -0
- package/dist/Dev/usecase/invalidate.d.ts +17 -0
- package/dist/Dev/usecase/invalidate.d.ts.map +1 -0
- package/dist/Dev/usecase/invalidate.js +57 -0
- package/dist/Dev/usecase/invalidate.js.map +1 -0
- package/dist/Dev/usecase/invalidate.spec.d.ts +2 -0
- package/dist/Dev/usecase/invalidate.spec.d.ts.map +1 -0
- package/dist/Dev/usecase/invalidate.spec.js +64 -0
- package/dist/Dev/usecase/invalidate.spec.js.map +1 -0
- package/dist/Dev/usecase/parseImports.d.ts +24 -0
- package/dist/Dev/usecase/parseImports.d.ts.map +1 -0
- package/dist/Dev/usecase/parseImports.js +40 -0
- package/dist/Dev/usecase/parseImports.js.map +1 -0
- package/dist/Dev/usecase/parseImports.spec.d.ts +2 -0
- package/dist/Dev/usecase/parseImports.spec.d.ts.map +1 -0
- package/dist/Dev/usecase/parseImports.spec.js +24 -0
- package/dist/Dev/usecase/parseImports.spec.js.map +1 -0
- package/dist/Dev/usecase/reloadDecision.d.ts +20 -0
- package/dist/Dev/usecase/reloadDecision.d.ts.map +1 -0
- package/dist/Dev/usecase/reloadDecision.js +52 -0
- package/dist/Dev/usecase/reloadDecision.js.map +1 -0
- package/dist/Dev/usecase/reloadDecision.spec.d.ts +2 -0
- package/dist/Dev/usecase/reloadDecision.spec.d.ts.map +1 -0
- package/dist/Dev/usecase/reloadDecision.spec.js +17 -0
- package/dist/Dev/usecase/reloadDecision.spec.js.map +1 -0
- package/dist/domain/model/BundleConfig.d.ts +136 -0
- package/dist/domain/model/BundleConfig.d.ts.map +1 -0
- package/dist/domain/model/BundleConfig.js +18 -0
- package/dist/domain/model/BundleConfig.js.map +1 -0
- package/dist/domain/model/BundleConfig.spec.d.ts +2 -0
- package/dist/domain/model/BundleConfig.spec.d.ts.map +1 -0
- package/dist/domain/model/BundleConfig.spec.js +8 -0
- package/dist/domain/model/BundleConfig.spec.js.map +1 -0
- package/dist/domain/usecase/asBundleConfig.d.ts +17 -0
- package/dist/domain/usecase/asBundleConfig.d.ts.map +1 -0
- package/dist/domain/usecase/asBundleConfig.js +179 -0
- package/dist/domain/usecase/asBundleConfig.js.map +1 -0
- package/dist/domain/usecase/asBundleConfig.spec.d.ts +2 -0
- package/dist/domain/usecase/asBundleConfig.spec.d.ts.map +1 -0
- package/dist/domain/usecase/asBundleConfig.spec.js +119 -0
- package/dist/domain/usecase/asBundleConfig.spec.js.map +1 -0
- package/dist/domain/usecase/build.d.ts +14 -0
- package/dist/domain/usecase/build.d.ts.map +1 -0
- package/dist/domain/usecase/build.js +177 -0
- package/dist/domain/usecase/build.js.map +1 -0
- package/dist/domain/usecase/collectModules.d.ts +53 -0
- package/dist/domain/usecase/collectModules.d.ts.map +1 -0
- package/dist/domain/usecase/collectModules.js +118 -0
- package/dist/domain/usecase/collectModules.js.map +1 -0
- package/dist/domain/usecase/deriveExternal.d.ts +25 -0
- package/dist/domain/usecase/deriveExternal.d.ts.map +1 -0
- package/dist/domain/usecase/deriveExternal.js +79 -0
- package/dist/domain/usecase/deriveExternal.js.map +1 -0
- package/dist/domain/usecase/deriveExternal.spec.d.ts +2 -0
- package/dist/domain/usecase/deriveExternal.spec.d.ts.map +1 -0
- package/dist/domain/usecase/deriveExternal.spec.js +56 -0
- package/dist/domain/usecase/deriveExternal.spec.js.map +1 -0
- package/dist/domain/usecase/discoverWorkspace.d.ts +27 -0
- package/dist/domain/usecase/discoverWorkspace.d.ts.map +1 -0
- package/dist/domain/usecase/discoverWorkspace.js +116 -0
- package/dist/domain/usecase/discoverWorkspace.js.map +1 -0
- package/dist/domain/usecase/discoverWorkspace.spec.d.ts +2 -0
- package/dist/domain/usecase/discoverWorkspace.spec.d.ts.map +1 -0
- package/dist/domain/usecase/discoverWorkspace.spec.js +23 -0
- package/dist/domain/usecase/discoverWorkspace.spec.js.map +1 -0
- package/dist/domain/usecase/emitBundle.d.ts +43 -0
- package/dist/domain/usecase/emitBundle.d.ts.map +1 -0
- package/dist/domain/usecase/emitBundle.js +157 -0
- package/dist/domain/usecase/emitBundle.js.map +1 -0
- package/dist/domain/usecase/emitBundle.spec.d.ts +2 -0
- package/dist/domain/usecase/emitBundle.spec.d.ts.map +1 -0
- package/dist/domain/usecase/emitBundle.spec.js +80 -0
- package/dist/domain/usecase/emitBundle.spec.js.map +1 -0
- package/dist/domain/usecase/emitDts.d.ts +32 -0
- package/dist/domain/usecase/emitDts.d.ts.map +1 -0
- package/dist/domain/usecase/emitDts.js +99 -0
- package/dist/domain/usecase/emitDts.js.map +1 -0
- package/dist/domain/usecase/isExternal.d.ts +12 -0
- package/dist/domain/usecase/isExternal.d.ts.map +1 -0
- package/dist/domain/usecase/isExternal.js +16 -0
- package/dist/domain/usecase/isExternal.js.map +1 -0
- package/dist/domain/usecase/isExternal.spec.d.ts +2 -0
- package/dist/domain/usecase/isExternal.spec.d.ts.map +1 -0
- package/dist/domain/usecase/isExternal.spec.js +16 -0
- package/dist/domain/usecase/isExternal.spec.js.map +1 -0
- package/dist/domain/usecase/resolveSpecifier.d.ts +35 -0
- package/dist/domain/usecase/resolveSpecifier.d.ts.map +1 -0
- package/dist/domain/usecase/resolveSpecifier.js +83 -0
- package/dist/domain/usecase/resolveSpecifier.js.map +1 -0
- package/dist/domain/usecase/resolveSpecifier.spec.d.ts +2 -0
- package/dist/domain/usecase/resolveSpecifier.spec.d.ts.map +1 -0
- package/dist/domain/usecase/resolveSpecifier.spec.js +44 -0
- package/dist/domain/usecase/resolveSpecifier.spec.js.map +1 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.d.ts +29 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.d.ts.map +1 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.js +89 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.js.map +1 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.spec.d.ts +2 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.spec.d.ts.map +1 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.spec.js +27 -0
- package/dist/domain/usecase/resolveWorkspaceSpecifier.spec.js.map +1 -0
- package/dist/domain/usecase/rewriteDtsAliases.d.ts +25 -0
- package/dist/domain/usecase/rewriteDtsAliases.d.ts.map +1 -0
- package/dist/domain/usecase/rewriteDtsAliases.js +71 -0
- package/dist/domain/usecase/rewriteDtsAliases.js.map +1 -0
- package/dist/domain/usecase/rewriteDtsAliases.spec.d.ts +2 -0
- package/dist/domain/usecase/rewriteDtsAliases.spec.d.ts.map +1 -0
- package/dist/domain/usecase/rewriteDtsAliases.spec.js +35 -0
- package/dist/domain/usecase/rewriteDtsAliases.spec.js.map +1 -0
- package/dist/entrypoints/cli.d.ts +2 -0
- package/dist/entrypoints/cli.d.ts.map +1 -0
- package/dist/entrypoints/cli.js +79 -0
- package/dist/entrypoints/cli.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/vendors/runner.d.ts +20 -0
- package/dist/vendors/runner.d.ts.map +1 -0
- package/dist/vendors/runner.js +60 -0
- package/dist/vendors/runner.js.map +1 -0
- package/dist/vendors/runner.spec.d.ts +2 -0
- package/dist/vendors/runner.spec.d.ts.map +1 -0
- package/dist/vendors/runner.spec.js +20 -0
- package/dist/vendors/runner.spec.js.map +1 -0
- package/dist/vendors/transpiler.d.ts +19 -0
- package/dist/vendors/transpiler.d.ts.map +1 -0
- package/dist/vendors/transpiler.js +42 -0
- package/dist/vendors/transpiler.js.map +1 -0
- package/package.json +31 -0
- package/src/Dev/fixtures/devPoc.spec.ts +235 -0
- package/src/Dev/model/Fetch.ts +31 -0
- package/src/Dev/model/ModuleGraph.ts +30 -0
- package/src/Dev/model/Protocol.ts +35 -0
- package/src/Dev/node/devServer.ts +503 -0
- package/src/Dev/node/httpAdapter.ts +162 -0
- package/src/Dev/node/scanGraph.ts +203 -0
- package/src/Dev/node/watch.ts +54 -0
- package/src/Dev/usecase/allowedHost.spec.ts +50 -0
- package/src/Dev/usecase/allowedHost.ts +39 -0
- package/src/Dev/usecase/buildGraph.spec.ts +30 -0
- package/src/Dev/usecase/buildGraph.ts +31 -0
- package/src/Dev/usecase/decorateDevHtml.spec.ts +39 -0
- package/src/Dev/usecase/decorateDevHtml.ts +21 -0
- package/src/Dev/usecase/invalidate.spec.ts +88 -0
- package/src/Dev/usecase/invalidate.ts +67 -0
- package/src/Dev/usecase/parseImports.spec.ts +58 -0
- package/src/Dev/usecase/parseImports.ts +50 -0
- package/src/Dev/usecase/reloadDecision.spec.ts +50 -0
- package/src/Dev/usecase/reloadDecision.ts +65 -0
- package/src/domain/model/BundleConfig.spec.ts +35 -0
- package/src/domain/model/BundleConfig.ts +162 -0
- package/src/domain/usecase/asBundleConfig.spec.ts +230 -0
- package/src/domain/usecase/asBundleConfig.ts +257 -0
- package/src/domain/usecase/build.ts +259 -0
- package/src/domain/usecase/collectModules.ts +226 -0
- package/src/domain/usecase/deriveExternal.spec.ts +125 -0
- package/src/domain/usecase/deriveExternal.ts +108 -0
- package/src/domain/usecase/discoverWorkspace.spec.ts +63 -0
- package/src/domain/usecase/discoverWorkspace.ts +183 -0
- package/src/domain/usecase/emitBundle.spec.ts +162 -0
- package/src/domain/usecase/emitBundle.ts +215 -0
- package/src/domain/usecase/emitDts.ts +156 -0
- package/src/domain/usecase/isExternal.spec.ts +53 -0
- package/src/domain/usecase/isExternal.ts +20 -0
- package/src/domain/usecase/resolveSpecifier.spec.ts +112 -0
- package/src/domain/usecase/resolveSpecifier.ts +127 -0
- package/src/domain/usecase/resolveWorkspaceSpecifier.spec.ts +79 -0
- package/src/domain/usecase/resolveWorkspaceSpecifier.ts +120 -0
- package/src/domain/usecase/rewriteDtsAliases.spec.ts +123 -0
- package/src/domain/usecase/rewriteDtsAliases.ts +133 -0
- package/src/entrypoints/cli.ts +105 -0
- package/src/index.ts +28 -0
- package/src/vendors/runner.spec.ts +50 -0
- package/src/vendors/runner.ts +78 -0
- package/src/vendors/transpiler.ts +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# plgg-bundle
|
|
2
|
+
|
|
3
|
+
> **UNSTABLE** - Experimental study work. Part of the [plgg monorepo](../../README.md).
|
|
4
|
+
|
|
5
|
+
The in-house minimal library bundler for the plgg monorepo. It reproduces what
|
|
6
|
+
`vite build` + `vite-plugin-dts` produced for a library package — dual ESM+CJS
|
|
7
|
+
output plus a per-file `.d.ts` tree — with **zero new dependencies** and **no
|
|
8
|
+
native binding**, retiring the rolldown native-binding fragility that broke CI.
|
|
9
|
+
|
|
10
|
+
## Why plain TypeScript (not the plgg Result/Option idiom)
|
|
11
|
+
|
|
12
|
+
Every other `packages/` TypeScript follows the house functional idiom
|
|
13
|
+
(`Result`/`Option`/`pipe`/exhaustive `match`). **plgg-bundle deliberately does
|
|
14
|
+
not.** It is written in plain TypeScript — native control flow, guard-clause
|
|
15
|
+
early returns, and `throw` on failure caught once at the bin boundary
|
|
16
|
+
(`src/entrypoints/cli.ts`) and turned into a process exit code.
|
|
17
|
+
|
|
18
|
+
The reason is a hard bootstrap constraint: **a foundation build tool must not
|
|
19
|
+
depend on the artifact it builds.** plgg-bundle's job is to build `plgg`'s
|
|
20
|
+
`dist`. If the bundler imported `plgg`, then:
|
|
21
|
+
|
|
22
|
+
- on a clean checkout / fresh CI runner with no `packages/plgg/dist`, the tool
|
|
23
|
+
could not even start (`ERR_MODULE_NOT_FOUND`) — it cannot import the thing it
|
|
24
|
+
is about to produce; and
|
|
25
|
+
- resolving `plgg` from *source* instead is not viable either: plgg's source is
|
|
26
|
+
not loadable under Node's type-stripping without converting the whole core
|
|
27
|
+
package to `verbatimModuleSyntax` + ESM (527 fixes across 109 files) — far out
|
|
28
|
+
of scope.
|
|
29
|
+
|
|
30
|
+
So the bundler depends on **nothing it builds** and nothing new — only the
|
|
31
|
+
project's own `typescript` (reused) via `ts.transpileModule` and
|
|
32
|
+
`tsc --emitDeclarationOnly`. This mirrors the existing repo precedent of
|
|
33
|
+
plgg-test's launcher being plain `.mjs` "because it runs at the very process
|
|
34
|
+
entry." The FP idiom governs the library and its consumers; bootstrap tooling
|
|
35
|
+
is exempt.
|
|
36
|
+
|
|
37
|
+
What still holds: the **CLAUDE.md hard rule** — no `as`, no `any`, no
|
|
38
|
+
`@ts-ignore`/`@ts-expect-error` — applies here too. Validation at the `unknown`
|
|
39
|
+
boundary (`asBundleConfig`) uses real `typeof`/`Array.isArray`/`instanceof`
|
|
40
|
+
guards; an unknown function's `any` return is contained to `unknown` before use.
|
|
41
|
+
|
|
42
|
+
## Layout
|
|
43
|
+
|
|
44
|
+
- `src/domain/model` — the config model (`BundleConfig`, `applyFileName`).
|
|
45
|
+
- `src/domain/usecase` — pure bundling logic: `asBundleConfig` (validate at the
|
|
46
|
+
`unknown` boundary), `collectModules` (import-graph walk), `emitBundle`
|
|
47
|
+
(collision-free module-registry runtime → ESM + CJS), `emitDts` (per-file
|
|
48
|
+
`.d.ts` via tsc + `rewriteDtsAliases`), `resolveSpecifier`, `isExternal`,
|
|
49
|
+
`build` (orchestrator).
|
|
50
|
+
- `src/vendors` — the anti-corruption boundary over the only externalized work:
|
|
51
|
+
`transpiler.ts` (`ts.transpileModule`) and `runner.ts` (`node:vm`
|
|
52
|
+
export-surface discovery, since ESM cannot declare exports dynamically).
|
|
53
|
+
- `src/entrypoints/cli.ts` + `bin/plgg-bundle.mjs` — the CLI; the bin installs a
|
|
54
|
+
self-alias resolver hook (`bin/hook.mjs`) and runs the TS CLI under Node's
|
|
55
|
+
native type-stripping.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
Each package carries a `bundle.config.ts` (plain data: entries, formats,
|
|
60
|
+
`fileNamePattern`, `external`, `alias`). Run from the package directory:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
plgg-bundle # reads ./bundle.config.ts
|
|
64
|
+
plgg-bundle my.config.ts
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
See `DEPENDENCY-LOG.md` for the vendor-neutrality decision record and the gaps
|
|
68
|
+
carried to the per-package migration (B2).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Dev-only cross-package source resolver, registered by
|
|
2
|
+
// `plgg-bundle dev` with the config's `dev.sourceAliases`.
|
|
3
|
+
// It resolves a dependency package's self-alias
|
|
4
|
+
// (`<prefix>/<sub>`) to that package's SOURCE `.ts` (not
|
|
5
|
+
// its built dist), so a theme/library package's source
|
|
6
|
+
// participates in hot-reload. It runs BEFORE the package's
|
|
7
|
+
// own `bin/hook.mjs` in the loader chain (registered
|
|
8
|
+
// later), so on an alias hit it resolves + version-stamps
|
|
9
|
+
// and short-circuits; everything else passes straight
|
|
10
|
+
// through to the next hook (which owns `plgg-bundle/*`
|
|
11
|
+
// resolution and the general `?v=` propagation) — so the
|
|
12
|
+
// version is never appended twice.
|
|
13
|
+
import { statSync } from "node:fs";
|
|
14
|
+
import { pathToFileURL } from "node:url";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
let aliases = [];
|
|
18
|
+
|
|
19
|
+
// `register(..., { data })` delivers the sourceAliases here
|
|
20
|
+
// (the hook runs on a separate thread; data is cloned in).
|
|
21
|
+
export const initialize = (data) => {
|
|
22
|
+
aliases = Array.isArray(data?.aliases)
|
|
23
|
+
? data.aliases
|
|
24
|
+
: [];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const isFile = (p) => {
|
|
28
|
+
try {
|
|
29
|
+
return statSync(p).isFile();
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Resolve a base to a FILE (never a directory): a bare
|
|
36
|
+
// `<prefix>` specifier's base is the src dir itself, which
|
|
37
|
+
// exists — so `index.ts` must be preferred, not the dir.
|
|
38
|
+
const pick = (base) =>
|
|
39
|
+
[
|
|
40
|
+
base,
|
|
41
|
+
`${base}.ts`,
|
|
42
|
+
join(base, "index.ts"),
|
|
43
|
+
].find(isFile);
|
|
44
|
+
|
|
45
|
+
const versionOf = (url) => {
|
|
46
|
+
if (!url) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const m = url.match(/[?&]v=([^&]+)/);
|
|
50
|
+
return m ? m[1] : null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const withVersion = (url, v) =>
|
|
54
|
+
`${url}${url.includes("?") ? "&" : "?"}v=${v}`;
|
|
55
|
+
|
|
56
|
+
export const resolve = (
|
|
57
|
+
specifier,
|
|
58
|
+
context,
|
|
59
|
+
nextResolve,
|
|
60
|
+
) => {
|
|
61
|
+
for (const { prefix, srcDir } of aliases) {
|
|
62
|
+
if (
|
|
63
|
+
specifier === prefix ||
|
|
64
|
+
specifier.startsWith(`${prefix}/`)
|
|
65
|
+
) {
|
|
66
|
+
const sub = specifier
|
|
67
|
+
.slice(prefix.length)
|
|
68
|
+
.replace(/^\//, "")
|
|
69
|
+
.split("?")[0];
|
|
70
|
+
const file = pick(
|
|
71
|
+
sub === "" ? srcDir : join(srcDir, sub),
|
|
72
|
+
);
|
|
73
|
+
if (file) {
|
|
74
|
+
const url = pathToFileURL(file).href;
|
|
75
|
+
const v = versionOf(context.parentURL);
|
|
76
|
+
return {
|
|
77
|
+
url:
|
|
78
|
+
v === null
|
|
79
|
+
? url
|
|
80
|
+
: withVersion(url, v),
|
|
81
|
+
shortCircuit: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Not an aliased specifier — defer to the next hook,
|
|
87
|
+
// which owns the general `?v=` propagation.
|
|
88
|
+
return nextResolve(specifier, context);
|
|
89
|
+
};
|
package/bin/hook.mjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// ESM resolver hook, two jobs:
|
|
2
|
+
//
|
|
3
|
+
// 1. Self-alias: rewrite `plgg-bundle/<sub>` to the
|
|
4
|
+
// on-disk `src/<sub>` file (`.ts` or `/index.ts`) so
|
|
5
|
+
// the bundler's own source uses extensionless
|
|
6
|
+
// self-alias specifiers like the rest of the monorepo.
|
|
7
|
+
//
|
|
8
|
+
// 2. Dev hot-reload version propagation: when the importer
|
|
9
|
+
// (`context.parentURL`) carries a `?v=<n>` query, append
|
|
10
|
+
// the SAME query to a resolved LOCAL file. `plgg-bundle
|
|
11
|
+
// dev` re-imports the app's dev entry as `entry?v=<n>`
|
|
12
|
+
// after a source edit; this propagation carries the
|
|
13
|
+
// version down the whole local import subgraph so Node
|
|
14
|
+
// re-evaluates every affected module (not just the
|
|
15
|
+
// entry) — real code hot-reload, no process restart.
|
|
16
|
+
// `node:*` and `node_modules` are never versioned (they
|
|
17
|
+
// don't change during a dev session), so they stay
|
|
18
|
+
// cached.
|
|
19
|
+
//
|
|
20
|
+
// Everything else (typescript, node:*) falls through to
|
|
21
|
+
// Node's default resolution.
|
|
22
|
+
import { existsSync } from "node:fs";
|
|
23
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
|
+
|
|
26
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const srcRoot = join(here, "..", "src");
|
|
28
|
+
const prefix = "plgg-bundle/";
|
|
29
|
+
|
|
30
|
+
const pick = (base) => {
|
|
31
|
+
const candidates = [
|
|
32
|
+
base,
|
|
33
|
+
`${base}.ts`,
|
|
34
|
+
join(base, "index.ts"),
|
|
35
|
+
];
|
|
36
|
+
return candidates.find((c) => existsSync(c));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// The `?v=<n>` dev version carried by an importer URL, or
|
|
40
|
+
// null when absent.
|
|
41
|
+
const versionOf = (url) => {
|
|
42
|
+
if (!url) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const match = url.match(/[?&]v=([^&]+)/);
|
|
46
|
+
return match ? match[1] : null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Append a dev version query to a resolved URL, respecting
|
|
50
|
+
// an existing query string.
|
|
51
|
+
const withVersion = (url, version) =>
|
|
52
|
+
`${url}${url.includes("?") ? "&" : "?"}v=${version}`;
|
|
53
|
+
|
|
54
|
+
// Whether a resolved URL is a workspace-local source file
|
|
55
|
+
// (a `file:` URL outside node_modules) — the only modules
|
|
56
|
+
// that participate in hot-reload.
|
|
57
|
+
const isLocalSource = (url) =>
|
|
58
|
+
url.startsWith("file:") &&
|
|
59
|
+
!url.includes("/node_modules/");
|
|
60
|
+
|
|
61
|
+
export const resolve = async (
|
|
62
|
+
specifier,
|
|
63
|
+
context,
|
|
64
|
+
nextResolve,
|
|
65
|
+
) => {
|
|
66
|
+
const version = versionOf(context.parentURL);
|
|
67
|
+
if (specifier.startsWith(prefix)) {
|
|
68
|
+
// Drop any propagated `?v=` before the on-disk lookup,
|
|
69
|
+
// then re-attach it so a versioned parent still busts
|
|
70
|
+
// a self-aliased child's module cache.
|
|
71
|
+
const [sub] = specifier
|
|
72
|
+
.slice(prefix.length)
|
|
73
|
+
.split("?");
|
|
74
|
+
const file = pick(join(srcRoot, sub));
|
|
75
|
+
if (file) {
|
|
76
|
+
const url = pathToFileURL(file).href;
|
|
77
|
+
return {
|
|
78
|
+
url:
|
|
79
|
+
version === null
|
|
80
|
+
? url
|
|
81
|
+
: withVersion(url, version),
|
|
82
|
+
shortCircuit: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const resolved = await nextResolve(
|
|
87
|
+
specifier,
|
|
88
|
+
context,
|
|
89
|
+
);
|
|
90
|
+
return version !== null &&
|
|
91
|
+
isLocalSource(resolved.url)
|
|
92
|
+
? { ...resolved, url: withVersion(resolved.url, version) }
|
|
93
|
+
: resolved;
|
|
94
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// plgg-bundle launcher. Installs the self-alias resolver
|
|
3
|
+
// hook, then hands off to the TypeScript CLI (Node 24
|
|
4
|
+
// strips types on load). Kept as plain `.mjs` because it
|
|
5
|
+
// runs at the very process entry, before the hook that
|
|
6
|
+
// resolves `plgg-bundle/*` specifiers is registered.
|
|
7
|
+
import { register } from "node:module";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
|
|
11
|
+
register("./hook.mjs", import.meta.url);
|
|
12
|
+
|
|
13
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const cli = join(here, "..", "src", "entrypoints", "cli.ts");
|
|
15
|
+
|
|
16
|
+
await import(cli);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devPoc.spec.d.ts","sourceRoot":"","sources":["../../../src/Dev/fixtures/devPoc.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { test, check, all, toBe, } from "plgg-test";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
// The end-to-end hot-reload PoC (ticket 041500). It runs
|
|
8
|
+
// the REAL toolchain — `node bin/plgg-bundle.mjs dev` on a
|
|
9
|
+
// throwaway fixture app — then edits a *deep* source module
|
|
10
|
+
// mid-run and proves the served output changes with NO
|
|
11
|
+
// process restart, and that the SSE reload channel opens.
|
|
12
|
+
// This is the fidelity oracle for the whole chain: bin +
|
|
13
|
+
// loader-hook version propagation + module-runner re-import
|
|
14
|
+
// + node:http serve + fs.watch + SSE.
|
|
15
|
+
/** This spec's dir → the package's bin/plgg-bundle.mjs. */
|
|
16
|
+
const BIN = join(fileURLToPath(import.meta.url), "..", "..", "..", "..", "bin", "plgg-bundle.mjs");
|
|
17
|
+
/** A dev entry that renders a deep child's message as HTML. */
|
|
18
|
+
const ENTRY_SRC = [
|
|
19
|
+
`import { message } from "./child.ts";`,
|
|
20
|
+
`export default () =>`,
|
|
21
|
+
` (_request) =>`,
|
|
22
|
+
` Promise.resolve(`,
|
|
23
|
+
` new Response(`,
|
|
24
|
+
' `<html><body>${message()}</body></html>`,',
|
|
25
|
+
` { headers: { "content-type": "text/html" } },`,
|
|
26
|
+
` ),`,
|
|
27
|
+
` );`,
|
|
28
|
+
].join("\n");
|
|
29
|
+
/** The deep child module the edit targets. */
|
|
30
|
+
const childSrc = (word) => `export const message = () => "${word}";\n`;
|
|
31
|
+
/** A minimal dev-only bundle config for the fixture app. */
|
|
32
|
+
const configSrc = (root) => `export default ${JSON.stringify({
|
|
33
|
+
root,
|
|
34
|
+
rootDir: ".",
|
|
35
|
+
outDir: "dist",
|
|
36
|
+
fileNamePattern: "[name].[format].js",
|
|
37
|
+
entries: [],
|
|
38
|
+
formats: ["es"],
|
|
39
|
+
alias: { prefix: "fixture", srcRoot: "." },
|
|
40
|
+
dev: {
|
|
41
|
+
entry: "entry.ts",
|
|
42
|
+
// Port 0 → ephemeral; the real port is read from
|
|
43
|
+
// the server's ready line.
|
|
44
|
+
port: 0,
|
|
45
|
+
watch: [root],
|
|
46
|
+
allowedHosts: [],
|
|
47
|
+
},
|
|
48
|
+
}, null, 2)};\n`;
|
|
49
|
+
/** Lay down the fixture app in a fresh temp dir. */
|
|
50
|
+
const writeFixture = () => {
|
|
51
|
+
const dir = mkdtempSync(join(tmpdir(), "plgg-dev-poc-"));
|
|
52
|
+
writeFileSync(join(dir, "child.ts"), childSrc("ALPHA"));
|
|
53
|
+
writeFileSync(join(dir, "entry.ts"), ENTRY_SRC);
|
|
54
|
+
writeFileSync(join(dir, "bundle.config.ts"), configSrc(dir));
|
|
55
|
+
return dir;
|
|
56
|
+
};
|
|
57
|
+
/** Sleep (test-only pacing for the watch/reload loop). */
|
|
58
|
+
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
59
|
+
/**
|
|
60
|
+
* Spawn `plgg-bundle dev` on the fixture and resolve the
|
|
61
|
+
* served URL once the ready line is printed.
|
|
62
|
+
*/
|
|
63
|
+
const startDev = (dir) => new Promise((resolve, reject) => {
|
|
64
|
+
const proc = spawn(process.execPath, [BIN, "dev", "bundle.config.ts"], { cwd: dir });
|
|
65
|
+
let out = "";
|
|
66
|
+
const onData = (chunk) => {
|
|
67
|
+
out += chunk.toString();
|
|
68
|
+
const m = out.match(/http:\/\/localhost:\d+\//);
|
|
69
|
+
if (m) {
|
|
70
|
+
proc.stdout?.off("data", onData);
|
|
71
|
+
resolve({ proc, url: m[0] });
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
proc.stdout?.on("data", onData);
|
|
75
|
+
proc.on("error", reject);
|
|
76
|
+
setTimeout(() => reject(new Error(`dev server did not start; output:\n${out}`)), 8000);
|
|
77
|
+
});
|
|
78
|
+
/** Fetch a URL's body text. */
|
|
79
|
+
const body = (url) => fetch(url).then((r) => r.text());
|
|
80
|
+
/**
|
|
81
|
+
* Poll `body(url)` until it contains `needle`, or throw on
|
|
82
|
+
* timeout. Proves the served output actually changed after
|
|
83
|
+
* the edit (not a fixed sleep guess).
|
|
84
|
+
*/
|
|
85
|
+
const pollFor = async (url, needle) => {
|
|
86
|
+
const deadline = 6000;
|
|
87
|
+
const step = 60;
|
|
88
|
+
for (let waited = 0; waited < deadline;) {
|
|
89
|
+
if ((await body(url)).includes(needle)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
await wait(step);
|
|
93
|
+
waited += step;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
};
|
|
97
|
+
test("plgg-bundle dev hot-reloads a deep source edit with no restart", async () => {
|
|
98
|
+
const dir = writeFixture();
|
|
99
|
+
const { proc, url } = await startDev(dir);
|
|
100
|
+
try {
|
|
101
|
+
const first = await body(url);
|
|
102
|
+
// Rewrite the DEEP child (not the entry) mid-run.
|
|
103
|
+
writeFileSync(join(dir, "child.ts"), childSrc("BETA"));
|
|
104
|
+
const reloaded = await pollFor(url, "BETA");
|
|
105
|
+
const stillAlive = proc.exitCode === null;
|
|
106
|
+
return all([
|
|
107
|
+
// Initial render came from the child module.
|
|
108
|
+
check(first.includes("ALPHA"), toBe(true)),
|
|
109
|
+
// The dev server injected its live-reload client.
|
|
110
|
+
check(first.includes("EventSource"), toBe(true)),
|
|
111
|
+
// The deep edit re-evaluated and is now served …
|
|
112
|
+
check(reloaded, toBe(true)),
|
|
113
|
+
// … by the SAME process (no restart).
|
|
114
|
+
check(stillAlive, toBe(true)),
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
proc.kill();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
test("plgg-bundle dev opens an SSE reload channel", async () => {
|
|
122
|
+
const dir = writeFixture();
|
|
123
|
+
const { proc, url } = await startDev(dir);
|
|
124
|
+
try {
|
|
125
|
+
const res = await fetch(`${url}__plgg_reload`);
|
|
126
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
127
|
+
// Read the opener frame, then release the stream.
|
|
128
|
+
const reader = res.body?.getReader();
|
|
129
|
+
const firstChunk = reader
|
|
130
|
+
? await reader.read()
|
|
131
|
+
: { value: undefined };
|
|
132
|
+
await reader?.cancel();
|
|
133
|
+
const text = firstChunk.value
|
|
134
|
+
? new TextDecoder().decode(firstChunk.value)
|
|
135
|
+
: "";
|
|
136
|
+
return all([
|
|
137
|
+
check(contentType.includes("text/event-stream"), toBe(true)),
|
|
138
|
+
check(text.includes("connected"), toBe(true)),
|
|
139
|
+
]);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
proc.kill();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=devPoc.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devPoc.spec.js","sourceRoot":"","sources":["../../../src/Dev/fixtures/devPoc.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Web-standard request handler an app hands the dev
|
|
3
|
+
* server: `Request => Promise<Response>`. This is the same
|
|
4
|
+
* shape plgg-server's `toFetch` produces, reproduced here
|
|
5
|
+
* as a bare structural type so the toolchain never imports
|
|
6
|
+
* a plgg package.
|
|
7
|
+
*/
|
|
8
|
+
export type Fetch = (request: Request) => Promise<Response>;
|
|
9
|
+
/**
|
|
10
|
+
* The app⇄dev-server contract. An app's **dev entry**
|
|
11
|
+
* module default-exports a factory that builds its
|
|
12
|
+
* {@link Fetch}. The dev server calls it once on start and
|
|
13
|
+
* again after every hot-reload — a cache-busted re-import
|
|
14
|
+
* of the entry re-runs the factory over freshly
|
|
15
|
+
* re-evaluated source, which is how a code edit takes
|
|
16
|
+
* effect with no process restart. The factory may be
|
|
17
|
+
* async (config load, route discovery).
|
|
18
|
+
*/
|
|
19
|
+
export type FetchFactory = () => Fetch | Promise<Fetch>;
|
|
20
|
+
//# sourceMappingURL=Fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fetch.d.ts","sourceRoot":"","sources":["../../../src/Dev/model/Fetch.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GAAG,CAClB,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG,MACvB,KAAK,GACL,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Plain-TS build tool: plgg-bundle is a foundation
|
|
2
|
+
// bundler and must not depend on the libraries it builds
|
|
3
|
+
// (plgg / plgg-server). So the dev server speaks the
|
|
4
|
+
// Web-standard request/response contract directly rather
|
|
5
|
+
// than plgg-server's branded `Fetch`. See the package
|
|
6
|
+
// README and the ticket's hard constraints.
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=Fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fetch.js","sourceRoot":"","sources":["../../../src/Dev/model/Fetch.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,yDAAyD;AACzD,qDAAqD;AACrD,yDAAyD;AACzD,sDAAsD;AACtD,4CAA4C"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dev server's view of the app's local module graph:
|
|
3
|
+
* a map from each source file (absolute path) to the
|
|
4
|
+
* absolute paths of the local files it imports. Only
|
|
5
|
+
* workspace-local edges are recorded — `node:*` and
|
|
6
|
+
* `node_modules` specifiers are not part of the watched
|
|
7
|
+
* graph (they do not change during a dev session).
|
|
8
|
+
*
|
|
9
|
+
* Built fresh from disk on each change by the node adapter
|
|
10
|
+
* (scan → resolve), then consumed by the pure
|
|
11
|
+
* {@link transitiveImporters} / reload-decision logic. The
|
|
12
|
+
* graph is what lets the server ignore a save to a file
|
|
13
|
+
* the app does not import, and report exactly which
|
|
14
|
+
* modules a change invalidates.
|
|
15
|
+
*/
|
|
16
|
+
export type ModuleGraph = ReadonlyMap<string, ReadonlyArray<string>>;
|
|
17
|
+
/**
|
|
18
|
+
* One resolved import edge: `from` imports `to` (both
|
|
19
|
+
* absolute paths). The adapter produces these by scanning
|
|
20
|
+
* a file's import specifiers and resolving the local ones;
|
|
21
|
+
* {@link buildGraph} folds them into a {@link ModuleGraph}.
|
|
22
|
+
*/
|
|
23
|
+
export type Edge = Readonly<{
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
}>;
|
|
27
|
+
//# sourceMappingURL=ModuleGraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModuleGraph.d.ts","sourceRoot":"","sources":["../../../src/Dev/model/ModuleGraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,CACnC,MAAM,EACN,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,QAAQ,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModuleGraph.js","sourceRoot":"","sources":["../../../src/Dev/model/ModuleGraph.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The dev-only Server-Sent-Events live-reload route. */
|
|
2
|
+
export declare const RELOAD_PATH = "/__plgg_reload";
|
|
3
|
+
/**
|
|
4
|
+
* The SSE opener frame. A comment line keeps the stream
|
|
5
|
+
* from idling before the first reload is pushed.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SSE_PRELUDE = ": connected\n\n";
|
|
8
|
+
/**
|
|
9
|
+
* A default-event SSE frame the injected client's
|
|
10
|
+
* `onmessage` handler reacts to by reloading the page.
|
|
11
|
+
*/
|
|
12
|
+
export declare const RELOAD_FRAME = "data: reload\n\n";
|
|
13
|
+
/**
|
|
14
|
+
* The dev-only live-reload client: a constant `<script>`
|
|
15
|
+
* opening an `EventSource` to {@link RELOAD_PATH} that
|
|
16
|
+
* reloads the page on every pushed frame. This literal is
|
|
17
|
+
* string-appended onto rendered HTML output ONLY (see
|
|
18
|
+
* `decorateDevHtml`) — it never enters a typed render
|
|
19
|
+
* tree, so a production build can never emit it.
|
|
20
|
+
*/
|
|
21
|
+
export declare const LIVE_RELOAD_SCRIPT: string;
|
|
22
|
+
//# sourceMappingURL=Protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Dev/model/Protocol.ts"],"names":[],"mappings":"AAQA,yDAAyD;AACzD,eAAO,MAAM,WAAW,mBAAmB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAE/C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,QAGiB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The dev-only live-reload wire protocol: the SSE route
|
|
2
|
+
// the injected client script connects to, and the frames
|
|
3
|
+
// pushed over it. Kept as pure constants/string builders
|
|
4
|
+
// (no `node:*`, no sockets) so the transport shape is
|
|
5
|
+
// unit-testable and the node adapter only owns the socket
|
|
6
|
+
// registry. Mirrors plggpress's hand-rolled SSE reload,
|
|
7
|
+
// reproduced here plgg-free in the toolchain.
|
|
8
|
+
/** The dev-only Server-Sent-Events live-reload route. */
|
|
9
|
+
export const RELOAD_PATH = "/__plgg_reload";
|
|
10
|
+
/**
|
|
11
|
+
* The SSE opener frame. A comment line keeps the stream
|
|
12
|
+
* from idling before the first reload is pushed.
|
|
13
|
+
*/
|
|
14
|
+
export const SSE_PRELUDE = ": connected\n\n";
|
|
15
|
+
/**
|
|
16
|
+
* A default-event SSE frame the injected client's
|
|
17
|
+
* `onmessage` handler reacts to by reloading the page.
|
|
18
|
+
*/
|
|
19
|
+
export const RELOAD_FRAME = "data: reload\n\n";
|
|
20
|
+
/**
|
|
21
|
+
* The dev-only live-reload client: a constant `<script>`
|
|
22
|
+
* opening an `EventSource` to {@link RELOAD_PATH} that
|
|
23
|
+
* reloads the page on every pushed frame. This literal is
|
|
24
|
+
* string-appended onto rendered HTML output ONLY (see
|
|
25
|
+
* `decorateDevHtml`) — it never enters a typed render
|
|
26
|
+
* tree, so a production build can never emit it.
|
|
27
|
+
*/
|
|
28
|
+
export const LIVE_RELOAD_SCRIPT = "<script>new EventSource('" +
|
|
29
|
+
RELOAD_PATH +
|
|
30
|
+
"').onmessage=()=>location.reload();</script>";
|
|
31
|
+
//# sourceMappingURL=Protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Protocol.js","sourceRoot":"","sources":["../../../src/Dev/model/Protocol.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,yDAAyD;AACzD,yDAAyD;AACzD,sDAAsD;AACtD,0DAA0D;AAC1D,wDAAwD;AACxD,8CAA8C;AAE9C,yDAAyD;AACzD,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAE5C;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,2BAA2B;IAC3B,WAAW;IACX,8CAA8C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type BundleConfig } from "plgg-bundle/domain/model/BundleConfig";
|
|
2
|
+
/**
|
|
3
|
+
* A running dev server: the URL it is reachable at and a
|
|
4
|
+
* `close` that stops the listener + watchers. The CLI
|
|
5
|
+
* ignores `close` (the server runs for the process
|
|
6
|
+
* lifetime); a test uses it to shut the server down so the
|
|
7
|
+
* process can exit.
|
|
8
|
+
*/
|
|
9
|
+
export type DevServer = Readonly<{
|
|
10
|
+
url: string;
|
|
11
|
+
close: () => void;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Start the dev server for a config's `dev` section: load
|
|
15
|
+
* the app Fetch, serve it over node:http, watch the source
|
|
16
|
+
* roots, and hot-reload on every relevant code/content
|
|
17
|
+
* edit. Resolves once the listener is up, with the
|
|
18
|
+
* reachable URL and a `close` handle.
|
|
19
|
+
*/
|
|
20
|
+
export declare const runDevServer: (config: BundleConfig) => Promise<DevServer>;
|
|
21
|
+
//# sourceMappingURL=devServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devServer.d.ts","sourceRoot":"","sources":["../../../src/Dev/node/devServer.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,uCAAuC,CAAC;AA8W1E;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC,CAAC;AAeH;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GACvB,QAAQ,YAAY,KACnB,OAAO,CAAC,SAAS,CAmFnB,CAAC"}
|