@astroscope/node 2.1.1 → 3.0.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 +8 -5
- package/dist/boot.d.ts +2 -2
- package/dist/boot.d.ts.map +1 -1
- package/dist/csrf-middleware-entrypoint.d.ts +1 -2
- package/dist/csrf-middleware-entrypoint.d.ts.map +1 -1
- package/dist/csrf-middleware-entrypoint.js +1 -1
- package/dist/dev-middleware-entrypoint.d.ts +1 -2
- package/dist/dev-middleware-entrypoint.d.ts.map +1 -1
- package/dist/{prepare-CXZsyAVk.js → duplicate-slashes-C6hmOLl9.js} +94 -38
- package/dist/duplicate-slashes-C6hmOLl9.js.map +1 -0
- package/dist/{excludes-pE23EbmQ.js → excludes-10eDopVB.js} +18 -4
- package/dist/excludes-10eDopVB.js.map +1 -0
- package/dist/{excludes-BDiE3eyp.d.ts → excludes-rvkVGg6B.d.ts} +11 -3
- package/dist/excludes-rvkVGg6B.d.ts.map +1 -0
- package/dist/excludes.d.ts +2 -2
- package/dist/excludes.js +2 -2
- package/dist/graph-DdAxiaxa.js +35 -0
- package/dist/graph-DdAxiaxa.js.map +1 -0
- package/dist/health.d.ts +3 -4
- package/dist/health.d.ts.map +1 -1
- package/dist/image-endpoint.d.ts +1 -2
- package/dist/image-endpoint.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -117
- package/dist/index.js.map +1 -1
- package/dist/islands-middleware-entrypoint.d.ts +1 -2
- package/dist/islands-middleware-entrypoint.d.ts.map +1 -1
- package/dist/islands-middleware-entrypoint.js +3 -1
- package/dist/islands-middleware-entrypoint.js.map +1 -1
- package/dist/islands.d.ts +56 -1
- package/dist/islands.d.ts.map +1 -0
- package/dist/islands.js +3 -1
- package/dist/log/index.d.ts +5 -5
- package/dist/log/index.d.ts.map +1 -1
- package/dist/native.d.ts +5 -6
- package/dist/native.d.ts.map +1 -1
- package/dist/{prerendered-DVjzWNbW.js → prerendered-Z-Qi-FFK.js} +4 -4
- package/dist/prerendered-Z-Qi-FFK.js.map +1 -0
- package/dist/request-route-DcnZOOM4.js.map +1 -1
- package/dist/route-islands-B53rE5MH.js +73 -0
- package/dist/route-islands-B53rE5MH.js.map +1 -0
- package/dist/route-middleware-entrypoint.d.ts +1 -2
- package/dist/route-middleware-entrypoint.d.ts.map +1 -1
- package/dist/route-store-neUA2nBb.d.ts.map +1 -1
- package/dist/routes-D1o_WrJ5.js +39 -0
- package/dist/routes-D1o_WrJ5.js.map +1 -0
- package/dist/server.d.ts +2 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +68 -45
- package/dist/server.js.map +1 -1
- package/dist/telemetry-B9aziFyQ.js +202 -0
- package/dist/telemetry-B9aziFyQ.js.map +1 -0
- package/dist/telemetry.d.ts +68 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +2 -0
- package/dist/transform-Bz5z5w2Y.js +594 -0
- package/dist/transform-Bz5z5w2Y.js.map +1 -0
- package/package.json +13 -11
- package/dist/excludes-BDiE3eyp.d.ts.map +0 -1
- package/dist/excludes-pE23EbmQ.js.map +0 -1
- package/dist/prepare-CXZsyAVk.js.map +0 -1
- package/dist/prerendered-DVjzWNbW.js.map +0 -1
- package/dist/transform-BqV8SOEm.js +0 -195
- package/dist/transform-BqV8SOEm.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { n as setBootContext } from "./context-Bkg-FnnQ.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as createRequestInstrumentation, n as preparePlatform, o as runShutdown, s as runStartup, t as redirectDuplicateSlashes } from "./duplicate-slashes-C6hmOLl9.js";
|
|
3
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
|
-
import { r as RECOMMENDED_EXCLUDES } from "./excludes-
|
|
5
|
+
import { r as RECOMMENDED_EXCLUDES, s as serializeExcludePatterns } from "./excludes-10eDopVB.js";
|
|
6
|
+
import { n as routeEntrypoints, r as stripQuery, t as collectRouteIslands } from "./route-islands-B53rE5MH.js";
|
|
6
7
|
import { i as registerIslandEmitter } from "./emitters-C20tjKLp.js";
|
|
7
8
|
import { t as getRequestRouteData } from "./route-store-DdxGePj2.js";
|
|
8
9
|
import fs, { readFileSync } from "node:fs";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { defined } from "@entwico/dash";
|
|
11
13
|
import { parse } from "@astrojs/compiler-rs";
|
|
12
|
-
import { Parser } from "acorn";
|
|
13
|
-
import MagicString from "magic-string";
|
|
14
14
|
//#region src/dev-mode/island-warmup.ts
|
|
15
15
|
function isAstNode(value) {
|
|
16
16
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
17
17
|
}
|
|
18
|
-
function walk
|
|
18
|
+
function walk(node, visit) {
|
|
19
19
|
if (Array.isArray(node)) {
|
|
20
|
-
for (const item of node) walk
|
|
20
|
+
for (const item of node) walk(item, visit);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
if (typeof node !== "object" || node === null) return;
|
|
24
24
|
if (isAstNode(node)) visit(node);
|
|
25
|
-
for (const value of Object.values(node)) walk
|
|
25
|
+
for (const value of Object.values(node)) walk(value, visit);
|
|
26
26
|
}
|
|
27
27
|
/** local binding name → import specifier, from the frontmatter program */
|
|
28
28
|
function collectImports(program) {
|
|
29
29
|
const imports = /* @__PURE__ */ new Map();
|
|
30
|
-
walk
|
|
30
|
+
walk(program, (node) => {
|
|
31
31
|
if (node.type !== "ImportDeclaration") return;
|
|
32
32
|
const decl = node;
|
|
33
33
|
if (decl.importKind === "type") return;
|
|
@@ -68,7 +68,7 @@ function scanAstroSource(source) {
|
|
|
68
68
|
const imports = collectImports(root.frontmatter?.program);
|
|
69
69
|
if (imports.size === 0) return [];
|
|
70
70
|
const specifiers = /* @__PURE__ */ new Set();
|
|
71
|
-
walk
|
|
71
|
+
walk(root.body, (node) => {
|
|
72
72
|
if (node.type !== "JSXOpeningElement") return;
|
|
73
73
|
const element = node;
|
|
74
74
|
if (!hasClientDirective(element.attributes)) return;
|
|
@@ -568,23 +568,16 @@ function createDevMachinery(options) {
|
|
|
568
568
|
}
|
|
569
569
|
}];
|
|
570
570
|
}
|
|
571
|
-
//#endregion
|
|
572
|
-
//#region src/excludes/serialize.ts
|
|
573
|
-
/**
|
|
574
|
-
* Serialize exclude patterns to JavaScript code for use in virtual modules.
|
|
575
|
-
* Handles RegExp objects which JSON.stringify cannot serialize.
|
|
576
|
-
*/
|
|
577
|
-
function serializeExcludePatterns(patterns) {
|
|
578
|
-
return `[${patterns.map((p) => "pattern" in p ? `{ pattern: ${p.pattern.toString()} }` : JSON.stringify(p)).join(", ")}]`;
|
|
579
|
-
}
|
|
580
571
|
const RESOLVED_ISLANDS_VIRTUAL_MODULE_ID = `\0virtual:@astroscope/node/islands-manifest`;
|
|
581
572
|
const MANIFEST_FILE_NAME = "islands-manifest.json";
|
|
582
573
|
/**
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
574
|
+
* Build side of island preloading: the server build attributes islands to routes
|
|
575
|
+
* (its module graph is the only place a page's components are visible together),
|
|
576
|
+
* the client build records the chunk import graph (direct static and dynamic edges
|
|
577
|
+
* per chunk), embeds the gate runtime source (inlined into documents rather than
|
|
578
|
+
* shipped as an asset — an external fetch would install the gates a round-trip
|
|
579
|
+
* after astro's inline island machinery), and drops the manifest next to the
|
|
580
|
+
* server chunks, mirroring the i18n manifest.
|
|
588
581
|
*
|
|
589
582
|
* The client build runs after the server build, so the SSR bundle can only carry
|
|
590
583
|
* code that reads the file lazily; in dev there is no manifest and the middleware
|
|
@@ -592,6 +585,7 @@ const MANIFEST_FILE_NAME = "islands-manifest.json";
|
|
|
592
585
|
*/
|
|
593
586
|
function createIslandsManifestPlugin(options) {
|
|
594
587
|
let isBuild = false;
|
|
588
|
+
const routeIslands = /* @__PURE__ */ new Map();
|
|
595
589
|
return {
|
|
596
590
|
name: "@astroscope/node/islands-manifest",
|
|
597
591
|
configResolved(config) {
|
|
@@ -628,21 +622,31 @@ for (const candidate of [
|
|
|
628
622
|
export { manifest };
|
|
629
623
|
`;
|
|
630
624
|
},
|
|
625
|
+
async generateBundle() {
|
|
626
|
+
if (!options.enabled || this.environment.name === "client") return;
|
|
627
|
+
const { routes } = await collectRouteIslands(this, options.pages());
|
|
628
|
+
for (const [pattern, islands] of routes) routeIslands.set(pattern, islands);
|
|
629
|
+
},
|
|
631
630
|
writeBundle(outputOptions, bundle) {
|
|
632
631
|
if (!options.enabled || this.environment.name !== "client" || !outputOptions.dir) return;
|
|
633
632
|
const chunks = {};
|
|
633
|
+
const entryByComponent = /* @__PURE__ */ new Map();
|
|
634
634
|
for (const chunk of Object.values(bundle)) {
|
|
635
635
|
if (chunk.type !== "chunk") continue;
|
|
636
636
|
chunks[chunk.fileName] = {
|
|
637
637
|
...chunk.imports.length > 0 && { i: chunk.imports },
|
|
638
638
|
...chunk.dynamicImports.length > 0 && { d: chunk.dynamicImports }
|
|
639
639
|
};
|
|
640
|
+
if (chunk.isEntry && chunk.facadeModuleId) entryByComponent.set(stripQuery(chunk.facadeModuleId), chunk.fileName);
|
|
640
641
|
}
|
|
642
|
+
const routes = {};
|
|
643
|
+
for (const [pattern, islands] of routeIslands) routes[pattern] = [...islands].map((component) => entryByComponent.get(component)).filter(defined);
|
|
641
644
|
const runtimeSource = fs.readFileSync(new URL("./islands-runtime.iife.js", import.meta.url), "utf-8").trim();
|
|
642
645
|
if (runtimeSource.toLowerCase().includes("<\/script")) throw new Error("islands gate runtime must not contain \"<\/script\"");
|
|
643
646
|
const manifest = {
|
|
644
647
|
runtimeSource,
|
|
645
|
-
chunks
|
|
648
|
+
chunks,
|
|
649
|
+
routes
|
|
646
650
|
};
|
|
647
651
|
const chunksDir = path.resolve(outputOptions.dir, "..", "server", "chunks");
|
|
648
652
|
if (fs.existsSync(chunksDir)) fs.writeFileSync(path.join(chunksDir, MANIFEST_FILE_NAME), JSON.stringify(manifest));
|
|
@@ -671,94 +675,6 @@ function ssrSourcemapPlugin() {
|
|
|
671
675
|
};
|
|
672
676
|
}
|
|
673
677
|
//#endregion
|
|
674
|
-
//#region src/tweaks/strip-effects.ts
|
|
675
|
-
const HOOK_NAMES = /* @__PURE__ */ new Set([
|
|
676
|
-
"useEffect",
|
|
677
|
-
"useLayoutEffect",
|
|
678
|
-
"useInsertionEffect"
|
|
679
|
-
]);
|
|
680
|
-
const REACT_SOURCE = /^react(\/.*)?$/;
|
|
681
|
-
const TRANSFORMABLE = /\.(?:[mc]?[jt]sx?)$/;
|
|
682
|
-
const EMPTY_FN = "(()=>{})";
|
|
683
|
-
/**
|
|
684
|
-
* in SSR builds, react effect hooks never execute. emptying their callbacks
|
|
685
|
-
* lets rolldown drop dead branches — including dynamic imports of client-only
|
|
686
|
-
* libs (maplibre-gl, hls.js, etc.) — from the server bundle, which in turn
|
|
687
|
-
* stops nft from tracing them at docker-image time.
|
|
688
|
-
*
|
|
689
|
-
* scope is deliberately narrow: first-party code only (no node_modules), only
|
|
690
|
-
* in the SSR pass, and binding-aware (the React import must resolve to the
|
|
691
|
-
* real react package). raw chunks where bundling has erased the binding are
|
|
692
|
-
* left alone — that's NFT's domain, not ours.
|
|
693
|
-
*/
|
|
694
|
-
function stripSsrEffectsPlugin() {
|
|
695
|
-
return {
|
|
696
|
-
name: "@astroscope/node/tweaks/strip-effects",
|
|
697
|
-
enforce: "post",
|
|
698
|
-
transform(code, id, options) {
|
|
699
|
-
if (!options?.ssr) return null;
|
|
700
|
-
if (id.includes("/node_modules/")) return null;
|
|
701
|
-
const cleanId = id.split("?")[0] ?? id;
|
|
702
|
-
if (!TRANSFORMABLE.test(cleanId)) return null;
|
|
703
|
-
if (!code.includes("useEffect") && !code.includes("useLayoutEffect") && !code.includes("useInsertionEffect")) return null;
|
|
704
|
-
let ast;
|
|
705
|
-
try {
|
|
706
|
-
ast = Parser.parse(code, {
|
|
707
|
-
ecmaVersion: "latest",
|
|
708
|
-
sourceType: "module",
|
|
709
|
-
allowReturnOutsideFunction: true,
|
|
710
|
-
allowAwaitOutsideFunction: true,
|
|
711
|
-
allowImportExportEverywhere: true,
|
|
712
|
-
allowHashBang: true
|
|
713
|
-
});
|
|
714
|
-
} catch {
|
|
715
|
-
return null;
|
|
716
|
-
}
|
|
717
|
-
const directHooks = /* @__PURE__ */ new Set();
|
|
718
|
-
const namespaceHooks = /* @__PURE__ */ new Set();
|
|
719
|
-
walk(ast, (node) => {
|
|
720
|
-
if (node.type !== "ImportDeclaration") return;
|
|
721
|
-
const src = node.source?.value;
|
|
722
|
-
if (typeof src !== "string" || !REACT_SOURCE.test(src)) return;
|
|
723
|
-
for (const spec of node.specifiers ?? []) if (spec.type === "ImportSpecifier") {
|
|
724
|
-
const imported = spec.imported?.name ?? spec.imported?.value;
|
|
725
|
-
if (HOOK_NAMES.has(imported)) directHooks.add(spec.local.name);
|
|
726
|
-
} else if (spec.type === "ImportDefaultSpecifier" || spec.type === "ImportNamespaceSpecifier") namespaceHooks.add(spec.local.name);
|
|
727
|
-
});
|
|
728
|
-
if (directHooks.size === 0 && namespaceHooks.size === 0) return null;
|
|
729
|
-
const replacements = [];
|
|
730
|
-
walk(ast, (node) => {
|
|
731
|
-
if (node.type !== "CallExpression" || !node.arguments?.length) return;
|
|
732
|
-
const callee = node.callee;
|
|
733
|
-
const isDirect = callee.type === "Identifier" && directHooks.has(callee.name);
|
|
734
|
-
const isMember = callee.type === "MemberExpression" && !callee.computed && callee.object?.type === "Identifier" && namespaceHooks.has(callee.object.name) && callee.property?.type === "Identifier" && HOOK_NAMES.has(callee.property.name);
|
|
735
|
-
if (!isDirect && !isMember) return;
|
|
736
|
-
const arg = node.arguments[0];
|
|
737
|
-
replacements.push({
|
|
738
|
-
start: arg.start,
|
|
739
|
-
end: arg.end
|
|
740
|
-
});
|
|
741
|
-
});
|
|
742
|
-
if (replacements.length === 0) return null;
|
|
743
|
-
const s = new MagicString(code);
|
|
744
|
-
for (const { start, end } of replacements) s.overwrite(start, end, EMPTY_FN);
|
|
745
|
-
return {
|
|
746
|
-
code: s.toString(),
|
|
747
|
-
map: s.generateMap({ hires: true }).toString()
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
function walk(node, visit) {
|
|
753
|
-
if (!node || typeof node !== "object") return;
|
|
754
|
-
if (typeof node.type === "string") visit(node);
|
|
755
|
-
for (const key of Object.keys(node)) {
|
|
756
|
-
const v = node[key];
|
|
757
|
-
if (Array.isArray(v)) for (const item of v) walk(item, visit);
|
|
758
|
-
else if (v && typeof v === "object") walk(v, visit);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
//#endregion
|
|
762
678
|
//#region src/integration/integration.ts
|
|
763
679
|
const CONFIG_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/config";
|
|
764
680
|
const BOOT_VIRTUAL_MODULE_ID = "virtual:@astroscope/node/boot";
|
|
@@ -813,6 +729,7 @@ function node(options = {}) {
|
|
|
813
729
|
const loggingExclude = loggingOptions ? loggingOptions.exclude ?? DEFAULT_REQUEST_EXCLUDES : [];
|
|
814
730
|
const telemetryExclude = telemetryOptions ? telemetryOptions.exclude ?? DEFAULT_REQUEST_EXCLUDES : [];
|
|
815
731
|
let astroConfig = null;
|
|
732
|
+
let resolvedRoutes = [];
|
|
816
733
|
let bootEntry;
|
|
817
734
|
let configSeam;
|
|
818
735
|
let instrumentationSeam;
|
|
@@ -845,7 +762,7 @@ function node(options = {}) {
|
|
|
845
762
|
const devMachinery = command === "dev" && bootOptions !== false ? createDevMachinery({
|
|
846
763
|
entry: bootEntry ? path.relative(root, bootEntry) : void 0,
|
|
847
764
|
watch,
|
|
848
|
-
watchEntries: [relativeSeam(configSeam), relativeSeam(logSeam)].filter(
|
|
765
|
+
watchEntries: [relativeSeam(configSeam), relativeSeam(logSeam)].filter(defined),
|
|
849
766
|
prepare: (importModule) => preparePlatform({
|
|
850
767
|
dev: true,
|
|
851
768
|
telemetry: telemetryOptions && telemetryOptions.dev ? { prometheus: telemetryOptions.prometheus ?? {} } : false,
|
|
@@ -891,10 +808,10 @@ function node(options = {}) {
|
|
|
891
808
|
...islandWarmup,
|
|
892
809
|
createIslandsManifestPlugin({
|
|
893
810
|
logger,
|
|
894
|
-
enabled: islandsEnabled
|
|
811
|
+
enabled: islandsEnabled,
|
|
812
|
+
pages: () => routeEntrypoints(root, resolvedRoutes)
|
|
895
813
|
}),
|
|
896
814
|
ssrSourcemapPlugin(),
|
|
897
|
-
stripSsrEffectsPlugin(),
|
|
898
815
|
{
|
|
899
816
|
name: "@astroscope/node",
|
|
900
817
|
configEnvironment(environmentName) {
|
|
@@ -951,6 +868,7 @@ function node(options = {}) {
|
|
|
951
868
|
}) : void 0;
|
|
952
869
|
server.middlewares.use((req, res, next) => {
|
|
953
870
|
const inner = () => {
|
|
871
|
+
if (redirectDuplicateSlashes(req, res)) return;
|
|
954
872
|
if (!dispatchNativeMount(req, res)) next();
|
|
955
873
|
};
|
|
956
874
|
if (instrument) instrument(req, res, inner);
|
|
@@ -978,14 +896,17 @@ function node(options = {}) {
|
|
|
978
896
|
}
|
|
979
897
|
});
|
|
980
898
|
},
|
|
899
|
+
"astro:routes:resolved": ({ routes }) => {
|
|
900
|
+
resolvedRoutes = routes;
|
|
901
|
+
},
|
|
981
902
|
"astro:build:done": async ({ logger }) => {
|
|
982
903
|
if (!astroConfig) return;
|
|
983
904
|
if (islandsEnabled) {
|
|
984
905
|
const manifestPath = path.join(fileURLToPath(astroConfig.build.server), "chunks", "islands-manifest.json");
|
|
985
906
|
if (fs.existsSync(manifestPath)) {
|
|
986
|
-
const { transformPrerenderedHtml } = await import("./prerendered-
|
|
907
|
+
const { transformPrerenderedHtml } = await import("./prerendered-Z-Qi-FFK.js");
|
|
987
908
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
988
|
-
const transformed =
|
|
909
|
+
const transformed = transformPrerenderedHtml(fileURLToPath(astroConfig.build.client), manifest);
|
|
989
910
|
if (transformed > 0) logger.info(`island preloading applied to ${transformed} prerendered page(s)`);
|
|
990
911
|
}
|
|
991
912
|
}
|