@astroscope/node 1.2.2 → 1.4.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/README.md +21 -0
- package/dist/boot.d.ts +0 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/{construct-DgB-jR0a.d.ts → construct-BGlPfWMF.d.ts} +1 -2
- package/dist/construct-BGlPfWMF.d.ts.map +1 -0
- package/dist/csrf-middleware-entrypoint.d.ts.map +1 -1
- package/dist/dev-middleware-entrypoint.d.ts +0 -1
- package/dist/dev-middleware-entrypoint.d.ts.map +1 -1
- package/dist/{events-CUzQ2_cp.d.ts → events-u7J3ezJR.d.ts} +1 -2
- package/dist/events-u7J3ezJR.d.ts.map +1 -0
- package/dist/{excludes-DLF3A_Cf.d.ts → excludes-BDiE3eyp.d.ts} +1 -2
- package/dist/excludes-BDiE3eyp.d.ts.map +1 -0
- package/dist/excludes.d.ts +1 -1
- package/dist/health.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +170 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/events.d.ts +1 -1
- package/dist/log/index.d.ts +33 -3
- package/dist/log/index.d.ts.map +1 -1
- package/dist/log/index.js +3 -2
- package/dist/{log-CSJlKaxY.js → log-B69HEBvg.js} +2 -2
- package/dist/{log-CSJlKaxY.js.map → log-B69HEBvg.js.map} +1 -1
- package/dist/{native-mount-hhwWdLtL.js → native-mount-DjYEnO4X.js} +4 -13
- package/dist/native-mount-DjYEnO4X.js.map +1 -0
- package/dist/native.d.ts +0 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +1 -1
- package/dist/{prepare-DQEf2Bnt.js → prepare-CXZsyAVk.js} +6 -4
- package/dist/prepare-CXZsyAVk.js.map +1 -0
- package/dist/preview.d.ts +0 -1
- package/dist/preview.d.ts.map +1 -1
- package/dist/request-route-DcnZOOM4.js +92 -0
- package/dist/request-route-DcnZOOM4.js.map +1 -0
- package/dist/route-middleware-entrypoint.d.ts +3 -2
- package/dist/route-middleware-entrypoint.d.ts.map +1 -1
- package/dist/route-middleware-entrypoint.js +5 -11
- package/dist/route-middleware-entrypoint.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -8
- package/dist/server.js.map +1 -1
- package/dist/types-D0uMBi2M.d.ts.map +1 -1
- package/package.json +8 -7
- package/dist/construct-DgB-jR0a.d.ts.map +0 -1
- package/dist/events-CUzQ2_cp.d.ts.map +0 -1
- package/dist/excludes-DLF3A_Cf.d.ts.map +0 -1
- package/dist/native-mount-hhwWdLtL.js.map +0 -1
- package/dist/prepare-DQEf2Bnt.js.map +0 -1
- package/dist/store-BIUF4lqk.js +0 -29
- package/dist/store-BIUF4lqk.js.map +0 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Opinionated, cloud-friendly Node adapter for Astro: boot lifecycle, health probe
|
|
|
15
15
|
- **Native mounts** — http-native handlers (`oidc-provider`, ACME) mounted on the adapter's server
|
|
16
16
|
- **Build tweaks** — SSR sourcemaps, SSR effect stripping
|
|
17
17
|
- **Dev restart machinery** — changes to the boot file or entry seams restart the dev server behind a holding page
|
|
18
|
+
- **Dev island warmup** — `.astro` sources are scanned for `client:*` components; their deps are pre-optimized and their module graphs warmed at server start, preventing "504 Outdated Optimize Dep" hydration failures from vite's lazy dep discovery
|
|
18
19
|
- **HTTPS for development** — `SERVER_CERT_PATH` / `SERVER_KEY_PATH` serve TLS directly for local runs of the built server ([HTTPS](#https)); in production, terminate TLS at the ingress
|
|
19
20
|
|
|
20
21
|
## What it does NOT do — beware
|
|
@@ -144,6 +145,26 @@ Entries logged before the logger is constructed (env loading, config, instrument
|
|
|
144
145
|
|
|
145
146
|
Request logging happens at the native handler on `finish`/`close`: real status code, response size, `ttfb`, aborted-vs-completed, and the route pattern (fed back by an internal astro middleware). An incoming `x-request-id` header is passed through (and echoed on the response); otherwise a short id is generated.
|
|
146
147
|
|
|
148
|
+
### Reporting the route yourself
|
|
149
|
+
|
|
150
|
+
The route comes from the route astro matched, which is wrong for a middleware that serves a request astro has no page for — one that rewrites via `next(url)`, or answers itself. Astro matches `/404` there, so the request is logged as `/404` despite its `200`, and every such request collapses into one `/404` bucket in the request duration metric. `overrideRequestRoute` lets the middleware report what it actually served:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { overrideRequestRoute } from '@astroscope/node/log';
|
|
154
|
+
|
|
155
|
+
export const onRequest: MiddlewareHandler = (ctx, next) => {
|
|
156
|
+
const page = lookupPage(ctx.url.pathname);
|
|
157
|
+
|
|
158
|
+
if (!page) return next();
|
|
159
|
+
|
|
160
|
+
overrideRequestRoute('/cms/pages/[id]');
|
|
161
|
+
|
|
162
|
+
return next(`/cms/pages/${page.id}`);
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This fixes the log line, the metric and the server span name together. Pass a templated label, not a concrete path, so metric cardinality stays bounded. An overridden route always wins over the one astro matched, regardless of middleware order.
|
|
167
|
+
|
|
147
168
|
The adapter itself emits exactly one info line on startup — `server ready { host, port, health, bootMs, warmupMs, totalMs }` — and `draining` / `shutdown complete { drainMs }` on the way down. Everything else is debug or error level.
|
|
148
169
|
|
|
149
170
|
## Telemetry
|
package/dist/boot.d.ts
CHANGED
package/dist/boot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot.d.ts","names":[],"sources":["../src/lifecycle/context.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"boot.d.ts","names":[],"sources":["../src/lifecycle/context.ts"],"mappings":";;;;;;;;;;iBAiBgB,kBAAkB"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Logger, LoggerOptions } from "pino";
|
|
2
|
-
|
|
3
2
|
//#region src/observability/log/construct.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Contract of the `src/log.ts` entry seam: pino logger options, or a factory
|
|
@@ -11,4 +10,4 @@ type LoggerOptionsFactory = LoggerOptions | ((ctx: {
|
|
|
11
10
|
}) => LoggerOptions | Promise<LoggerOptions>);
|
|
12
11
|
//#endregion
|
|
13
12
|
export { LoggerOptionsFactory as t };
|
|
14
|
-
//# sourceMappingURL=construct-
|
|
13
|
+
//# sourceMappingURL=construct-BGlPfWMF.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"construct-BGlPfWMF.d.ts","names":[],"sources":["../src/observability/log/construct.ts"],"mappings":";;;;;;;KASY,uBAAuB,kBAAkB;EAAO;MAAmB,gBAAgB,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrf-middleware-entrypoint.d.ts","names":[],"sources":["../src/csrf/middleware-entrypoint.ts"],"mappings":";cAKa,
|
|
1
|
+
{"version":3,"file":"csrf-middleware-entrypoint.d.ts","names":[],"sources":["../src/csrf/middleware-entrypoint.ts"],"mappings":";cAKa,2BAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-middleware-entrypoint.d.ts","names":[],"sources":["../src/dev-mode/middleware-entrypoint.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev-middleware-entrypoint.d.ts","names":[],"sources":["../src/dev-mode/middleware-entrypoint.ts"],"mappings":";;;;;;;;;;cAWa,WAAW"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { t as BootContext } from "./types-D0uMBi2M.js";
|
|
2
|
-
|
|
3
2
|
//#region src/lifecycle/events.d.ts
|
|
4
3
|
type BootEventName = 'beforeOnStartup' | 'afterOnStartup' | 'beforeOnShutdown' | 'afterOnShutdown';
|
|
5
4
|
type BootEventHandler = (context: BootContext) => Promise<void> | void;
|
|
@@ -17,4 +16,4 @@ declare function off(event: BootEventName, handler: BootEventHandler): void;
|
|
|
17
16
|
declare function emit(event: BootEventName, context: BootContext): Promise<void>;
|
|
18
17
|
//#endregion
|
|
19
18
|
export { on as a, off as i, BootEventName as n, emit as r, BootEventHandler as t };
|
|
20
|
-
//# sourceMappingURL=events-
|
|
19
|
+
//# sourceMappingURL=events-u7J3ezJR.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-u7J3ezJR.d.ts","names":[],"sources":["../src/lifecycle/events.ts"],"mappings":";;KAEY;KAEA,oBAAoB,SAAS,gBAAgB;;;;iBAuBzC,GAAG,OAAO,eAAe,SAAS;;;;iBAelC,IAAI,OAAO,eAAe,SAAS;;;;iBAS7B,KAAK,OAAO,eAAe,SAAS,cAAc"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { StringPattern } from "@entwico/dash/match";
|
|
2
2
|
import { APIContext, MiddlewareHandler } from "astro";
|
|
3
|
-
|
|
4
3
|
//#region src/excludes/excludes.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Patterns accepted by @astroscope/node exclude options. The serializable
|
|
@@ -60,4 +59,4 @@ declare function shouldExclude(ctx: APIContext, exclude: readonly StringPattern[
|
|
|
60
59
|
declare function withExcluded(middleware: MiddlewareHandler, exclude: readonly StringPattern[] | ((context: APIContext) => boolean)): MiddlewareHandler;
|
|
61
60
|
//#endregion
|
|
62
61
|
export { STATIC_EXCLUDES as a, RECOMMENDED_EXCLUDES as i, DEV_EXCLUDES as n, shouldExclude as o, ExcludePattern as r, withExcluded as s, ASTRO_STATIC_EXCLUDES as t };
|
|
63
|
-
//# sourceMappingURL=excludes-
|
|
62
|
+
//# sourceMappingURL=excludes-BDiE3eyp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excludes-BDiE3eyp.d.ts","names":[],"sources":["../src/excludes/excludes.ts"],"mappings":";;;;;;;;KAQY,iBAAiB,QAAQ,gBAAgB;;;;cAKxC,cAAc;;;;cAWd,uBAAuB;;;;cAKvB,iBAAiB;;;;;;;;;;;;;;;;cAwBjB,sBAAsB;;;;iBAKnB,cACd,KAAK,YACL,kBAAkB,oBAAoB,SAAS;;;;;;;;;;;;;;;;;;iBA4BjC,aACd,YAAY,mBACZ,kBAAkB,oBAAoB,SAAS,0BAC9C"}
|
package/dist/excludes.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as STATIC_EXCLUDES, i as RECOMMENDED_EXCLUDES, n as DEV_EXCLUDES, o as shouldExclude, r as ExcludePattern, s as withExcluded, t as ASTRO_STATIC_EXCLUDES } from "./excludes-
|
|
1
|
+
import { a as STATIC_EXCLUDES, i as RECOMMENDED_EXCLUDES, n as DEV_EXCLUDES, o as shouldExclude, r as ExcludePattern, s as withExcluded, t as ASTRO_STATIC_EXCLUDES } from "./excludes-BDiE3eyp.js";
|
|
2
2
|
export { ASTRO_STATIC_EXCLUDES, DEV_EXCLUDES, ExcludePattern, RECOMMENDED_EXCLUDES, STATIC_EXCLUDES, shouldExclude, withExcluded };
|
package/dist/health.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.d.ts","names":[],"sources":["../src/health/health.ts"],"mappings":";UAEiB
|
|
1
|
+
{"version":3,"file":"health.d.ts","names":[],"sources":["../src/health/health.ts"],"mappings":";UAEiB;EACf;EACA;EACA;;UAGe;;;;EAIf;;;;;EAMA,aAAa,QAAQ,4BAA4B;;;;;EAMjD;;;;;EAMA;;;;;;;;;iBAUc,oBAAoB,YAAY"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { r as ExcludePattern } from "./excludes-
|
|
1
|
+
import { r as ExcludePattern } from "./excludes-BDiE3eyp.js";
|
|
2
|
+
import "./construct-BGlPfWMF.js";
|
|
2
3
|
import { t as BootContext } from "./types-D0uMBi2M.js";
|
|
3
|
-
import { n as BootEventName, t as BootEventHandler } from "./events-
|
|
4
|
+
import { n as BootEventName, t as BootEventHandler } from "./events-u7J3ezJR.js";
|
|
4
5
|
import { AstroIntegration } from "astro";
|
|
5
|
-
|
|
6
6
|
//#region src/types.d.ts
|
|
7
7
|
interface NodeBootOptions {
|
|
8
8
|
/**
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/integration/integration.ts","../src/platform/prepare.ts","../src/lifecycle/lifecycle.ts"],"mappings":";;;;;;UAEiB
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/integration/integration.ts","../src/platform/prepare.ts","../src/lifecycle/lifecycle.ts"],"mappings":";;;;;;UAEiB;;;;;EAKf;;;;;;EAOA;;;;;UAMe;EACf;EACA;EACA;EACA;;UAGe;;;;;EAKf;;;;;EAMA;;;;;EAMA,QAAQ;;UAGO;;;;;EAKf,UAAU;;;;;;EAOV;;;;;;EAOA;;UAGe;;;;;EAKf;;;;;EAMA;;UAGe;;;;;EAKf,UAAU;;;;;EAMV,aAAa;;;;;;EAOb;;UAGe;;;;;;;;EAQf,OAAO;;;;;;;EAQP,UAAU;;;;;;;EAQV,YAAY;;;;;;EAOZ,SAAS;;;;;;;EAQT;IAAS,UAAU;;;;;;EAMnB;;;;;;EAOA;;;;;;;;;;iBCtFsB,KAAK,UAAS,cAAmB;;;UChExC;EACf;;;;UCJe;EACf,cAAc,SAAS,gBAAgB;EACvC,eAAe,SAAS,gBAAgB"}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,175 @@
|
|
|
1
1
|
import { n as setBootContext } from "./context-Bkg-FnnQ.js";
|
|
2
|
-
import { a as runShutdown, i as createRequestInstrumentation, o as runStartup, t as preparePlatform } from "./prepare-
|
|
3
|
-
import { n as dispatchNativeMount, t as clearNativeMounts } from "./native-mount-
|
|
2
|
+
import { a as runShutdown, i as createRequestInstrumentation, o as runStartup, t as preparePlatform } from "./prepare-CXZsyAVk.js";
|
|
3
|
+
import { n as dispatchNativeMount, t as clearNativeMounts } from "./native-mount-DjYEnO4X.js";
|
|
4
4
|
import { n as getCurrentGeneration, r as incrementGeneration, t as GEN_HEADER } from "./generation-Bp2IA0jf.js";
|
|
5
5
|
import { r as RECOMMENDED_EXCLUDES } from "./excludes-pE23EbmQ.js";
|
|
6
6
|
import fs, { readFileSync } from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { parse } from "@astrojs/compiler-rs";
|
|
9
10
|
import { Parser } from "acorn";
|
|
10
11
|
import MagicString from "magic-string";
|
|
12
|
+
//#region src/dev-mode/island-warmup.ts
|
|
13
|
+
function isAstNode(value) {
|
|
14
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
15
|
+
}
|
|
16
|
+
function walk$1(node, visit) {
|
|
17
|
+
if (Array.isArray(node)) {
|
|
18
|
+
for (const item of node) walk$1(item, visit);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (typeof node !== "object" || node === null) return;
|
|
22
|
+
if (isAstNode(node)) visit(node);
|
|
23
|
+
for (const value of Object.values(node)) walk$1(value, visit);
|
|
24
|
+
}
|
|
25
|
+
/** local binding name → import specifier, from the frontmatter program */
|
|
26
|
+
function collectImports(program) {
|
|
27
|
+
const imports = /* @__PURE__ */ new Map();
|
|
28
|
+
walk$1(program, (node) => {
|
|
29
|
+
if (node.type !== "ImportDeclaration") return;
|
|
30
|
+
const decl = node;
|
|
31
|
+
if (decl.importKind === "type") return;
|
|
32
|
+
const specifier = typeof decl.source?.value === "string" ? decl.source.value : void 0;
|
|
33
|
+
if (!specifier) return;
|
|
34
|
+
for (const spec of decl.specifiers ?? []) {
|
|
35
|
+
if (spec.importKind === "type") continue;
|
|
36
|
+
if (typeof spec.local?.name === "string") imports.set(spec.local.name, specifier);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return imports;
|
|
40
|
+
}
|
|
41
|
+
/** the root identifier of a JSX tag: `Foo` → Foo, `Ns.Chart` → Ns */
|
|
42
|
+
function tagRootIdentifier(name) {
|
|
43
|
+
let current = name;
|
|
44
|
+
while (isAstNode(current) && current.type === "JSXMemberExpression") current = current.object;
|
|
45
|
+
if (isAstNode(current) && current.type === "JSXIdentifier") {
|
|
46
|
+
const identifier = current;
|
|
47
|
+
return typeof identifier.name === "string" ? identifier.name : void 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function hasClientDirective(attributes) {
|
|
51
|
+
if (!Array.isArray(attributes)) return false;
|
|
52
|
+
return attributes.some((attr) => {
|
|
53
|
+
if (!isAstNode(attr) || attr.type !== "JSXAttribute") return false;
|
|
54
|
+
const name = attr.name;
|
|
55
|
+
return typeof name?.name === "string" && name.name.startsWith("client:");
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extract the import specifiers of all hydrated (`client:*`) components from
|
|
60
|
+
* raw `.astro` source. Astro components can't hydrate, so `.astro` specifiers
|
|
61
|
+
* are skipped; dynamic tags without a frontmatter import are invisible here.
|
|
62
|
+
*/
|
|
63
|
+
function scanAstroSource(source) {
|
|
64
|
+
const { ast } = parse(source);
|
|
65
|
+
const root = ast;
|
|
66
|
+
const imports = collectImports(root.frontmatter?.program);
|
|
67
|
+
if (imports.size === 0) return [];
|
|
68
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
69
|
+
walk$1(root.body, (node) => {
|
|
70
|
+
if (node.type !== "JSXOpeningElement") return;
|
|
71
|
+
const element = node;
|
|
72
|
+
if (!hasClientDirective(element.attributes)) return;
|
|
73
|
+
const rootName = tagRootIdentifier(element.name);
|
|
74
|
+
if (!rootName || /^[a-z]/.test(rootName)) return;
|
|
75
|
+
const specifier = imports.get(rootName);
|
|
76
|
+
if (specifier && !specifier.endsWith(".astro")) specifiers.add(specifier);
|
|
77
|
+
});
|
|
78
|
+
return [...specifiers];
|
|
79
|
+
}
|
|
80
|
+
/** scan all `.astro` files under `srcDir` for hydrated components */
|
|
81
|
+
async function scanProjectIslands(srcDir, logger) {
|
|
82
|
+
const islands = [];
|
|
83
|
+
let entries;
|
|
84
|
+
try {
|
|
85
|
+
entries = await fs.promises.readdir(srcDir, {
|
|
86
|
+
recursive: true,
|
|
87
|
+
withFileTypes: true
|
|
88
|
+
});
|
|
89
|
+
} catch {
|
|
90
|
+
return islands;
|
|
91
|
+
}
|
|
92
|
+
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".astro")).map((entry) => path.join(entry.parentPath, entry.name));
|
|
93
|
+
await Promise.all(files.map(async (file) => {
|
|
94
|
+
try {
|
|
95
|
+
const source = await fs.promises.readFile(file, "utf8");
|
|
96
|
+
for (const specifier of scanAstroSource(source)) islands.push({
|
|
97
|
+
importer: file,
|
|
98
|
+
specifier
|
|
99
|
+
});
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.debug(`island scan skipped ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
102
|
+
}
|
|
103
|
+
}));
|
|
104
|
+
return islands;
|
|
105
|
+
}
|
|
106
|
+
function packageNameOf(specifier) {
|
|
107
|
+
const segments = specifier.split("/");
|
|
108
|
+
return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? specifier;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Bare package specifiers resolvable from the project's `node_modules` — these
|
|
112
|
+
* go into `optimizeDeps.include`. Anything else (relative paths, tsconfig
|
|
113
|
+
* aliases, hoisted workspace deps) is resolved through vite at server start;
|
|
114
|
+
* misclassification is harmless, just slightly later discovery.
|
|
115
|
+
*/
|
|
116
|
+
function selectBareSpecifiers(islands, root) {
|
|
117
|
+
const bare = /* @__PURE__ */ new Set();
|
|
118
|
+
for (const { specifier } of islands) {
|
|
119
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("#")) continue;
|
|
120
|
+
if (fs.existsSync(path.join(root, "node_modules", packageNameOf(specifier)))) bare.add(specifier);
|
|
121
|
+
}
|
|
122
|
+
return [...bare];
|
|
123
|
+
}
|
|
124
|
+
function toRequestUrl(id, root) {
|
|
125
|
+
const normalized = id.split(path.sep).join("/");
|
|
126
|
+
const normalizedRoot = root.split(path.sep).join("/");
|
|
127
|
+
return normalized.startsWith(`${normalizedRoot}/`) ? normalized.slice(normalizedRoot.length) : `/@fs/${normalized}`;
|
|
128
|
+
}
|
|
129
|
+
function createIslandWarmup(options) {
|
|
130
|
+
const { root, srcDir, logger } = options;
|
|
131
|
+
let islands = [];
|
|
132
|
+
const warmIslands = async (server, batch, warmed) => {
|
|
133
|
+
const env = server.environments.client;
|
|
134
|
+
await Promise.all(batch.map(async ({ importer, specifier }) => {
|
|
135
|
+
try {
|
|
136
|
+
const resolved = await env.pluginContainer.resolveId(specifier, importer);
|
|
137
|
+
if (!resolved || resolved.external) return;
|
|
138
|
+
if (resolved.id.startsWith("\0") || resolved.id.includes("node_modules")) return;
|
|
139
|
+
if (warmed.has(resolved.id)) return;
|
|
140
|
+
warmed.add(resolved.id);
|
|
141
|
+
await env.warmupRequest(toRequestUrl(resolved.id, root));
|
|
142
|
+
} catch {}
|
|
143
|
+
}));
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
name: "@astroscope/node/island-warmup",
|
|
147
|
+
async config() {
|
|
148
|
+
islands = await scanProjectIslands(srcDir, logger);
|
|
149
|
+
const include = selectBareSpecifiers(islands, root);
|
|
150
|
+
if (islands.length > 0) logger.info(`warming ${islands.length} island(s), pre-optimizing ${include.length} dependenc(ies)`);
|
|
151
|
+
return include.length > 0 ? { optimizeDeps: { include } } : void 0;
|
|
152
|
+
},
|
|
153
|
+
configureServer(server) {
|
|
154
|
+
const warmed = /* @__PURE__ */ new Set();
|
|
155
|
+
if (server.httpServer) server.httpServer.once("listening", () => void warmIslands(server, islands, warmed));
|
|
156
|
+
else warmIslands(server, islands, warmed);
|
|
157
|
+
const onWatcherEvent = (file) => {
|
|
158
|
+
if (!file.endsWith(".astro") || !file.startsWith(srcDir)) return;
|
|
159
|
+
fs.promises.readFile(file, "utf8").then((source) => {
|
|
160
|
+
const batch = scanAstroSource(source).map((specifier) => ({
|
|
161
|
+
importer: file,
|
|
162
|
+
specifier
|
|
163
|
+
}));
|
|
164
|
+
return warmIslands(server, batch, warmed);
|
|
165
|
+
}).catch(() => {});
|
|
166
|
+
};
|
|
167
|
+
server.watcher.on("add", onWatcherEvent);
|
|
168
|
+
server.watcher.on("change", onWatcherEvent);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
11
173
|
//#region src/dev-mode/serialize-error.ts
|
|
12
174
|
function serializeError(error) {
|
|
13
175
|
if (error instanceof Error) return error.stack ?? error.message;
|
|
@@ -620,6 +782,11 @@ function node(options = {}) {
|
|
|
620
782
|
entrypoint: "@astroscope/node/dev-middleware",
|
|
621
783
|
order: "pre"
|
|
622
784
|
});
|
|
785
|
+
const islandWarmup = command === "dev" ? [createIslandWarmup({
|
|
786
|
+
root,
|
|
787
|
+
srcDir: fileURLToPath(config.srcDir),
|
|
788
|
+
logger
|
|
789
|
+
})] : [];
|
|
623
790
|
updateConfig({
|
|
624
791
|
build: { redirects: false },
|
|
625
792
|
...config.trailingSlash === "ignore" && { trailingSlash: "never" },
|
|
@@ -633,6 +800,7 @@ function node(options = {}) {
|
|
|
633
800
|
} },
|
|
634
801
|
vite: { plugins: [
|
|
635
802
|
...devMachinery,
|
|
803
|
+
...islandWarmup,
|
|
636
804
|
ssrSourcemapPlugin(),
|
|
637
805
|
stripSsrEffectsPlugin(),
|
|
638
806
|
{
|