@bleedingdev/modern-js-app-tools-extensions 3.8.3-ultramodern.2
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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/cjs/cloudflare/artifacts.js +127 -0
- package/dist/cjs/cloudflare/constants.js +141 -0
- package/dist/cjs/cloudflare/delivery-unit.js +161 -0
- package/dist/cjs/cloudflare/i18n-worker.js +135 -0
- package/dist/cjs/cloudflare/index.js +156 -0
- package/dist/cjs/cloudflare/security-policies.js +205 -0
- package/dist/cjs/cloudflare/types.js +18 -0
- package/dist/cjs/cloudflare/utils.js +66 -0
- package/dist/cjs/cloudflare/worker-manifest.js +239 -0
- package/dist/cjs/cloudflare/wrangler-config.js +205 -0
- package/dist/cjs/cloudflare-builder.js +394 -0
- package/dist/cjs/cloudflare-output-contract.js +108 -0
- package/dist/cjs/cloudflare-output-plan.js +93 -0
- package/dist/cjs/cloudflare-output-verifier/identity.js +92 -0
- package/dist/cjs/cloudflare-output-verifier/index.js +188 -0
- package/dist/cjs/cloudflare-output-verifier/issues.js +54 -0
- package/dist/cjs/cloudflare-output-verifier/manifest.js +89 -0
- package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +151 -0
- package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +378 -0
- package/dist/cjs/config.js +18 -0
- package/dist/cjs/css-extract-runtime-plugin.js +74 -0
- package/dist/cjs/index.js +125 -0
- package/dist/cjs/read-template.js +56 -0
- package/dist/cjs/release-envelope/canonical.js +68 -0
- package/dist/cjs/release-envelope/framework-output.js +649 -0
- package/dist/cjs/release-envelope/index.js +399 -0
- package/dist/cjs/release-envelope/plugin.js +102 -0
- package/dist/cjs/release-envelope/types.js +51 -0
- package/dist/cjs/release-identity.js +110 -0
- package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
- package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
- package/dist/cjs/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
- package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
- package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
- package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +11 -0
- package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +281 -0
- package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
- package/dist/cjs/templates/cloudflare-worker-path.mjs +98 -0
- package/dist/esm/cloudflare/artifacts.mjs +64 -0
- package/dist/esm/cloudflare/constants.mjs +34 -0
- package/dist/esm/cloudflare/delivery-unit.mjs +110 -0
- package/dist/esm/cloudflare/i18n-worker.mjs +97 -0
- package/dist/esm/cloudflare/index.mjs +108 -0
- package/dist/esm/cloudflare/security-policies.mjs +167 -0
- package/dist/esm/cloudflare/types.mjs +0 -0
- package/dist/esm/cloudflare/utils.mjs +12 -0
- package/dist/esm/cloudflare/worker-manifest.mjs +179 -0
- package/dist/esm/cloudflare/wrangler-config.mjs +151 -0
- package/dist/esm/cloudflare-builder.mjs +327 -0
- package/dist/esm/cloudflare-output-contract.mjs +31 -0
- package/dist/esm/cloudflare-output-plan.mjs +45 -0
- package/dist/esm/cloudflare-output-verifier/identity.mjs +54 -0
- package/dist/esm/cloudflare-output-verifier/index.mjs +133 -0
- package/dist/esm/cloudflare-output-verifier/issues.mjs +10 -0
- package/dist/esm/cloudflare-output-verifier/manifest.mjs +51 -0
- package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +99 -0
- package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +313 -0
- package/dist/esm/config.mjs +0 -0
- package/dist/esm/css-extract-runtime-plugin.mjs +32 -0
- package/dist/esm/index.mjs +10 -0
- package/dist/esm/read-template.mjs +5 -0
- package/dist/esm/release-envelope/canonical.mjs +21 -0
- package/dist/esm/release-envelope/framework-output.mjs +573 -0
- package/dist/esm/release-envelope/index.mjs +335 -0
- package/dist/esm/release-envelope/plugin.mjs +54 -0
- package/dist/esm/release-envelope/types.mjs +7 -0
- package/dist/esm/release-identity.mjs +66 -0
- package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
- package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
- package/dist/esm/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
- package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
- package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
- package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +11 -0
- package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +281 -0
- package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
- package/dist/esm/templates/cloudflare-worker-path.mjs +98 -0
- package/dist/esm-node/cloudflare/artifacts.mjs +65 -0
- package/dist/esm-node/cloudflare/constants.mjs +35 -0
- package/dist/esm-node/cloudflare/delivery-unit.mjs +111 -0
- package/dist/esm-node/cloudflare/i18n-worker.mjs +98 -0
- package/dist/esm-node/cloudflare/index.mjs +109 -0
- package/dist/esm-node/cloudflare/security-policies.mjs +168 -0
- package/dist/esm-node/cloudflare/types.mjs +1 -0
- package/dist/esm-node/cloudflare/utils.mjs +13 -0
- package/dist/esm-node/cloudflare/worker-manifest.mjs +180 -0
- package/dist/esm-node/cloudflare/wrangler-config.mjs +152 -0
- package/dist/esm-node/cloudflare-builder.mjs +328 -0
- package/dist/esm-node/cloudflare-output-contract.mjs +32 -0
- package/dist/esm-node/cloudflare-output-plan.mjs +46 -0
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +55 -0
- package/dist/esm-node/cloudflare-output-verifier/index.mjs +134 -0
- package/dist/esm-node/cloudflare-output-verifier/issues.mjs +11 -0
- package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +52 -0
- package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +100 -0
- package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +314 -0
- package/dist/esm-node/config.mjs +1 -0
- package/dist/esm-node/css-extract-runtime-plugin.mjs +33 -0
- package/dist/esm-node/index.mjs +11 -0
- package/dist/esm-node/read-template.mjs +9 -0
- package/dist/esm-node/release-envelope/canonical.mjs +22 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +574 -0
- package/dist/esm-node/release-envelope/index.mjs +336 -0
- package/dist/esm-node/release-envelope/plugin.mjs +55 -0
- package/dist/esm-node/release-envelope/types.mjs +8 -0
- package/dist/esm-node/release-identity.mjs +67 -0
- package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
- package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
- package/dist/esm-node/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
- package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
- package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
- package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +11 -0
- package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +281 -0
- package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
- package/dist/esm-node/templates/cloudflare-worker-path.mjs +98 -0
- package/dist/types/cloudflare/artifacts.d.ts +15 -0
- package/dist/types/cloudflare/constants.d.ts +24 -0
- package/dist/types/cloudflare/delivery-unit.d.ts +23 -0
- package/dist/types/cloudflare/i18n-worker.d.ts +22 -0
- package/dist/types/cloudflare/index.d.ts +2 -0
- package/dist/types/cloudflare/security-policies.d.ts +52 -0
- package/dist/types/cloudflare/types.d.ts +28 -0
- package/dist/types/cloudflare/utils.d.ts +6 -0
- package/dist/types/cloudflare/worker-manifest.d.ts +149 -0
- package/dist/types/cloudflare/wrangler-config.d.ts +51 -0
- package/dist/types/cloudflare-builder.d.ts +59 -0
- package/dist/types/cloudflare-output-contract.d.ts +14 -0
- package/dist/types/cloudflare-output-plan.d.ts +34 -0
- package/dist/types/cloudflare-output-verifier/identity.d.ts +8 -0
- package/dist/types/cloudflare-output-verifier/index.d.ts +18 -0
- package/dist/types/cloudflare-output-verifier/issues.d.ts +14 -0
- package/dist/types/cloudflare-output-verifier/manifest.d.ts +2 -0
- package/dist/types/cloudflare-output-verifier/mutation-policy.d.ts +14 -0
- package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +20 -0
- package/dist/types/config.d.ts +175 -0
- package/dist/types/css-extract-runtime-plugin.d.ts +6 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/read-template.d.ts +2 -0
- package/dist/types/release-envelope/canonical.d.ts +5 -0
- package/dist/types/release-envelope/framework-output.d.ts +32 -0
- package/dist/types/release-envelope/index.d.ts +6 -0
- package/dist/types/release-envelope/plugin.d.ts +30 -0
- package/dist/types/release-envelope/types.d.ts +62 -0
- package/dist/types/release-identity.d.ts +16 -0
- package/dist/types/templates/cloudflare-entry.001-bootstrap-security.d.mts +1 -0
- package/dist/types/templates/cloudflare-entry.002-assets-routes.d.mts +1 -0
- package/dist/types/templates/cloudflare-entry.003-i18n-locales.d.mts +1 -0
- package/dist/types/templates/cloudflare-entry.004-rendering-css.d.mts +1 -0
- package/dist/types/templates/cloudflare-entry.005-worker-dispatch.d.mts +1 -0
- package/dist/types/templates/cloudflare-entry.006-fetch-handler.d.mts +4 -0
- package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +5 -0
- package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +48 -0
- package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +13 -0
- package/dist/types/templates/cloudflare-worker-path.d.mts +21 -0
- package/package.json +131 -0
- package/src/cloudflare/artifacts.ts +165 -0
- package/src/cloudflare/constants.ts +50 -0
- package/src/cloudflare/delivery-unit.ts +195 -0
- package/src/cloudflare/i18n-worker.ts +197 -0
- package/src/cloudflare/index.ts +259 -0
- package/src/cloudflare/security-policies.ts +194 -0
- package/src/cloudflare/types.ts +33 -0
- package/src/cloudflare/utils.ts +40 -0
- package/src/cloudflare/worker-manifest.ts +313 -0
- package/src/cloudflare/wrangler-config.ts +338 -0
- package/src/cloudflare-builder.ts +653 -0
- package/src/cloudflare-output-contract.ts +30 -0
- package/src/cloudflare-output-plan.ts +108 -0
- package/src/cloudflare-output-verifier/identity.ts +87 -0
- package/src/cloudflare-output-verifier/index.ts +277 -0
- package/src/cloudflare-output-verifier/issues.ts +54 -0
- package/src/cloudflare-output-verifier/manifest.ts +76 -0
- package/src/cloudflare-output-verifier/mutation-policy.ts +159 -0
- package/src/cloudflare-output-verifier/worker-bundles.ts +664 -0
- package/src/config.ts +196 -0
- package/src/css-extract-runtime-plugin.ts +70 -0
- package/src/index.ts +10 -0
- package/src/read-template.ts +8 -0
- package/src/release-envelope/canonical.ts +58 -0
- package/src/release-envelope/framework-output.ts +1121 -0
- package/src/release-envelope/index.ts +656 -0
- package/src/release-envelope/plugin.ts +125 -0
- package/src/release-envelope/types.ts +83 -0
- package/src/release-identity.ts +134 -0
- package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
- package/src/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
- package/src/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
- package/src/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
- package/src/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
- package/src/templates/cloudflare-worker-fs-promises.mjs +11 -0
- package/src/templates/cloudflare-worker-loadable-server.mjs +281 -0
- package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
- package/src/templates/cloudflare-worker-path.mjs +98 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fs as fse } from '@modern-js/utils';
|
|
3
|
+
import {
|
|
4
|
+
type DeliveryUnitIdentity,
|
|
5
|
+
isUltramodernBuildArtifact,
|
|
6
|
+
nonEmptyString,
|
|
7
|
+
toDeliveryUnitIdentity,
|
|
8
|
+
ULTRAMODERN_BUILD_ARTIFACT_PATH,
|
|
9
|
+
ULTRAMODERN_BUILD_MODULE_PATH,
|
|
10
|
+
} from '@modern-js/utils/universal';
|
|
11
|
+
import { resolveUltramodernReleaseIdentity } from '../release-identity';
|
|
12
|
+
import { isRecord } from './utils';
|
|
13
|
+
|
|
14
|
+
const COMPACT_CONFIG_PATH = '.modernjs/ultramodern.json';
|
|
15
|
+
|
|
16
|
+
export type DeliveryUnitStamp = DeliveryUnitIdentity & {
|
|
17
|
+
surfaces: {
|
|
18
|
+
ui?: DeliveryUnitIdentity & { surface: 'ui' };
|
|
19
|
+
api?: DeliveryUnitIdentity & { surface: 'api' };
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type CompactAppResolution = {
|
|
24
|
+
app?: Record<string, unknown>;
|
|
25
|
+
workspaceRoot: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const findWorkspaceRoot = async (
|
|
29
|
+
appDirectory: string,
|
|
30
|
+
): Promise<string | undefined> => {
|
|
31
|
+
let current = path.resolve(appDirectory);
|
|
32
|
+
|
|
33
|
+
for (;;) {
|
|
34
|
+
if (await fse.pathExists(path.join(current, COMPACT_CONFIG_PATH))) {
|
|
35
|
+
return current;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const parent = path.dirname(current);
|
|
39
|
+
if (parent === current) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
current = parent;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const stampReleaseIdentity = (
|
|
48
|
+
identity: DeliveryUnitIdentity,
|
|
49
|
+
workspaceRoot: string,
|
|
50
|
+
): DeliveryUnitIdentity => {
|
|
51
|
+
if (identity.sourceRevision !== 'workspace') {
|
|
52
|
+
return identity;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
...identity,
|
|
56
|
+
...resolveUltramodernReleaseIdentity({
|
|
57
|
+
generationBuildMarker: identity.buildMarker,
|
|
58
|
+
unitId: identity.unitId,
|
|
59
|
+
workspaceRoot,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const resolveCompactApp = async (
|
|
65
|
+
appDirectory: string,
|
|
66
|
+
): Promise<CompactAppResolution | undefined> => {
|
|
67
|
+
const workspaceRoot = await findWorkspaceRoot(appDirectory);
|
|
68
|
+
if (!workspaceRoot) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let compactConfig: unknown;
|
|
73
|
+
try {
|
|
74
|
+
compactConfig = await fse.readJSON(
|
|
75
|
+
path.join(workspaceRoot, COMPACT_CONFIG_PATH),
|
|
76
|
+
);
|
|
77
|
+
} catch {
|
|
78
|
+
return { workspaceRoot };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!isRecord(compactConfig)) {
|
|
82
|
+
return { workspaceRoot };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const topology = isRecord(compactConfig.topology)
|
|
86
|
+
? compactConfig.topology
|
|
87
|
+
: undefined;
|
|
88
|
+
const apps = Array.isArray(topology?.apps) ? topology.apps : undefined;
|
|
89
|
+
if (!apps) {
|
|
90
|
+
return { app: compactConfig, workspaceRoot };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const resolvedAppDirectory = path.resolve(appDirectory);
|
|
94
|
+
const app = apps.find(candidate => {
|
|
95
|
+
if (!isRecord(candidate)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const appPath = nonEmptyString(candidate.path);
|
|
99
|
+
return (
|
|
100
|
+
appPath !== undefined &&
|
|
101
|
+
path.resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) ===
|
|
102
|
+
resolvedAppDirectory
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
...(isRecord(app) ? { app } : {}),
|
|
107
|
+
workspaceRoot,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const createDeliveryUnitStamp = (
|
|
112
|
+
identity: DeliveryUnitIdentity,
|
|
113
|
+
app?: Record<string, unknown>,
|
|
114
|
+
): DeliveryUnitStamp => {
|
|
115
|
+
const surfaceProfile = nonEmptyString(app?.surfaceProfile);
|
|
116
|
+
const emitsUi = surfaceProfile !== 'api-only';
|
|
117
|
+
const emitsApi = surfaceProfile !== 'ui-only';
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
...identity,
|
|
121
|
+
surfaces: {
|
|
122
|
+
...(emitsUi ? { ui: { ...identity, surface: 'ui' as const } } : {}),
|
|
123
|
+
...(emitsApi ? { api: { ...identity, surface: 'api' as const } } : {}),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the delivery-unit record declared for this app by the workspace
|
|
130
|
+
* compact config (`.modernjs/ultramodern.json`). This is the topology source
|
|
131
|
+
* of truth the Cloudflare worker snapshot is verified against.
|
|
132
|
+
*/
|
|
133
|
+
export const resolveTopologyDeliveryUnit = async (
|
|
134
|
+
appDirectory: string,
|
|
135
|
+
): Promise<DeliveryUnitStamp | undefined> => {
|
|
136
|
+
const resolved = await resolveCompactApp(appDirectory);
|
|
137
|
+
if (!resolved?.app) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const identity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
|
|
141
|
+
return identity
|
|
142
|
+
? createDeliveryUnitStamp(
|
|
143
|
+
stampReleaseIdentity(identity, resolved.workspaceRoot),
|
|
144
|
+
resolved.app,
|
|
145
|
+
)
|
|
146
|
+
: undefined;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Resolve the delivery-unit identity actually bundled into the worker by
|
|
151
|
+
* parsing the generated `shared/ultramodern-build.ts` module. This is the
|
|
152
|
+
* worker snapshot / declared surface source that gets stamped into the manifest.
|
|
153
|
+
*/
|
|
154
|
+
export const resolveWorkerDeliveryUnitStamp = async (
|
|
155
|
+
appDirectory: string,
|
|
156
|
+
): Promise<DeliveryUnitStamp | undefined> => {
|
|
157
|
+
const buildArtifactPath = path.join(
|
|
158
|
+
appDirectory,
|
|
159
|
+
ULTRAMODERN_BUILD_ARTIFACT_PATH,
|
|
160
|
+
);
|
|
161
|
+
const buildModulePath = path.join(
|
|
162
|
+
appDirectory,
|
|
163
|
+
ULTRAMODERN_BUILD_MODULE_PATH,
|
|
164
|
+
);
|
|
165
|
+
let identity: DeliveryUnitIdentity | undefined;
|
|
166
|
+
|
|
167
|
+
if (await fse.pathExists(buildArtifactPath)) {
|
|
168
|
+
const artifact = await fse.readJSON(buildArtifactPath);
|
|
169
|
+
if (!isUltramodernBuildArtifact(artifact)) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
identity = toDeliveryUnitIdentity(artifact.deliveryUnit);
|
|
173
|
+
} else if (await fse.pathExists(buildModulePath)) {
|
|
174
|
+
console.warn(
|
|
175
|
+
`[cloudflare] ${buildArtifactPath} missing; falling back to legacy regex parsing of ${buildModulePath}. Regenerate the workspace to emit ultramodern-build.json.`,
|
|
176
|
+
);
|
|
177
|
+
const source = await fse.readFile(buildModulePath, 'utf8');
|
|
178
|
+
identity = toDeliveryUnitIdentity({
|
|
179
|
+
buildMarker: source.match(/\bbuild:\s*['"]([^'"]+)['"]/u)?.[1],
|
|
180
|
+
unitId: source.match(/\bunitId:\s*['"]([^'"]+)['"]/u)?.[1],
|
|
181
|
+
sourceRevision: source.match(
|
|
182
|
+
/\bsourceRevision:\s*['"]([^'"]+)['"]/u,
|
|
183
|
+
)?.[1],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!identity) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
const resolved = await resolveCompactApp(appDirectory);
|
|
191
|
+
if (resolved) {
|
|
192
|
+
identity = stampReleaseIdentity(identity, resolved.workspaceRoot);
|
|
193
|
+
}
|
|
194
|
+
return createDeliveryUnitStamp(identity, resolved?.app);
|
|
195
|
+
};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { CloudflareAppContext } from './types';
|
|
2
|
+
import { isRecord } from './utils';
|
|
3
|
+
|
|
4
|
+
const I18N_SERVER_PLUGIN_NAME = 'plugin-i18n/server';
|
|
5
|
+
const DEFAULT_I18NEXT_DETECTION_OPTIONS = {
|
|
6
|
+
order: [
|
|
7
|
+
'querystring',
|
|
8
|
+
'cookie',
|
|
9
|
+
'localStorage',
|
|
10
|
+
'header',
|
|
11
|
+
'navigator',
|
|
12
|
+
'htmlTag',
|
|
13
|
+
'path',
|
|
14
|
+
'subdomain',
|
|
15
|
+
],
|
|
16
|
+
lookupQuerystring: 'lng',
|
|
17
|
+
lookupCookie: 'i18next',
|
|
18
|
+
lookupHeader: 'accept-language',
|
|
19
|
+
} as const;
|
|
20
|
+
const SERVER_SIDE_I18N_DETECTORS = new Set(['querystring', 'cookie', 'header']);
|
|
21
|
+
|
|
22
|
+
type ServerPluginConfig = NonNullable<
|
|
23
|
+
CloudflareAppContext['serverPlugins']
|
|
24
|
+
>[number];
|
|
25
|
+
|
|
26
|
+
type WorkerI18nDetection = {
|
|
27
|
+
order: string[];
|
|
28
|
+
lookupQuerystring: string;
|
|
29
|
+
lookupCookie: string;
|
|
30
|
+
lookupHeader: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type WorkerI18nEntry = {
|
|
34
|
+
i18nextDetector: boolean;
|
|
35
|
+
languages: string[];
|
|
36
|
+
fallbackLanguage: string;
|
|
37
|
+
detection: WorkerI18nDetection;
|
|
38
|
+
ignoreRedirectRoutes: string[];
|
|
39
|
+
staticRoutePrefixes: string[];
|
|
40
|
+
localisedUrls?: Record<string, Record<string, string>>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const toStringArray = (value: unknown): string[] =>
|
|
44
|
+
Array.isArray(value)
|
|
45
|
+
? value.filter((entry): entry is string => typeof entry === 'string')
|
|
46
|
+
: [];
|
|
47
|
+
|
|
48
|
+
const getI18nServerPlugin = (
|
|
49
|
+
appContext: CloudflareAppContext,
|
|
50
|
+
): ServerPluginConfig | undefined =>
|
|
51
|
+
appContext.serverPlugins?.find(plugin =>
|
|
52
|
+
plugin.name.includes(I18N_SERVER_PLUGIN_NAME),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const removeLocaleDetectionByEntry = (config: Record<string, unknown>) => {
|
|
56
|
+
const { localeDetectionByEntry: _localeDetectionByEntry, ...rest } = config;
|
|
57
|
+
return rest;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getEntryLocaleDetection = (
|
|
61
|
+
entryName: string,
|
|
62
|
+
localeDetection: Record<string, unknown>,
|
|
63
|
+
) => {
|
|
64
|
+
const byEntry = isRecord(localeDetection.localeDetectionByEntry)
|
|
65
|
+
? localeDetection.localeDetectionByEntry
|
|
66
|
+
: undefined;
|
|
67
|
+
const entryConfig = byEntry?.[entryName];
|
|
68
|
+
const globalConfig = removeLocaleDetectionByEntry(localeDetection);
|
|
69
|
+
|
|
70
|
+
return isRecord(entryConfig)
|
|
71
|
+
? { ...globalConfig, ...entryConfig }
|
|
72
|
+
: globalConfig;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const normalizeI18nDetection = (detection: unknown): WorkerI18nDetection => {
|
|
76
|
+
const configured = isRecord(detection) ? detection : {};
|
|
77
|
+
const rawOrder = Array.isArray(configured.order)
|
|
78
|
+
? configured.order
|
|
79
|
+
: DEFAULT_I18NEXT_DETECTION_OPTIONS.order;
|
|
80
|
+
const order = rawOrder.filter(
|
|
81
|
+
(entry): entry is string =>
|
|
82
|
+
typeof entry === 'string' && SERVER_SIDE_I18N_DETECTORS.has(entry),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
order: order.length > 0 ? order : ['querystring', 'cookie', 'header'],
|
|
87
|
+
lookupQuerystring:
|
|
88
|
+
typeof configured.lookupQuerystring === 'string' &&
|
|
89
|
+
configured.lookupQuerystring
|
|
90
|
+
? configured.lookupQuerystring
|
|
91
|
+
: DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring,
|
|
92
|
+
lookupCookie:
|
|
93
|
+
typeof configured.lookupCookie === 'string' && configured.lookupCookie
|
|
94
|
+
? configured.lookupCookie
|
|
95
|
+
: DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie,
|
|
96
|
+
lookupHeader:
|
|
97
|
+
typeof configured.lookupHeader === 'string' && configured.lookupHeader
|
|
98
|
+
? configured.lookupHeader
|
|
99
|
+
: DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const normalizeLocalisedUrls = (
|
|
104
|
+
localisedUrls: unknown,
|
|
105
|
+
): Record<string, Record<string, string>> | undefined => {
|
|
106
|
+
if (!isRecord(localisedUrls) || Object.keys(localisedUrls).length === 0) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const normalized: Record<string, Record<string, string>> = {};
|
|
111
|
+
for (const [canonicalPath, languageMap] of Object.entries(localisedUrls)) {
|
|
112
|
+
if (!isRecord(languageMap)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const normalizedLanguageMap = Object.fromEntries(
|
|
117
|
+
Object.entries(languageMap).filter(
|
|
118
|
+
(entry): entry is [string, string] =>
|
|
119
|
+
typeof entry[1] === 'string' && entry[1].length > 0,
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (Object.keys(normalizedLanguageMap).length > 0) {
|
|
124
|
+
normalized[canonicalPath] = normalizedLanguageMap;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const normalizeWorkerI18nEntry = (
|
|
132
|
+
entryName: string,
|
|
133
|
+
localeDetection: Record<string, unknown>,
|
|
134
|
+
staticRoutePrefixes: string[],
|
|
135
|
+
): WorkerI18nEntry | undefined => {
|
|
136
|
+
const entryConfig = getEntryLocaleDetection(entryName, localeDetection);
|
|
137
|
+
const languages = toStringArray(entryConfig.languages);
|
|
138
|
+
|
|
139
|
+
if (entryConfig.localePathRedirect !== true || languages.length === 0) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const fallbackLanguage =
|
|
144
|
+
typeof entryConfig.fallbackLanguage === 'string' &&
|
|
145
|
+
entryConfig.fallbackLanguage
|
|
146
|
+
? entryConfig.fallbackLanguage
|
|
147
|
+
: 'en';
|
|
148
|
+
const ignoreRedirectRoutes = Array.isArray(entryConfig.ignoreRedirectRoutes)
|
|
149
|
+
? toStringArray(entryConfig.ignoreRedirectRoutes)
|
|
150
|
+
: [];
|
|
151
|
+
const localisedUrls = normalizeLocalisedUrls(entryConfig.localisedUrls);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
i18nextDetector: entryConfig.i18nextDetector !== false,
|
|
155
|
+
languages,
|
|
156
|
+
fallbackLanguage,
|
|
157
|
+
detection: normalizeI18nDetection(entryConfig.detection),
|
|
158
|
+
ignoreRedirectRoutes,
|
|
159
|
+
staticRoutePrefixes,
|
|
160
|
+
...(localisedUrls ? { localisedUrls } : {}),
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const createI18nWorkerManifest = (
|
|
165
|
+
routeSpec: { routes: Array<Record<string, unknown>> },
|
|
166
|
+
appContext: CloudflareAppContext,
|
|
167
|
+
) => {
|
|
168
|
+
const i18nServerPlugin = getI18nServerPlugin(appContext);
|
|
169
|
+
const localeDetection = i18nServerPlugin?.options?.localeDetection;
|
|
170
|
+
|
|
171
|
+
if (!isRecord(localeDetection)) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const staticRoutePrefixes = toStringArray(
|
|
176
|
+
i18nServerPlugin?.options?.staticRoutePrefixes,
|
|
177
|
+
);
|
|
178
|
+
const entries: Record<string, WorkerI18nEntry> = {};
|
|
179
|
+
|
|
180
|
+
for (const route of routeSpec.routes) {
|
|
181
|
+
if (typeof route.entryName !== 'string' || entries[route.entryName]) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const entry = normalizeWorkerI18nEntry(
|
|
186
|
+
route.entryName,
|
|
187
|
+
localeDetection,
|
|
188
|
+
staticRoutePrefixes,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (entry) {
|
|
192
|
+
entries[route.entryName] = entry;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return Object.keys(entries).length > 0 ? { entries } : undefined;
|
|
197
|
+
};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fs as fse } from '@modern-js/utils';
|
|
3
|
+
import { createCloudflareOutputPlan } from '../cloudflare-output-plan';
|
|
4
|
+
import { assertCloudflareOutput } from '../cloudflare-output-verifier/index';
|
|
5
|
+
import { readTemplate } from '../read-template';
|
|
6
|
+
import {
|
|
7
|
+
emitCloudflareStagedReleaseEnvelope,
|
|
8
|
+
emitFrameworkMicroVerticalReleaseEnvelope,
|
|
9
|
+
MICROVERTICAL_RELEASE_ENVELOPE_PATH,
|
|
10
|
+
stageCloudflareReleaseEnvelope,
|
|
11
|
+
verifyBuildOutputReleaseEnvelope,
|
|
12
|
+
verifyCloudflareReleaseEnvelopeStaging,
|
|
13
|
+
} from '../release-envelope/framework-output';
|
|
14
|
+
import {
|
|
15
|
+
copyCloudflareArtifacts,
|
|
16
|
+
copyCloudflareD1Migrations,
|
|
17
|
+
copyCloudflarePublicAssets,
|
|
18
|
+
getCloudflareArtifacts,
|
|
19
|
+
getCloudflarePublicAssets,
|
|
20
|
+
} from './artifacts';
|
|
21
|
+
import {
|
|
22
|
+
ROUTE_SPEC_FILE,
|
|
23
|
+
ROUTE_SPEC_OUTPUT,
|
|
24
|
+
WORKER_BUNDLE_DIRECTORY,
|
|
25
|
+
WORKER_ENTRY,
|
|
26
|
+
WORKER_MANIFEST,
|
|
27
|
+
WRANGLER_CONFIG_FILE,
|
|
28
|
+
} from './constants';
|
|
29
|
+
import {
|
|
30
|
+
resolveTopologyDeliveryUnit,
|
|
31
|
+
resolveWorkerDeliveryUnitStamp,
|
|
32
|
+
} from './delivery-unit';
|
|
33
|
+
import type { CreateCloudflarePreset } from './types';
|
|
34
|
+
import {
|
|
35
|
+
createWorkerManifest,
|
|
36
|
+
createWorkerModuleLoaders,
|
|
37
|
+
getPublicAssetExcludes,
|
|
38
|
+
shouldCopyToPublicAssets,
|
|
39
|
+
shouldCopyToWorkerBundle,
|
|
40
|
+
} from './worker-manifest';
|
|
41
|
+
import { createWranglerConfig } from './wrangler-config';
|
|
42
|
+
|
|
43
|
+
const CLOUDFLARE_ENTRY_TEMPLATE_FRAGMENTS = [
|
|
44
|
+
'cloudflare-entry.001-bootstrap-security.mjs',
|
|
45
|
+
'cloudflare-entry.002-assets-routes.mjs',
|
|
46
|
+
'cloudflare-entry.003-i18n-locales.mjs',
|
|
47
|
+
'cloudflare-entry.004-rendering-css.mjs',
|
|
48
|
+
'cloudflare-entry.005-worker-dispatch.mjs',
|
|
49
|
+
'cloudflare-entry.006-fetch-handler.mjs',
|
|
50
|
+
] as const;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The Cloudflare entry is emitted as a raw module worker, not bundled by
|
|
54
|
+
* app-tools. Keep worker-runtime helpers inline unless the helper is emitted
|
|
55
|
+
* into `.output` or provided by a generated-worker dependency. Concatenate
|
|
56
|
+
* fragments without separators so existing generated output stays byte-identical.
|
|
57
|
+
*/
|
|
58
|
+
const readCloudflareEntryTemplate = async () =>
|
|
59
|
+
(
|
|
60
|
+
await Promise.all(
|
|
61
|
+
CLOUDFLARE_ENTRY_TEMPLATE_FRAGMENTS.map(fragment =>
|
|
62
|
+
readTemplate(fragment),
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
).join('');
|
|
66
|
+
|
|
67
|
+
export const createCloudflarePreset: CreateCloudflarePreset = ({
|
|
68
|
+
api,
|
|
69
|
+
appContext,
|
|
70
|
+
modernConfig,
|
|
71
|
+
}) => {
|
|
72
|
+
const { apiOnly, appDirectory, distDirectory } = appContext;
|
|
73
|
+
|
|
74
|
+
const outputDirectory = path.join(appDirectory, '.output');
|
|
75
|
+
const outputPlan = createCloudflareOutputPlan(outputDirectory);
|
|
76
|
+
const publicDirectory = outputPlan.paths.publicAssets;
|
|
77
|
+
const workerEntryPath = outputPlan.paths.workerEntry;
|
|
78
|
+
const workerManifestPath = outputPlan.paths.workerManifest;
|
|
79
|
+
const routeSpecOutputPath = path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
80
|
+
const wranglerConfigPath = outputPlan.paths.wranglerConfig;
|
|
81
|
+
const cloudflareArtifacts = getCloudflareArtifacts(modernConfig);
|
|
82
|
+
const publicAssetExcludes = getPublicAssetExcludes(
|
|
83
|
+
appDirectory,
|
|
84
|
+
modernConfig,
|
|
85
|
+
);
|
|
86
|
+
const releaseEnvelopeEnabled = api.isPluginExists(
|
|
87
|
+
'@modern-js/ultramodern-release-envelope',
|
|
88
|
+
);
|
|
89
|
+
let hasReleaseEnvelope = false;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
async prepare() {
|
|
93
|
+
await fse.remove(outputDirectory);
|
|
94
|
+
},
|
|
95
|
+
async writeOutput() {
|
|
96
|
+
if (
|
|
97
|
+
releaseEnvelopeEnabled &&
|
|
98
|
+
(await fse.pathExists(
|
|
99
|
+
path.join(distDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH),
|
|
100
|
+
))
|
|
101
|
+
) {
|
|
102
|
+
await emitFrameworkMicroVerticalReleaseEnvelope({
|
|
103
|
+
apiOnly,
|
|
104
|
+
distDirectory,
|
|
105
|
+
target: 'cloudflare',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (releaseEnvelopeEnabled) {
|
|
109
|
+
await verifyBuildOutputReleaseEnvelope(distDirectory, 'cloudflare');
|
|
110
|
+
}
|
|
111
|
+
await fse.copy(distDirectory, publicDirectory, {
|
|
112
|
+
filter: src => {
|
|
113
|
+
const relativePath = path
|
|
114
|
+
.relative(distDirectory, src)
|
|
115
|
+
.replace(/\\/gu, '/');
|
|
116
|
+
return (
|
|
117
|
+
relativePath !== 'release' &&
|
|
118
|
+
!relativePath.startsWith('release/') &&
|
|
119
|
+
relativePath !== 'public' &&
|
|
120
|
+
!relativePath.startsWith('public/') &&
|
|
121
|
+
shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes)
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
const generatedPublicDirectory = path.join(distDirectory, 'public');
|
|
126
|
+
if (await fse.pathExists(generatedPublicDirectory)) {
|
|
127
|
+
await fse.copy(generatedPublicDirectory, publicDirectory, {
|
|
128
|
+
filter: src => {
|
|
129
|
+
const relativePath = path
|
|
130
|
+
.relative(generatedPublicDirectory, src)
|
|
131
|
+
.replace(/\\/gu, '/');
|
|
132
|
+
return (
|
|
133
|
+
relativePath !== 'release' &&
|
|
134
|
+
!relativePath.startsWith('release/') &&
|
|
135
|
+
shouldCopyToPublicAssets(
|
|
136
|
+
src,
|
|
137
|
+
generatedPublicDirectory,
|
|
138
|
+
publicAssetExcludes,
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
await fse.ensureDir(path.dirname(workerEntryPath));
|
|
145
|
+
await fse.ensureDir(path.dirname(workerManifestPath));
|
|
146
|
+
|
|
147
|
+
const routeSpecSourcePath = path.join(distDirectory, ROUTE_SPEC_FILE);
|
|
148
|
+
if (await fse.pathExists(routeSpecSourcePath)) {
|
|
149
|
+
await fse.copy(routeSpecSourcePath, routeSpecOutputPath);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const workerBundleSourceDirectory = path.join(
|
|
153
|
+
distDirectory,
|
|
154
|
+
WORKER_BUNDLE_DIRECTORY,
|
|
155
|
+
);
|
|
156
|
+
const workerBundleOutputDirectory = outputPlan.paths.workerBundle;
|
|
157
|
+
if (await fse.pathExists(workerBundleSourceDirectory)) {
|
|
158
|
+
await fse.copy(
|
|
159
|
+
workerBundleSourceDirectory,
|
|
160
|
+
workerBundleOutputDirectory,
|
|
161
|
+
{
|
|
162
|
+
filter: src =>
|
|
163
|
+
shouldCopyToWorkerBundle(src, workerBundleSourceDirectory),
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
await fse.writeJSON(
|
|
167
|
+
outputPlan.paths.workerPackage,
|
|
168
|
+
outputPlan.packages.worker,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
await copyCloudflareArtifacts(
|
|
172
|
+
appDirectory,
|
|
173
|
+
outputDirectory,
|
|
174
|
+
cloudflareArtifacts,
|
|
175
|
+
);
|
|
176
|
+
await copyCloudflarePublicAssets(
|
|
177
|
+
appDirectory,
|
|
178
|
+
publicDirectory,
|
|
179
|
+
getCloudflarePublicAssets(modernConfig),
|
|
180
|
+
);
|
|
181
|
+
await copyCloudflareD1Migrations(
|
|
182
|
+
appDirectory,
|
|
183
|
+
outputDirectory,
|
|
184
|
+
modernConfig,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
await fse.writeJSON(
|
|
188
|
+
wranglerConfigPath,
|
|
189
|
+
createWranglerConfig(appDirectory, modernConfig),
|
|
190
|
+
{
|
|
191
|
+
spaces: 2,
|
|
192
|
+
},
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const deliveryUnitStamp =
|
|
196
|
+
await resolveWorkerDeliveryUnitStamp(appDirectory);
|
|
197
|
+
await fse.writeJSON(
|
|
198
|
+
workerManifestPath,
|
|
199
|
+
await createWorkerManifest(
|
|
200
|
+
outputDirectory,
|
|
201
|
+
modernConfig,
|
|
202
|
+
appContext,
|
|
203
|
+
deliveryUnitStamp,
|
|
204
|
+
),
|
|
205
|
+
{
|
|
206
|
+
spaces: 2,
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
await fse.writeJSON(
|
|
210
|
+
outputPlan.paths.outputPackage,
|
|
211
|
+
outputPlan.packages.output,
|
|
212
|
+
);
|
|
213
|
+
hasReleaseEnvelope =
|
|
214
|
+
releaseEnvelopeEnabled &&
|
|
215
|
+
Boolean(
|
|
216
|
+
await stageCloudflareReleaseEnvelope({
|
|
217
|
+
distDirectory,
|
|
218
|
+
outputDirectory,
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
async genEntry() {
|
|
223
|
+
const template = await readCloudflareEntryTemplate();
|
|
224
|
+
const manifest = await fse.readJSON(workerManifestPath);
|
|
225
|
+
|
|
226
|
+
await fse.writeFile(
|
|
227
|
+
workerEntryPath,
|
|
228
|
+
template
|
|
229
|
+
.replace('p_workerManifest', JSON.stringify(manifest, null, 2))
|
|
230
|
+
.replace(
|
|
231
|
+
'p_workerModuleLoaders',
|
|
232
|
+
createWorkerModuleLoaders(manifest),
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
const topologyDeliveryUnit =
|
|
236
|
+
await resolveTopologyDeliveryUnit(appDirectory);
|
|
237
|
+
await assertCloudflareOutput({
|
|
238
|
+
outputDirectory,
|
|
239
|
+
importWorker: false,
|
|
240
|
+
...(topologyDeliveryUnit ? { deliveryUnit: topologyDeliveryUnit } : {}),
|
|
241
|
+
});
|
|
242
|
+
if (hasReleaseEnvelope) {
|
|
243
|
+
await emitCloudflareStagedReleaseEnvelope({
|
|
244
|
+
distDirectory,
|
|
245
|
+
outputDirectory,
|
|
246
|
+
});
|
|
247
|
+
await verifyCloudflareReleaseEnvelopeStaging(outputDirectory);
|
|
248
|
+
} else if (
|
|
249
|
+
await fse.pathExists(
|
|
250
|
+
path.join(outputDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH),
|
|
251
|
+
)
|
|
252
|
+
) {
|
|
253
|
+
throw new Error(
|
|
254
|
+
'[ultramodern-release-envelope] Cloudflare staging contains an envelope not selected from this MicroVertical build.',
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
};
|