@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.14 → 3.9.0-ultramodern.16
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/cloudflare/artifacts.js +8 -19
- package/dist/cjs/cloudflare/index.js +3 -1
- package/dist/cjs/deploy-output/plugin.js +22 -2
- package/dist/cjs/deploy-output/public-assets.js +146 -0
- package/dist/cjs/release-envelope/framework-output.js +24 -8
- package/dist/cjs/release-envelope/plugin.js +9 -1
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +52 -10
- package/dist/esm/cloudflare/artifacts.mjs +8 -19
- package/dist/esm/cloudflare/index.mjs +3 -1
- package/dist/esm/deploy-output/plugin.mjs +18 -1
- package/dist/esm/deploy-output/public-assets.mjs +82 -0
- package/dist/esm/release-envelope/framework-output.mjs +24 -8
- package/dist/esm/release-envelope/plugin.mjs +9 -1
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +52 -10
- package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
- package/dist/esm-node/cloudflare/index.mjs +3 -1
- package/dist/esm-node/deploy-output/plugin.mjs +18 -1
- package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +24 -8
- package/dist/esm-node/release-envelope/plugin.mjs +9 -1
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +52 -10
- package/dist/types/cloudflare/artifacts.d.ts +7 -6
- package/dist/types/config.d.ts +24 -0
- package/dist/types/deploy-output/plugin.d.ts +26 -0
- package/dist/types/deploy-output/public-assets.d.ts +51 -0
- package/dist/types/release-envelope/framework-output.d.ts +6 -2
- package/dist/types/release-envelope/plugin.d.ts +4 -0
- package/package.json +14 -14
- package/src/cloudflare/artifacts.ts +22 -43
- package/src/cloudflare/index.ts +4 -2
- package/src/config.ts +26 -0
- package/src/deploy-output/plugin.ts +54 -0
- package/src/deploy-output/public-assets.ts +224 -0
- package/src/release-envelope/framework-output.ts +49 -1
- package/src/release-envelope/plugin.ts +22 -1
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +52 -10
|
@@ -160,14 +160,46 @@ async function dispatchRouteWorker(route, request, env, ctx) {
|
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
|
|
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) {
|
|
164
169
|
if (!prefix || prefix === '/') {
|
|
165
|
-
return
|
|
170
|
+
return pathname;
|
|
166
171
|
}
|
|
167
172
|
|
|
168
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
|
+
}
|
|
183
|
+
|
|
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
|
+
}
|
|
169
197
|
|
|
170
|
-
return
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function matchesPrefix(pathname, prefix) {
|
|
202
|
+
return resolvePrefixPathname(pathname, prefix) !== null;
|
|
171
203
|
}
|
|
172
204
|
|
|
173
205
|
function createRequestForMountedPrefix(request, prefix) {
|
|
@@ -177,12 +209,13 @@ function createRequestForMountedPrefix(request, prefix) {
|
|
|
177
209
|
|
|
178
210
|
const url = new URL(request.url);
|
|
179
211
|
const normalized = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
212
|
+
const matchedPathname = resolvePrefixPathname(url.pathname, normalized);
|
|
180
213
|
|
|
181
|
-
if (
|
|
214
|
+
if (matchedPathname === null) {
|
|
182
215
|
return request;
|
|
183
216
|
}
|
|
184
217
|
|
|
185
|
-
const nextPath =
|
|
218
|
+
const nextPath = matchedPathname.slice(normalized.length) || '/';
|
|
186
219
|
url.pathname = nextPath.startsWith('/') ? nextPath : `/${nextPath}`;
|
|
187
220
|
|
|
188
221
|
return new Request(url, request);
|
|
@@ -309,12 +342,21 @@ function getEffectBffDispatcher(bff, runtime) {
|
|
|
309
342
|
async function dispatchBffRequest(request, env) {
|
|
310
343
|
const bff = MODERN_WORKER_MANIFEST.bff;
|
|
311
344
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
345
|
+
const requestUrl = new URL(request.url);
|
|
346
|
+
const matchedPathname = resolvePrefixPathname(
|
|
347
|
+
requestUrl.pathname,
|
|
348
|
+
bff?.prefix,
|
|
349
|
+
);
|
|
350
|
+
if (!bff?.worker || matchedPathname === null) {
|
|
316
351
|
return null;
|
|
317
352
|
}
|
|
353
|
+
const canonicalRequest =
|
|
354
|
+
matchedPathname === requestUrl.pathname
|
|
355
|
+
? request
|
|
356
|
+
: new Request(
|
|
357
|
+
Object.assign(requestUrl, { pathname: matchedPathname }),
|
|
358
|
+
request,
|
|
359
|
+
);
|
|
318
360
|
if (bff.runtimeFramework !== 'effect') {
|
|
319
361
|
return createEffectBffDispatcherErrorResponse(
|
|
320
362
|
bff,
|
|
@@ -347,7 +389,7 @@ async function dispatchBffRequest(request, env) {
|
|
|
347
389
|
return createEffectBffDispatcherErrorResponse(bff, error);
|
|
348
390
|
}
|
|
349
391
|
|
|
350
|
-
return effectDispatcher.dispatch(
|
|
392
|
+
return effectDispatcher.dispatch(canonicalRequest, { env });
|
|
351
393
|
}
|
|
352
394
|
|
|
353
395
|
const directHandler =
|
|
@@ -4,12 +4,13 @@ export declare const getCloudflareArtifacts: (modernConfig: CloudflareModernConf
|
|
|
4
4
|
to: string;
|
|
5
5
|
index: number;
|
|
6
6
|
}[];
|
|
7
|
-
export declare const getCloudflarePublicAssets: (modernConfig: CloudflareModernConfig) =>
|
|
8
|
-
from: string;
|
|
9
|
-
to: string;
|
|
10
|
-
index: number;
|
|
11
|
-
}[];
|
|
7
|
+
export declare const getCloudflarePublicAssets: (modernConfig: CloudflareModernConfig) => import("../deploy-output/public-assets.js").DeclaredPublicAsset[];
|
|
12
8
|
export declare const copyCloudflareArtifacts: (appDirectory: string, outputDirectory: string, artifacts: ReturnType<typeof getCloudflareArtifacts>) => Promise<void>;
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Stage `deploy.worker.publicAssets` into Worker Static Assets.
|
|
11
|
+
*
|
|
12
|
+
* @returns the staged files relative to the Cloudflare output root.
|
|
13
|
+
*/
|
|
14
|
+
export declare const copyCloudflarePublicAssets: (appDirectory: string, outputDirectory: string, publicAssets: ReturnType<typeof getCloudflarePublicAssets>) => Promise<string[]>;
|
|
14
15
|
export declare const copyCloudflareD1Migrations: (appDirectory: string, outputDirectory: string, modernConfig: CloudflareModernConfig) => Promise<void>;
|
|
15
16
|
export declare const readRouteSpec: (outputDirectory: string) => Promise<any>;
|
package/dist/types/config.d.ts
CHANGED
|
@@ -28,6 +28,17 @@ export interface CloudflareWorkerPublicAssetConfig {
|
|
|
28
28
|
*/
|
|
29
29
|
to: string;
|
|
30
30
|
}
|
|
31
|
+
export interface NodePublicAssetConfig {
|
|
32
|
+
/**
|
|
33
|
+
* Source file or directory, relative to the app root, to copy into the
|
|
34
|
+
* Node deploy output's public directory.
|
|
35
|
+
*/
|
|
36
|
+
from: string;
|
|
37
|
+
/**
|
|
38
|
+
* Destination path relative to `.output/public`.
|
|
39
|
+
*/
|
|
40
|
+
to: string;
|
|
41
|
+
}
|
|
31
42
|
export interface CloudflareWorkerD1DatabaseConfig {
|
|
32
43
|
/**
|
|
33
44
|
* Worker binding name, for example `DB`.
|
|
@@ -176,3 +187,16 @@ export interface CloudflareDeployConfig {
|
|
|
176
187
|
publicAssetExcludes?: string[];
|
|
177
188
|
};
|
|
178
189
|
}
|
|
190
|
+
export interface NodeDeployConfig {
|
|
191
|
+
node?: {
|
|
192
|
+
/**
|
|
193
|
+
* Additional app-root files or directories to stage under
|
|
194
|
+
* `.output/public` when `modern deploy` targets Node, the counterpart of
|
|
195
|
+
* `deploy.worker.publicAssets`. The Node server does not route these
|
|
196
|
+
* files; serve them from the app, for example from an API handler.
|
|
197
|
+
* Release envelopes record them as declared public assets, so API-only
|
|
198
|
+
* units may ship them.
|
|
199
|
+
*/
|
|
200
|
+
publicAssets?: NodePublicAssetConfig[];
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NodePublicAssetConfig } from '../config.js';
|
|
1
2
|
import { type DeployOutputConfig } from './target.js';
|
|
2
3
|
export interface DeployOutputPluginApi {
|
|
3
4
|
getAppContext(): {
|
|
@@ -13,3 +14,28 @@ export declare const createDeployOutputAliasesPlugin: ({ resolveDeployTarget, }?
|
|
|
13
14
|
name: string;
|
|
14
15
|
setup(api: DeployOutputPluginApi): void;
|
|
15
16
|
};
|
|
17
|
+
export interface DeployOutputPublicAssetsConfig extends DeployOutputConfig {
|
|
18
|
+
deploy?: DeployOutputConfig['deploy'] & {
|
|
19
|
+
node?: {
|
|
20
|
+
publicAssets?: NodePublicAssetConfig[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface DeployOutputPublicAssetsPluginApi {
|
|
25
|
+
getAppContext(): {
|
|
26
|
+
appDirectory: string;
|
|
27
|
+
metaName: string;
|
|
28
|
+
};
|
|
29
|
+
getNormalizedConfig(): DeployOutputPublicAssetsConfig;
|
|
30
|
+
onAfterDeploy(handler: () => Promise<void>): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Stage `deploy.node.publicAssets` into the Node deploy output. Cloudflare
|
|
34
|
+
* stages `deploy.worker.publicAssets` inside its deploy preset.
|
|
35
|
+
*/
|
|
36
|
+
export declare const createDeployOutputPublicAssetsPlugin: ({ resolveDeployTarget, }?: {
|
|
37
|
+
resolveDeployTarget?: (config: DeployOutputConfig) => string;
|
|
38
|
+
}) => {
|
|
39
|
+
name: string;
|
|
40
|
+
setup(api: DeployOutputPublicAssetsPluginApi): void;
|
|
41
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy output directory that receives declared public assets on every
|
|
3
|
+
* target: Cloudflare Worker Static Assets and the Node deploy output.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DECLARED_PUBLIC_ASSET_DIRECTORY = "public";
|
|
6
|
+
export interface DeclaredPublicAssetConfig {
|
|
7
|
+
from: string;
|
|
8
|
+
to: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DeclaredPublicAssetScope {
|
|
11
|
+
/** Config path used in errors, for example `deploy.node.publicAssets`. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Output described in destination errors. */
|
|
14
|
+
output: string;
|
|
15
|
+
}
|
|
16
|
+
export interface DeclaredPublicAsset {
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
index: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const CLOUDFLARE_PUBLIC_ASSET_SCOPE: DeclaredPublicAssetScope;
|
|
22
|
+
export declare const NODE_PUBLIC_ASSET_SCOPE: DeclaredPublicAssetScope;
|
|
23
|
+
export declare const normalizeDeclaredPublicAssets: (assets: readonly DeclaredPublicAssetConfig[] | undefined, scope: DeclaredPublicAssetScope) => DeclaredPublicAsset[];
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the files declared public assets add to a deploy output that was
|
|
26
|
+
* copied from `generatedRoot`, relative to the deploy output root. Declared
|
|
27
|
+
* files that replace a generated file are not part of the result, matching
|
|
28
|
+
* {@link stageDeclaredPublicAssets}.
|
|
29
|
+
*/
|
|
30
|
+
export declare const resolveAddedDeclaredPublicAssetPaths: ({ appDirectory, assets, generatedRoot, scope, }: {
|
|
31
|
+
appDirectory: string;
|
|
32
|
+
assets: readonly DeclaredPublicAsset[];
|
|
33
|
+
generatedRoot: string;
|
|
34
|
+
scope: DeclaredPublicAssetScope;
|
|
35
|
+
}) => Promise<string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Copy declared public assets into the deploy output after the framework has
|
|
38
|
+
* staged its own public files.
|
|
39
|
+
*
|
|
40
|
+
* A declared file may replace a generated public file, such as an app-owned
|
|
41
|
+
* `robots.txt`; the replaced file keeps its generated release classification.
|
|
42
|
+
*
|
|
43
|
+
* @returns the files the declarations added, relative to the deploy output
|
|
44
|
+
* root. The release envelope records these as declared public assets.
|
|
45
|
+
*/
|
|
46
|
+
export declare const stageDeclaredPublicAssets: ({ appDirectory, outputDirectory, assets, scope, }: {
|
|
47
|
+
appDirectory: string;
|
|
48
|
+
outputDirectory: string;
|
|
49
|
+
assets: readonly DeclaredPublicAsset[];
|
|
50
|
+
scope: DeclaredPublicAssetScope;
|
|
51
|
+
}) => Promise<string[]>;
|
|
@@ -18,7 +18,9 @@ export declare const verifyNodeReleaseEnvelopeStaging: ({ outputDirectory, }: {
|
|
|
18
18
|
distDirectory?: string;
|
|
19
19
|
outputDirectory: string;
|
|
20
20
|
}) => Promise<MicroVerticalReleaseEnvelope | undefined>;
|
|
21
|
-
export declare const emitNodeStagedReleaseEnvelope: ({ distDirectory, outputDirectory, }: {
|
|
21
|
+
export declare const emitNodeStagedReleaseEnvelope: ({ declaredPublicAssets, distDirectory, outputDirectory, }: {
|
|
22
|
+
/** Staged `deploy.node.publicAssets` files, relative to `outputDirectory`. */
|
|
23
|
+
declaredPublicAssets?: readonly string[];
|
|
22
24
|
distDirectory: string;
|
|
23
25
|
outputDirectory: string;
|
|
24
26
|
}) => Promise<MicroVerticalReleaseEnvelope | undefined>;
|
|
@@ -26,7 +28,9 @@ export declare const stageCloudflareReleaseEnvelope: ({ distDirectory, outputDir
|
|
|
26
28
|
distDirectory: string;
|
|
27
29
|
outputDirectory: string;
|
|
28
30
|
}) => Promise<MicroVerticalReleaseEnvelope | undefined>;
|
|
29
|
-
export declare const emitCloudflareStagedReleaseEnvelope: ({ distDirectory, outputDirectory, }: {
|
|
31
|
+
export declare const emitCloudflareStagedReleaseEnvelope: ({ declaredPublicAssets, distDirectory, outputDirectory, }: {
|
|
32
|
+
/** Staged `deploy.worker.publicAssets` files, relative to `outputDirectory`. */
|
|
33
|
+
declaredPublicAssets?: readonly string[];
|
|
30
34
|
distDirectory: string;
|
|
31
35
|
outputDirectory: string;
|
|
32
36
|
}) => Promise<MicroVerticalReleaseEnvelope | undefined>;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { NodePublicAssetConfig } from '../config.js';
|
|
1
2
|
type ReleaseEnvelopeTarget = 'cloudflare' | 'node' | string;
|
|
2
3
|
export interface ReleaseEnvelopeConfig {
|
|
3
4
|
deploy?: {
|
|
4
5
|
target?: string;
|
|
6
|
+
node?: {
|
|
7
|
+
publicAssets?: NodePublicAssetConfig[];
|
|
8
|
+
};
|
|
5
9
|
};
|
|
6
10
|
}
|
|
7
11
|
export interface ReleaseEnvelopeAppContext {
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"directory": "packages/solutions/app-tools-extensions"
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
|
-
"version": "3.9.0-ultramodern.
|
|
14
|
+
"version": "3.9.0-ultramodern.16",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
17
|
"src"
|
|
@@ -145,16 +145,16 @@
|
|
|
145
145
|
"@babel/parser": "^8.0.6",
|
|
146
146
|
"@babel/traverse": "^8.0.6",
|
|
147
147
|
"@loadable/component": "5.16.7",
|
|
148
|
-
"@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.
|
|
149
|
-
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.
|
|
150
|
-
"@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.
|
|
151
|
-
"@modern-js/runtime-renderer-extensions": "npm:@bleedingdev/modern-js-runtime-renderer-extensions@3.9.0-ultramodern.
|
|
152
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.
|
|
153
|
-
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.
|
|
154
|
-
"@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.
|
|
155
|
-
"@modern-js/surface-resolution": "npm:@bleedingdev/modern-js-surface-resolution@3.9.0-ultramodern.
|
|
156
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.
|
|
157
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.
|
|
148
|
+
"@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.16",
|
|
149
|
+
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.16",
|
|
150
|
+
"@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.16",
|
|
151
|
+
"@modern-js/runtime-renderer-extensions": "npm:@bleedingdev/modern-js-runtime-renderer-extensions@3.9.0-ultramodern.16",
|
|
152
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.16",
|
|
153
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.16",
|
|
154
|
+
"@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.16",
|
|
155
|
+
"@modern-js/surface-resolution": "npm:@bleedingdev/modern-js-surface-resolution@3.9.0-ultramodern.16",
|
|
156
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.16",
|
|
157
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.16",
|
|
158
158
|
"@swc/helpers": "^0.5.23",
|
|
159
159
|
"compression-webpack-plugin": "^12.0.0",
|
|
160
160
|
"esbuild": "^0.28.2",
|
|
@@ -164,9 +164,9 @@
|
|
|
164
164
|
"@rsbuild/core": "2.2.9"
|
|
165
165
|
},
|
|
166
166
|
"devDependencies": {
|
|
167
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.
|
|
168
|
-
"@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.
|
|
169
|
-
"@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.9.0-ultramodern.
|
|
167
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.16",
|
|
168
|
+
"@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.16",
|
|
169
|
+
"@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.9.0-ultramodern.16",
|
|
170
170
|
"@module-federation/runtime-tools": "npm:@bleedingdev/mf-runtime-tools@2.9.1",
|
|
171
171
|
"@rsbuild/core": "2.2.9",
|
|
172
172
|
"@rslib/core": "1.0.2",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { fs as fse } from '@modern-js/utils';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import type { CloudflareWorkerArtifactConfig } from '../config';
|
|
4
|
+
import {
|
|
5
|
+
CLOUDFLARE_PUBLIC_ASSET_SCOPE,
|
|
6
|
+
normalizeDeclaredPublicAssets,
|
|
7
|
+
stageDeclaredPublicAssets,
|
|
8
|
+
} from '../deploy-output/public-assets';
|
|
7
9
|
import {
|
|
8
10
|
RESERVED_ARTIFACT_DESTINATION_DIRECTORIES,
|
|
9
11
|
RESERVED_ARTIFACT_DESTINATION_FILES,
|
|
@@ -54,34 +56,12 @@ export const getCloudflareArtifacts = (modernConfig: CloudflareModernConfig) =>
|
|
|
54
56
|
normalizeCloudflareArtifact,
|
|
55
57
|
);
|
|
56
58
|
|
|
57
|
-
const normalizeCloudflarePublicAsset = (
|
|
58
|
-
asset: CloudflareWorkerPublicAssetConfig,
|
|
59
|
-
index: number,
|
|
60
|
-
) => {
|
|
61
|
-
const from = normalizeRelativePath(
|
|
62
|
-
asset.from,
|
|
63
|
-
`deploy.worker.publicAssets[${index}].from`,
|
|
64
|
-
'app root',
|
|
65
|
-
);
|
|
66
|
-
const to = normalizeRelativePath(
|
|
67
|
-
asset.to,
|
|
68
|
-
`deploy.worker.publicAssets[${index}].to`,
|
|
69
|
-
'Cloudflare public output',
|
|
70
|
-
{ allowRoot: true },
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
from,
|
|
75
|
-
to,
|
|
76
|
-
index,
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
|
|
80
59
|
export const getCloudflarePublicAssets = (
|
|
81
60
|
modernConfig: CloudflareModernConfig,
|
|
82
61
|
) =>
|
|
83
|
-
(
|
|
84
|
-
|
|
62
|
+
normalizeDeclaredPublicAssets(
|
|
63
|
+
modernConfig.deploy?.worker?.publicAssets,
|
|
64
|
+
CLOUDFLARE_PUBLIC_ASSET_SCOPE,
|
|
85
65
|
);
|
|
86
66
|
|
|
87
67
|
export const copyCloudflareArtifacts = async (
|
|
@@ -102,23 +82,22 @@ export const copyCloudflareArtifacts = async (
|
|
|
102
82
|
}
|
|
103
83
|
};
|
|
104
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Stage `deploy.worker.publicAssets` into Worker Static Assets.
|
|
87
|
+
*
|
|
88
|
+
* @returns the staged files relative to the Cloudflare output root.
|
|
89
|
+
*/
|
|
105
90
|
export const copyCloudflarePublicAssets = async (
|
|
106
91
|
appDirectory: string,
|
|
107
|
-
|
|
92
|
+
outputDirectory: string,
|
|
108
93
|
publicAssets: ReturnType<typeof getCloudflarePublicAssets>,
|
|
109
|
-
) =>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
await fse.copy(sourcePath, path.join(publicDirectory, asset.to));
|
|
120
|
-
}
|
|
121
|
-
};
|
|
94
|
+
) =>
|
|
95
|
+
stageDeclaredPublicAssets({
|
|
96
|
+
appDirectory,
|
|
97
|
+
outputDirectory,
|
|
98
|
+
assets: publicAssets,
|
|
99
|
+
scope: CLOUDFLARE_PUBLIC_ASSET_SCOPE,
|
|
100
|
+
});
|
|
122
101
|
|
|
123
102
|
export const copyCloudflareD1Migrations = async (
|
|
124
103
|
appDirectory: string,
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ export const createCloudflarePreset: CreateCloudflarePreset = ({
|
|
|
92
92
|
'@modern-js/ultramodern-release-envelope',
|
|
93
93
|
);
|
|
94
94
|
let hasReleaseEnvelope = false;
|
|
95
|
+
let declaredPublicAssets: string[] = [];
|
|
95
96
|
|
|
96
97
|
return {
|
|
97
98
|
async prepare() {
|
|
@@ -187,9 +188,9 @@ export const createCloudflarePreset: CreateCloudflarePreset = ({
|
|
|
187
188
|
outputDirectory,
|
|
188
189
|
cloudflareArtifacts,
|
|
189
190
|
);
|
|
190
|
-
await copyCloudflarePublicAssets(
|
|
191
|
+
declaredPublicAssets = await copyCloudflarePublicAssets(
|
|
191
192
|
appDirectory,
|
|
192
|
-
|
|
193
|
+
outputDirectory,
|
|
193
194
|
getCloudflarePublicAssets(modernConfig),
|
|
194
195
|
);
|
|
195
196
|
await copyCloudflareD1Migrations(
|
|
@@ -255,6 +256,7 @@ export const createCloudflarePreset: CreateCloudflarePreset = ({
|
|
|
255
256
|
});
|
|
256
257
|
if (hasReleaseEnvelope) {
|
|
257
258
|
await emitCloudflareStagedReleaseEnvelope({
|
|
259
|
+
declaredPublicAssets,
|
|
258
260
|
distDirectory,
|
|
259
261
|
outputDirectory,
|
|
260
262
|
});
|
package/src/config.ts
CHANGED
|
@@ -47,6 +47,18 @@ export interface CloudflareWorkerPublicAssetConfig {
|
|
|
47
47
|
to: string;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface NodePublicAssetConfig {
|
|
51
|
+
/**
|
|
52
|
+
* Source file or directory, relative to the app root, to copy into the
|
|
53
|
+
* Node deploy output's public directory.
|
|
54
|
+
*/
|
|
55
|
+
from: string;
|
|
56
|
+
/**
|
|
57
|
+
* Destination path relative to `.output/public`.
|
|
58
|
+
*/
|
|
59
|
+
to: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
50
62
|
export interface CloudflareWorkerD1DatabaseConfig {
|
|
51
63
|
/**
|
|
52
64
|
* Worker binding name, for example `DB`.
|
|
@@ -202,3 +214,17 @@ export interface CloudflareDeployConfig {
|
|
|
202
214
|
publicAssetExcludes?: string[];
|
|
203
215
|
};
|
|
204
216
|
}
|
|
217
|
+
|
|
218
|
+
export interface NodeDeployConfig {
|
|
219
|
+
node?: {
|
|
220
|
+
/**
|
|
221
|
+
* Additional app-root files or directories to stage under
|
|
222
|
+
* `.output/public` when `modern deploy` targets Node, the counterpart of
|
|
223
|
+
* `deploy.worker.publicAssets`. The Node server does not route these
|
|
224
|
+
* files; serve them from the app, for example from an API handler.
|
|
225
|
+
* Release envelopes record them as declared public assets, so API-only
|
|
226
|
+
* units may ship them.
|
|
227
|
+
*/
|
|
228
|
+
publicAssets?: NodePublicAssetConfig[];
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import type { NodePublicAssetConfig } from '../config';
|
|
3
4
|
import { preserveNpmAliases, readPackageIdentity } from './npmAliases';
|
|
5
|
+
import {
|
|
6
|
+
NODE_PUBLIC_ASSET_SCOPE,
|
|
7
|
+
normalizeDeclaredPublicAssets,
|
|
8
|
+
stageDeclaredPublicAssets,
|
|
9
|
+
} from './public-assets';
|
|
4
10
|
import {
|
|
5
11
|
type DeployOutputConfig,
|
|
6
12
|
resolveDeployTarget as defaultResolveDeployTarget,
|
|
@@ -47,3 +53,51 @@ export const createDeployOutputAliasesPlugin = ({
|
|
|
47
53
|
});
|
|
48
54
|
},
|
|
49
55
|
});
|
|
56
|
+
|
|
57
|
+
export interface DeployOutputPublicAssetsConfig extends DeployOutputConfig {
|
|
58
|
+
deploy?: DeployOutputConfig['deploy'] & {
|
|
59
|
+
node?: { publicAssets?: NodePublicAssetConfig[] };
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface DeployOutputPublicAssetsPluginApi {
|
|
64
|
+
getAppContext(): { appDirectory: string; metaName: string };
|
|
65
|
+
getNormalizedConfig(): DeployOutputPublicAssetsConfig;
|
|
66
|
+
onAfterDeploy(handler: () => Promise<void>): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Stage `deploy.node.publicAssets` into the Node deploy output. Cloudflare
|
|
71
|
+
* stages `deploy.worker.publicAssets` inside its deploy preset.
|
|
72
|
+
*/
|
|
73
|
+
export const createDeployOutputPublicAssetsPlugin = ({
|
|
74
|
+
resolveDeployTarget = defaultResolveDeployTarget,
|
|
75
|
+
}: {
|
|
76
|
+
resolveDeployTarget?: (config: DeployOutputConfig) => string;
|
|
77
|
+
} = {}) => ({
|
|
78
|
+
name: '@modern-js/deploy-output-public-assets',
|
|
79
|
+
setup(api: DeployOutputPublicAssetsPluginApi) {
|
|
80
|
+
api.onAfterDeploy(async () => {
|
|
81
|
+
const { appDirectory, metaName } = api.getAppContext();
|
|
82
|
+
const config = api.getNormalizedConfig();
|
|
83
|
+
if (
|
|
84
|
+
(metaName !== 'modern-js' &&
|
|
85
|
+
!config.deploy?.target &&
|
|
86
|
+
!process.env.MODERNJS_DEPLOY) ||
|
|
87
|
+
resolveDeployTarget(config) !== 'node'
|
|
88
|
+
) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await stageDeclaredPublicAssets({
|
|
93
|
+
appDirectory,
|
|
94
|
+
outputDirectory: path.join(appDirectory, '.output'),
|
|
95
|
+
assets: normalizeDeclaredPublicAssets(
|
|
96
|
+
config.deploy?.node?.publicAssets,
|
|
97
|
+
NODE_PUBLIC_ASSET_SCOPE,
|
|
98
|
+
),
|
|
99
|
+
scope: NODE_PUBLIC_ASSET_SCOPE,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
});
|