veryfront 0.1.1102 → 0.1.1103
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/esm/deno.js +1 -1
- package/esm/src/data/helpers.d.ts +20 -3
- package/esm/src/data/helpers.d.ts.map +1 -1
- package/esm/src/data/helpers.js +46 -12
- package/esm/src/data/static-data-fetcher.d.ts.map +1 -1
- package/esm/src/data/static-data-fetcher.js +15 -0
- package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
- package/esm/src/html/hydration-script-builder/templates/renderer.js +52 -28
- package/esm/src/modules/server/module-server.d.ts.map +1 -1
- package/esm/src/modules/server/module-server.js +15 -9
- package/esm/src/rendering/orchestrator/module-loader/build-failure.d.ts +20 -0
- package/esm/src/rendering/orchestrator/module-loader/build-failure.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/module-loader/build-failure.js +26 -0
- package/esm/src/rendering/orchestrator/module-loader/index.d.ts +2 -1
- package/esm/src/rendering/orchestrator/module-loader/index.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-loader/index.js +69 -7
- package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts +6 -0
- package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-loader/module-persistence.js +49 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +10 -1
- package/esm/src/routing/api/context-builder.d.ts +26 -15
- package/esm/src/routing/api/context-builder.d.ts.map +1 -1
- package/esm/src/routing/api/context-builder.js +34 -15
- package/esm/src/routing/api/module-loader/loader.js +57 -1
- package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
- package/esm/src/server/dev-server/middleware.js +29 -6
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/ssr/ssr.handler.js +6 -4
- package/esm/src/transforms/esm/import-attributes.d.ts +32 -0
- package/esm/src/transforms/esm/import-attributes.d.ts.map +1 -0
- package/esm/src/transforms/esm/import-attributes.js +116 -0
- package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-parser.js +68 -18
- package/esm/src/transforms/esm/lexer.d.ts +18 -0
- package/esm/src/transforms/esm/lexer.d.ts.map +1 -1
- package/esm/src/transforms/esm/lexer.js +21 -0
- package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
- package/esm/src/transforms/esm/specifier-resolver.js +13 -0
- package/esm/src/transforms/esm/transform-utils.d.ts +8 -0
- package/esm/src/transforms/esm/transform-utils.d.ts.map +1 -1
- package/esm/src/transforms/esm/transform-utils.js +11 -1
- package/esm/src/transforms/import-rewriter/project-paths.d.ts +26 -0
- package/esm/src/transforms/import-rewriter/project-paths.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/project-paths.js +49 -0
- package/esm/src/transforms/import-rewriter/strategies/alias-strategy.d.ts +0 -1
- package/esm/src/transforms/import-rewriter/strategies/alias-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/alias-strategy.js +5 -20
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +1 -1
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +62 -12
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +19 -3
- package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.d.ts +11 -0
- package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.js +13 -0
- package/esm/src/transforms/import-rewriter/strategies/relative-strategy.d.ts +0 -1
- package/esm/src/transforms/import-rewriter/strategies/relative-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/relative-strategy.js +2 -16
- package/esm/src/transforms/import-rewriter/strategies/veryfront-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/veryfront-strategy.js +4 -3
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +4 -2
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -7
- package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.js +5 -0
- package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts +25 -3
- package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.js +285 -44
- package/esm/src/transforms/pipeline/stages/compile.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/compile.js +6 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +9 -5
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +23 -9
- package/esm/src/transforms/shared/server-only-packages.d.ts +30 -0
- package/esm/src/transforms/shared/server-only-packages.d.ts.map +1 -0
- package/esm/src/transforms/shared/server-only-packages.js +47 -0
- package/esm/src/types/entities/getEntityInfo.d.ts.map +1 -1
- package/esm/src/types/entities/getEntityInfo.js +10 -2
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +5 -5
package/esm/deno.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import type { DataResult } from "./types.js";
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Redirect the request from a data loader.
|
|
4
|
+
*
|
|
5
|
+
* Return it or throw it. `throw redirect("/login")` behaves exactly like
|
|
6
|
+
* `return redirect("/login")`.
|
|
7
|
+
*/
|
|
3
8
|
export declare function redirect(destination: string, permanent?: boolean): DataResult;
|
|
4
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Render the 404 page from a data loader.
|
|
11
|
+
*
|
|
12
|
+
* Return it or throw it. `throw notFound()` behaves exactly like
|
|
13
|
+
* `return notFound()`, which is useful deep inside a helper that has no clean
|
|
14
|
+
* way to return to the loader.
|
|
15
|
+
*/
|
|
5
16
|
export declare function notFound(): DataResult;
|
|
6
17
|
/**
|
|
7
18
|
* True when `value` is a control-flow result produced by {@link notFound} or
|
|
@@ -11,8 +22,14 @@ export declare function notFound(): DataResult;
|
|
|
11
22
|
* naturally and is what people coming from other frameworks reach for. Thrown,
|
|
12
23
|
* the plain object is not an `Error`, so the SSR error handler stringified it
|
|
13
24
|
* to `[object Object]` and returned a 500 instead of the intended 404 or
|
|
14
|
-
* redirect. Recognising the
|
|
25
|
+
* redirect. Recognising the brand lets a thrown result behave like a returned
|
|
15
26
|
* one.
|
|
27
|
+
*
|
|
28
|
+
* The check is on the brand, never on the shape. A loader that does
|
|
29
|
+
* `throw await response.json()` against an upstream answering
|
|
30
|
+
* `{ notFound: true, message: "record locked" }` is reporting a failure, and
|
|
31
|
+
* reading that as a 404 would render the wrong page, log nothing, and cache a
|
|
32
|
+
* 404 the site never asked for.
|
|
16
33
|
*/
|
|
17
34
|
export declare function isDataControlResult(value: unknown): value is DataResult;
|
|
18
35
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/src/data/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/src/data/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA2B7C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,UAAU,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,IAAI,UAAU,CAErC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAIvE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAGlE"}
|
package/esm/src/data/helpers.js
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Brand marking an object as produced by {@link notFound} or {@link redirect}.
|
|
3
|
+
*
|
|
4
|
+
* A registered symbol, so a result built by one copy of this module is
|
|
5
|
+
* recognised by another. Project code and the framework do not always share a
|
|
6
|
+
* module instance, and isolated data fetching crosses a realm boundary.
|
|
7
|
+
*
|
|
8
|
+
* Symbols are dropped by `structuredClone`, so the brand does not survive
|
|
9
|
+
* `postMessage`. Worker-side code normalises a thrown control result before it
|
|
10
|
+
* is posted back, while the object is still in-realm.
|
|
11
|
+
*/
|
|
12
|
+
const DATA_CONTROL_RESULT = Symbol.for("veryfront.dataControlResult");
|
|
13
|
+
/**
|
|
14
|
+
* Mark a result as framework-produced control flow.
|
|
15
|
+
*
|
|
16
|
+
* The brand is non-enumerable, so it stays out of `Object.keys`,
|
|
17
|
+
* `JSON.stringify`, and the `DataResult` schema. A returned control result
|
|
18
|
+
* behaves exactly as it did before the brand existed.
|
|
19
|
+
*/
|
|
20
|
+
function brandDataControlResult(result) {
|
|
21
|
+
Object.defineProperty(result, DATA_CONTROL_RESULT, { value: true });
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Redirect the request from a data loader.
|
|
26
|
+
*
|
|
27
|
+
* Return it or throw it. `throw redirect("/login")` behaves exactly like
|
|
28
|
+
* `return redirect("/login")`.
|
|
29
|
+
*/
|
|
2
30
|
export function redirect(destination, permanent = false) {
|
|
3
|
-
return { redirect: { destination, permanent } };
|
|
31
|
+
return brandDataControlResult({ redirect: { destination, permanent } });
|
|
4
32
|
}
|
|
5
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Render the 404 page from a data loader.
|
|
35
|
+
*
|
|
36
|
+
* Return it or throw it. `throw notFound()` behaves exactly like
|
|
37
|
+
* `return notFound()`, which is useful deep inside a helper that has no clean
|
|
38
|
+
* way to return to the loader.
|
|
39
|
+
*/
|
|
6
40
|
export function notFound() {
|
|
7
|
-
return { notFound: true };
|
|
41
|
+
return brandDataControlResult({ notFound: true });
|
|
8
42
|
}
|
|
9
43
|
/**
|
|
10
44
|
* True when `value` is a control-flow result produced by {@link notFound} or
|
|
@@ -14,19 +48,19 @@ export function notFound() {
|
|
|
14
48
|
* naturally and is what people coming from other frameworks reach for. Thrown,
|
|
15
49
|
* the plain object is not an `Error`, so the SSR error handler stringified it
|
|
16
50
|
* to `[object Object]` and returned a 500 instead of the intended 404 or
|
|
17
|
-
* redirect. Recognising the
|
|
51
|
+
* redirect. Recognising the brand lets a thrown result behave like a returned
|
|
18
52
|
* one.
|
|
53
|
+
*
|
|
54
|
+
* The check is on the brand, never on the shape. A loader that does
|
|
55
|
+
* `throw await response.json()` against an upstream answering
|
|
56
|
+
* `{ notFound: true, message: "record locked" }` is reporting a failure, and
|
|
57
|
+
* reading that as a 404 would render the wrong page, log nothing, and cache a
|
|
58
|
+
* 404 the site never asked for.
|
|
19
59
|
*/
|
|
20
60
|
export function isDataControlResult(value) {
|
|
21
61
|
if (value === null || typeof value !== "object")
|
|
22
62
|
return false;
|
|
23
|
-
|
|
24
|
-
return false;
|
|
25
|
-
const candidate = value;
|
|
26
|
-
if (candidate.notFound === true)
|
|
27
|
-
return true;
|
|
28
|
-
const destination = candidate.redirect?.destination;
|
|
29
|
-
return typeof destination === "string";
|
|
63
|
+
return value[DATA_CONTROL_RESULT] === true;
|
|
30
64
|
}
|
|
31
65
|
/**
|
|
32
66
|
* Reduce a thrown control result to the shape a returned one produces.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/static-data-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA2DxE,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,YAAY;IAFhC,OAAO,CAAC,oBAAoB,CAAoC;gBAE5C,YAAY,EAAE,YAAY;IAExC,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IA4ChF,OAAO,CAAC,uBAAuB;YAMjB,iBAAiB;IAuB/B,OAAO,CAAC,eAAe;YAQT,iBAAiB;YA+BjB,UAAU;YAqEV,sBAAsB;
|
|
1
|
+
{"version":3,"file":"static-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/static-data-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA2DxE,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,YAAY;IAFhC,OAAO,CAAC,oBAAoB,CAAoC;gBAE5C,YAAY,EAAE,YAAY;IAExC,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IA4ChF,OAAO,CAAC,uBAAuB;YAMjB,iBAAiB;IAuB/B,OAAO,CAAC,eAAe;YAQT,iBAAiB;YA+BjB,UAAU;YAqEV,sBAAsB;IAuFpC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;CAIjB"}
|
|
@@ -188,6 +188,21 @@ export class StaticDataFetcher {
|
|
|
188
188
|
const start = performance.now();
|
|
189
189
|
try {
|
|
190
190
|
const result = await this.executeStaticData(getStaticData, context, REVALIDATION_TIMEOUT_MS, `getStaticData revalidation for ${pathname}`);
|
|
191
|
+
// A background revalidation refreshes a page that is already being
|
|
192
|
+
// served. A notFound or redirect is not a refreshed page, so keep
|
|
193
|
+
// the entry that is live and let the next revalidation try again.
|
|
194
|
+
// Storing it would serve a 404 for the previously healthy page, and
|
|
195
|
+
// a control result carries no revalidate interval, so the entry
|
|
196
|
+
// would never revalidate again either.
|
|
197
|
+
if (result.notFound || result.redirect) {
|
|
198
|
+
serverLogger.warn("DATA_REVALIDATION_CONTROL_RESULT background revalidation returned a control result, keeping the cached entry", {
|
|
199
|
+
pathname,
|
|
200
|
+
durationMs: Math.round(performance.now() - start),
|
|
201
|
+
cacheKey,
|
|
202
|
+
control: result.notFound ? "notFound" : "redirect",
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
191
206
|
this.storeCacheEntry(cacheKey, result);
|
|
192
207
|
}
|
|
193
208
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/renderer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/renderer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,cAoT7B,CAAC"}
|
|
@@ -3,17 +3,59 @@ export const getRendererScript = () => `
|
|
|
3
3
|
|
|
4
4
|
// True when a dynamic import failed because the module could not be
|
|
5
5
|
// fetched (404 / network), as opposed to being fetched and then failing to
|
|
6
|
-
// link or evaluate. Browsers
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// link or evaluate. Browsers word this as a TypeError naming the dynamic
|
|
7
|
+
// import itself; link failures are SyntaxErrors and evaluation failures are
|
|
8
|
+
// whatever the module threw. The wording is matched against the dynamic
|
|
9
|
+
// import phrases only, so app code throwing "Failed to load user profile"
|
|
10
|
+
// at module scope is not mistaken for a missing module.
|
|
9
11
|
function isModuleNotFoundError(error) {
|
|
10
12
|
if (!error) return false;
|
|
11
13
|
if (error instanceof SyntaxError) return false;
|
|
12
14
|
const message = String((error && error.message) || error);
|
|
13
|
-
return /(?:
|
|
15
|
+
return /(?:dynamically imported module|Importing a module script failed|Failed to load module script)/i
|
|
14
16
|
.test(message);
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
// Picks the error that describes the failure best. An error that proves a
|
|
20
|
+
// module was reached (link or evaluation) always beats one that only proves
|
|
21
|
+
// a URL could not be fetched, because a 404 on a path that was never
|
|
22
|
+
// expected to exist explains nothing. Otherwise the earlier error wins: it
|
|
23
|
+
// names the module the router actually intended to load.
|
|
24
|
+
function preferReachedModuleError(earlier, later) {
|
|
25
|
+
if (!earlier) return later;
|
|
26
|
+
if (!later) return earlier;
|
|
27
|
+
if (isModuleNotFoundError(earlier) && !isModuleNotFoundError(later)) return later;
|
|
28
|
+
return earlier;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Loads a Pages Router page, retrying at <route>/index.js because both
|
|
32
|
+
// pages/about.tsx and pages/about/index.tsx are valid sources for the same
|
|
33
|
+
// route. The retry is unconditional: gating it on the wording of the
|
|
34
|
+
// first rejection turned any unrecognized wording into a blank page. Error
|
|
35
|
+
// selection, not the retry, is what must stay precise.
|
|
36
|
+
async function loadPageModuleWithIndexFallback(
|
|
37
|
+
basePath,
|
|
38
|
+
pageSlug,
|
|
39
|
+
pageModuleError,
|
|
40
|
+
importModule,
|
|
41
|
+
) {
|
|
42
|
+
try {
|
|
43
|
+
return await importModule(basePath + '.js');
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const routeError = preferReachedModuleError(pageModuleError, error);
|
|
46
|
+
|
|
47
|
+
// An index slug already resolves to <route>/index.js, so the retry
|
|
48
|
+
// would ask for <route>/index/index.js.
|
|
49
|
+
if (pageSlug === 'index' || pageSlug.endsWith('/index')) throw routeError;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
return await importModule(basePath + '/index.js');
|
|
53
|
+
} catch (indexError) {
|
|
54
|
+
throw preferReachedModuleError(routeError, indexError);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
17
59
|
async function renderPage(pathname) {
|
|
18
60
|
const resolvedPathname = (() => {
|
|
19
61
|
const input = typeof pathname === 'string' ? pathname : window.location.pathname;
|
|
@@ -128,30 +170,12 @@ export const getRendererScript = () => `
|
|
|
128
170
|
const prefix = pageSlug.startsWith('@/') ? '' : '/pages';
|
|
129
171
|
const basePath = MODULE_SERVER_URL + prefix + '/' + pageSlug;
|
|
130
172
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
pageModuleError
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// be found. If it was found but failed to link or evaluate, retrying
|
|
138
|
-
// a path that does not exist replaces a precise error ("does not
|
|
139
|
-
// provide an export named 'createHash'") with a misleading 404.
|
|
140
|
-
const canRetryAsIndex = isModuleNotFoundError(error) &&
|
|
141
|
-
pageSlug !== 'index' && !pageSlug.endsWith('/index');
|
|
142
|
-
|
|
143
|
-
if (!canRetryAsIndex) throw pageModuleError;
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
pageModule = await import(basePath + '/index.js');
|
|
147
|
-
} catch (indexError) {
|
|
148
|
-
// For a real <route>/index.tsx page, the first 404 was expected
|
|
149
|
-
// and this retry is the path that matters: if it reached a module
|
|
150
|
-
// and failed to link or evaluate, its error is the real one. Only
|
|
151
|
-
// when the retry 404s as well does the original describe more.
|
|
152
|
-
throw isModuleNotFoundError(indexError) ? pageModuleError : indexError;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
173
|
+
pageModule = await loadPageModuleWithIndexFallback(
|
|
174
|
+
basePath,
|
|
175
|
+
pageSlug,
|
|
176
|
+
pageModuleError,
|
|
177
|
+
(moduleUrl) => import(moduleUrl),
|
|
178
|
+
);
|
|
155
179
|
}
|
|
156
180
|
|
|
157
181
|
if (!pageModule) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-server.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-server.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AA2DtE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"module-server.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-server.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AA2DtE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyDrD,CAAC;AAyEF,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqczF;AAwWD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGrD;AAiBD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA8BlE"}
|
|
@@ -87,8 +87,9 @@ export default {};
|
|
|
87
87
|
].join("\n") + "\n",
|
|
88
88
|
"_dnt.polyfills": `export default {};\n`,
|
|
89
89
|
// Deno import-map alias stub for browser/HTTP-served framework modules.
|
|
90
|
-
// Must be a JS module (not JSON)
|
|
91
|
-
//
|
|
90
|
+
// Must be a JS module (not JSON): a browser refuses a JSON module unless the
|
|
91
|
+
// importer carries `with { type: "json" }`, so serving JS keeps the stub
|
|
92
|
+
// independent of how far import attribute support has reached the browser.
|
|
92
93
|
"_veryfront/_deno-config": `export default ${JSON.stringify({ version: VERSION })};\n`,
|
|
93
94
|
// dnt rewrites #deno-config to relative deno.js in npm framework modules.
|
|
94
95
|
"deno": `export default ${JSON.stringify({ version: VERSION })};\n`,
|
|
@@ -795,17 +796,22 @@ const COMPILED_TO_JS_EXTENSIONS = /\.(?:tsx?|jsx|mdx|md)$/;
|
|
|
795
796
|
*/
|
|
796
797
|
export function getDevModuleContentType(modulePath) {
|
|
797
798
|
const normalizedPath = modulePath.toLowerCase();
|
|
798
|
-
|
|
799
|
+
// The import rewriter appends `.js` to any specifier whose extension it does
|
|
800
|
+
// not recognise, so `@/lib/data.json` arrives here as `lib/data.json.js`
|
|
801
|
+
// while the source file, and therefore the body, is still raw JSON. Resolve
|
|
802
|
+
// the source extension the same way the module lookup does before deciding.
|
|
803
|
+
const sourcePath = normalizedPath.replace(/\.(?:mjs|js)$/, "");
|
|
804
|
+
if (sourcePath.endsWith(".map") || sourcePath.endsWith(".json")) {
|
|
799
805
|
return "application/json; charset=utf-8";
|
|
800
806
|
}
|
|
801
|
-
if (
|
|
807
|
+
if (sourcePath.endsWith(".css")) {
|
|
802
808
|
return "text/css; charset=utf-8";
|
|
803
809
|
}
|
|
804
|
-
// The request path
|
|
805
|
-
//
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
if (COMPILED_TO_JS_EXTENSIONS.test(
|
|
810
|
+
// The request path can carry a source extension, but the body served for one
|
|
811
|
+
// is the compiled JavaScript. Typing the response from the source extension
|
|
812
|
+
// yields `application/typescript`, which browsers refuse to execute as a
|
|
813
|
+
// module under strict MIME checking.
|
|
814
|
+
if (COMPILED_TO_JS_EXTENSIONS.test(sourcePath)) {
|
|
809
815
|
return "application/javascript; charset=utf-8";
|
|
810
816
|
}
|
|
811
817
|
const detected = getContentTypeForPath(normalizedPath);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-failure tagging for module loads.
|
|
3
|
+
*
|
|
4
|
+
* A page module can fail for two very different reasons, and callers need to
|
|
5
|
+
* tell them apart:
|
|
6
|
+
*
|
|
7
|
+
* - The source could not be compiled or resolved. That is a developer-facing
|
|
8
|
+
* build failure, and the message says how to fix it.
|
|
9
|
+
* - The module compiled, ran, and threw at module scope (a missing environment
|
|
10
|
+
* variable, a rejected top-level `await`). That is an ordinary application
|
|
11
|
+
* error, and a project's own error page should present it.
|
|
12
|
+
*
|
|
13
|
+
* Only the loader is in a position to know which happened, so it tags the
|
|
14
|
+
* error at the point of failure instead of leaving later layers to infer it.
|
|
15
|
+
*/
|
|
16
|
+
/** Tag `error` as a build failure and return it. */
|
|
17
|
+
export declare function markBuildFailure(error: unknown): unknown;
|
|
18
|
+
/** True when `error` was raised while compiling or resolving project source. */
|
|
19
|
+
export declare function isBuildFailure(error: unknown): boolean;
|
|
20
|
+
//# sourceMappingURL=build-failure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-failure.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/build-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGxD;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEtD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-failure tagging for module loads.
|
|
3
|
+
*
|
|
4
|
+
* A page module can fail for two very different reasons, and callers need to
|
|
5
|
+
* tell them apart:
|
|
6
|
+
*
|
|
7
|
+
* - The source could not be compiled or resolved. That is a developer-facing
|
|
8
|
+
* build failure, and the message says how to fix it.
|
|
9
|
+
* - The module compiled, ran, and threw at module scope (a missing environment
|
|
10
|
+
* variable, a rejected top-level `await`). That is an ordinary application
|
|
11
|
+
* error, and a project's own error page should present it.
|
|
12
|
+
*
|
|
13
|
+
* Only the loader is in a position to know which happened, so it tags the
|
|
14
|
+
* error at the point of failure instead of leaving later layers to infer it.
|
|
15
|
+
*/
|
|
16
|
+
const BUILD_FAILURE = Symbol.for("veryfront.module-loader.build-failure");
|
|
17
|
+
/** Tag `error` as a build failure and return it. */
|
|
18
|
+
export function markBuildFailure(error) {
|
|
19
|
+
if (error instanceof Error)
|
|
20
|
+
error[BUILD_FAILURE] = true;
|
|
21
|
+
return error;
|
|
22
|
+
}
|
|
23
|
+
/** True when `error` was raised while compiling or resolving project source. */
|
|
24
|
+
export function isBuildFailure(error) {
|
|
25
|
+
return error instanceof Error && error[BUILD_FAILURE] === true;
|
|
26
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @module rendering/orchestrator/module-loader
|
|
8
8
|
*/
|
|
9
9
|
import type { RuntimeAdapter } from "../../../platform/adapters/base.js";
|
|
10
|
+
export { isBuildFailure } from "./build-failure.js";
|
|
10
11
|
export { createEsmCache, createModuleCache, generateHash } from "./cache.js";
|
|
11
12
|
export { fetchEsmModule, rewriteEsmPaths } from "./esm-rewriter.js";
|
|
12
13
|
/**
|
|
@@ -19,7 +20,7 @@ export { fetchEsmModule, rewriteEsmPaths } from "./esm-rewriter.js";
|
|
|
19
20
|
* @param useLocalAdapter - Whether to use local adapter for reading
|
|
20
21
|
* @returns Path to the transformed module file
|
|
21
22
|
*/
|
|
22
|
-
export declare function transformModuleWithDeps(filePath: string, tmpDir: string, localAdapter: RuntimeAdapter, config: ModuleLoaderConfig, useLocalAdapter?: boolean): Promise<string>;
|
|
23
|
+
export declare function transformModuleWithDeps(filePath: string, tmpDir: string, localAdapter: RuntimeAdapter, config: ModuleLoaderConfig, useLocalAdapter?: boolean, lineage?: ReadonlySet<string>, cycleTargets?: Set<string>): Promise<string>;
|
|
23
24
|
export interface ModuleLoaderConfig {
|
|
24
25
|
projectDir: string;
|
|
25
26
|
projectId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAgBzE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKpD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAOpE;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,cAAc,EAC5B,MAAM,EAAE,kBAAkB,EAC1B,eAAe,UAAQ,EACvB,OAAO,GAAE,WAAW,CAAC,MAAM,CAAa,EAKxC,YAAY,GAAE,GAAG,CAAC,MAAM,CAAa,GACpC,OAAO,CAAC,MAAM,CAAC,CAyIjB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAyBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI5D;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAuFlC"}
|
|
@@ -16,6 +16,8 @@ import { resolveModuleDependencies, rewriteResolvedDependencyImports, } from "./
|
|
|
16
16
|
import { persistTransformedModule } from "./module-persistence.js";
|
|
17
17
|
import { transformModuleCodeWithCache } from "./module-transform-cache.js";
|
|
18
18
|
import { getModuleCacheKey, resolveCachedModulePath } from "./module-cache-lookup.js";
|
|
19
|
+
import { markBuildFailure } from "./build-failure.js";
|
|
20
|
+
export { isBuildFailure } from "./build-failure.js";
|
|
19
21
|
const logger = rendererLogger.component("module-loader");
|
|
20
22
|
// Re-export utilities
|
|
21
23
|
export { createEsmCache, createModuleCache, generateHash } from "./cache.js";
|
|
@@ -35,7 +37,12 @@ function decodeFileContent(fileContent) {
|
|
|
35
37
|
* @param useLocalAdapter - Whether to use local adapter for reading
|
|
36
38
|
* @returns Path to the transformed module file
|
|
37
39
|
*/
|
|
38
|
-
export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, config, useLocalAdapter = false)
|
|
40
|
+
export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, config, useLocalAdapter = false, lineage = new Set(),
|
|
41
|
+
// Shared by reference across the whole transform tree (unlike `lineage`, which
|
|
42
|
+
// is copied per level): a descendant records a cycle target here, and the
|
|
43
|
+
// ancestor that eventually persists that target reads it to write a stable
|
|
44
|
+
// alias the left-as-authored cycle edge can resolve to.
|
|
45
|
+
cycleTargets = new Set()) {
|
|
39
46
|
const { moduleCache, projectDir, projectId, contentSourceId, adapter, mode } = config;
|
|
40
47
|
const cacheKey = getModuleCacheKey(filePath, projectId, projectDir, contentSourceId, config.reactVersion, mode);
|
|
41
48
|
const cachedPath = await resolveCachedModulePath({
|
|
@@ -58,15 +65,55 @@ export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, co
|
|
|
58
65
|
filePath,
|
|
59
66
|
projectDir,
|
|
60
67
|
});
|
|
61
|
-
|
|
68
|
+
// The module cache is only written once a transform completes, so it cannot
|
|
69
|
+
// break a cycle that is still in progress. Carry the chain instead.
|
|
70
|
+
const nextLineage = new Set(lineage).add(filePath);
|
|
71
|
+
const transformedDeps = (await Promise.all(resolvedDeps.filter((d) => d.depFilePath).map(async (dep) => {
|
|
72
|
+
// `await import()` is how a module graph legitimately breaks an import
|
|
73
|
+
// cycle, so following one eagerly can lead straight back to a module
|
|
74
|
+
// further up this chain and recurse until the worker dies. Leave the
|
|
75
|
+
// specifier as authored so the recursion terminates.
|
|
76
|
+
//
|
|
77
|
+
// The cycle target is persisted as a content-hashed artifact whose hash
|
|
78
|
+
// is derived from transformed output we do not produce here (producing it
|
|
79
|
+
// is the recursion we are breaking), so the edge cannot be rewritten to
|
|
80
|
+
// that hashed path. Instead we record the target: when its ancestor
|
|
81
|
+
// persists it, a stable non-hashed alias is written next to the hashed
|
|
82
|
+
// artifact so the relative `.js` specifier esbuild leaves behind resolves.
|
|
83
|
+
// NOTE: this alias path is not yet runtime-verified end to end; if it does
|
|
84
|
+
// not resolve in a real runtime the cycle branch stays broken, which is no
|
|
85
|
+
// worse than before (and still a strict improvement over hanging).
|
|
86
|
+
if (nextLineage.has(dep.depFilePath)) {
|
|
87
|
+
cycleTargets.add(dep.depFilePath);
|
|
88
|
+
logger.debug("Skipping dependency already in the transform chain:", {
|
|
89
|
+
path: dep.path,
|
|
90
|
+
depFilePath: dep.depFilePath,
|
|
91
|
+
});
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
62
94
|
logger.debug("Found dependency:", {
|
|
63
95
|
path: dep.path,
|
|
64
96
|
depFilePath: dep.depFilePath,
|
|
65
97
|
isLocalLib: dep.isLocalLib,
|
|
66
98
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
try {
|
|
100
|
+
const depTempPath = await transformModuleWithDeps(dep.depFilePath, tmpDir, localAdapter, config, dep.isLocalLib, nextLineage, cycleTargets);
|
|
101
|
+
return { ...dep, depTempPath };
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
// A static import has to resolve for the importer to run at all. A
|
|
105
|
+
// dynamic one may never be evaluated, so a module behind an untaken
|
|
106
|
+
// branch must not fail the page that merely mentions it.
|
|
107
|
+
if (!dep.isDynamic)
|
|
108
|
+
throw error;
|
|
109
|
+
logger.warn("Leaving an unresolvable dynamic dependency as authored:", {
|
|
110
|
+
path: dep.path,
|
|
111
|
+
depFilePath: dep.depFilePath,
|
|
112
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
113
|
+
});
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}))).filter((dep) => dep !== null);
|
|
70
117
|
fileContent = rewriteResolvedDependencyImports(fileContent, transformedDeps);
|
|
71
118
|
for (const dep of transformedDeps) {
|
|
72
119
|
logger.debug("Replaced import:", {
|
|
@@ -103,6 +150,7 @@ export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, co
|
|
|
103
150
|
cacheKey,
|
|
104
151
|
contentSourceId,
|
|
105
152
|
reactVersion: config.reactVersion,
|
|
153
|
+
isCycleTarget: cycleTargets.has(filePath),
|
|
106
154
|
});
|
|
107
155
|
}
|
|
108
156
|
/**
|
|
@@ -146,7 +194,15 @@ export function isMissingModuleError(error) {
|
|
|
146
194
|
export async function loadModule(filePath, config) {
|
|
147
195
|
const tmpDir = await getModuleCacheDir(config);
|
|
148
196
|
const localAdapter = await getLocalAdapter();
|
|
149
|
-
|
|
197
|
+
// Everything up to here compiles and resolves source, so a failure is a build
|
|
198
|
+
// failure. Everything after it is the module running.
|
|
199
|
+
let tempFilePath;
|
|
200
|
+
try {
|
|
201
|
+
tempFilePath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
throw markBuildFailure(error);
|
|
205
|
+
}
|
|
150
206
|
const moduleUrl = `file://${tempFilePath}`;
|
|
151
207
|
try {
|
|
152
208
|
return await import(moduleUrl);
|
|
@@ -189,7 +245,13 @@ export async function loadModule(filePath, config) {
|
|
|
189
245
|
// invalidation stays scoped to this tenant (the path-cache key is not
|
|
190
246
|
// project-scoped — see invalidateMdxEsmModule).
|
|
191
247
|
invalidateMdxEsmModule(tmpDir, filePath, config.projectDir, config.reactVersion);
|
|
192
|
-
|
|
248
|
+
let rebuiltPath;
|
|
249
|
+
try {
|
|
250
|
+
rebuiltPath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
|
|
251
|
+
}
|
|
252
|
+
catch (rebuildError) {
|
|
253
|
+
throw markBuildFailure(rebuildError);
|
|
254
|
+
}
|
|
193
255
|
return await import(`file://${rebuiltPath}?t=${Date.now()}&rebuilt=1`);
|
|
194
256
|
}
|
|
195
257
|
logger.error("Failed to import module:", {
|
|
@@ -14,6 +14,12 @@ export interface PersistTransformedModuleInput {
|
|
|
14
14
|
cacheKey: string;
|
|
15
15
|
contentSourceId?: string;
|
|
16
16
|
reactVersion?: string;
|
|
17
|
+
/**
|
|
18
|
+
* True when a dynamic import elsewhere closes a cycle back onto this module.
|
|
19
|
+
* Such an edge is left as authored (`import("../app/page.js")`), so it needs a
|
|
20
|
+
* stable, non-hashed alias next to the content-hashed artifact to resolve to.
|
|
21
|
+
*/
|
|
22
|
+
isCycleTarget?: boolean;
|
|
17
23
|
}
|
|
18
24
|
/** Write a transformed module artifact and register cache pointers. */
|
|
19
25
|
export declare function persistTransformedModule(input: PersistTransformedModuleInput): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAuDzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAuDzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAqDD,uEAAuE;AACvE,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,MAAM,CAAC,CAqEjB"}
|
|
@@ -44,6 +44,51 @@ async function ensureDir(adapter, dir, force = false) {
|
|
|
44
44
|
createdDirs.add(dir);
|
|
45
45
|
pruneCreatedDirs();
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Whether transformed output exposes a default export, so a cycle alias knows
|
|
49
|
+
* to re-export it. Covers esbuild's `export default …`, `… as default`, and
|
|
50
|
+
* `export { default } from …` forms.
|
|
51
|
+
*/
|
|
52
|
+
function hasDefaultExport(code) {
|
|
53
|
+
return /\bexport\s+default\b/.test(code) ||
|
|
54
|
+
/\bas\s+default\b/.test(code) ||
|
|
55
|
+
/\bexport\s*\{[^}]*\bdefault\b[^}]*\}/.test(code);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Write a stable, non-hashed alias next to a cycle target's hashed artifact.
|
|
59
|
+
*
|
|
60
|
+
* A dynamic import that closes an import cycle is left as the author wrote it
|
|
61
|
+
* (see the module loader), so esbuild normalises it to a relative `.js` path
|
|
62
|
+
* (`../app/page.js`) that does not match the content-hashed artifact
|
|
63
|
+
* (`../app/page.<hash>.js`). The alias sits at that relative path and re-exports
|
|
64
|
+
* the real artifact, so the edge resolves if the branch runs. Best-effort: a
|
|
65
|
+
* failed alias just leaves the pre-existing (unresolved) cycle edge in place.
|
|
66
|
+
*/
|
|
67
|
+
async function writeCycleTargetAlias(input, relativePath, hashedFileName) {
|
|
68
|
+
const aliasRelativePath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, ".js");
|
|
69
|
+
// Same extension in and out means nothing was renamed (already `.js`): the
|
|
70
|
+
// authored edge already points at the real artifact, so no alias is needed.
|
|
71
|
+
if (aliasRelativePath === relativePath)
|
|
72
|
+
return;
|
|
73
|
+
const aliasPath = join(input.tmpDir, aliasRelativePath);
|
|
74
|
+
const lines = [`export * from "./${hashedFileName}";`];
|
|
75
|
+
if (hasDefaultExport(input.transformedCode)) {
|
|
76
|
+
lines.push(`export { default } from "./${hashedFileName}";`);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await input.localAdapter.fs.writeFile(aliasPath, lines.join("\n"));
|
|
80
|
+
logger.debug("Wrote cycle-target alias", {
|
|
81
|
+
alias: aliasRelativePath,
|
|
82
|
+
target: hashedFileName,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger.warn("Failed to write cycle-target alias", {
|
|
87
|
+
filePath: input.filePath.slice(-40),
|
|
88
|
+
error: error instanceof Error ? error.message : String(error),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
47
92
|
/** Write a transformed module artifact and register cache pointers. */
|
|
48
93
|
export async function persistTransformedModule(input) {
|
|
49
94
|
const transformedHash = hashCodeHex(input.transformedCode).slice(0, 8);
|
|
@@ -100,5 +145,9 @@ export async function persistTransformedModule(input) {
|
|
|
100
145
|
});
|
|
101
146
|
}
|
|
102
147
|
input.moduleCache.set(input.cacheKey, tempFilePath);
|
|
148
|
+
if (input.isCycleTarget) {
|
|
149
|
+
const hashedFileName = jsPath.slice(jsPath.lastIndexOf("/") + 1);
|
|
150
|
+
await writeCycleTargetAlias(input, relativePath, hashedFileName);
|
|
151
|
+
}
|
|
103
152
|
return tempFilePath;
|
|
104
153
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,iCAAiC,CAAC;AASzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,iCAAiC,CAAC;AASzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAsDhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxE,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,qBAAqB,EAAE,sBAAsB,CAAC;CAC1E;AA0BD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,mBAAmB,CAAgC;gBAE/C,MAAM,EAAE,oBAAoB;IAcxC,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,oBAAoB;IA0B5B;;;;OAIG;YACW,yBAAyB;IAWvC;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAKxB,OAAO,CAAC,UAAU;YAOJ,0BAA0B;IAaxC;;;;;;;;;OASG;YACW,qBAAqB;IAqEnC;;;OAGG;YACW,mBAAmB;IA6GjC,OAAO,CAAC,uBAAuB;IAkCzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAyT9E,+EAA+E;IACzE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAsHzE,kBAAkB;YA2ClB,cAAc;IAW5B,OAAO,CAAC,uBAAuB;YAYjB,kBAAkB;IAyFhC,OAAO,CAAC,kBAAkB;YAQZ,uBAAuB;IAmBrC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAStB"}
|