@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.8 → 3.9.3-ultramodern.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/cjs/backend-federation-build/codegen.js +29 -18
- package/dist/cjs/backend-federation-build/config.js +84 -45
- package/dist/cjs/backend-federation-build/emit.js +63 -22
- package/dist/cjs/build-config/build-environment.js +25 -162
- package/dist/cjs/build-config/internal-effect-discovery.js +143 -0
- package/dist/cjs/build-config/public.js +2 -5
- package/dist/cjs/build-config/remote-address.js +58 -0
- package/dist/cjs/cloudflare/artifacts.js +8 -19
- package/dist/cjs/cloudflare/delivery-unit.js +125 -43
- package/dist/cjs/cloudflare/index.js +14 -4
- package/dist/cjs/cloudflare/native-renderer.js +100 -0
- package/dist/cjs/cloudflare/renderer-identity.js +106 -0
- package/dist/cjs/cloudflare/worker-manifest.js +54 -13
- package/dist/cjs/cloudflare/worker-options.js +48 -0
- package/dist/cjs/cloudflare/wrangler-config.js +210 -13
- package/dist/cjs/cloudflare-builder.js +307 -89
- package/dist/cjs/cloudflare-output-contract.js +36 -4
- package/dist/cjs/cloudflare-output-verifier/identity.js +112 -23
- 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 +10 -5
- package/dist/cjs/config.js +10 -5
- package/dist/cjs/deploy-output/npmAliases.js +92 -6
- package/dist/cjs/deploy-output/plugin.js +27 -6
- package/dist/cjs/deploy-output/public-assets.js +146 -0
- package/dist/cjs/deploy-output/target.js +57 -1
- package/dist/cjs/deploy-output/trace-files.js +99 -0
- package/dist/cjs/module-federation-config/exposes.js +70 -0
- package/dist/cjs/module-federation-config/index.js +73 -0
- package/dist/cjs/module-federation-config/inspect.js +103 -0
- package/dist/cjs/module-federation-config/object-literal.js +87 -0
- package/dist/cjs/module-federation-config/syntax.js +151 -0
- package/dist/cjs/module-federation-config/types.js +18 -0
- package/dist/cjs/native-type-checker.js +164 -18
- package/dist/cjs/policy-defaults.js +110 -0
- package/dist/cjs/release-envelope/canonical.js +2 -9
- package/dist/cjs/release-envelope/contract.js +68 -0
- package/dist/cjs/release-envelope/framework-output.js +296 -105
- package/dist/cjs/release-envelope/index.js +150 -36
- package/dist/cjs/release-envelope/plugin.js +24 -15
- package/dist/cjs/release-envelope/renderer-output-stamp.js +170 -0
- package/dist/cjs/release-envelope/types.js +10 -8
- package/dist/cjs/release-identity.js +8 -5
- package/dist/cjs/renderer-build-identity.js +134 -0
- package/dist/cjs/runtime-package-resolution.js +118 -0
- package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/cjs/workspace-topology.js +84 -0
- package/dist/esm/backend-federation-build/codegen.mjs +29 -18
- package/dist/esm/backend-federation-build/config.mjs +74 -38
- package/dist/esm/backend-federation-build/emit.mjs +64 -23
- package/dist/esm/build-config/build-environment.mjs +26 -160
- package/dist/esm/build-config/internal-effect-discovery.mjs +102 -0
- package/dist/esm/build-config/public.mjs +1 -1
- package/dist/esm/build-config/remote-address.mjs +20 -0
- package/dist/esm/cloudflare/artifacts.mjs +8 -19
- package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
- package/dist/esm/cloudflare/index.mjs +15 -5
- package/dist/esm/cloudflare/native-renderer.mjs +48 -0
- package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
- package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
- package/dist/esm/cloudflare/worker-options.mjs +10 -0
- package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
- package/dist/esm/cloudflare-builder.mjs +301 -89
- package/dist/esm/cloudflare-output-contract.mjs +29 -3
- package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -24
- 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 +11 -6
- package/dist/esm/config.mjs +3 -1
- package/dist/esm/deploy-output/npmAliases.mjs +92 -6
- package/dist/esm/deploy-output/plugin.mjs +24 -6
- package/dist/esm/deploy-output/public-assets.mjs +82 -0
- package/dist/esm/deploy-output/target.mjs +50 -3
- package/dist/esm/deploy-output/trace-files.mjs +47 -0
- package/dist/esm/module-federation-config/exposes.mjs +18 -0
- package/dist/esm/module-federation-config/index.mjs +4 -0
- package/dist/esm/module-federation-config/inspect.mjs +65 -0
- package/dist/esm/module-federation-config/object-literal.mjs +43 -0
- package/dist/esm/module-federation-config/syntax.mjs +101 -0
- package/dist/esm/module-federation-config/types.mjs +0 -0
- package/dist/esm/native-type-checker.mjs +158 -18
- package/dist/esm/policy-defaults.mjs +51 -0
- package/dist/esm/release-envelope/canonical.mjs +2 -9
- package/dist/esm/release-envelope/contract.mjs +21 -0
- package/dist/esm/release-envelope/framework-output.mjs +297 -106
- package/dist/esm/release-envelope/index.mjs +149 -38
- package/dist/esm/release-envelope/plugin.mjs +24 -15
- package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
- package/dist/esm/release-envelope/types.mjs +2 -3
- package/dist/esm/release-identity.mjs +8 -5
- package/dist/esm/renderer-build-identity.mjs +82 -0
- package/dist/esm/runtime-package-resolution.mjs +60 -0
- package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm/workspace-topology.mjs +21 -0
- package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
- package/dist/esm-node/backend-federation-build/config.mjs +74 -38
- package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
- package/dist/esm-node/build-config/build-environment.mjs +26 -160
- package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -0
- package/dist/esm-node/build-config/public.mjs +1 -1
- 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/delivery-unit.mjs +124 -45
- package/dist/esm-node/cloudflare/index.mjs +15 -5
- package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
- package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
- package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
- package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
- package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
- package/dist/esm-node/cloudflare-builder.mjs +301 -89
- package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -24
- 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 +11 -6
- package/dist/esm-node/config.mjs +3 -1
- package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
- package/dist/esm-node/deploy-output/plugin.mjs +24 -6
- package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
- package/dist/esm-node/deploy-output/target.mjs +50 -3
- package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
- package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
- package/dist/esm-node/module-federation-config/index.mjs +5 -0
- package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
- package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
- package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
- package/dist/esm-node/module-federation-config/types.mjs +1 -0
- package/dist/esm-node/native-type-checker.mjs +158 -18
- package/dist/esm-node/policy-defaults.mjs +52 -0
- package/dist/esm-node/release-envelope/canonical.mjs +2 -9
- package/dist/esm-node/release-envelope/contract.mjs +22 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
- package/dist/esm-node/release-envelope/index.mjs +149 -38
- package/dist/esm-node/release-envelope/plugin.mjs +24 -15
- package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
- package/dist/esm-node/release-envelope/types.mjs +2 -3
- package/dist/esm-node/release-identity.mjs +8 -5
- package/dist/esm-node/renderer-build-identity.mjs +83 -0
- package/dist/esm-node/runtime-package-resolution.mjs +61 -0
- package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm-node/workspace-topology.mjs +22 -0
- package/dist/types/backend-federation-build/codegen.d.ts +30 -0
- package/dist/types/backend-federation-build/config.d.ts +10 -57
- package/dist/types/backend-federation-build/emit.d.ts +12 -2
- package/dist/types/backend-federation-build/index.d.ts +1 -1
- package/dist/types/build-config/build-environment.d.ts +1 -9
- package/dist/types/build-config/internal-effect-discovery.d.ts +4 -0
- package/dist/types/build-config/public.d.ts +1 -1
- 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 +38 -7
- package/dist/types/cloudflare/native-renderer.d.ts +30 -0
- package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
- package/dist/types/cloudflare/worker-manifest.d.ts +17 -2
- package/dist/types/cloudflare/worker-options.d.ts +20 -0
- package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
- package/dist/types/cloudflare-builder.d.ts +51 -8
- package/dist/types/cloudflare-output-contract.d.ts +5 -3
- package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
- package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
- package/dist/types/config.d.ts +98 -13
- package/dist/types/deploy-output/plugin.d.ts +29 -9
- package/dist/types/deploy-output/public-assets.d.ts +51 -0
- package/dist/types/deploy-output/target.d.ts +28 -5
- package/dist/types/deploy-output/trace-files.d.ts +17 -0
- package/dist/types/module-federation-config/exposes.d.ts +11 -0
- package/dist/types/module-federation-config/index.d.ts +5 -0
- package/dist/types/module-federation-config/inspect.d.ts +2 -0
- package/dist/types/module-federation-config/object-literal.d.ts +4 -0
- package/dist/types/module-federation-config/syntax.d.ts +7 -0
- package/dist/types/module-federation-config/types.d.ts +20 -0
- package/dist/types/native-type-checker.d.ts +26 -3
- package/dist/types/policy-defaults.d.ts +56 -0
- package/dist/types/release-envelope/canonical.d.ts +5 -5
- package/dist/types/release-envelope/contract.d.mts +8 -0
- package/dist/types/release-envelope/framework-output.d.ts +23 -13
- package/dist/types/release-envelope/index.d.ts +17 -5
- package/dist/types/release-envelope/plugin.d.ts +10 -6
- package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
- package/dist/types/release-envelope/types.d.ts +42 -4
- package/dist/types/release-identity.d.ts +3 -2
- package/dist/types/renderer-build-identity.d.ts +30 -0
- package/dist/types/runtime-package-resolution.d.ts +28 -0
- package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
- package/dist/types/workspace-topology.d.ts +91 -0
- package/package.json +97 -26
- package/src/backend-federation-build/codegen.ts +28 -19
- package/src/backend-federation-build/config.ts +189 -123
- package/src/backend-federation-build/emit.ts +119 -38
- package/src/backend-federation-build/index.ts +4 -1
- package/src/build-config/build-environment.ts +59 -285
- package/src/build-config/internal-effect-discovery.ts +239 -0
- package/src/build-config/public.ts +0 -1
- package/src/build-config/remote-address.ts +33 -0
- package/src/cloudflare/artifacts.ts +22 -43
- package/src/cloudflare/delivery-unit.ts +283 -60
- package/src/cloudflare/index.ts +35 -19
- package/src/cloudflare/native-renderer.ts +117 -0
- package/src/cloudflare/renderer-identity.ts +171 -0
- package/src/cloudflare/worker-manifest.ts +108 -10
- package/src/cloudflare/worker-options.ts +60 -0
- package/src/cloudflare/wrangler-config.ts +390 -14
- package/src/cloudflare-builder.ts +543 -170
- package/src/cloudflare-output-contract.ts +45 -2
- package/src/cloudflare-output-verifier/identity.ts +138 -7
- 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 +27 -11
- package/src/config.ts +101 -19
- package/src/deploy-output/npmAliases.ts +141 -4
- package/src/deploy-output/plugin.ts +55 -18
- package/src/deploy-output/public-assets.ts +224 -0
- package/src/deploy-output/target.ts +97 -14
- package/src/deploy-output/trace-files.ts +80 -0
- package/src/module-federation-config/exposes.ts +46 -0
- package/src/module-federation-config/index.ts +22 -0
- package/src/module-federation-config/inspect.ts +157 -0
- package/src/module-federation-config/object-literal.ts +76 -0
- package/src/module-federation-config/syntax.ts +212 -0
- package/src/module-federation-config/types.ts +22 -0
- package/src/native-type-checker.ts +281 -26
- package/src/policy-defaults.ts +157 -0
- package/src/release-envelope/canonical.ts +6 -18
- package/src/release-envelope/contract.mjs +28 -0
- package/src/release-envelope/framework-output.ts +681 -156
- package/src/release-envelope/index.ts +315 -60
- package/src/release-envelope/plugin.ts +54 -38
- package/src/release-envelope/renderer-output-stamp.ts +210 -0
- package/src/release-envelope/types.ts +75 -6
- package/src/release-identity.ts +19 -2
- package/src/renderer-build-identity.ts +168 -0
- package/src/runtime-package-resolution.ts +115 -0
- package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/src/workspace-topology.ts +125 -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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { DELIVERY_UNIT_IDENTITY_FIELDS, deliveryUnitIdentityFieldValue } from "@modern-js/backend-federation-contracts";
|
|
2
|
+
import { DELIVERY_UNIT_IDENTITY_FIELDS, deliveryUnitIdentityFieldValue, validateRendererIdentity, validateRendererProfile, validateRendererProfileCompatibility } from "@modern-js/backend-federation-contracts";
|
|
3
|
+
import { isRecord } from "../cloudflare/utils.mjs";
|
|
3
4
|
import { addIssue, assertEqual } from "./issues.mjs";
|
|
4
5
|
const verifyDeliveryUnitIdentity = (issues, manifest, manifestPath, declared)=>{
|
|
5
6
|
const stamped = manifest?.deliveryUnit;
|
|
6
|
-
const hasStamp =
|
|
7
|
+
const hasStamp = isRecord(stamped);
|
|
7
8
|
if (declared) {
|
|
8
9
|
if (!hasStamp) return void addIssue(issues, {
|
|
9
10
|
code: 'missing-delivery-unit',
|
|
@@ -19,36 +20,124 @@ const verifyDeliveryUnitIdentity = (issues, manifest, manifestPath, declared)=>{
|
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
if (hasStamp
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (hasStamp) {
|
|
24
|
+
if (!isRecord(stamped.surfaces)) return void addIssue(issues, {
|
|
25
|
+
code: 'missing-delivery-unit',
|
|
26
|
+
message: 'Cloudflare worker manifest is missing delivery-unit surface markers.',
|
|
27
|
+
path: manifestPath
|
|
28
|
+
});
|
|
29
|
+
for (const surface of Object.keys(stamped.surfaces))if ('ui' !== surface && 'api' !== surface) addIssue(issues, {
|
|
30
|
+
code: 'delivery-unit-drift',
|
|
31
|
+
message: `Cloudflare worker manifest declares an unsupported ${surface} delivery-unit surface.`,
|
|
32
|
+
path: manifestPath
|
|
33
|
+
});
|
|
34
|
+
if (!declared?.surfaces && !Object.hasOwn(stamped.surfaces, 'ui') && !Object.hasOwn(stamped.surfaces, 'api')) return void addIssue(issues, {
|
|
35
|
+
code: 'missing-delivery-unit',
|
|
36
|
+
message: 'Cloudflare worker manifest is missing delivery-unit surface markers.',
|
|
37
|
+
path: manifestPath
|
|
38
|
+
});
|
|
39
|
+
for (const surface of [
|
|
40
|
+
'ui',
|
|
41
|
+
'api'
|
|
42
|
+
]){
|
|
43
|
+
const marker = stamped.surfaces[surface];
|
|
44
|
+
const expected = declared?.surfaces?.[surface];
|
|
45
|
+
if (!expected && !marker) continue;
|
|
46
|
+
if (declared?.surfaces && !expected) {
|
|
47
|
+
if (marker) addIssue(issues, {
|
|
48
|
+
code: 'delivery-unit-drift',
|
|
49
|
+
message: `Cloudflare worker manifest declares an unexpected ${surface} delivery-unit surface for this topology profile.`,
|
|
50
|
+
path: manifestPath
|
|
51
|
+
});
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!isRecord(marker)) {
|
|
55
|
+
addIssue(issues, {
|
|
56
|
+
code: 'missing-delivery-unit',
|
|
57
|
+
message: `Cloudflare worker manifest is missing the ${surface} delivery-unit surface marker.`,
|
|
58
|
+
path: manifestPath
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
assertEqual(issues, marker.surface, surface, {
|
|
30
63
|
code: 'delivery-unit-drift',
|
|
31
|
-
message: `Cloudflare worker manifest
|
|
64
|
+
message: `Cloudflare worker manifest ${surface} surface must declare surface "${surface}".`,
|
|
32
65
|
path: manifestPath
|
|
33
66
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
for (const field of DELIVERY_UNIT_IDENTITY_FIELDS){
|
|
68
|
+
const markerValue = deliveryUnitIdentityFieldValue(marker, field);
|
|
69
|
+
const stampedValue = deliveryUnitIdentityFieldValue(stamped, field);
|
|
70
|
+
assertEqual(issues, markerValue, stampedValue, {
|
|
71
|
+
code: 'delivery-unit-drift',
|
|
72
|
+
message: `Cloudflare worker manifest ${surface} surface deliveryUnit.${field} must derive from one delivery-unit record (expected ${stampedValue ?? 'undefined'}, received ${markerValue ?? 'undefined'}).`,
|
|
73
|
+
path: manifestPath
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if ('api' === surface) {
|
|
77
|
+
for (const field of [
|
|
78
|
+
'rendererIdentity',
|
|
79
|
+
'rendererProfile'
|
|
80
|
+
])if (Object.hasOwn(marker, field)) addIssue(issues, {
|
|
81
|
+
code: 'delivery-unit-drift',
|
|
82
|
+
message: `Cloudflare worker manifest API surface must not declare ${field}.`,
|
|
83
|
+
path: manifestPath
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const identity = marker.rendererIdentity;
|
|
88
|
+
const profile = marker.rendererProfile;
|
|
89
|
+
const errors = [
|
|
90
|
+
...validateRendererIdentity(identity).errors,
|
|
91
|
+
...validateRendererProfile(profile).errors
|
|
92
|
+
];
|
|
93
|
+
for (const error of errors)addIssue(issues, {
|
|
94
|
+
code: 'delivery-unit-drift',
|
|
95
|
+
message: `Cloudflare worker manifest UI ${error.path} ${error.message}`,
|
|
40
96
|
path: manifestPath
|
|
41
97
|
});
|
|
42
|
-
continue;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
98
|
+
if (errors.length || !isRecord(identity) || !isRecord(profile)) continue;
|
|
99
|
+
assertEqual(issues, identity.buildId, marker.buildMarker, {
|
|
100
|
+
code: 'delivery-unit-drift',
|
|
101
|
+
message: 'Cloudflare worker manifest UI rendererIdentity.buildId must match its surface buildMarker.',
|
|
102
|
+
path: manifestPath
|
|
103
|
+
});
|
|
104
|
+
assertEqual(issues, identity.appId, marker.appId, {
|
|
105
|
+
code: 'delivery-unit-drift',
|
|
106
|
+
message: 'Cloudflare worker manifest UI rendererIdentity.appId must match its surface appId.',
|
|
107
|
+
path: manifestPath
|
|
108
|
+
});
|
|
109
|
+
assertEqual(issues, marker.appId, stamped.appId, {
|
|
110
|
+
code: 'delivery-unit-drift',
|
|
111
|
+
message: 'Cloudflare worker manifest UI surface appId must match its delivery-unit appId.',
|
|
112
|
+
path: manifestPath
|
|
113
|
+
});
|
|
114
|
+
for (const field of [
|
|
115
|
+
'renderer',
|
|
116
|
+
'protocolVersion'
|
|
117
|
+
])assertEqual(issues, identity[field], profile[field], {
|
|
48
118
|
code: 'delivery-unit-drift',
|
|
49
|
-
message: `Cloudflare worker manifest
|
|
119
|
+
message: `Cloudflare worker manifest UI rendererProfile.${field} must match its renderer identity.`,
|
|
50
120
|
path: manifestPath
|
|
51
121
|
});
|
|
122
|
+
const expectedUi = declared?.surfaces?.ui;
|
|
123
|
+
if (expectedUi?.rendererIdentity) {
|
|
124
|
+
for (const field of [
|
|
125
|
+
'renderer',
|
|
126
|
+
'appId',
|
|
127
|
+
'entryName',
|
|
128
|
+
'protocolVersion',
|
|
129
|
+
'buildId'
|
|
130
|
+
])assertEqual(issues, identity[field], expectedUi.rendererIdentity[field], {
|
|
131
|
+
code: 'delivery-unit-drift',
|
|
132
|
+
message: `Cloudflare worker manifest UI rendererIdentity.${field} must match the topology renderer identity.`,
|
|
133
|
+
path: manifestPath
|
|
134
|
+
});
|
|
135
|
+
for (const error of validateRendererProfileCompatibility(expectedUi.rendererProfile, profile).errors)addIssue(issues, {
|
|
136
|
+
code: 'delivery-unit-drift',
|
|
137
|
+
message: `Cloudflare worker manifest UI ${error.path} ${error.message}`,
|
|
138
|
+
path: manifestPath
|
|
139
|
+
});
|
|
140
|
+
}
|
|
52
141
|
}
|
|
53
142
|
}
|
|
54
143
|
};
|
|
@@ -73,6 +73,16 @@ const verifyCloudflareOutput = async (options)=>{
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
if (wrangler) {
|
|
76
|
+
if ('string' != typeof wrangler.name || 0 === wrangler.name.trim().length) addIssue(issues, {
|
|
77
|
+
code: 'invalid-wrangler',
|
|
78
|
+
message: 'wrangler.json name must be a non-empty worker name.',
|
|
79
|
+
path: wranglerPath
|
|
80
|
+
});
|
|
81
|
+
if ('string' != typeof wrangler.compatibility_date || !/^\d{4}-\d{2}-\d{2}$/u.test(wrangler.compatibility_date)) addIssue(issues, {
|
|
82
|
+
code: 'invalid-wrangler',
|
|
83
|
+
message: 'wrangler.json compatibility_date must use YYYY-MM-DD.',
|
|
84
|
+
path: wranglerPath
|
|
85
|
+
});
|
|
76
86
|
assertEqual(issues, wrangler.main, outputPlan.wrangler.main, {
|
|
77
87
|
code: 'invalid-wrangler',
|
|
78
88
|
message: `wrangler.json main must be ${outputPlan.wrangler.main}.`,
|
|
@@ -43,6 +43,52 @@ const verifyManifestShape = (issues, manifest, manifestPath)=>{
|
|
|
43
43
|
message: 'Cloudflare output manifest routeSpec.routes must be array.',
|
|
44
44
|
path: manifestPath
|
|
45
45
|
});
|
|
46
|
+
const security = manifest.security;
|
|
47
|
+
if (security && 'object' == typeof security && 'boolean' == typeof security.enabled) {
|
|
48
|
+
const cors = security.cors;
|
|
49
|
+
if (!cors || 'object' != typeof cors || 'boolean' != typeof cors.assets || !Array.isArray(cors.allowedOrigins) || !Array.isArray(cors.allowedMethods) || !Array.isArray(cors.allowedHeaders)) addIssue(issues, {
|
|
50
|
+
code: 'invalid-manifest',
|
|
51
|
+
message: 'Cloudflare output manifest security.cors is incomplete.',
|
|
52
|
+
path: manifestPath
|
|
53
|
+
});
|
|
54
|
+
if (security.enabled) {
|
|
55
|
+
const headers = security.headers;
|
|
56
|
+
const csp = security.contentSecurityPolicy;
|
|
57
|
+
const noindex = security.noindex;
|
|
58
|
+
if (!headers || 'object' != typeof headers || ![
|
|
59
|
+
'string',
|
|
60
|
+
'boolean'
|
|
61
|
+
].includes(typeof headers.referrerPolicy) || ![
|
|
62
|
+
'string',
|
|
63
|
+
'boolean'
|
|
64
|
+
].includes(typeof headers.contentTypeOptions) || ![
|
|
65
|
+
'string',
|
|
66
|
+
'boolean'
|
|
67
|
+
].includes(typeof headers.permissionsPolicy)) addIssue(issues, {
|
|
68
|
+
code: 'invalid-manifest',
|
|
69
|
+
message: 'Cloudflare output manifest security.headers is incomplete.',
|
|
70
|
+
path: manifestPath
|
|
71
|
+
});
|
|
72
|
+
if (!csp || ![
|
|
73
|
+
'off',
|
|
74
|
+
'report-only',
|
|
75
|
+
'enforce'
|
|
76
|
+
].includes(csp.mode) || !csp.directives || 'object' != typeof csp.directives || Array.isArray(csp.directives) || Object.values(csp.directives).some((value)=>!Array.isArray(value) || value.some((entry)=>'string' != typeof entry))) addIssue(issues, {
|
|
77
|
+
code: 'invalid-manifest',
|
|
78
|
+
message: 'Cloudflare output manifest security.contentSecurityPolicy is invalid.',
|
|
79
|
+
path: manifestPath
|
|
80
|
+
});
|
|
81
|
+
if (!noindex || 'boolean' != typeof noindex.workersDev || 'boolean' != typeof noindex.localhost || !Array.isArray(noindex.previewHostnames)) addIssue(issues, {
|
|
82
|
+
code: 'invalid-manifest',
|
|
83
|
+
message: 'Cloudflare output manifest security.noindex is incomplete.',
|
|
84
|
+
path: manifestPath
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
} else addIssue(issues, {
|
|
88
|
+
code: 'invalid-manifest',
|
|
89
|
+
message: 'Cloudflare output manifest security.enabled must be a boolean.',
|
|
90
|
+
path: manifestPath
|
|
91
|
+
});
|
|
46
92
|
if (manifest.bff?.runtimeFramework === 'effect') assertEqual(issues, manifest.bff.dispatcherExport, BFF_EFFECT_WORKER_DISPATCHER_EXPORT, {
|
|
47
93
|
code: 'invalid-manifest',
|
|
48
94
|
message: `Cloudflare Effect BFF manifest dispatcherExport must be ${BFF_EFFECT_WORKER_DISPATCHER_EXPORT}.`,
|
|
@@ -85,7 +85,13 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
|
|
|
85
85
|
const files = (await Promise.all(options.scanRoots.map((scanRoot)=>walkFiles(node_path.resolve(scanRoot))))).flat();
|
|
86
86
|
for (const file of files){
|
|
87
87
|
if (excludedFiles.has(file)) continue;
|
|
88
|
-
|
|
88
|
+
let source;
|
|
89
|
+
try {
|
|
90
|
+
source = await promises.readFile(file, 'utf-8');
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if ('ENOENT' === error.code) continue;
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
89
95
|
for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (forbidden.pattern.test(source)) addIssue(issues, {
|
|
90
96
|
code: forbidden.code,
|
|
91
97
|
message: forbidden.message,
|
|
@@ -5,9 +5,9 @@ import { pathToFileURL } from "node:url";
|
|
|
5
5
|
import { isDeepStrictEqual } from "node:util";
|
|
6
6
|
import { parse } from "@babel/parser";
|
|
7
7
|
import traverse from "@babel/traverse";
|
|
8
|
-
import { CLOUDFLARE_WORKER_BUNDLE_DIRECTORY, CLOUDFLARE_WORKER_NODE_BUILTINS, CLOUDFLARE_WORKER_PLATFORM_MODULES } from "../cloudflare-output-contract.mjs";
|
|
8
|
+
import { CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT, CLOUDFLARE_WORKER_BUNDLE_DIRECTORY, CLOUDFLARE_WORKER_NODE_BUILTINS, CLOUDFLARE_WORKER_PLATFORM_MODULES } from "../cloudflare-output-contract.mjs";
|
|
9
9
|
import { addIssue } from "./issues.mjs";
|
|
10
|
-
const getReferencedRouteWorkers = (manifest)=>Array.isArray(manifest?.routeSpec?.routes) ? manifest.routeSpec.routes.map((route)=>'string' == typeof route?.
|
|
10
|
+
const getReferencedRouteWorkers = (manifest, field)=>Array.isArray(manifest?.routeSpec?.routes) ? manifest.routeSpec.routes.map((route)=>'string' == typeof route?.[field] && route[field].length > 0 ? route[field] : void 0).filter((worker)=>'string' == typeof worker) : [];
|
|
11
11
|
const getEffectBffWorker = (manifest)=>manifest?.bff?.runtimeFramework === 'effect' && 'string' == typeof manifest.bff.worker ? manifest.bff.worker : void 0;
|
|
12
12
|
const getWorkerBundleReferences = (manifest)=>{
|
|
13
13
|
const effectBffWorker = getEffectBffWorker(manifest);
|
|
@@ -19,9 +19,14 @@ const getWorkerBundleReferences = (manifest)=>{
|
|
|
19
19
|
reference: effectBffWorker
|
|
20
20
|
}
|
|
21
21
|
] : [],
|
|
22
|
-
...getReferencedRouteWorkers(manifest).map((reference)=>({
|
|
22
|
+
...getReferencedRouteWorkers(manifest, 'worker').map((reference)=>({
|
|
23
23
|
kind: 'route',
|
|
24
24
|
reference
|
|
25
|
+
})),
|
|
26
|
+
...getReferencedRouteWorkers(manifest, 'routeDataWorker').map((reference)=>({
|
|
27
|
+
dispatcherExport: CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT,
|
|
28
|
+
kind: 'route-data',
|
|
29
|
+
reference
|
|
25
30
|
}))
|
|
26
31
|
];
|
|
27
32
|
};
|
|
@@ -42,7 +47,7 @@ const resolveWorkerBundleReference = (issues, outputDirectory, reference, manife
|
|
|
42
47
|
path: workerPath
|
|
43
48
|
};
|
|
44
49
|
};
|
|
45
|
-
const missingWorkerBundleMessage = (reference)=>'effect-bff' === reference.kind ? 'Cloudflare Effect BFF manifest points to a missing worker bundle.' : 'Cloudflare route worker manifest points to a missing worker bundle.';
|
|
50
|
+
const missingWorkerBundleMessage = (reference)=>'effect-bff' === reference.kind ? 'Cloudflare Effect BFF manifest points to a missing worker bundle.' : 'route-data' === reference.kind ? 'Cloudflare route data worker manifest points to a missing worker bundle.' : 'Cloudflare route worker manifest points to a missing worker bundle.';
|
|
46
51
|
const isAstNode = (value)=>null !== value && 'object' == typeof value && 'type' in value && 'string' == typeof value.type;
|
|
47
52
|
const getNodeName = (node)=>{
|
|
48
53
|
if (!isAstNode(node)) return;
|
|
@@ -286,9 +291,9 @@ const verifyWorkerModuleClosure = async (issues, entryPath, entrySource, contrac
|
|
|
286
291
|
};
|
|
287
292
|
const verifyWorkerBundleReferences = async (issues, worker, source, contract)=>{
|
|
288
293
|
const entryAnalysis = await verifyWorkerModuleClosure(issues, worker.path, source, contract);
|
|
289
|
-
if ('
|
|
294
|
+
if ('string' == typeof worker.dispatcherExport && !entryAnalysis?.exports.has(worker.dispatcherExport)) addIssue(issues, {
|
|
290
295
|
code: 'invalid-worker-bundle',
|
|
291
|
-
message: 'Cloudflare Effect BFF worker bundle must expose its manifest dispatcherExport.',
|
|
296
|
+
message: 'route-data' === worker.kind ? `Cloudflare route data worker bundle must expose ${worker.dispatcherExport}.` : 'Cloudflare Effect BFF worker bundle must expose its manifest dispatcherExport.',
|
|
292
297
|
path: worker.path
|
|
293
298
|
});
|
|
294
299
|
};
|
package/dist/esm-node/config.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
export { getBuildConfigEnvironment, resolveEffectTsgoCompiler
|
|
2
|
+
export { getBuildConfigEnvironment, resolveEffectTsgoCompiler } from "./build-config/public.mjs";
|
|
3
|
+
export { createRemoteManifestUrl } from "./build-config/remote-address.mjs";
|
|
4
|
+
export { resolveDeployTarget } from "./deploy-output/target.mjs";
|
|
@@ -5,6 +5,10 @@ const dependencyKeys = [
|
|
|
5
5
|
'dependencies',
|
|
6
6
|
'optionalDependencies'
|
|
7
7
|
];
|
|
8
|
+
const rootDependencyKeys = [
|
|
9
|
+
...dependencyKeys,
|
|
10
|
+
'devDependencies'
|
|
11
|
+
];
|
|
8
12
|
const exactVersionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
|
|
9
13
|
async function readPackageJson(filePath) {
|
|
10
14
|
return JSON.parse(await promises.readFile(filePath, 'utf8'));
|
|
@@ -20,14 +24,87 @@ function parseNpmAlias(aliasName, specifier) {
|
|
|
20
24
|
targetVersion: target.slice(versionSeparator + 1)
|
|
21
25
|
};
|
|
22
26
|
}
|
|
23
|
-
function aliasesFromPackageJson(packageJson) {
|
|
27
|
+
function aliasesFromPackageJson(packageJson, keys = dependencyKeys) {
|
|
24
28
|
const aliases = [];
|
|
25
|
-
for (const dependencyKey of
|
|
29
|
+
for (const dependencyKey of keys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
|
|
26
30
|
const alias = parseNpmAlias(aliasName, specifier);
|
|
27
31
|
if (alias) aliases.push(alias);
|
|
28
32
|
}
|
|
29
33
|
return aliases;
|
|
30
34
|
}
|
|
35
|
+
async function catalogAliasesFromInstalledPackages(appDirectory, packageJson) {
|
|
36
|
+
const aliases = [];
|
|
37
|
+
for (const dependencyKey of rootDependencyKeys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
|
|
38
|
+
if (!specifier.startsWith('catalog:')) continue;
|
|
39
|
+
let installedPackage;
|
|
40
|
+
try {
|
|
41
|
+
installedPackage = await readPackageJson(node_path.join(appDirectory, 'node_modules', aliasName, 'package.json'));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if ('ENOENT' === error.code) continue;
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
if (!installedPackage.name || !installedPackage.version) throw new Error(`Installed catalog package ${aliasName} has no identity`);
|
|
47
|
+
if (installedPackage.name !== aliasName) aliases.push({
|
|
48
|
+
aliasName,
|
|
49
|
+
targetName: installedPackage.name,
|
|
50
|
+
targetVersion: installedPackage.version
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return aliases;
|
|
54
|
+
}
|
|
55
|
+
async function installedAliasesByOwner(appDirectory, emittedOwners) {
|
|
56
|
+
const aliasesByOwner = new Map();
|
|
57
|
+
let rootAliases = [];
|
|
58
|
+
const visited = new Set();
|
|
59
|
+
const rootFile = await promises.realpath(node_path.join(appDirectory, 'package.json'));
|
|
60
|
+
const pending = [
|
|
61
|
+
rootFile
|
|
62
|
+
];
|
|
63
|
+
while(pending.length){
|
|
64
|
+
const file = await promises.realpath(pending.pop());
|
|
65
|
+
if (visited.has(file)) continue;
|
|
66
|
+
visited.add(file);
|
|
67
|
+
const owner = await readPackageJson(file);
|
|
68
|
+
const aliases = [];
|
|
69
|
+
const keys = file === rootFile ? rootDependencyKeys : dependencyKeys;
|
|
70
|
+
for (const key of keys)for (const name of Object.keys(owner[key] ?? {})){
|
|
71
|
+
let directory = node_path.dirname(file);
|
|
72
|
+
while(true){
|
|
73
|
+
const packageFile = node_path.join(directory, 'node_modules', name, 'package.json');
|
|
74
|
+
let target;
|
|
75
|
+
try {
|
|
76
|
+
target = await readPackageJson(packageFile);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if ('ENOENT' !== error.code) throw error;
|
|
79
|
+
const parent = node_path.dirname(directory);
|
|
80
|
+
if (parent === directory) break;
|
|
81
|
+
directory = parent;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!target.name || !target.version) throw new Error(`Installed package ${name} has no identity`);
|
|
85
|
+
pending.push(packageFile);
|
|
86
|
+
if (target.name !== name) aliases.push({
|
|
87
|
+
aliasName: name,
|
|
88
|
+
targetName: target.name,
|
|
89
|
+
targetVersion: target.version
|
|
90
|
+
});
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (file === rootFile) rootAliases = aliases;
|
|
95
|
+
const identity = `${owner.name}@${owner.version}`;
|
|
96
|
+
if (emittedOwners.has(identity)) {
|
|
97
|
+
const existing = aliasesByOwner.get(identity);
|
|
98
|
+
const normalized = (items)=>items.map((item)=>`${item.aliasName}=${aliasSpecifier(item)}`).sort().join('\n');
|
|
99
|
+
if (existing && normalized(existing) !== normalized(aliases)) throw new Error(`Cannot preserve npm aliases for ${identity}: installed contexts resolve different dependency identities`);
|
|
100
|
+
aliasesByOwner.set(identity, aliases);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
aliasesByOwner,
|
|
105
|
+
rootAliases
|
|
106
|
+
};
|
|
107
|
+
}
|
|
31
108
|
async function collectPackageRecords(nodeModulesDirectory) {
|
|
32
109
|
const records = [];
|
|
33
110
|
const visitedNodeModules = new Set();
|
|
@@ -148,8 +225,11 @@ async function readPackageIdentity(entryPath) {
|
|
|
148
225
|
async function preserveNpmAliases({ appDirectory, outputDirectory, implicitAliases = [] }) {
|
|
149
226
|
const records = await collectPackageRecords(node_path.join(outputDirectory, 'node_modules'));
|
|
150
227
|
const appPackageJson = await readPackageJson(node_path.join(appDirectory, 'package.json'));
|
|
228
|
+
const installedAliases = await installedAliasesByOwner(appDirectory, new Set(records.map(({ packageJson })=>`${packageJson.name}@${packageJson.version}`)));
|
|
151
229
|
const rootAliases = [
|
|
152
|
-
...aliasesFromPackageJson(appPackageJson),
|
|
230
|
+
...aliasesFromPackageJson(appPackageJson, rootDependencyKeys),
|
|
231
|
+
...installedAliases.rootAliases,
|
|
232
|
+
...await catalogAliasesFromInstalledPackages(appDirectory, appPackageJson),
|
|
153
233
|
...implicitAliases
|
|
154
234
|
].filter((alias)=>alias.aliasName !== alias.targetName);
|
|
155
235
|
const emittedRootAliases = [];
|
|
@@ -162,9 +242,15 @@ async function preserveNpmAliases({ appDirectory, outputDirectory, implicitAlias
|
|
|
162
242
|
await ensureAliasLink(outputDirectory, alias, target);
|
|
163
243
|
emittedRootAliases.push(alias);
|
|
164
244
|
}
|
|
165
|
-
for (const record of records)
|
|
166
|
-
const
|
|
167
|
-
|
|
245
|
+
for (const record of records){
|
|
246
|
+
const ownerAliases = [
|
|
247
|
+
...aliasesFromPackageJson(record.packageJson),
|
|
248
|
+
...installedAliases.aliasesByOwner.get(`${record.packageJson.name}@${record.packageJson.version}`) ?? []
|
|
249
|
+
];
|
|
250
|
+
for (const alias of ownerAliases.filter((candidate)=>candidate.aliasName !== candidate.targetName)){
|
|
251
|
+
const target = await resolveAliasTarget(record.directory, alias, outputDirectory);
|
|
252
|
+
if (target) await ensureAliasLink(record.directory, alias, target);
|
|
253
|
+
}
|
|
168
254
|
}
|
|
169
255
|
const outputPackageJsonPath = node_path.join(outputDirectory, 'package.json');
|
|
170
256
|
const outputPackageJson = await readPackageJson(outputPackageJsonPath);
|
|
@@ -2,16 +2,17 @@ import "node:module";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import node_path from "node:path";
|
|
4
4
|
import { preserveNpmAliases, readPackageIdentity } from "./npmAliases.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { NODE_PUBLIC_ASSET_SCOPE, normalizeDeclaredPublicAssets, stageDeclaredPublicAssets } from "./public-assets.mjs";
|
|
6
|
+
import { getDeployingTarget } from "./target.mjs";
|
|
6
7
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
7
8
|
var __rspack_import_meta_filename__ = __rspack_fileURLToPath(import.meta.url);
|
|
8
|
-
const createDeployOutputAliasesPlugin = (
|
|
9
|
+
const createDeployOutputAliasesPlugin = ()=>({
|
|
9
10
|
name: '@modern-js/deploy-output-aliases',
|
|
10
11
|
setup (api) {
|
|
11
12
|
api.onAfterDeploy(async ()=>{
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const appContext = api.getAppContext();
|
|
14
|
+
if ('node' !== getDeployingTarget(appContext)) return;
|
|
15
|
+
const { appDirectory } = appContext;
|
|
15
16
|
const entry = createRequire(__rspack_import_meta_filename__).resolve('@modern-js/prod-server');
|
|
16
17
|
const identity = await readPackageIdentity(entry);
|
|
17
18
|
await preserveNpmAliases({
|
|
@@ -28,4 +29,21 @@ const createDeployOutputAliasesPlugin = ({ resolveDeployTarget = external_target
|
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
31
|
});
|
|
31
|
-
|
|
32
|
+
const createDeployOutputPublicAssetsPlugin = ()=>({
|
|
33
|
+
name: '@modern-js/deploy-output-public-assets',
|
|
34
|
+
setup (api) {
|
|
35
|
+
api.onAfterDeploy(async ()=>{
|
|
36
|
+
const appContext = api.getAppContext();
|
|
37
|
+
if ('node' !== getDeployingTarget(appContext)) return;
|
|
38
|
+
const { appDirectory } = appContext;
|
|
39
|
+
const config = api.getNormalizedConfig();
|
|
40
|
+
await stageDeclaredPublicAssets({
|
|
41
|
+
appDirectory,
|
|
42
|
+
outputDirectory: node_path.join(appDirectory, '.output'),
|
|
43
|
+
assets: normalizeDeclaredPublicAssets(config.deploy?.node?.publicAssets, NODE_PUBLIC_ASSET_SCOPE),
|
|
44
|
+
scope: NODE_PUBLIC_ASSET_SCOPE
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
export { createDeployOutputAliasesPlugin, createDeployOutputPublicAssetsPlugin };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import promises from "node:fs/promises";
|
|
3
|
+
import node_path from "node:path";
|
|
4
|
+
import { normalizeRelativePath } from "../cloudflare/utils.mjs";
|
|
5
|
+
const DECLARED_PUBLIC_ASSET_DIRECTORY = 'public';
|
|
6
|
+
const CLOUDFLARE_PUBLIC_ASSET_SCOPE = {
|
|
7
|
+
label: 'deploy.worker.publicAssets',
|
|
8
|
+
output: 'Cloudflare public output'
|
|
9
|
+
};
|
|
10
|
+
const NODE_PUBLIC_ASSET_SCOPE = {
|
|
11
|
+
label: 'deploy.node.publicAssets',
|
|
12
|
+
output: 'Node public output'
|
|
13
|
+
};
|
|
14
|
+
const normalizeDeclaredPublicAssets = (assets, scope)=>(assets ?? []).map((asset, index)=>({
|
|
15
|
+
from: normalizeRelativePath(asset?.from, `${scope.label}[${index}].from`, 'app root'),
|
|
16
|
+
to: normalizeRelativePath(asset?.to, `${scope.label}[${index}].to`, scope.output, {
|
|
17
|
+
allowRoot: true
|
|
18
|
+
}),
|
|
19
|
+
index
|
|
20
|
+
}));
|
|
21
|
+
const isMissingPathError = (error)=>error instanceof Error && 'code' in error && 'ENOENT' === error.code;
|
|
22
|
+
const pathExists = async (filePath)=>{
|
|
23
|
+
try {
|
|
24
|
+
await promises.lstat(filePath);
|
|
25
|
+
return true;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (isMissingPathError(error)) return false;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const listDeclaredPublicAssetFiles = async (appDirectory, assets, scope)=>{
|
|
32
|
+
const files = [];
|
|
33
|
+
const owners = new Map();
|
|
34
|
+
const add = (index, logicalPath)=>{
|
|
35
|
+
const owner = owners.get(logicalPath);
|
|
36
|
+
if (void 0 !== owner) throw new Error(`${scope.label}[${index}] and ${scope.label}[${owner}] both stage ${JSON.stringify(logicalPath)}.`);
|
|
37
|
+
owners.set(logicalPath, index);
|
|
38
|
+
files.push({
|
|
39
|
+
index,
|
|
40
|
+
logicalPath
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const visit = async (asset, sourcePath, destination)=>{
|
|
44
|
+
const stat = await promises.lstat(sourcePath);
|
|
45
|
+
if (stat.isSymbolicLink()) throw new Error(`${scope.label}[${asset.index}].from must not contain symbolic links: ${node_path.relative(appDirectory, sourcePath).replace(/\\/gu, '/')}`);
|
|
46
|
+
if (stat.isFile()) {
|
|
47
|
+
if (destination === DECLARED_PUBLIC_ASSET_DIRECTORY) throw new Error(`${scope.label}[${asset.index}].to must name a file when ${scope.label}[${asset.index}].from is a file.`);
|
|
48
|
+
add(asset.index, destination);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!stat.isDirectory()) throw new Error(`${scope.label}[${asset.index}].from must contain only files and directories: ${node_path.relative(appDirectory, sourcePath).replace(/\\/gu, '/')}`);
|
|
52
|
+
const entries = (await promises.readdir(sourcePath)).sort();
|
|
53
|
+
for (const entry of entries)await visit(asset, node_path.join(sourcePath, entry), node_path.posix.join(destination, entry));
|
|
54
|
+
};
|
|
55
|
+
for (const asset of assets){
|
|
56
|
+
const sourcePath = node_path.join(appDirectory, asset.from);
|
|
57
|
+
if (!await pathExists(sourcePath)) throw new Error(`${scope.label}[${asset.index}].from does not exist: ${asset.from}`);
|
|
58
|
+
await visit(asset, sourcePath, node_path.posix.join(DECLARED_PUBLIC_ASSET_DIRECTORY, asset.to));
|
|
59
|
+
}
|
|
60
|
+
return files;
|
|
61
|
+
};
|
|
62
|
+
const resolveAddedDeclaredPublicAssetPaths = async ({ appDirectory, assets, generatedRoot, scope })=>{
|
|
63
|
+
const added = [];
|
|
64
|
+
for (const file of (await listDeclaredPublicAssetFiles(appDirectory, assets, scope)))if (!await pathExists(node_path.join(generatedRoot, file.logicalPath))) added.push(file.logicalPath);
|
|
65
|
+
return added.sort();
|
|
66
|
+
};
|
|
67
|
+
const stageDeclaredPublicAssets = async ({ appDirectory, outputDirectory, assets, scope })=>{
|
|
68
|
+
const files = await listDeclaredPublicAssetFiles(appDirectory, assets, scope);
|
|
69
|
+
const added = [];
|
|
70
|
+
for (const file of files){
|
|
71
|
+
const asset = assets.find((candidate)=>candidate.index === file.index);
|
|
72
|
+
const relativePath = node_path.posix.relative(node_path.posix.join(DECLARED_PUBLIC_ASSET_DIRECTORY, asset.to), file.logicalPath);
|
|
73
|
+
const destination = node_path.join(outputDirectory, file.logicalPath);
|
|
74
|
+
const replacesGeneratedFile = await pathExists(destination);
|
|
75
|
+
await promises.mkdir(node_path.dirname(destination), {
|
|
76
|
+
recursive: true
|
|
77
|
+
});
|
|
78
|
+
await promises.copyFile(node_path.join(appDirectory, asset.from, relativePath), destination);
|
|
79
|
+
if (!replacesGeneratedFile) added.push(file.logicalPath);
|
|
80
|
+
}
|
|
81
|
+
return added.sort();
|
|
82
|
+
};
|
|
83
|
+
export { CLOUDFLARE_PUBLIC_ASSET_SCOPE, DECLARED_PUBLIC_ASSET_DIRECTORY, NODE_PUBLIC_ASSET_SCOPE, normalizeDeclaredPublicAssets, resolveAddedDeclaredPublicAssetPaths, stageDeclaredPublicAssets };
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import {
|
|
2
|
+
import { getArgv } from "@modern-js/utils";
|
|
3
|
+
import { provider as external_std_env_provider } from "std-env";
|
|
4
|
+
const DEPLOY_TARGETS = [
|
|
5
|
+
'node',
|
|
6
|
+
'vercel',
|
|
7
|
+
'netlify',
|
|
8
|
+
'ghPages',
|
|
9
|
+
'cloudflare'
|
|
10
|
+
];
|
|
11
|
+
const DEPLOY_TARGET_FLAG = '--deploy-target';
|
|
3
12
|
const providerDeployTargets = {
|
|
4
13
|
vercel: 'vercel',
|
|
5
14
|
netlify: 'netlify',
|
|
@@ -7,5 +16,43 @@ const providerDeployTargets = {
|
|
|
7
16
|
cloudflare_pages: 'cloudflare',
|
|
8
17
|
cloudflare_workers: 'cloudflare'
|
|
9
18
|
};
|
|
10
|
-
const
|
|
11
|
-
|
|
19
|
+
const readDeployTargetFlag = (argv)=>{
|
|
20
|
+
for (const [index, arg] of argv.entries()){
|
|
21
|
+
if (arg === DEPLOY_TARGET_FLAG) {
|
|
22
|
+
const value = argv[index + 1];
|
|
23
|
+
if (void 0 === value || value.startsWith('-')) throw new Error(`${DEPLOY_TARGET_FLAG} needs a value.`);
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
if (arg.startsWith(`${DEPLOY_TARGET_FLAG}=`)) return arg.slice(DEPLOY_TARGET_FLAG.length + 1);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const assertDeployTarget = (target, source)=>{
|
|
30
|
+
if (!DEPLOY_TARGETS.includes(target)) throw new Error(`Unknown deploy target '${target}' from ${source}. Use one of: ${DEPLOY_TARGETS.join(', ')}.`);
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
33
|
+
const resolveDeployTarget = ({ argv = getArgv(), configTarget, env = process.env.MODERNJS_DEPLOY, provider = external_std_env_provider } = {})=>{
|
|
34
|
+
const explicit = [
|
|
35
|
+
[
|
|
36
|
+
readDeployTargetFlag(argv),
|
|
37
|
+
DEPLOY_TARGET_FLAG
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
configTarget,
|
|
41
|
+
'deploy.target'
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
env,
|
|
45
|
+
'MODERNJS_DEPLOY'
|
|
46
|
+
]
|
|
47
|
+
].find(([target])=>target);
|
|
48
|
+
if (explicit?.[0]) return {
|
|
49
|
+
target: assertDeployTarget(explicit[0], explicit[1]),
|
|
50
|
+
explicit: true
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
target: provider && providerDeployTargets[provider] || 'node',
|
|
54
|
+
explicit: false
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const getDeployingTarget = ({ deployTarget, metaName })=>deployTarget && ('modern-js' === metaName || deployTarget.explicit) ? deployTarget.target : void 0;
|
|
58
|
+
export { DEPLOY_TARGETS, DEPLOY_TARGET_FLAG, getDeployingTarget, resolveDeployTarget };
|