@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20
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/dist/cjs/backend-federation-build/codegen.js +267 -0
- package/dist/cjs/backend-federation-build/config.js +239 -0
- package/dist/cjs/backend-federation-build/emit.js +154 -0
- package/dist/cjs/backend-federation-build/index.js +53 -0
- package/dist/cjs/build-config/build-environment.js +129 -0
- package/dist/cjs/build-config/precompress/plugin.js +124 -0
- package/dist/cjs/build-config/precompress/types.js +18 -0
- package/dist/cjs/build-config/public.js +43 -0
- package/dist/cjs/build-config/remote-address.js +58 -0
- package/dist/cjs/cloudflare/artifacts.js +8 -19
- package/dist/cjs/cloudflare/constants.js +1 -1
- package/dist/cjs/cloudflare/delivery-unit.js +23 -42
- package/dist/cjs/cloudflare/index.js +10 -2
- package/dist/cjs/cloudflare/worker-manifest.js +13 -4
- package/dist/cjs/cloudflare/wrangler-config.js +35 -7
- package/dist/cjs/cloudflare-builder.js +251 -75
- package/dist/cjs/cloudflare-output-contract.js +69 -3
- package/dist/cjs/cloudflare-output-verifier/identity.js +29 -27
- package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
- package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
- package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
- package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +14 -6
- package/dist/cjs/config.js +34 -1
- package/dist/cjs/deploy-output/npmAliases.js +251 -0
- package/dist/cjs/deploy-output/plugin.js +97 -0
- package/dist/cjs/deploy-output/public-assets.js +146 -0
- package/dist/cjs/deploy-output/target.js +104 -0
- package/dist/cjs/deploy-output/trace-files.js +90 -0
- package/dist/cjs/native-type-checker.js +193 -0
- package/dist/cjs/policy-defaults.js +101 -0
- package/dist/cjs/release-envelope/framework-output.js +153 -79
- package/dist/cjs/release-envelope/index.js +10 -4
- package/dist/cjs/release-envelope/plugin.js +18 -13
- package/dist/cjs/release-identity.js +5 -6
- package/dist/cjs/runtime-package-resolution.js +118 -0
- package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm/backend-federation-build/codegen.mjs +216 -0
- package/dist/esm/backend-federation-build/config.mjs +163 -0
- package/dist/esm/backend-federation-build/emit.mjs +103 -0
- package/dist/esm/backend-federation-build/index.mjs +1 -0
- package/dist/esm/build-config/build-environment.mjs +88 -0
- package/dist/esm/build-config/precompress/plugin.mjs +76 -0
- package/dist/esm/build-config/precompress/types.mjs +0 -0
- package/dist/esm/build-config/public.mjs +1 -0
- package/dist/esm/build-config/remote-address.mjs +20 -0
- package/dist/esm/cloudflare/artifacts.mjs +8 -19
- package/dist/esm/cloudflare/constants.mjs +1 -1
- package/dist/esm/cloudflare/delivery-unit.mjs +17 -36
- package/dist/esm/cloudflare/index.mjs +10 -2
- package/dist/esm/cloudflare/worker-manifest.mjs +13 -4
- package/dist/esm/cloudflare/wrangler-config.mjs +35 -7
- package/dist/esm/cloudflare-builder.mjs +245 -75
- package/dist/esm/cloudflare-output-contract.mjs +59 -2
- package/dist/esm/cloudflare-output-verifier/identity.mjs +27 -25
- package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
- package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
- package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
- package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +15 -7
- package/dist/esm/config.mjs +3 -0
- package/dist/esm/deploy-output/npmAliases.mjs +199 -0
- package/dist/esm/deploy-output/plugin.mjs +46 -0
- package/dist/esm/deploy-output/public-assets.mjs +82 -0
- package/dist/esm/deploy-output/target.mjs +57 -0
- package/dist/esm/deploy-output/trace-files.mjs +38 -0
- package/dist/esm/native-type-checker.mjs +142 -0
- package/dist/esm/policy-defaults.mjs +45 -0
- package/dist/esm/release-envelope/framework-output.mjs +130 -56
- package/dist/esm/release-envelope/index.mjs +10 -4
- package/dist/esm/release-envelope/plugin.mjs +18 -13
- package/dist/esm/release-identity.mjs +5 -6
- package/dist/esm/runtime-package-resolution.mjs +60 -0
- package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm-node/backend-federation-build/codegen.mjs +217 -0
- package/dist/esm-node/backend-federation-build/config.mjs +164 -0
- package/dist/esm-node/backend-federation-build/emit.mjs +104 -0
- package/dist/esm-node/backend-federation-build/index.mjs +2 -0
- package/dist/esm-node/build-config/build-environment.mjs +89 -0
- package/dist/esm-node/build-config/precompress/plugin.mjs +77 -0
- package/dist/esm-node/build-config/precompress/types.mjs +1 -0
- package/dist/esm-node/build-config/public.mjs +2 -0
- package/dist/esm-node/build-config/remote-address.mjs +21 -0
- package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
- package/dist/esm-node/cloudflare/constants.mjs +1 -1
- package/dist/esm-node/cloudflare/delivery-unit.mjs +17 -36
- package/dist/esm-node/cloudflare/index.mjs +10 -2
- package/dist/esm-node/cloudflare/worker-manifest.mjs +13 -4
- package/dist/esm-node/cloudflare/wrangler-config.mjs +35 -7
- package/dist/esm-node/cloudflare-builder.mjs +245 -75
- package/dist/esm-node/cloudflare-output-contract.mjs +59 -2
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +27 -25
- package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
- package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
- package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
- package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +15 -7
- package/dist/esm-node/config.mjs +3 -0
- package/dist/esm-node/deploy-output/npmAliases.mjs +200 -0
- package/dist/esm-node/deploy-output/plugin.mjs +49 -0
- package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
- package/dist/esm-node/deploy-output/target.mjs +58 -0
- package/dist/esm-node/deploy-output/trace-files.mjs +39 -0
- package/dist/esm-node/native-type-checker.mjs +143 -0
- package/dist/esm-node/policy-defaults.mjs +46 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +130 -56
- package/dist/esm-node/release-envelope/index.mjs +10 -4
- package/dist/esm-node/release-envelope/plugin.mjs +18 -13
- package/dist/esm-node/release-identity.mjs +5 -6
- package/dist/esm-node/runtime-package-resolution.mjs +61 -0
- package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/types/backend-federation-build/codegen.d.ts +116 -0
- package/dist/types/backend-federation-build/config.d.ts +121 -0
- package/dist/types/backend-federation-build/emit.d.ts +21 -0
- package/dist/types/backend-federation-build/index.d.ts +2 -0
- package/dist/types/build-config/build-environment.d.ts +14 -0
- package/dist/types/build-config/precompress/plugin.d.ts +4 -0
- package/dist/types/build-config/precompress/types.d.ts +19 -0
- package/dist/types/build-config/public.d.ts +2 -0
- package/dist/types/build-config/remote-address.d.ts +3 -0
- package/dist/types/cloudflare/artifacts.d.ts +7 -6
- package/dist/types/cloudflare/constants.d.ts +1 -1
- package/dist/types/cloudflare/delivery-unit.d.ts +3 -3
- package/dist/types/cloudflare/worker-manifest.d.ts +2 -2
- package/dist/types/cloudflare/wrangler-config.d.ts +6 -0
- package/dist/types/cloudflare-builder.d.ts +49 -9
- package/dist/types/cloudflare-output-contract.d.ts +4 -1
- package/dist/types/cloudflare-output-verifier/identity.d.ts +1 -1
- package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
- package/dist/types/config.d.ts +29 -1
- package/dist/types/deploy-output/npmAliases.d.ts +14 -0
- package/dist/types/deploy-output/plugin.d.ts +35 -0
- package/dist/types/deploy-output/public-assets.d.ts +51 -0
- package/dist/types/deploy-output/target.d.ts +29 -0
- package/dist/types/deploy-output/trace-files.d.ts +17 -0
- package/dist/types/native-type-checker.d.ts +15 -0
- package/dist/types/policy-defaults.d.ts +46 -0
- package/dist/types/precompress/LICENSE +20 -0
- package/dist/types/precompress/index.d.ts +76 -0
- package/dist/types/release-envelope/framework-output.d.ts +8 -3
- package/dist/types/release-envelope/plugin.d.ts +7 -7
- package/dist/types/runtime-package-resolution.d.ts +28 -0
- package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
- package/package.json +97 -15
- package/src/backend-federation-build/codegen.ts +254 -0
- package/src/backend-federation-build/config.ts +492 -0
- package/src/backend-federation-build/emit.ts +213 -0
- package/src/backend-federation-build/index.ts +2 -0
- package/src/build-config/build-environment.ts +160 -0
- package/src/build-config/precompress/plugin.ts +140 -0
- package/src/build-config/precompress/types.ts +26 -0
- package/src/build-config/public.ts +5 -0
- package/src/build-config/remote-address.ts +33 -0
- package/src/cloudflare/artifacts.ts +22 -43
- package/src/cloudflare/constants.ts +1 -1
- package/src/cloudflare/delivery-unit.ts +42 -53
- package/src/cloudflare/index.ts +24 -11
- package/src/cloudflare/worker-manifest.ts +20 -3
- package/src/cloudflare/wrangler-config.ts +86 -10
- package/src/cloudflare-builder.ts +472 -151
- package/src/cloudflare-output-contract.ts +70 -0
- package/src/cloudflare-output-verifier/identity.ts +5 -1
- package/src/cloudflare-output-verifier/index.ts +20 -0
- package/src/cloudflare-output-verifier/manifest.ts +77 -0
- package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
- package/src/cloudflare-output-verifier/worker-bundles.ts +32 -14
- package/src/config.ts +34 -6
- package/src/deploy-output/npmAliases.ts +382 -0
- package/src/deploy-output/plugin.ts +86 -0
- package/src/deploy-output/public-assets.ts +224 -0
- package/src/deploy-output/target.ts +106 -0
- package/src/deploy-output/trace-files.ts +67 -0
- package/src/native-type-checker.ts +183 -0
- package/src/policy-defaults.ts +127 -0
- package/src/release-envelope/framework-output.ts +346 -94
- package/src/release-envelope/index.ts +26 -5
- package/src/release-envelope/plugin.ts +43 -40
- package/src/release-identity.ts +13 -2
- package/src/runtime-package-resolution.ts +115 -0
- package/src/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/src/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/src/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
- package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
- package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
- package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
- package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/src/templates/cloudflare-worker-path.mjs +0 -98
|
@@ -381,8 +381,8 @@ const assertUniqueSortedArtifacts = (
|
|
|
381
381
|
};
|
|
382
382
|
|
|
383
383
|
const assertSurfacePaths = (value: unknown, location: string): string[] => {
|
|
384
|
-
if (!Array.isArray(value)
|
|
385
|
-
throw new Error(`${location} must
|
|
384
|
+
if (!Array.isArray(value)) {
|
|
385
|
+
throw new Error(`${location} must be an array of artifact paths.`);
|
|
386
386
|
}
|
|
387
387
|
const paths = value.map((item, index) =>
|
|
388
388
|
assertNormalizedLogicalPath(item, `${location}[${index}]`),
|
|
@@ -413,10 +413,31 @@ const assertSurfaces = (value: unknown): MicroVerticalReleaseSurfaces => {
|
|
|
413
413
|
['manifest', 'container'],
|
|
414
414
|
'surfaces.backendFederation',
|
|
415
415
|
);
|
|
416
|
+
const uiClient = assertSurfacePaths(surfaces.uiClient, 'surfaces.uiClient');
|
|
417
|
+
const ssr = assertSurfacePaths(surfaces.ssr, 'surfaces.ssr');
|
|
418
|
+
const apiBackend = assertSurfacePaths(
|
|
419
|
+
surfaces.apiBackend,
|
|
420
|
+
'surfaces.apiBackend',
|
|
421
|
+
);
|
|
422
|
+
if (uiClient.length > 0 && ssr.length === 0) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
'surfaces.ssr must contain at least one artifact path when UI/client is declared.',
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
if (ssr.length > 0 && uiClient.length === 0) {
|
|
428
|
+
throw new Error(
|
|
429
|
+
'surfaces.uiClient must contain at least one artifact path when SSR is declared.',
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
if (apiBackend.length === 0) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
'surfaces.apiBackend must contain at least one artifact path.',
|
|
435
|
+
);
|
|
436
|
+
}
|
|
416
437
|
return {
|
|
417
|
-
uiClient
|
|
418
|
-
ssr
|
|
419
|
-
apiBackend
|
|
438
|
+
uiClient,
|
|
439
|
+
ssr,
|
|
440
|
+
apiBackend,
|
|
420
441
|
backendFederation: {
|
|
421
442
|
manifest: assertNormalizedLogicalPath(
|
|
422
443
|
backendFederation.manifest,
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import type { NodePublicAssetConfig } from '../config';
|
|
3
|
+
import {
|
|
4
|
+
NODE_PUBLIC_ASSET_SCOPE,
|
|
5
|
+
normalizeDeclaredPublicAssets,
|
|
6
|
+
resolveAddedDeclaredPublicAssetPaths,
|
|
7
|
+
} from '../deploy-output/public-assets';
|
|
8
|
+
import {
|
|
9
|
+
getDeployingTarget,
|
|
10
|
+
type ResolvedDeployTarget,
|
|
11
|
+
} from '../deploy-output/target';
|
|
2
12
|
import {
|
|
3
13
|
emitFrameworkMicroVerticalReleaseEnvelope,
|
|
4
14
|
emitNodeStagedReleaseEnvelope,
|
|
@@ -6,10 +16,8 @@ import {
|
|
|
6
16
|
verifyNodeReleaseEnvelopeStaging,
|
|
7
17
|
} from './framework-output';
|
|
8
18
|
|
|
9
|
-
type ReleaseEnvelopeTarget = 'cloudflare' | 'node' | string;
|
|
10
|
-
|
|
11
19
|
export interface ReleaseEnvelopeConfig {
|
|
12
|
-
deploy?: {
|
|
20
|
+
deploy?: { node?: { publicAssets?: NodePublicAssetConfig[] } };
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
export interface ReleaseEnvelopeAppContext {
|
|
@@ -17,6 +25,7 @@ export interface ReleaseEnvelopeAppContext {
|
|
|
17
25
|
appDirectory: string;
|
|
18
26
|
distDirectory: string;
|
|
19
27
|
metaName: string;
|
|
28
|
+
deployTarget?: ResolvedDeployTarget;
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export interface ReleaseEnvelopePluginApi<
|
|
@@ -38,60 +47,46 @@ export interface ReleaseEnvelopePlugin<
|
|
|
38
47
|
setup(api: ReleaseEnvelopePluginApi<Config>): void;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
|
-
export type ResolveDeployTarget<
|
|
42
|
-
Config extends ReleaseEnvelopeConfig = ReleaseEnvelopeConfig,
|
|
43
|
-
> = (config: Config) => ReleaseEnvelopeTarget;
|
|
44
|
-
|
|
45
|
-
const resolveActiveDeployTarget = <Config extends ReleaseEnvelopeConfig>(
|
|
46
|
-
api: ReleaseEnvelopePluginApi<Config>,
|
|
47
|
-
resolveDeployTarget: ResolveDeployTarget<Config>,
|
|
48
|
-
) => {
|
|
49
|
-
const { metaName } = api.getAppContext();
|
|
50
|
-
const config = api.getNormalizedConfig();
|
|
51
|
-
if (
|
|
52
|
-
metaName !== 'modern-js' &&
|
|
53
|
-
!config.deploy?.target &&
|
|
54
|
-
!process.env.MODERNJS_DEPLOY
|
|
55
|
-
) {
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
return resolveDeployTarget(config);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
50
|
export const createUltramodernReleaseEnvelopePlugin = <
|
|
62
51
|
Config extends ReleaseEnvelopeConfig,
|
|
63
|
-
>({
|
|
64
|
-
resolveDeployTarget,
|
|
65
|
-
}: {
|
|
66
|
-
resolveDeployTarget: ResolveDeployTarget<Config>;
|
|
67
|
-
}): ReleaseEnvelopePlugin<Config> => {
|
|
52
|
+
>(): ReleaseEnvelopePlugin<Config> => {
|
|
68
53
|
return {
|
|
69
54
|
name: '@modern-js/ultramodern-release-envelope',
|
|
70
|
-
pre: [
|
|
55
|
+
pre: [
|
|
56
|
+
'@modern-js/backend-federation-build',
|
|
57
|
+
'@modern-js/plugin-bff',
|
|
58
|
+
'@modern-js/deploy-output-aliases',
|
|
59
|
+
'@modern-js/deploy-output-public-assets',
|
|
60
|
+
],
|
|
71
61
|
post: ['@modern-js/plugin-deploy'],
|
|
72
62
|
setup(api) {
|
|
73
|
-
const emitBuildEnvelope = async (
|
|
63
|
+
const emitBuildEnvelope = async (
|
|
64
|
+
target: 'node' | 'cloudflare',
|
|
65
|
+
requirePromotable = true,
|
|
66
|
+
) => {
|
|
74
67
|
const { apiOnly, distDirectory } = api.getAppContext();
|
|
75
68
|
await emitFrameworkMicroVerticalReleaseEnvelope({
|
|
76
69
|
apiOnly,
|
|
77
70
|
distDirectory,
|
|
71
|
+
requirePromotable,
|
|
78
72
|
target,
|
|
79
73
|
});
|
|
80
74
|
};
|
|
81
75
|
|
|
82
76
|
api.onAfterBuild(async () => {
|
|
83
|
-
const configuredTarget =
|
|
77
|
+
const configuredTarget = api.getAppContext().deployTarget?.target;
|
|
84
78
|
if (configuredTarget !== 'node' && configuredTarget !== 'cloudflare') {
|
|
85
79
|
return;
|
|
86
80
|
}
|
|
87
|
-
|
|
81
|
+
// A plain `modern build` is the development path: a dirty or non-Git
|
|
82
|
+
// checkout resolves to the `workspace` source revision, which is not
|
|
83
|
+
// promotable but must still build. Deploy re-emits the envelope and
|
|
84
|
+
// verifies it, so the release gate keeps its teeth there.
|
|
85
|
+
await emitBuildEnvelope(configuredTarget, false);
|
|
88
86
|
});
|
|
89
87
|
|
|
90
88
|
api.onBeforeDeploy(async () => {
|
|
91
|
-
const configuredTarget =
|
|
92
|
-
api,
|
|
93
|
-
resolveDeployTarget,
|
|
94
|
-
);
|
|
89
|
+
const configuredTarget = getDeployingTarget(api.getAppContext());
|
|
95
90
|
if (configuredTarget !== 'node') {
|
|
96
91
|
return;
|
|
97
92
|
}
|
|
@@ -101,16 +96,24 @@ export const createUltramodernReleaseEnvelopePlugin = <
|
|
|
101
96
|
});
|
|
102
97
|
|
|
103
98
|
api.onAfterDeploy(async () => {
|
|
104
|
-
const configuredTarget =
|
|
105
|
-
api,
|
|
106
|
-
resolveDeployTarget,
|
|
107
|
-
);
|
|
99
|
+
const configuredTarget = getDeployingTarget(api.getAppContext());
|
|
108
100
|
if (configuredTarget !== 'node') {
|
|
109
101
|
return;
|
|
110
102
|
}
|
|
111
103
|
const { appDirectory, distDirectory } = api.getAppContext();
|
|
112
104
|
const outputDirectory = path.join(appDirectory, '.output');
|
|
113
105
|
const releaseEnvelope = await emitNodeStagedReleaseEnvelope({
|
|
106
|
+
// The Node deploy output is a copy of dist, so a declared file
|
|
107
|
+
// added output exactly when dist has no file at its path.
|
|
108
|
+
declaredPublicAssets: await resolveAddedDeclaredPublicAssetPaths({
|
|
109
|
+
appDirectory,
|
|
110
|
+
assets: normalizeDeclaredPublicAssets(
|
|
111
|
+
api.getNormalizedConfig().deploy?.node?.publicAssets,
|
|
112
|
+
NODE_PUBLIC_ASSET_SCOPE,
|
|
113
|
+
),
|
|
114
|
+
generatedRoot: distDirectory,
|
|
115
|
+
scope: NODE_PUBLIC_ASSET_SCOPE,
|
|
116
|
+
}),
|
|
114
117
|
distDirectory,
|
|
115
118
|
outputDirectory,
|
|
116
119
|
});
|
package/src/release-identity.ts
CHANGED
|
@@ -10,13 +10,24 @@ export type UltramodernReleaseIdentity = {
|
|
|
10
10
|
sourceRevision: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
// Inherited GIT_* variables (git hooks export GIT_DIR and GIT_INDEX_FILE)
|
|
14
|
+
// would make git describe some other repository than `workspaceRoot`.
|
|
15
|
+
const workspaceGitEnv = (): NodeJS.ProcessEnv =>
|
|
16
|
+
Object.fromEntries(
|
|
17
|
+
Object.entries(process.env).filter(
|
|
18
|
+
([name]) => !name.toUpperCase().startsWith('GIT_'),
|
|
19
|
+
),
|
|
20
|
+
);
|
|
21
|
+
|
|
13
22
|
const gitOutput = (
|
|
14
23
|
workspaceRoot: string,
|
|
15
24
|
args: string[],
|
|
16
25
|
): string | undefined => {
|
|
17
26
|
try {
|
|
18
|
-
return execFileSync('git',
|
|
27
|
+
return execFileSync('git', args, {
|
|
28
|
+
cwd: workspaceRoot,
|
|
19
29
|
encoding: 'utf8',
|
|
30
|
+
env: workspaceGitEnv(),
|
|
20
31
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
21
32
|
}).trim();
|
|
22
33
|
} catch {
|
|
@@ -84,7 +95,7 @@ export const resolveUltramodernSourceRevision = (
|
|
|
84
95
|
const revision = gitOutput(gitRoot, ['rev-parse', 'HEAD']);
|
|
85
96
|
const status = gitOutput(gitRoot, [
|
|
86
97
|
'status',
|
|
87
|
-
'--porcelain
|
|
98
|
+
'--porcelain',
|
|
88
99
|
'--untracked-files=all',
|
|
89
100
|
]);
|
|
90
101
|
if (!revision || status === undefined || status.length > 0) {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import type { RsbuildPlugin, Rspack } from '@rsbuild/core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A generated `runtime-register.js` lives inside the *application*, so every
|
|
9
|
+
* runtime plugin it imports has to resolve from the application's own
|
|
10
|
+
* `node_modules`. Packages registered by a CLI plugin are dependencies of that
|
|
11
|
+
* CLI plugin, not of the application, and under an isolated (pnpm) linker the
|
|
12
|
+
* application cannot see them — the build fails with `Module not found`.
|
|
13
|
+
*
|
|
14
|
+
* Instead of freezing a single resolved file (which would lock one export
|
|
15
|
+
* condition and break the web/node split), the registering package contributes
|
|
16
|
+
* the `node_modules` directory that actually hosts the package as a *fallback*
|
|
17
|
+
* module directory. Bare specifiers keep their normal `exports` resolution, the
|
|
18
|
+
* application's own copy still wins when it declares the dependency, and both
|
|
19
|
+
* the workspace source layout and a published pnpm layout work.
|
|
20
|
+
*/
|
|
21
|
+
export const findHostingModuleDirectory = (
|
|
22
|
+
packageName: string,
|
|
23
|
+
fromDirectory: string,
|
|
24
|
+
): string | undefined => {
|
|
25
|
+
let directory = path.resolve(fromDirectory);
|
|
26
|
+
for (;;) {
|
|
27
|
+
const candidate = path.join(directory, 'node_modules');
|
|
28
|
+
if (fs.existsSync(path.join(candidate, packageName))) {
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
const parent = path.dirname(directory);
|
|
32
|
+
if (parent === directory) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
directory = parent;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Collect the fallback module directories for the given packages, starting the
|
|
41
|
+
* search at the module that registers them.
|
|
42
|
+
*/
|
|
43
|
+
export const collectRuntimePackageModuleDirectories = (
|
|
44
|
+
packageNames: readonly string[],
|
|
45
|
+
registrarUrl: string,
|
|
46
|
+
): string[] => {
|
|
47
|
+
const registrarDirectory = path.dirname(fileURLToPath(registrarUrl));
|
|
48
|
+
const directories: string[] = [];
|
|
49
|
+
for (const packageName of packageNames) {
|
|
50
|
+
const directory = findHostingModuleDirectory(
|
|
51
|
+
packageName,
|
|
52
|
+
registrarDirectory,
|
|
53
|
+
);
|
|
54
|
+
if (directory && !directories.includes(directory)) {
|
|
55
|
+
directories.push(directory);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return directories;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const appendModuleDirectories = (
|
|
62
|
+
config: Rspack.Configuration,
|
|
63
|
+
directories: readonly string[],
|
|
64
|
+
) => {
|
|
65
|
+
if (directories.length === 0) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
config.resolve ??= {};
|
|
69
|
+
const existing = config.resolve.modules ?? ['node_modules'];
|
|
70
|
+
// Appended, never prepended: the application's own upward `node_modules`
|
|
71
|
+
// search keeps priority, so a declared dependency still wins.
|
|
72
|
+
config.resolve.modules = [
|
|
73
|
+
...existing,
|
|
74
|
+
...directories.filter(directory => !existing.includes(directory)),
|
|
75
|
+
];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Rsbuild plugin that adds the given directories as fallback module roots for
|
|
80
|
+
* every environment (web, node and worker bundles alike).
|
|
81
|
+
*/
|
|
82
|
+
export const createRuntimePackageResolutionPlugin = (
|
|
83
|
+
directories: readonly string[],
|
|
84
|
+
): RsbuildPlugin => ({
|
|
85
|
+
name: 'ultramodern:runtime-package-resolution',
|
|
86
|
+
setup(api) {
|
|
87
|
+
api.modifyRspackConfig({
|
|
88
|
+
order: 'post',
|
|
89
|
+
handler(config) {
|
|
90
|
+
appendModuleDirectories(config, directories);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
api.onBeforeCreateCompiler({
|
|
94
|
+
order: 'post',
|
|
95
|
+
handler({ bundlerConfigs }) {
|
|
96
|
+
for (const config of bundlerConfigs) {
|
|
97
|
+
appendModuleDirectories(config, directories);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/** `true` when the package resolves from the registering module at all. */
|
|
105
|
+
export const canResolveRuntimePackage = (
|
|
106
|
+
packageName: string,
|
|
107
|
+
registrarUrl: string,
|
|
108
|
+
): boolean => {
|
|
109
|
+
try {
|
|
110
|
+
createRequire(registrarUrl).resolve(packageName);
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
@@ -2,9 +2,13 @@ function createNoopMonitors() {
|
|
|
2
2
|
const noop = () => {};
|
|
3
3
|
|
|
4
4
|
return {
|
|
5
|
+
counter: noop,
|
|
5
6
|
debug: noop,
|
|
6
7
|
error: noop,
|
|
7
8
|
info: noop,
|
|
9
|
+
push: noop,
|
|
10
|
+
timing: noop,
|
|
11
|
+
trace: noop,
|
|
8
12
|
warn: noop,
|
|
9
13
|
};
|
|
10
14
|
}
|
|
@@ -1134,7 +1138,7 @@ async function withRouteCssLinks(
|
|
|
1134
1138
|
seenCssHrefs.add(entry.preloadHref);
|
|
1135
1139
|
return true;
|
|
1136
1140
|
});
|
|
1137
|
-
for (const {
|
|
1141
|
+
for (const { preloadHref } of uniqueCssEntries) {
|
|
1138
1142
|
const preloadUrl = new URL(preloadHref);
|
|
1139
1143
|
const preloadReference =
|
|
1140
1144
|
preloadUrl.origin === requestOrigin
|
|
@@ -52,6 +52,52 @@ async function getRequestHandler(workerModule) {
|
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
const ROUTE_DATA_REQUEST_PARAM = '__loader';
|
|
56
|
+
|
|
57
|
+
// Route data requests (`?__loader=`) are answered by the entry's route data
|
|
58
|
+
// worker, like the Node server's data handler. Without a match the request
|
|
59
|
+
// falls through to page rendering, as it does on Node.
|
|
60
|
+
async function dispatchRouteDataRequest(route, request) {
|
|
61
|
+
const routeDataWorkerPath = route.routeDataWorker;
|
|
62
|
+
if (
|
|
63
|
+
!routeDataWorkerPath ||
|
|
64
|
+
!new URL(request.url).searchParams.has(ROUTE_DATA_REQUEST_PARAM)
|
|
65
|
+
) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const routeDataWorkerModule = await loadWorkerModule(routeDataWorkerPath);
|
|
70
|
+
const handleRouteDataRequest = routeDataWorkerModule
|
|
71
|
+
? getRuntimeModule(routeDataWorkerModule).handleRouteDataRequest
|
|
72
|
+
: undefined;
|
|
73
|
+
|
|
74
|
+
if (typeof handleRouteDataRequest !== 'function') {
|
|
75
|
+
return new Response(
|
|
76
|
+
`Route data worker bundle has no handleRouteDataRequest export: ${routeDataWorkerPath}`,
|
|
77
|
+
{
|
|
78
|
+
status: 500,
|
|
79
|
+
headers: {
|
|
80
|
+
'content-type': 'text/plain; charset=utf-8',
|
|
81
|
+
'x-modern-js-route-data-worker': routeDataWorkerPath,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return handleRouteDataRequest({
|
|
88
|
+
request,
|
|
89
|
+
serverRoutes: MODERN_WORKER_MANIFEST.routeSpec.routes,
|
|
90
|
+
context: {
|
|
91
|
+
loaderContext: new Map(),
|
|
92
|
+
monitors: createNoopMonitors(),
|
|
93
|
+
reporter: {
|
|
94
|
+
reportTiming: () => {},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
onTiming() {},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
55
101
|
async function dispatchRouteWorker(route, request, env, ctx) {
|
|
56
102
|
const workerPath = route.worker;
|
|
57
103
|
if (!workerPath) {
|
|
@@ -114,14 +160,46 @@ async function dispatchRouteWorker(route, request, env, ctx) {
|
|
|
114
160
|
);
|
|
115
161
|
}
|
|
116
162
|
|
|
117
|
-
|
|
163
|
+
// Worker Static Assets decode percent-encoded paths, so an encoded spelling
|
|
164
|
+
// of a mounted prefix (`/%70refix/...`, `/prefix%2F...`) would otherwise skip
|
|
165
|
+
// the BFF or service binding that owns the prefix and reach ASSETS directly.
|
|
166
|
+
// Resolve such spellings to the canonical pathname the owner sees; a
|
|
167
|
+
// backslash or undecodable spelling becomes a path the owner rejects.
|
|
168
|
+
function resolvePrefixPathname(pathname, prefix) {
|
|
118
169
|
if (!prefix || prefix === '/') {
|
|
119
|
-
return
|
|
170
|
+
return pathname;
|
|
120
171
|
}
|
|
121
172
|
|
|
122
173
|
const normalized = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
174
|
+
let candidate = pathname;
|
|
175
|
+
|
|
176
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
177
|
+
if (candidate === normalized || candidate.startsWith(`${normalized}/`)) {
|
|
178
|
+
return candidate;
|
|
179
|
+
}
|
|
180
|
+
if (candidate.startsWith(`${normalized}\\`)) {
|
|
181
|
+
return `${normalized}/__invalid_encoded_path__`;
|
|
182
|
+
}
|
|
123
183
|
|
|
124
|
-
|
|
184
|
+
let decoded;
|
|
185
|
+
try {
|
|
186
|
+
decoded = decodeURIComponent(candidate);
|
|
187
|
+
} catch {
|
|
188
|
+
return candidate.toLowerCase().startsWith(normalized.toLowerCase())
|
|
189
|
+
? `${normalized}/__invalid_encoded_path__`
|
|
190
|
+
: null;
|
|
191
|
+
}
|
|
192
|
+
if (decoded === candidate) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
candidate = decoded;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function matchesPrefix(pathname, prefix) {
|
|
202
|
+
return resolvePrefixPathname(pathname, prefix) !== null;
|
|
125
203
|
}
|
|
126
204
|
|
|
127
205
|
function createRequestForMountedPrefix(request, prefix) {
|
|
@@ -131,12 +209,13 @@ function createRequestForMountedPrefix(request, prefix) {
|
|
|
131
209
|
|
|
132
210
|
const url = new URL(request.url);
|
|
133
211
|
const normalized = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
212
|
+
const matchedPathname = resolvePrefixPathname(url.pathname, normalized);
|
|
134
213
|
|
|
135
|
-
if (
|
|
214
|
+
if (matchedPathname === null) {
|
|
136
215
|
return request;
|
|
137
216
|
}
|
|
138
217
|
|
|
139
|
-
const nextPath =
|
|
218
|
+
const nextPath = matchedPathname.slice(normalized.length) || '/';
|
|
140
219
|
url.pathname = nextPath.startsWith('/') ? nextPath : `/${nextPath}`;
|
|
141
220
|
|
|
142
221
|
return new Request(url, request);
|
|
@@ -263,12 +342,21 @@ function getEffectBffDispatcher(bff, runtime) {
|
|
|
263
342
|
async function dispatchBffRequest(request, env) {
|
|
264
343
|
const bff = MODERN_WORKER_MANIFEST.bff;
|
|
265
344
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
345
|
+
const requestUrl = new URL(request.url);
|
|
346
|
+
const matchedPathname = resolvePrefixPathname(
|
|
347
|
+
requestUrl.pathname,
|
|
348
|
+
bff?.prefix,
|
|
349
|
+
);
|
|
350
|
+
if (!bff?.worker || matchedPathname === null) {
|
|
270
351
|
return null;
|
|
271
352
|
}
|
|
353
|
+
const canonicalRequest =
|
|
354
|
+
matchedPathname === requestUrl.pathname
|
|
355
|
+
? request
|
|
356
|
+
: new Request(
|
|
357
|
+
Object.assign(requestUrl, { pathname: matchedPathname }),
|
|
358
|
+
request,
|
|
359
|
+
);
|
|
272
360
|
if (bff.runtimeFramework !== 'effect') {
|
|
273
361
|
return createEffectBffDispatcherErrorResponse(
|
|
274
362
|
bff,
|
|
@@ -301,7 +389,7 @@ async function dispatchBffRequest(request, env) {
|
|
|
301
389
|
return createEffectBffDispatcherErrorResponse(bff, error);
|
|
302
390
|
}
|
|
303
391
|
|
|
304
|
-
return effectDispatcher.dispatch(
|
|
392
|
+
return effectDispatcher.dispatch(canonicalRequest, { env });
|
|
305
393
|
}
|
|
306
394
|
|
|
307
395
|
const directHandler =
|
|
@@ -62,11 +62,18 @@ export default {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (route?.worker) {
|
|
65
|
-
|
|
65
|
+
// Route data requests keep their method, as on the Node server.
|
|
66
|
+
const routeDataResponse = await dispatchRouteDataRequest(route, request);
|
|
66
67
|
|
|
67
68
|
return finalizeResponseForRequest(
|
|
68
69
|
withAppCorsHeaders(
|
|
69
|
-
|
|
70
|
+
routeDataResponse ??
|
|
71
|
+
(await dispatchRouteWorker(
|
|
72
|
+
route,
|
|
73
|
+
createRenderableRequest(request),
|
|
74
|
+
env,
|
|
75
|
+
ctx,
|
|
76
|
+
)),
|
|
70
77
|
request,
|
|
71
78
|
),
|
|
72
79
|
request,
|