@bleedingdev/modern-js-app-tools 3.2.0-ultramodern.9 → 3.2.0-ultramodern.91
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/baseline.js +43 -1
- package/dist/cjs/builder/generator/getBuilderEnvironments.js +176 -8
- package/dist/cjs/builder/shared/builderPlugins/adapterBasic.js +41 -5
- package/dist/cjs/builder/shared/builderPlugins/adapterSSR.js +3 -1
- package/dist/cjs/plugins/deploy/index.js +17 -6
- package/dist/cjs/plugins/deploy/platforms/cloudflare.js +222 -0
- package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.mjs +438 -0
- package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-worker-fs-promises.mjs +7 -0
- package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-worker-loadable-server.mjs +185 -0
- package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-worker-path.mjs +59 -0
- package/dist/cjs/rsbuild.js +3 -0
- package/dist/esm/baseline.mjs +33 -1
- package/dist/esm/builder/generator/getBuilderEnvironments.mjs +165 -8
- package/dist/esm/builder/shared/builderPlugins/adapterBasic.mjs +41 -5
- package/dist/esm/builder/shared/builderPlugins/adapterSSR.mjs +3 -1
- package/dist/esm/plugins/deploy/index.mjs +10 -4
- package/dist/esm/plugins/deploy/platforms/cloudflare.mjs +178 -0
- package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.mjs +438 -0
- package/dist/esm/plugins/deploy/platforms/templates/cloudflare-worker-fs-promises.mjs +7 -0
- package/dist/esm/plugins/deploy/platforms/templates/cloudflare-worker-loadable-server.mjs +185 -0
- package/dist/esm/plugins/deploy/platforms/templates/cloudflare-worker-path.mjs +59 -0
- package/dist/esm/rsbuild.mjs +5 -2
- package/dist/esm-node/baseline.mjs +33 -1
- package/dist/esm-node/builder/generator/getBuilderEnvironments.mjs +170 -9
- package/dist/esm-node/builder/shared/builderPlugins/adapterBasic.mjs +41 -5
- package/dist/esm-node/builder/shared/builderPlugins/adapterSSR.mjs +3 -1
- package/dist/esm-node/plugins/deploy/index.mjs +10 -4
- package/dist/esm-node/plugins/deploy/platforms/cloudflare.mjs +179 -0
- package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.mjs +438 -0
- package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-worker-fs-promises.mjs +7 -0
- package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-worker-loadable-server.mjs +185 -0
- package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-worker-path.mjs +59 -0
- package/dist/esm-node/rsbuild.mjs +5 -2
- package/dist/types/locale/en.d.ts +1 -1
- package/dist/types/locale/zh.d.ts +1 -1
- package/dist/types/plugins/deploy/index.d.ts +4 -1
- package/dist/types/plugins/deploy/platforms/cloudflare.d.ts +2 -0
- package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.d.mts +4 -0
- package/dist/types/plugins/deploy/platforms/templates/cloudflare-worker-fs-promises.d.mts +5 -0
- package/dist/types/plugins/deploy/platforms/templates/cloudflare-worker-loadable-server.d.mts +48 -0
- package/dist/types/plugins/deploy/platforms/templates/cloudflare-worker-path.d.mts +21 -0
- package/dist/types/types/config/deploy.d.ts +8 -0
- package/package.json +18 -17
package/dist/esm/rsbuild.mjs
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { parseRspackConfig } from "@modern-js/builder";
|
|
2
|
+
import { INTERNAL_RUNTIME_PLUGINS } from "@modern-js/utils";
|
|
2
3
|
import { builderPluginAdapterBasic, builderPluginAdapterHooks } from "./builder/shared/builderPlugins/index.mjs";
|
|
3
4
|
import { DEFAULT_CONFIG_FILE } from "./constants.mjs";
|
|
4
5
|
import { getConfigFile } from "./utils/getConfigFile.mjs";
|
|
6
|
+
import { loadInternalPlugins } from "./utils/loadPlugins.mjs";
|
|
5
7
|
import { __webpack_require__ } from "./rslib-runtime.mjs";
|
|
6
8
|
import * as __rspack_external__modern_js_plugin_cli_caa09fa2 from "@modern-js/plugin/cli";
|
|
7
9
|
__webpack_require__.add({
|
|
8
|
-
"@modern-js/plugin/cli?
|
|
10
|
+
"@modern-js/plugin/cli?8936" (module) {
|
|
9
11
|
module.exports = __rspack_external__modern_js_plugin_cli_caa09fa2;
|
|
10
12
|
}
|
|
11
13
|
});
|
|
12
14
|
const MODERN_META_NAME = 'modern-js';
|
|
13
|
-
const { createConfigOptions: createConfigOptions } = __webpack_require__("@modern-js/plugin/cli?
|
|
15
|
+
const { createConfigOptions: createConfigOptions } = __webpack_require__("@modern-js/plugin/cli?8936");
|
|
14
16
|
async function resolveModernRsbuildConfig(options) {
|
|
15
17
|
const { cwd = process.cwd(), metaName = MODERN_META_NAME } = options;
|
|
16
18
|
const configFile = options.configPath || getConfigFile(void 0, cwd);
|
|
@@ -19,6 +21,7 @@ async function resolveModernRsbuildConfig(options) {
|
|
|
19
21
|
command: options.command,
|
|
20
22
|
cwd,
|
|
21
23
|
configFile,
|
|
24
|
+
internalPlugins: await loadInternalPlugins(cwd, INTERNAL_RUNTIME_PLUGINS),
|
|
22
25
|
metaName,
|
|
23
26
|
modifyModernConfig: options.modifyModernConfig
|
|
24
27
|
});
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import node_path from "node:path";
|
|
2
4
|
import { mergeConfig } from "@modern-js/plugin/cli";
|
|
5
|
+
const resolveReactRouterPackageDir = ()=>{
|
|
6
|
+
const localRequire = createRequire(node_path.join(process.cwd(), 'package.json'));
|
|
7
|
+
try {
|
|
8
|
+
const reactRouterDomPackageJson = localRequire.resolve('react-router-dom/package.json', {
|
|
9
|
+
paths: [
|
|
10
|
+
process.cwd()
|
|
11
|
+
]
|
|
12
|
+
});
|
|
13
|
+
const reactRouterPackageJson = localRequire.resolve('react-router/package.json', {
|
|
14
|
+
paths: [
|
|
15
|
+
node_path.dirname(reactRouterDomPackageJson)
|
|
16
|
+
]
|
|
17
|
+
});
|
|
18
|
+
return node_path.dirname(reactRouterPackageJson);
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const setReactRouterBridgeSafeAliases = (chain)=>{
|
|
24
|
+
const reactRouterPackageDir = resolveReactRouterPackageDir();
|
|
25
|
+
if (!reactRouterPackageDir) return;
|
|
26
|
+
const productionEntry = node_path.join(reactRouterPackageDir, 'dist/production/index.mjs');
|
|
27
|
+
const developmentEntry = node_path.join(reactRouterPackageDir, 'dist/development/index.mjs');
|
|
28
|
+
chain.resolve.alias.set('react-router$', productionEntry);
|
|
29
|
+
chain.resolve.alias.set('react-router/dist/production/index.js', productionEntry);
|
|
30
|
+
chain.resolve.alias.set('react-router/dist/development/index.js', developmentEntry);
|
|
31
|
+
};
|
|
3
32
|
const createAppBaselineConfig = (options = {})=>{
|
|
4
33
|
const { appId = 'app', enableBffRequestId = true, enableTelemetry = true, enableTelemetryExporters = true, otlpEndpoint = process.env.MODERN_TELEMETRY_OTLP_ENDPOINT || 'http://127.0.0.1:4318/v1/logs', victoriaMetricsEndpoint = process.env.MODERN_TELEMETRY_VICTORIA_ENDPOINT || 'http://127.0.0.1:8428/api/v1/import/prometheus', telemetryFailLoudStartup = true, enableModuleFederationSSR = true } = options;
|
|
5
34
|
const server = {};
|
|
@@ -33,7 +62,10 @@ const createAppBaselineConfig = (options = {})=>{
|
|
|
33
62
|
disableClientServer: true
|
|
34
63
|
}
|
|
35
64
|
},
|
|
36
|
-
server
|
|
65
|
+
server,
|
|
66
|
+
tools: {
|
|
67
|
+
bundlerChain: setReactRouterBridgeSafeAliases
|
|
68
|
+
}
|
|
37
69
|
};
|
|
38
70
|
if (enableBffRequestId) baselineConfig.bff = {
|
|
39
71
|
requestId: appId
|
|
@@ -1,6 +1,61 @@
|
|
|
1
|
-
import "node:module";
|
|
1
|
+
import __rslib_shim_module__ from "node:module";
|
|
2
|
+
const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
|
|
3
|
+
import node_fs from "node:fs";
|
|
4
|
+
import node_path from "node:path";
|
|
2
5
|
import { SERVICE_WORKER_ENVIRONMENT_NAME } from "@modern-js/builder";
|
|
3
6
|
import { isProd, isSSR, isServiceWorker, isUseRsc, isUseSSRBundle } from "@modern-js/utils";
|
|
7
|
+
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
8
|
+
import { dirname as __rspack_dirname } from "node:path";
|
|
9
|
+
var getBuilderEnvironments_dirname = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
|
|
10
|
+
const BFF_EFFECT_WORKER_ENTRY_NAME = '__modern_bff_effect';
|
|
11
|
+
const BFF_EFFECT_WORKER_RUNTIME_QUERY = 'modern-bff-runtime';
|
|
12
|
+
const JS_OR_TS_EXTS = [
|
|
13
|
+
'.ts',
|
|
14
|
+
'.tsx',
|
|
15
|
+
'.js',
|
|
16
|
+
'.jsx',
|
|
17
|
+
'.mjs',
|
|
18
|
+
'.cjs'
|
|
19
|
+
];
|
|
20
|
+
const CLOUDFLARE_WORKER_COMPAT_TEMPLATE_DIR = node_path.resolve(getBuilderEnvironments_dirname, '../../plugins/deploy/platforms/templates');
|
|
21
|
+
function findExistingFile(candidates) {
|
|
22
|
+
return candidates.find((candidate)=>node_fs.existsSync(candidate));
|
|
23
|
+
}
|
|
24
|
+
function resolvePackageEntry(packageName, paths) {
|
|
25
|
+
try {
|
|
26
|
+
return node_fs.realpathSync(require.resolve(packageName, {
|
|
27
|
+
paths
|
|
28
|
+
}));
|
|
29
|
+
} catch {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function resolvePackageFile(packageName, filePath, paths) {
|
|
34
|
+
try {
|
|
35
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`, {
|
|
36
|
+
paths
|
|
37
|
+
});
|
|
38
|
+
const packageFile = node_path.join(node_path.dirname(packageJsonPath), filePath);
|
|
39
|
+
return node_fs.existsSync(packageFile) ? node_fs.realpathSync(packageFile) : void 0;
|
|
40
|
+
} catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function setAliasIfPresent(alias, name, value) {
|
|
45
|
+
if (value) alias.set(name, value);
|
|
46
|
+
}
|
|
47
|
+
function getCloudflareWorkerCompatFile(file) {
|
|
48
|
+
return node_path.join(CLOUDFLARE_WORKER_COMPAT_TEMPLATE_DIR, file);
|
|
49
|
+
}
|
|
50
|
+
function getEffectBffEntry(normalizedConfig, appContext) {
|
|
51
|
+
if (!normalizedConfig.bff || 'hono' === normalizedConfig.bff.runtimeFramework) return;
|
|
52
|
+
const configuredEntry = normalizedConfig.bff.effect?.entry;
|
|
53
|
+
const entryWithoutExtension = configuredEntry ? node_path.isAbsolute(configuredEntry) ? configuredEntry : node_path.resolve(appContext.appDirectory, configuredEntry) : node_path.resolve(appContext.apiDirectory, 'effect', 'index');
|
|
54
|
+
return findExistingFile(JS_OR_TS_EXTS.map((extension)=>`${entryWithoutExtension}${extension}`));
|
|
55
|
+
}
|
|
56
|
+
function isCloudflareWorkerDeploy(normalizedConfig) {
|
|
57
|
+
return normalizedConfig.deploy?.target === 'cloudflare' || 'cloudflare' === process.env.MODERNJS_DEPLOY;
|
|
58
|
+
}
|
|
4
59
|
function getBuilderEnvironments(normalizedConfig, appContext, tempBuilderConfig) {
|
|
5
60
|
const entries = {};
|
|
6
61
|
const { entrypoints = [], checkedEntries } = appContext;
|
|
@@ -17,6 +72,11 @@ function getBuilderEnvironments(normalizedConfig, appContext, tempBuilderConfig)
|
|
|
17
72
|
const v = entries[entry];
|
|
18
73
|
serverEntries[entry] = v.map((entry)=>entry.replace('index.jsx', 'index.server.jsx')).map((entry)=>entry.replace('bootstrap.jsx', 'bootstrap.server.jsx'));
|
|
19
74
|
}
|
|
75
|
+
const cloudflareWorkerServerEntries = {};
|
|
76
|
+
for(const entry in entries){
|
|
77
|
+
const v = entries[entry];
|
|
78
|
+
cloudflareWorkerServerEntries[entry] = v.map((entry)=>entry.replace('index.jsx', 'index.server.jsx')).map((entry)=>entry.replace('bootstrap.jsx', 'index.server.jsx'));
|
|
79
|
+
}
|
|
20
80
|
const environments = {
|
|
21
81
|
client: {
|
|
22
82
|
output: {
|
|
@@ -41,14 +101,115 @@ function getBuilderEnvironments(normalizedConfig, appContext, tempBuilderConfig)
|
|
|
41
101
|
}
|
|
42
102
|
};
|
|
43
103
|
const useWorkerTarget = isServiceWorker(normalizedConfig);
|
|
44
|
-
if (useWorkerTarget)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
104
|
+
if (useWorkerTarget) {
|
|
105
|
+
const useCloudflareModuleWorker = isCloudflareWorkerDeploy(normalizedConfig);
|
|
106
|
+
const effectBffEntry = useCloudflareModuleWorker ? getEffectBffEntry(normalizedConfig, appContext) : void 0;
|
|
107
|
+
const tanstackRouterSsrServerFile = useCloudflareModuleWorker ? resolvePackageFile('@tanstack/router-core', 'dist/esm/ssr/ssr-server.js', [
|
|
108
|
+
appContext.appDirectory,
|
|
109
|
+
process.cwd()
|
|
110
|
+
]) : void 0;
|
|
111
|
+
const runtimeRscWorkerFile = useCloudflareModuleWorker ? resolvePackageFile('@modern-js/runtime', 'dist/esm/rsc/server.worker.mjs', [
|
|
112
|
+
appContext.appDirectory,
|
|
113
|
+
process.cwd()
|
|
114
|
+
]) : void 0;
|
|
115
|
+
const renderRscWorkerFile = useCloudflareModuleWorker ? resolvePackageFile('@modern-js/render', 'dist/esm/rscWorker.mjs', [
|
|
116
|
+
appContext.appDirectory,
|
|
117
|
+
process.cwd()
|
|
118
|
+
]) : void 0;
|
|
119
|
+
const reactFile = useCloudflareModuleWorker ? resolvePackageFile('react', 'index.js', [
|
|
120
|
+
appContext.appDirectory,
|
|
121
|
+
process.cwd()
|
|
122
|
+
]) : void 0;
|
|
123
|
+
const reactJsxRuntimeFile = useCloudflareModuleWorker ? resolvePackageFile('react', 'jsx-runtime.js', [
|
|
124
|
+
appContext.appDirectory,
|
|
125
|
+
process.cwd()
|
|
126
|
+
]) : void 0;
|
|
127
|
+
const reactJsxDevRuntimeFile = useCloudflareModuleWorker ? resolvePackageFile('react', 'jsx-dev-runtime.js', [
|
|
128
|
+
appContext.appDirectory,
|
|
129
|
+
process.cwd()
|
|
130
|
+
]) : void 0;
|
|
131
|
+
const reactDomFile = useCloudflareModuleWorker ? resolvePackageFile('react-dom', 'index.js', [
|
|
132
|
+
appContext.appDirectory,
|
|
133
|
+
process.cwd()
|
|
134
|
+
]) : void 0;
|
|
135
|
+
const reactDomServerEdgeFile = useCloudflareModuleWorker ? resolvePackageFile('react-dom', 'server.edge.js', [
|
|
136
|
+
appContext.appDirectory,
|
|
137
|
+
process.cwd()
|
|
138
|
+
]) : void 0;
|
|
139
|
+
const loadableComponentFile = useCloudflareModuleWorker ? resolvePackageEntry('@loadable/component', [
|
|
140
|
+
appContext.appDirectory,
|
|
141
|
+
process.cwd(),
|
|
142
|
+
CLOUDFLARE_WORKER_COMPAT_TEMPLATE_DIR
|
|
143
|
+
]) : void 0;
|
|
144
|
+
const loadableServerWorkerFile = useCloudflareModuleWorker ? getCloudflareWorkerCompatFile('cloudflare-worker-loadable-server.mjs') : void 0;
|
|
145
|
+
const fsPromisesWorkerFile = useCloudflareModuleWorker ? getCloudflareWorkerCompatFile('cloudflare-worker-fs-promises.mjs') : void 0;
|
|
146
|
+
const pathWorkerFile = useCloudflareModuleWorker ? getCloudflareWorkerCompatFile('cloudflare-worker-path.mjs') : void 0;
|
|
147
|
+
const baseWorkerEntries = useCloudflareModuleWorker ? cloudflareWorkerServerEntries : serverEntries;
|
|
148
|
+
const workerEntries = effectBffEntry ? {
|
|
149
|
+
...baseWorkerEntries,
|
|
150
|
+
[BFF_EFFECT_WORKER_ENTRY_NAME]: [
|
|
151
|
+
`${effectBffEntry}?${BFF_EFFECT_WORKER_RUNTIME_QUERY}`
|
|
152
|
+
]
|
|
153
|
+
} : baseWorkerEntries;
|
|
154
|
+
environments[SERVICE_WORKER_ENVIRONMENT_NAME] = {
|
|
155
|
+
output: {
|
|
156
|
+
target: useCloudflareModuleWorker ? 'web' : 'web-worker',
|
|
157
|
+
...useCloudflareModuleWorker ? {
|
|
158
|
+
module: true
|
|
159
|
+
} : {}
|
|
160
|
+
},
|
|
161
|
+
source: {
|
|
162
|
+
entry: workerEntries
|
|
163
|
+
},
|
|
164
|
+
tools: {
|
|
165
|
+
htmlPlugin: false,
|
|
166
|
+
...useCloudflareModuleWorker ? {
|
|
167
|
+
bundlerChain: (chain)=>{
|
|
168
|
+
chain.merge({
|
|
169
|
+
experiments: {
|
|
170
|
+
outputModule: true
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
chain.output.chunkFormat('module').chunkLoading('import').workerChunkLoading('import');
|
|
174
|
+
chain.target('webworker');
|
|
175
|
+
chain.plugins.delete('plugin-module-federation');
|
|
176
|
+
if (tanstackRouterSsrServerFile) {
|
|
177
|
+
chain.resolve.alias.set('@tanstack/router-core/ssr/server$', tanstackRouterSsrServerFile);
|
|
178
|
+
chain.resolve.alias.set('@tanstack/router-core/ssr/server', tanstackRouterSsrServerFile);
|
|
179
|
+
}
|
|
180
|
+
if (runtimeRscWorkerFile) {
|
|
181
|
+
chain.resolve.alias.set('@modern-js/runtime/rsc/server$', runtimeRscWorkerFile);
|
|
182
|
+
chain.resolve.alias.set('@modern-js/runtime/rsc/server', runtimeRscWorkerFile);
|
|
183
|
+
}
|
|
184
|
+
if (renderRscWorkerFile) {
|
|
185
|
+
chain.resolve.alias.set('@modern-js/render/rsc$', renderRscWorkerFile);
|
|
186
|
+
chain.resolve.alias.set('@modern-js/render/rsc', renderRscWorkerFile);
|
|
187
|
+
chain.resolve.alias.set('@modern-js/render/rsc-worker$', renderRscWorkerFile);
|
|
188
|
+
}
|
|
189
|
+
setAliasIfPresent(chain.resolve.alias, 'react$', reactFile);
|
|
190
|
+
setAliasIfPresent(chain.resolve.alias, 'react/jsx-runtime$', reactJsxRuntimeFile);
|
|
191
|
+
setAliasIfPresent(chain.resolve.alias, 'react/jsx-dev-runtime$', reactJsxDevRuntimeFile);
|
|
192
|
+
setAliasIfPresent(chain.resolve.alias, 'react-dom$', reactDomFile);
|
|
193
|
+
setAliasIfPresent(chain.resolve.alias, 'react-dom/server.edge$', reactDomServerEdgeFile);
|
|
194
|
+
setAliasIfPresent(chain.resolve.alias, '@loadable/component$', loadableComponentFile);
|
|
195
|
+
setAliasIfPresent(chain.resolve.alias, '@loadable/server$', loadableServerWorkerFile);
|
|
196
|
+
setAliasIfPresent(chain.resolve.alias, 'fs/promises$', fsPromisesWorkerFile);
|
|
197
|
+
setAliasIfPresent(chain.resolve.alias, 'node:fs/promises$', fsPromisesWorkerFile);
|
|
198
|
+
setAliasIfPresent(chain.resolve.alias, 'path$', pathWorkerFile);
|
|
199
|
+
setAliasIfPresent(chain.resolve.alias, 'node:path$', pathWorkerFile);
|
|
200
|
+
chain.resolve.alias.set('react-server-dom-rspack/server.node$', 'react-server-dom-rspack/server.edge');
|
|
201
|
+
chain.resolve.alias.set('react-server-dom-rspack/server.node', 'react-server-dom-rspack/server.edge');
|
|
202
|
+
chain.resolve.alias.set('react-server-dom-rspack/client.node$', 'react-server-dom-rspack/client.edge');
|
|
203
|
+
chain.resolve.alias.set('react-server-dom-rspack/client.node', 'react-server-dom-rspack/client.edge');
|
|
204
|
+
chain.resolve.fallback.set('async_hooks', false);
|
|
205
|
+
chain.resolve.fallback.set('node:async_hooks', false);
|
|
206
|
+
chain.resolve.fallback.set('fs', false);
|
|
207
|
+
chain.resolve.fallback.set('node:fs', false);
|
|
208
|
+
}
|
|
209
|
+
} : {}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
52
213
|
return {
|
|
53
214
|
environments,
|
|
54
215
|
builderConfig: tempBuilderConfig
|
|
@@ -9,8 +9,11 @@ const builderPluginAdapterBasic = (options)=>({
|
|
|
9
9
|
setup (api) {
|
|
10
10
|
api.modifyBundlerChain((chain, { target, CHAIN_ID, environment })=>{
|
|
11
11
|
const isServiceWorker = environment.name === SERVICE_WORKER_ENVIRONMENT_NAME;
|
|
12
|
-
|
|
13
|
-
if ('
|
|
12
|
+
const isWebTargetServiceWorker = isServiceWorker && 'web' === target;
|
|
13
|
+
if ('node' === target || isServiceWorker) applyNodeCompat(isServiceWorker, chain, {
|
|
14
|
+
includeNodeExtensions: !isWebTargetServiceWorker
|
|
15
|
+
});
|
|
16
|
+
if ('web' === target && !isServiceWorker) {
|
|
14
17
|
const bareServerModuleReg = /\.(server|node)\.[tj]sx?$/;
|
|
15
18
|
const depExt = 'mjs';
|
|
16
19
|
chain.module.rule(CHAIN_ID.RULE.JS).exclude.add(bareServerModuleReg);
|
|
@@ -26,8 +29,40 @@ const builderPluginAdapterBasic = (options)=>({
|
|
|
26
29
|
});
|
|
27
30
|
api.modifyRspackConfig((config, { target, environment })=>{
|
|
28
31
|
const isServiceWorker = environment.name === SERVICE_WORKER_ENVIRONMENT_NAME;
|
|
32
|
+
const isWebTargetServiceWorker = isServiceWorker && 'web' === target;
|
|
29
33
|
if ('node' === target || isServiceWorker) {
|
|
30
|
-
const extensionAlias = {
|
|
34
|
+
const extensionAlias = isWebTargetServiceWorker ? {
|
|
35
|
+
'.js': [
|
|
36
|
+
'.worker.js',
|
|
37
|
+
'.server.js',
|
|
38
|
+
'.js'
|
|
39
|
+
],
|
|
40
|
+
'.jsx': [
|
|
41
|
+
'.worker.jsx',
|
|
42
|
+
'.server.jsx',
|
|
43
|
+
'.jsx'
|
|
44
|
+
],
|
|
45
|
+
'.ts': [
|
|
46
|
+
'.worker.ts',
|
|
47
|
+
'.server.ts',
|
|
48
|
+
'.ts'
|
|
49
|
+
],
|
|
50
|
+
'.tsx': [
|
|
51
|
+
'.worker.tsx',
|
|
52
|
+
'.server.tsx',
|
|
53
|
+
'.tsx'
|
|
54
|
+
],
|
|
55
|
+
'.mjs': [
|
|
56
|
+
'.worker.mjs',
|
|
57
|
+
'.server.mjs',
|
|
58
|
+
'.mjs'
|
|
59
|
+
],
|
|
60
|
+
'.json': [
|
|
61
|
+
'.worker.json',
|
|
62
|
+
'.server.json',
|
|
63
|
+
'.json'
|
|
64
|
+
]
|
|
65
|
+
} : {
|
|
31
66
|
'.js': [
|
|
32
67
|
'.node.js',
|
|
33
68
|
'.server.js',
|
|
@@ -68,7 +103,8 @@ const builderPluginAdapterBasic = (options)=>({
|
|
|
68
103
|
});
|
|
69
104
|
}
|
|
70
105
|
});
|
|
71
|
-
function applyNodeCompat(isServiceWorker, chain) {
|
|
106
|
+
function applyNodeCompat(isServiceWorker, chain, options = {}) {
|
|
107
|
+
const { includeNodeExtensions = true } = options;
|
|
72
108
|
const nodeExts = [
|
|
73
109
|
'.node.js',
|
|
74
110
|
'.node.jsx',
|
|
@@ -87,7 +123,7 @@ function applyNodeCompat(isServiceWorker, chain) {
|
|
|
87
123
|
'.worker.ts',
|
|
88
124
|
'.worker.tsx'
|
|
89
125
|
];
|
|
90
|
-
for (const ext of nodeExts)chain.resolve.extensions.prepend(ext);
|
|
126
|
+
if (includeNodeExtensions) for (const ext of nodeExts)chain.resolve.extensions.prepend(ext);
|
|
91
127
|
if (isServiceWorker) for (const ext of webWorkerExts)chain.resolve.extensions.prepend(ext);
|
|
92
128
|
}
|
|
93
129
|
export { builderPluginAdapterBasic };
|
|
@@ -96,9 +96,10 @@ function applyFilterEntriesBySSRConfig({ isProd, chain, appNormalizedConfig }) {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
async function applySSRLoaderEntry(chain, optinos, isServer) {
|
|
99
|
-
const { appContext } = optinos;
|
|
99
|
+
const { appContext, normalizedConfig } = optinos;
|
|
100
100
|
const { internalDirectory } = appContext;
|
|
101
101
|
const { entrypoints } = appContext;
|
|
102
|
+
const isRsc = isUseRsc(normalizedConfig);
|
|
102
103
|
await Promise.all(entrypoints.map(async (entrypoint)=>{
|
|
103
104
|
const { entryName } = entrypoint;
|
|
104
105
|
const serverLoadersFile = getServerCombinedModuleFile(internalDirectory, entryName);
|
|
@@ -106,6 +107,7 @@ async function applySSRLoaderEntry(chain, optinos, isServer) {
|
|
|
106
107
|
await fs.access(serverLoadersFile, fs.constants.F_OK);
|
|
107
108
|
chain.entry(`${entryName}-server-loaders`).add(serverLoadersFile);
|
|
108
109
|
} catch (err) {}
|
|
110
|
+
else if (isRsc) chain.entry(`${entryName}-server-loaders`).add("data:text/javascript,export%20{};");
|
|
109
111
|
}));
|
|
110
112
|
}
|
|
111
113
|
function applySSRDataLoader(chain, options) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { provider } from "std-env";
|
|
3
|
+
import { createCloudflarePreset } from "./platforms/cloudflare.mjs";
|
|
3
4
|
import { createGhPagesPreset } from "./platforms/gh-pages.mjs";
|
|
4
5
|
import { createNetlifyPreset } from "./platforms/netlify.mjs";
|
|
5
6
|
import { createNodePreset } from "./platforms/node.mjs";
|
|
@@ -9,14 +10,18 @@ const deployPresets = {
|
|
|
9
10
|
node: createNodePreset,
|
|
10
11
|
vercel: createVercelPreset,
|
|
11
12
|
netlify: createNetlifyPreset,
|
|
12
|
-
ghPages: createGhPagesPreset
|
|
13
|
+
ghPages: createGhPagesPreset,
|
|
14
|
+
cloudflare: createCloudflarePreset
|
|
13
15
|
};
|
|
16
|
+
const getSupportedDeployTargets = ()=>Object.keys(deployPresets);
|
|
17
|
+
const isDeployTarget = (target)=>Object.prototype.hasOwnProperty.call(deployPresets, target);
|
|
18
|
+
const resolveDeployTarget = (modernConfig, envDeployTarget = process.env.MODERNJS_DEPLOY, detectedProvider = provider)=>modernConfig.deploy?.target || envDeployTarget || detectedProvider || 'node';
|
|
14
19
|
async function getDeployPreset(appContext, modernConfig, deployTarget, api) {
|
|
15
20
|
const { appDirectory, distDirectory, metaName } = appContext;
|
|
16
21
|
const { useSSR, useAPI, useWebServer } = getProjectUsage(appDirectory, distDirectory, metaName);
|
|
17
22
|
const needModernServer = useSSR || useAPI || useWebServer;
|
|
23
|
+
if (!isDeployTarget(deployTarget)) throw new Error(`Unknown deploy target: '${deployTarget}'. deploy.target or MODERNJS_DEPLOY should be one of: ${getSupportedDeployTargets().join(', ')}.`);
|
|
18
24
|
const createPreset = deployPresets[deployTarget];
|
|
19
|
-
if (!createPreset) throw new Error(`Unknown deploy target: '${deployTarget}'. MODERNJS_DEPLOY should be 'node', 'vercel', or 'netlify'.`);
|
|
20
25
|
return createPreset({
|
|
21
26
|
appContext,
|
|
22
27
|
modernConfig,
|
|
@@ -27,12 +32,12 @@ async function getDeployPreset(appContext, modernConfig, deployTarget, api) {
|
|
|
27
32
|
const deploy = ()=>({
|
|
28
33
|
name: '@modern-js/plugin-deploy',
|
|
29
34
|
setup: (api)=>{
|
|
30
|
-
const deployTarget = process.env.MODERNJS_DEPLOY || provider || 'node';
|
|
31
35
|
api.deploy(async ()=>{
|
|
32
36
|
const appContext = api.getAppContext();
|
|
33
37
|
const { metaName } = appContext;
|
|
34
|
-
if ('modern-js' !== metaName && !process.env.MODERNJS_DEPLOY) return;
|
|
35
38
|
const modernConfig = api.getNormalizedConfig();
|
|
39
|
+
const deployTarget = resolveDeployTarget(modernConfig);
|
|
40
|
+
if ('modern-js' !== metaName && !modernConfig.deploy?.target && !process.env.MODERNJS_DEPLOY) return;
|
|
36
41
|
const deployPreset = await getDeployPreset(appContext, modernConfig, deployTarget, api);
|
|
37
42
|
deployPreset?.prepare && await deployPreset?.prepare();
|
|
38
43
|
deployPreset?.writeOutput && await deployPreset?.writeOutput();
|
|
@@ -42,3 +47,4 @@ const deploy = ()=>({
|
|
|
42
47
|
}
|
|
43
48
|
});
|
|
44
49
|
export default deploy;
|
|
50
|
+
export { getSupportedDeployTargets, resolveDeployTarget };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
import { fs } from "@modern-js/utils";
|
|
4
|
+
import { readTemplate } from "../utils/index.mjs";
|
|
5
|
+
const WORKER_ENTRY = 'server/index.mjs';
|
|
6
|
+
const WORKER_MANIFEST = 'server/modern-worker-manifest.json';
|
|
7
|
+
const ASSETS_BINDING = 'ASSETS';
|
|
8
|
+
const ROUTE_SPEC_FILE = 'route.json';
|
|
9
|
+
const ROUTE_SPEC_OUTPUT = `server/${ROUTE_SPEC_FILE}`;
|
|
10
|
+
const LOADABLE_STATS_FILE = 'loadable-stats.json';
|
|
11
|
+
const ROUTE_MANIFEST_FILE = 'routes-manifest.json';
|
|
12
|
+
const PUBLIC_ASSETS_DIRECTORY = 'public';
|
|
13
|
+
const WORKER_BUNDLE_DIRECTORY = 'worker';
|
|
14
|
+
const SERVER_BUNDLE_DIRECTORY = 'bundles';
|
|
15
|
+
const BFF_EFFECT_WORKER_ENTRY = `${WORKER_BUNDLE_DIRECTORY}/__modern_bff_effect.js`;
|
|
16
|
+
const getCompatibilityDate = ()=>new Date().toISOString().slice(0, 10);
|
|
17
|
+
const getWorkerName = (appDirectory)=>{
|
|
18
|
+
const basename = node_path.basename(appDirectory);
|
|
19
|
+
return basename.replace(/[^a-zA-Z0-9-_]/g, '-') || 'modern-cloudflare-worker';
|
|
20
|
+
};
|
|
21
|
+
const getConfiguredWorkerName = (appDirectory, modernConfig)=>{
|
|
22
|
+
const configuredName = modernConfig.deploy?.worker?.name?.trim();
|
|
23
|
+
return configuredName || getWorkerName(appDirectory);
|
|
24
|
+
};
|
|
25
|
+
const readRouteSpec = async (outputDirectory)=>{
|
|
26
|
+
const routeSpecPath = node_path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
27
|
+
if (!await fs.pathExists(routeSpecPath)) return {
|
|
28
|
+
routes: []
|
|
29
|
+
};
|
|
30
|
+
const routeSpec = await fs.readJSON(routeSpecPath);
|
|
31
|
+
return {
|
|
32
|
+
...routeSpec,
|
|
33
|
+
routes: Array.isArray(routeSpec.routes) ? routeSpec.routes : []
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const createWorkerManifest = async (outputDirectory, modernConfig)=>{
|
|
37
|
+
const routeSpec = await readRouteSpec(outputDirectory);
|
|
38
|
+
const routes = await Promise.all(routeSpec.routes.map(async (route)=>{
|
|
39
|
+
const worker = 'string' == typeof route.worker ? route.worker : void 0;
|
|
40
|
+
return {
|
|
41
|
+
urlPath: route.urlPath,
|
|
42
|
+
entryName: route.entryName,
|
|
43
|
+
entryPath: route.entryPath,
|
|
44
|
+
isSSR: Boolean(route.isSSR),
|
|
45
|
+
worker,
|
|
46
|
+
workerExists: worker ? await fs.pathExists(node_path.join(outputDirectory, worker)) : false
|
|
47
|
+
};
|
|
48
|
+
}));
|
|
49
|
+
const bffPrefix = modernConfig.bff?.prefix;
|
|
50
|
+
const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
|
|
51
|
+
const isEffectBff = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
|
|
52
|
+
const effectBffWorkerExists = await fs.pathExists(node_path.join(outputDirectory, BFF_EFFECT_WORKER_ENTRY));
|
|
53
|
+
return {
|
|
54
|
+
version: 1,
|
|
55
|
+
runtime: {
|
|
56
|
+
type: 'cloudflare-module-worker',
|
|
57
|
+
entry: WORKER_ENTRY,
|
|
58
|
+
fetchExport: true,
|
|
59
|
+
nodeListen: false
|
|
60
|
+
},
|
|
61
|
+
assets: {
|
|
62
|
+
binding: ASSETS_BINDING,
|
|
63
|
+
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
64
|
+
runWorkerFirst: true
|
|
65
|
+
},
|
|
66
|
+
routeSpec: {
|
|
67
|
+
file: ROUTE_SPEC_OUTPUT,
|
|
68
|
+
routes
|
|
69
|
+
},
|
|
70
|
+
workerBundles: {
|
|
71
|
+
directory: WORKER_BUNDLE_DIRECTORY,
|
|
72
|
+
format: 'commonjs',
|
|
73
|
+
importableFromModuleWorker: true,
|
|
74
|
+
requestHandlerExport: 'requestHandler'
|
|
75
|
+
},
|
|
76
|
+
resources: {
|
|
77
|
+
loadableStats: LOADABLE_STATS_FILE,
|
|
78
|
+
routeManifest: ROUTE_MANIFEST_FILE
|
|
79
|
+
},
|
|
80
|
+
bff: isEffectBff && primaryBffPrefix && effectBffWorkerExists ? {
|
|
81
|
+
runtimeFramework: 'effect',
|
|
82
|
+
prefix: primaryBffPrefix,
|
|
83
|
+
worker: BFF_EFFECT_WORKER_ENTRY
|
|
84
|
+
} : void 0
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const createWorkerModuleLoaders = (manifest)=>{
|
|
88
|
+
const imports = new Map();
|
|
89
|
+
for (const route of manifest.routeSpec.routes)if (route.worker && route.workerExists) {
|
|
90
|
+
const importPath = `../${String(route.worker).replace(/^\/+/u, '')}`;
|
|
91
|
+
imports.set(route.worker, `() => import(${JSON.stringify(importPath)})`);
|
|
92
|
+
}
|
|
93
|
+
if (manifest.bff?.worker) {
|
|
94
|
+
const importPath = `../${String(manifest.bff.worker).replace(/^\/+/u, '')}`;
|
|
95
|
+
imports.set(manifest.bff.worker, `() => import(${JSON.stringify(importPath)})`);
|
|
96
|
+
}
|
|
97
|
+
if (0 === imports.size) return '{}';
|
|
98
|
+
const loaders = [
|
|
99
|
+
...imports.entries()
|
|
100
|
+
].map(([worker, loader])=>`${JSON.stringify(worker)}: ${loader}`);
|
|
101
|
+
return `{\n${loaders.join(',\n')}\n}`;
|
|
102
|
+
};
|
|
103
|
+
const shouldCopyToPublicAssets = (src, distDirectory)=>{
|
|
104
|
+
const relativePath = node_path.relative(distDirectory, src);
|
|
105
|
+
if (!relativePath) return true;
|
|
106
|
+
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
107
|
+
const [topLevelDirectory] = normalizedRelativePath.split('/');
|
|
108
|
+
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY;
|
|
109
|
+
};
|
|
110
|
+
const shouldCopyToWorkerBundle = (src, workerBundleDirectory)=>{
|
|
111
|
+
const relativePath = node_path.relative(workerBundleDirectory, src);
|
|
112
|
+
if (!relativePath) return true;
|
|
113
|
+
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
114
|
+
const basename = node_path.basename(normalizedRelativePath);
|
|
115
|
+
if (basename.startsWith('.') || normalizedRelativePath.includes('/.')) return false;
|
|
116
|
+
if (fs.statSync(src).isDirectory()) return true;
|
|
117
|
+
return [
|
|
118
|
+
'.cjs',
|
|
119
|
+
'.js',
|
|
120
|
+
'.mjs'
|
|
121
|
+
].includes(node_path.extname(normalizedRelativePath));
|
|
122
|
+
};
|
|
123
|
+
const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
124
|
+
const { appDirectory, distDirectory } = appContext;
|
|
125
|
+
const outputDirectory = node_path.join(appDirectory, '.output');
|
|
126
|
+
const publicDirectory = node_path.join(outputDirectory, PUBLIC_ASSETS_DIRECTORY);
|
|
127
|
+
const workerEntryPath = node_path.join(outputDirectory, WORKER_ENTRY);
|
|
128
|
+
const workerManifestPath = node_path.join(outputDirectory, WORKER_MANIFEST);
|
|
129
|
+
const routeSpecOutputPath = node_path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
130
|
+
const wranglerConfigPath = node_path.join(outputDirectory, 'wrangler.json');
|
|
131
|
+
const workerName = getConfiguredWorkerName(appDirectory, modernConfig);
|
|
132
|
+
return {
|
|
133
|
+
async prepare () {
|
|
134
|
+
await fs.remove(outputDirectory);
|
|
135
|
+
},
|
|
136
|
+
async writeOutput () {
|
|
137
|
+
await fs.copy(distDirectory, publicDirectory, {
|
|
138
|
+
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory)
|
|
139
|
+
});
|
|
140
|
+
await fs.ensureDir(node_path.dirname(workerEntryPath));
|
|
141
|
+
await fs.ensureDir(node_path.dirname(workerManifestPath));
|
|
142
|
+
const routeSpecSourcePath = node_path.join(distDirectory, ROUTE_SPEC_FILE);
|
|
143
|
+
if (await fs.pathExists(routeSpecSourcePath)) await fs.copy(routeSpecSourcePath, routeSpecOutputPath);
|
|
144
|
+
const workerBundleSourceDirectory = node_path.join(distDirectory, WORKER_BUNDLE_DIRECTORY);
|
|
145
|
+
if (await fs.pathExists(workerBundleSourceDirectory)) await fs.copy(workerBundleSourceDirectory, node_path.join(outputDirectory, WORKER_BUNDLE_DIRECTORY), {
|
|
146
|
+
filter: (src)=>shouldCopyToWorkerBundle(src, workerBundleSourceDirectory)
|
|
147
|
+
});
|
|
148
|
+
await fs.writeJSON(wranglerConfigPath, {
|
|
149
|
+
$schema: 'node_modules/wrangler/config-schema.json',
|
|
150
|
+
name: workerName,
|
|
151
|
+
main: WORKER_ENTRY,
|
|
152
|
+
compatibility_date: getCompatibilityDate(),
|
|
153
|
+
compatibility_flags: [
|
|
154
|
+
'nodejs_compat',
|
|
155
|
+
'global_fetch_strictly_public'
|
|
156
|
+
],
|
|
157
|
+
assets: {
|
|
158
|
+
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
159
|
+
binding: ASSETS_BINDING,
|
|
160
|
+
run_worker_first: true
|
|
161
|
+
}
|
|
162
|
+
}, {
|
|
163
|
+
spaces: 2
|
|
164
|
+
});
|
|
165
|
+
await fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig), {
|
|
166
|
+
spaces: 2
|
|
167
|
+
});
|
|
168
|
+
await fs.writeJSON(node_path.join(outputDirectory, 'package.json'), {
|
|
169
|
+
type: 'commonjs'
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
async genEntry () {
|
|
173
|
+
const template = await readTemplate('cloudflare-entry.mjs');
|
|
174
|
+
const manifest = await fs.readJSON(workerManifestPath);
|
|
175
|
+
await fs.writeFile(workerEntryPath, template.replace('p_workerManifest', JSON.stringify(manifest, null, 2)).replace('p_workerModuleLoaders', createWorkerModuleLoaders(manifest)));
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
export { createCloudflarePreset };
|