@bleedingdev/modern-js-plugin-bff 3.5.0-ultramodern.98 → 3.8.1-ultramodern.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/cjs/cli/compress.js +1 -1
  2. package/dist/cjs/cli/generator.js +106 -97
  3. package/dist/cjs/cli/serverGlobalVars.js +43 -57
  4. package/dist/cjs/cli.js +6 -2
  5. package/dist/cjs/loader.js +6 -5
  6. package/dist/cjs/runtime/effect/adapter/cross-project-policy.js +17 -12
  7. package/dist/cjs/runtime/effect/adapter/entry.js +13 -5
  8. package/dist/cjs/runtime/effect/adapter/error-response.js +11 -9
  9. package/dist/cjs/runtime/effect/adapter.js +2 -2
  10. package/dist/cjs/runtime/effect/backend-federation/load.js +26 -17
  11. package/dist/cjs/runtime/effect/backend-federation/plugin.js +12 -2
  12. package/dist/cjs/runtime/effect/backend-federation/remotes.js +5 -4
  13. package/dist/cjs/runtime/effect/backend-federation/runtime.js +80 -16
  14. package/dist/cjs/runtime/effect/edge-dispatcher.js +148 -0
  15. package/dist/cjs/runtime/effect/edge.js +19 -100
  16. package/dist/cjs/runtime/effect/endpoint-contracts.js +6 -1
  17. package/dist/cjs/runtime/effect/handler/definition.js +1 -0
  18. package/dist/cjs/runtime/effect/handler/http.js +17 -6
  19. package/dist/cjs/runtime/hono/adapter.js +32 -79
  20. package/dist/cjs/server.js +19 -30
  21. package/dist/cjs/utils/client-generator/write-package.js +1 -1
  22. package/dist/cjs/utils/crossProjectApiPlugin.js +21 -2
  23. package/dist/cjs/utils/effect-client-generator/generator.js +17 -16
  24. package/dist/cjs/utils/effect-client-generator/paths.js +4 -15
  25. package/dist/cjs/utils/effect-client-generator/rendering.js +8 -19
  26. package/dist/cjs/utils/effect-client-generator/runtime.js +15 -32
  27. package/dist/cjs/utils/effectSourceLoader.js +70 -10
  28. package/dist/cjs/utils/pluginGenerator.js +4 -4
  29. package/dist/esm/cli/compress.mjs +1 -1
  30. package/dist/esm/cli/generator.mjs +108 -98
  31. package/dist/esm/cli/serverGlobalVars.mjs +44 -48
  32. package/dist/esm/cli.mjs +2 -0
  33. package/dist/esm/loader.mjs +6 -5
  34. package/dist/esm/runtime/effect/adapter/cross-project-policy.mjs +17 -12
  35. package/dist/esm/runtime/effect/adapter/entry.mjs +13 -5
  36. package/dist/esm/runtime/effect/adapter/error-response.mjs +11 -9
  37. package/dist/esm/runtime/effect/adapter.mjs +3 -3
  38. package/dist/esm/runtime/effect/backend-federation/load.mjs +26 -17
  39. package/dist/esm/runtime/effect/backend-federation/plugin.mjs +12 -2
  40. package/dist/esm/runtime/effect/backend-federation/remotes.mjs +5 -4
  41. package/dist/esm/runtime/effect/backend-federation/runtime.mjs +80 -16
  42. package/dist/esm/runtime/effect/edge-dispatcher.mjs +38 -0
  43. package/dist/esm/runtime/effect/edge.mjs +1 -38
  44. package/dist/esm/runtime/effect/endpoint-contracts.mjs +6 -1
  45. package/dist/esm/runtime/effect/handler/definition.mjs +1 -0
  46. package/dist/esm/runtime/effect/handler/http.mjs +17 -6
  47. package/dist/esm/runtime/hono/adapter.mjs +33 -80
  48. package/dist/esm/server.mjs +18 -29
  49. package/dist/esm/utils/client-generator/write-package.mjs +1 -1
  50. package/dist/esm/utils/crossProjectApiPlugin.mjs +16 -3
  51. package/dist/esm/utils/effect-client-generator/generator.mjs +17 -16
  52. package/dist/esm/utils/effect-client-generator/paths.mjs +5 -6
  53. package/dist/esm/utils/effect-client-generator/rendering.mjs +9 -10
  54. package/dist/esm/utils/effect-client-generator/runtime.mjs +16 -23
  55. package/dist/esm/utils/effectSourceLoader.mjs +69 -12
  56. package/dist/esm/utils/pluginGenerator.mjs +5 -5
  57. package/dist/esm-node/cli/compress.mjs +1 -1
  58. package/dist/esm-node/cli/generator.mjs +108 -98
  59. package/dist/esm-node/cli/serverGlobalVars.mjs +44 -48
  60. package/dist/esm-node/cli.mjs +2 -0
  61. package/dist/esm-node/loader.mjs +6 -5
  62. package/dist/esm-node/runtime/effect/adapter/cross-project-policy.mjs +17 -12
  63. package/dist/esm-node/runtime/effect/adapter/entry.mjs +13 -5
  64. package/dist/esm-node/runtime/effect/adapter/error-response.mjs +11 -9
  65. package/dist/esm-node/runtime/effect/adapter.mjs +3 -3
  66. package/dist/esm-node/runtime/effect/backend-federation/load.mjs +26 -17
  67. package/dist/esm-node/runtime/effect/backend-federation/plugin.mjs +12 -2
  68. package/dist/esm-node/runtime/effect/backend-federation/remotes.mjs +5 -4
  69. package/dist/esm-node/runtime/effect/backend-federation/runtime.mjs +80 -16
  70. package/dist/esm-node/runtime/effect/edge-dispatcher.mjs +39 -0
  71. package/dist/esm-node/runtime/effect/edge.mjs +1 -38
  72. package/dist/esm-node/runtime/effect/endpoint-contracts.mjs +6 -1
  73. package/dist/esm-node/runtime/effect/handler/definition.mjs +1 -0
  74. package/dist/esm-node/runtime/effect/handler/http.mjs +17 -6
  75. package/dist/esm-node/runtime/hono/adapter.mjs +33 -80
  76. package/dist/esm-node/server.mjs +18 -29
  77. package/dist/esm-node/utils/client-generator/write-package.mjs +1 -1
  78. package/dist/esm-node/utils/crossProjectApiPlugin.mjs +16 -3
  79. package/dist/esm-node/utils/effect-client-generator/generator.mjs +17 -16
  80. package/dist/esm-node/utils/effect-client-generator/paths.mjs +5 -6
  81. package/dist/esm-node/utils/effect-client-generator/rendering.mjs +9 -10
  82. package/dist/esm-node/utils/effect-client-generator/runtime.mjs +16 -23
  83. package/dist/esm-node/utils/effectSourceLoader.mjs +69 -12
  84. package/dist/esm-node/utils/pluginGenerator.mjs +5 -5
  85. package/dist/types/cli/generator.d.ts +2 -2
  86. package/dist/types/cli/serverGlobalVars.d.ts +4 -2
  87. package/dist/types/cli.d.ts +1 -0
  88. package/dist/types/runtime/effect/adapter/cross-project-policy.d.ts +1 -1
  89. package/dist/types/runtime/effect/backend-federation/runtime.d.ts +1 -1
  90. package/dist/types/runtime/effect/edge-dispatcher.d.ts +26 -0
  91. package/dist/types/runtime/effect/edge.d.ts +1 -26
  92. package/dist/types/runtime/effect/endpoint-contracts.d.ts +6 -0
  93. package/dist/types/runtime/effect/handler/definition.d.ts +2 -12
  94. package/dist/types/runtime/effect/handler/http.d.ts +3 -2
  95. package/dist/types/runtime/effect/handler/openapi.d.ts +1 -1
  96. package/dist/types/runtime/effect/handler/types.d.ts +10 -5
  97. package/dist/types/runtime/effect-client/index.d.ts +1 -1
  98. package/dist/types/runtime/hono/adapter.d.ts +14 -8
  99. package/dist/types/utils/crossProjectApiPlugin.d.ts +2 -0
  100. package/dist/types/utils/effectSourceLoader.d.ts +10 -0
  101. package/dist/types/utils/pluginGenerator.d.ts +3 -1
  102. package/package.json +44 -16
@@ -38,6 +38,7 @@ var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
40
  bundleEffectEntryForNode: ()=>bundleEffectEntryForNode,
41
+ loadEffectBuiltModule: ()=>loadEffectBuiltModule,
41
42
  loadEffectSourceModule: ()=>loadEffectSourceModule
42
43
  });
43
44
  const external_node_crypto_namespaceObject = require("node:crypto");
@@ -120,10 +121,29 @@ const nodeBuiltins = new Set([
120
121
  ...external_node_module_namespaceObject.builtinModules.map((name)=>`node:${name}`)
121
122
  ]);
122
123
  const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
124
+ async function isEsmOnlyFile(filename) {
125
+ const extension = external_node_path_default().extname(filename).toLowerCase();
126
+ if ('.mjs' === extension || '.mts' === extension) return true;
127
+ if ('.cjs' === extension || '.cts' === extension) return false;
128
+ let directory = external_node_path_default().dirname(filename);
129
+ while(true){
130
+ const packagePath = external_node_path_default().join(directory, 'package.json');
131
+ try {
132
+ const packageJson = JSON.parse(await external_node_fs_default().promises.readFile(packagePath, 'utf8'));
133
+ return 'module' === packageJson.type;
134
+ } catch (error) {
135
+ if (!error || 'object' != typeof error || !('code' in error) || 'ENOENT' !== error.code) throw error;
136
+ }
137
+ const parent = external_node_path_default().dirname(directory);
138
+ if (parent === directory) return false;
139
+ directory = parent;
140
+ }
141
+ }
123
142
  function isBareSpecifier(specifier) {
124
143
  return !specifier.startsWith('.') && !specifier.startsWith('/') && !external_node_path_default().isAbsolute(specifier);
125
144
  }
126
- function externalizeInstalledDependencies() {
145
+ function externalizeInstalledDependencies(options) {
146
+ const runtimeRequire = (0, external_node_module_namespaceObject.createRequire)(external_node_path_default().join(options.runtimeResolveDir, '__modern_effect_entry.cjs'));
127
147
  return {
128
148
  name: 'modern-js-effect-installed-dependencies',
129
149
  setup (buildApi) {
@@ -153,19 +173,46 @@ function externalizeInstalledDependencies() {
153
173
  path: args.path
154
174
  };
155
175
  const realPath = await external_node_fs_default().promises.realpath(resolution.path).catch(()=>resolution.path);
156
- if (realPath.includes(`${external_node_path_default().sep}node_modules${external_node_path_default().sep}`)) return {
157
- external: true,
158
- path: args.path
159
- };
176
+ if (realPath.includes(`${external_node_path_default().sep}node_modules${external_node_path_default().sep}`)) {
177
+ const runtimeResolution = await buildApi.resolve(args.path, {
178
+ importer: '',
179
+ kind: args.kind,
180
+ namespace: 'file',
181
+ pluginData: {
182
+ [dependencyResolutionMarker]: true
183
+ },
184
+ resolveDir: options.runtimeResolveDir
185
+ });
186
+ if (0 === runtimeResolution.errors.length && runtimeResolution.path) {
187
+ if ('cjs' === options.format) {
188
+ let requiredPath;
189
+ try {
190
+ requiredPath = runtimeRequire.resolve(args.path);
191
+ } catch {
192
+ requiredPath = runtimeResolution.path;
193
+ }
194
+ if (await isEsmOnlyFile(requiredPath)) return {
195
+ namespace: resolution.namespace,
196
+ path: resolution.path,
197
+ pluginData: resolution.pluginData,
198
+ sideEffects: resolution.sideEffects,
199
+ suffix: resolution.suffix,
200
+ warnings: resolution.warnings
201
+ };
202
+ }
203
+ return {
204
+ external: true,
205
+ path: args.path
206
+ };
207
+ }
208
+ }
160
209
  return {
161
210
  namespace: resolution.namespace,
162
211
  path: resolution.path,
163
212
  pluginData: resolution.pluginData,
164
213
  sideEffects: resolution.sideEffects,
165
214
  suffix: resolution.suffix,
166
- warnings: resolution.warnings,
167
- watchDirs: resolution.watchDirs,
168
- watchFiles: resolution.watchFiles
215
+ warnings: resolution.warnings
169
216
  };
170
217
  });
171
218
  }
@@ -184,7 +231,10 @@ async function bundleEffectEntryForNode(options) {
184
231
  outfile: options.entryPath,
185
232
  platform: 'node',
186
233
  plugins: [
187
- externalizeInstalledDependencies(),
234
+ externalizeInstalledDependencies({
235
+ format: options.format,
236
+ runtimeResolveDir: options.appDir
237
+ }),
188
238
  preserveSourceModuleSemantics()
189
239
  ],
190
240
  target: 'node20'
@@ -200,6 +250,11 @@ function normalizeImportedModule(module) {
200
250
  if (1 === Object.keys(namespace).length && null !== commonJsModule && 'object' == typeof commonJsModule && true === commonJsModule.__esModule) return commonJsModule;
201
251
  return module;
202
252
  }
253
+ async function loadEffectBuiltModule(resourcePath) {
254
+ const canonicalPath = await external_node_fs_default().promises.realpath(external_node_path_default().resolve(resourcePath));
255
+ if (!await isEsmOnlyFile(canonicalPath)) return (0, external_node_module_namespaceObject.createRequire)(canonicalPath)(canonicalPath);
256
+ return normalizeImportedModule(await dynamicImport((0, external_node_url_namespaceObject.pathToFileURL)(canonicalPath).href));
257
+ }
203
258
  async function loadEffectSourceModule(options) {
204
259
  const resourcePath = external_node_path_default().resolve(options.resourcePath);
205
260
  const appDir = external_node_path_default().resolve(options.appDir ?? external_node_path_default().dirname(resourcePath));
@@ -226,7 +281,10 @@ async function loadEffectSourceModule(options) {
226
281
  outfile: outputPath,
227
282
  platform: 'node',
228
283
  plugins: [
229
- externalizeInstalledDependencies(),
284
+ externalizeInstalledDependencies({
285
+ format: 'esm',
286
+ runtimeResolveDir: appDir
287
+ }),
230
288
  preserveSourceModuleSemantics()
231
289
  ],
232
290
  sourcemap: 'inline',
@@ -242,9 +300,11 @@ async function loadEffectSourceModule(options) {
242
300
  return normalizeImportedModule(await dynamicImport(`${(0, external_node_url_namespaceObject.pathToFileURL)(outputPath).href}?v=${revision}`));
243
301
  }
244
302
  exports.bundleEffectEntryForNode = __webpack_exports__.bundleEffectEntryForNode;
303
+ exports.loadEffectBuiltModule = __webpack_exports__.loadEffectBuiltModule;
245
304
  exports.loadEffectSourceModule = __webpack_exports__.loadEffectSourceModule;
246
305
  for(var __rspack_i in __webpack_exports__)if (-1 === [
247
306
  "bundleEffectEntryForNode",
307
+ "loadEffectBuiltModule",
248
308
  "loadEffectSourceModule"
249
309
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
250
310
  Object.defineProperty(exports, '__esModule', {
@@ -43,18 +43,18 @@ const utils_namespaceObject = require("@modern-js/utils");
43
43
  const external_path_namespaceObject = require("path");
44
44
  var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
45
45
  const external_crossProjectApiPlugin_js_namespaceObject = require("./crossProjectApiPlugin.js");
46
- function replaceContent(source, packageName, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework) {
47
- const updatedSource = source.replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.PACKAGE_NAME, 'g'), packageName).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.PREFIX, 'g'), prefix).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.DIST_DIR, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeDistPath)).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.API_DIR, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeApiPath)).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.LAMBDA_DIR, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeLambdaPath)).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.RUNTIME_FRAMEWORK, 'g'), runtimeFramework);
46
+ function replaceContent(source, packageName, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts) {
47
+ const updatedSource = source.replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.PACKAGE_NAME, 'g'), packageName).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.PREFIX, 'g'), prefix).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.DIST_DIR, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeDistPath)).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.API_DIR, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeApiPath)).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.LAMBDA_DIR, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeLambdaPath)).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.RUNTIME_FRAMEWORK, 'g'), runtimeFramework).replace(new RegExp(external_crossProjectApiPlugin_js_namespaceObject.EFFECT_ENTRY, 'g'), (0, utils_namespaceObject.normalizeToPosixPath)(relativeEffectEntry)).replace(`'${external_crossProjectApiPlugin_js_namespaceObject.OPERATION_CONTRACTS_JSON}'`, ()=>JSON.stringify(JSON.stringify(operationContracts)));
48
48
  return updatedSource;
49
49
  }
50
- async function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework }) {
50
+ async function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts }) {
51
51
  try {
52
52
  const packageContent = await utils_namespaceObject.fs.readFile(external_path_default().resolve(appDirectory, './package.json'), 'utf8');
53
53
  const packageJson = JSON.parse(packageContent);
54
54
  const pluginDir = external_path_default().resolve(appDirectory, `./${relativeDistPath}`, 'plugin');
55
55
  const pluginPath = external_path_default().join(pluginDir, 'index.js');
56
56
  const pluginTemplate = await utils_namespaceObject.fs.readFile(external_path_default().resolve(__dirname, 'crossProjectApiPlugin.js'), 'utf8');
57
- const updatedPlugin = replaceContent(pluginTemplate, packageJson.name, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework);
57
+ const updatedPlugin = replaceContent(pluginTemplate, packageJson.name, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts);
58
58
  await utils_namespaceObject.fs.ensureFile(pluginPath);
59
59
  await utils_namespaceObject.fs.writeFile(pluginPath, updatedPlugin);
60
60
  const typeContent = `import type { AppTools, CliPlugin } from '@modern-js/app-tools';
@@ -1,6 +1,6 @@
1
1
  import { normalizePrefixList } from "./prefix.mjs";
2
2
  const createCompressConfig = (devServer, prefix)=>{
3
- if (!devServer || 'object' != typeof devServer || Array.isArray(devServer)) return;
3
+ if (void 0 === devServer || 'object' != typeof devServer || Array.isArray(devServer)) return;
4
4
  const { compress } = devServer;
5
5
  if (void 0 === compress || true === compress) {
6
6
  const prefixes = normalizePrefixList(prefix);
@@ -1,7 +1,6 @@
1
- import { ApiRouter } from "@modern-js/bff-core";
1
+ import { ApiRouter, buildOperationContractMap, deriveOperationVersion } from "@modern-js/bff-core";
2
2
  import { compile } from "@modern-js/server-utils";
3
- import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig } from "@modern-js/utils";
4
- import path from "path";
3
+ import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig, upath } from "@modern-js/utils";
5
4
  import clientGenerator from "../utils/clientGenerator.mjs";
6
5
  import { bundleEffectEntryForNode } from "../utils/effectSourceLoader.mjs";
7
6
  import pluginGenerator from "../utils/pluginGenerator.mjs";
@@ -20,110 +19,121 @@ const effectEntryExtensions = [
20
19
  '.cts'
21
20
  ];
22
21
  function resolveEffectSourceEntry(appDirectory, apiDirectory, configuredEntry) {
23
- const entry = configuredEntry ? path.isAbsolute(configuredEntry) ? configuredEntry : path.resolve(appDirectory, configuredEntry) : path.resolve(apiDirectory, 'index');
24
- return path.extname(entry) ? entry : effectEntryExtensions.map((extension)=>`${entry}${extension}`).find((candidate)=>fs.existsSync(candidate));
22
+ const entry = void 0 !== configuredEntry && configuredEntry.length > 0 ? upath.isAbsolute(configuredEntry) ? configuredEntry : upath.resolve(appDirectory, configuredEntry) : upath.resolve(apiDirectory, 'index');
23
+ return upath.extname(entry).length > 0 ? entry : effectEntryExtensions.map((extension)=>`${entry}${extension}`).find((candidate)=>fs.existsSync(candidate));
25
24
  }
26
25
  function resolveBuiltEffectEntry(appDirectory, distDirectory, sourceEntry) {
27
- if (!sourceEntry) return;
28
- const relativeEntry = path.relative(appDirectory, sourceEntry);
29
- if ('..' === relativeEntry || relativeEntry.startsWith(`..${path.sep}`)) throw new Error(`Effect BFF entry must be inside the application directory: ${sourceEntry}`);
30
- const builtEntry = path.resolve(distDirectory, relativeEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js');
26
+ if (void 0 === sourceEntry || 0 === sourceEntry.length) return;
27
+ const relativeEntry = upath.relative(appDirectory, sourceEntry);
28
+ if ('..' === relativeEntry || relativeEntry.startsWith(`..${upath.sep}`)) throw new Error(`Effect BFF entry must be inside the application directory: ${sourceEntry}`);
29
+ const builtEntry = upath.resolve(distDirectory, relativeEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js');
31
30
  return fs.existsSync(builtEntry) ? builtEntry : void 0;
32
31
  }
33
32
  const createBffGenerator = (api)=>{
34
- const compileApi = async ()=>{
35
- const { appDirectory, distDirectory, apiDirectory, bffRuntimeFramework, sharedDirectory, moduleType } = api.getAppContext();
36
- const modernConfig = api.getNormalizedConfig();
37
- const distDir = path.resolve(distDirectory);
38
- const apiDir = apiDirectory || path.resolve(appDirectory, API_DIR);
39
- const sharedDir = sharedDirectory || path.resolve(appDirectory, SHARED_DIR);
40
- const tsconfigPath = resolveServerTsconfig(appDirectory, modernConfig?.server?.tsconfigPath);
41
- const sourceDirs = [];
42
- if (await fs.pathExists(apiDir)) sourceDirs.push(apiDir);
43
- if (await fs.pathExists(sharedDir)) sourceDirs.push(sharedDir);
44
- const { alias, globalVars } = modernConfig.source;
45
- const { alias: resolveAlias } = modernConfig.resolve;
46
- if (sourceDirs.length > 0) {
47
- const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
48
- const serializedGlobalVars = serializeServerGlobalVars(globalVars);
49
- await compile(appDirectory, {
50
- alias: combinedAlias
51
- }, {
52
- sourceDirs,
53
- distDir,
54
- tsconfigPath,
55
- moduleType,
56
- throwErrorInsteadOfExit: true
57
- });
58
- await transformServerGlobalVars(sourceDirs.map((sourceDir)=>path.resolve(distDir, path.relative(appDirectory, sourceDir))), serializedGlobalVars);
59
- if ('effect' === bffRuntimeFramework) {
60
- const sourceEntry = resolveEffectSourceEntry(appDirectory, apiDir, modernConfig.bff?.effect?.entry);
61
- const builtEntry = resolveBuiltEffectEntry(appDirectory, distDir, sourceEntry);
62
- if (!builtEntry) throw new Error(`Effect BFF entry was not emitted into ${distDir}: ${sourceEntry ?? path.resolve(apiDir, 'index')}`);
63
- await bundleEffectEntryForNode({
64
- appDir: appDirectory,
65
- entryPath: builtEntry,
66
- format: 'module' === moduleType ? 'esm' : 'cjs'
33
+ const compileApi = ()=>Promise.resolve().then(()=>{
34
+ const { appDirectory, distDirectory, apiDirectory, bffRuntimeFramework, sharedDirectory, moduleType } = api.getAppContext();
35
+ const modernConfig = api.getNormalizedConfig();
36
+ const distDir = upath.resolve(distDirectory);
37
+ const apiDir = apiDirectory || upath.resolve(appDirectory, API_DIR);
38
+ const sharedDir = sharedDirectory || upath.resolve(appDirectory, SHARED_DIR);
39
+ const tsconfigPath = resolveServerTsconfig(appDirectory, modernConfig?.server?.tsconfigPath);
40
+ const sourceDirs = [];
41
+ return fs.pathExists(apiDir).then((apiDirectoryExists)=>{
42
+ if (apiDirectoryExists) sourceDirs.push(apiDir);
43
+ return fs.pathExists(sharedDir);
44
+ }).then((sharedDirectoryExists)=>{
45
+ if (sharedDirectoryExists) sourceDirs.push(sharedDir);
46
+ if (0 === sourceDirs.length) return;
47
+ const { alias, globalVars } = modernConfig.source;
48
+ const { alias: resolveAlias } = modernConfig.resolve;
49
+ const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
50
+ const serializedGlobalVars = serializeServerGlobalVars(globalVars);
51
+ return compile(appDirectory, {
52
+ alias: combinedAlias
53
+ }, {
54
+ sourceDirs,
55
+ distDir,
56
+ tsconfigPath,
57
+ moduleType,
58
+ throwErrorInsteadOfExit: true
59
+ }).then(()=>transformServerGlobalVars(sourceDirs.map((sourceDir)=>upath.resolve(distDir, upath.relative(appDirectory, sourceDir))), serializedGlobalVars)).then(()=>{
60
+ if ('effect' !== bffRuntimeFramework) return;
61
+ const sourceEntry = resolveEffectSourceEntry(appDirectory, apiDir, modernConfig.bff?.effect?.entry);
62
+ const builtEntry = resolveBuiltEffectEntry(appDirectory, distDir, sourceEntry);
63
+ if (void 0 === builtEntry || 0 === builtEntry.length) throw new Error(`Effect BFF entry was not emitted into ${distDir}: ${sourceEntry ?? upath.resolve(apiDir, 'index')}`);
64
+ return bundleEffectEntryForNode({
65
+ appDir: appDirectory,
66
+ entryPath: builtEntry,
67
+ format: 'module' === moduleType ? 'esm' : 'cjs'
68
+ });
67
69
  });
68
- }
69
- }
70
- };
71
- const generate = async ()=>{
72
- const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework } = api.getAppContext();
73
- const modernConfig = api.getNormalizedConfig();
74
- const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
75
- const { bff } = modernConfig || {};
76
- const prefix = getPrimaryPrefix(bff?.prefix);
77
- const httpMethodDecider = bff?.httpMethodDecider;
78
- const apiRouter = new ApiRouter({
79
- apiDir: apiDirectory,
80
- appDir: appDirectory,
81
- lambdaDir: lambdaDirectory,
82
- prefix,
83
- httpMethodDecider,
84
- isBuild: true
85
- });
86
- const lambdaDir = apiRouter.getLambdaDir();
87
- const existLambda = apiRouter.isExistLambda();
88
- const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
89
- const relativeApiPath = path.relative(appDirectory, apiDirectory);
90
- const relativeLambdaPath = path.relative(appDirectory, lambdaDir);
91
- await pluginGenerator({
92
- prefix,
93
- appDirectory,
94
- relativeDistPath,
95
- relativeApiPath,
96
- relativeLambdaPath,
97
- runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect'
70
+ });
98
71
  });
99
- await clientGenerator({
100
- prefix,
101
- appDir: appDirectory,
102
- apiDir: apiDirectory,
103
- lambdaDir,
104
- existLambda,
105
- port,
106
- requestCreator: bff?.requestCreator,
107
- httpMethodDecider,
108
- relativeDistPath,
109
- relativeApiPath,
110
- bffRuntimeFramework,
111
- effectEntry: bff?.effect?.entry,
112
- effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
72
+ const generate = ()=>Promise.resolve().then(()=>{
73
+ const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework, packageName } = api.getAppContext();
74
+ const modernConfig = api.getNormalizedConfig();
75
+ const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
76
+ const { bff } = modernConfig || {};
77
+ const prefix = getPrimaryPrefix(bff?.prefix);
78
+ const httpMethodDecider = bff?.httpMethodDecider;
79
+ const apiRouter = new ApiRouter({
80
+ apiDir: apiDirectory,
81
+ appDir: appDirectory,
82
+ lambdaDir: lambdaDirectory,
83
+ prefix,
84
+ httpMethodDecider,
85
+ isBuild: true
86
+ });
87
+ const lambdaDir = apiRouter.getLambdaDir();
88
+ const existLambda = apiRouter.isExistLambda();
89
+ const packageJson = fs.readJSONSync(upath.resolve(appDirectory, 'package.json'));
90
+ const requestId = packageJson.name || packageName;
91
+ const operationContractsPromise = existLambda ? apiRouter.getApiHandlers().then((handlers)=>buildOperationContractMap({
92
+ handlers,
93
+ requestId,
94
+ operationVersion: deriveOperationVersion(packageJson.version)
95
+ })) : Promise.resolve({});
96
+ return operationContractsPromise.then((operationContracts)=>{
97
+ const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
98
+ const relativeApiPath = upath.relative(appDirectory, apiDirectory);
99
+ const relativeLambdaPath = upath.relative(appDirectory, lambdaDir);
100
+ const sourceEffectEntry = 'effect' === bffRuntimeFramework ? resolveEffectSourceEntry(appDirectory, apiDirectory, bff?.effect?.entry) : void 0;
101
+ const relativeEffectEntry = void 0 !== sourceEffectEntry && sourceEffectEntry.length > 0 ? upath.relative(appDirectory, sourceEffectEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js') : '';
102
+ return pluginGenerator({
103
+ prefix,
104
+ appDirectory,
105
+ relativeDistPath,
106
+ relativeApiPath,
107
+ relativeLambdaPath,
108
+ runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect',
109
+ relativeEffectEntry,
110
+ operationContracts
111
+ }).then(()=>clientGenerator({
112
+ prefix,
113
+ appDir: appDirectory,
114
+ apiDir: apiDirectory,
115
+ lambdaDir,
116
+ existLambda,
117
+ port,
118
+ requestCreator: bff?.requestCreator,
119
+ httpMethodDecider,
120
+ relativeDistPath,
121
+ relativeApiPath,
122
+ bffRuntimeFramework,
123
+ effectEntry: bff?.effect?.entry,
124
+ effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
125
+ })).then(()=>runtimeGenerator({
126
+ runtime,
127
+ appDirectory,
128
+ relativeDistPath
129
+ }));
130
+ });
113
131
  });
114
- await runtimeGenerator({
115
- runtime,
116
- appDirectory,
117
- relativeDistPath
132
+ const handleCrossProjectInvocation = (isBuild = false)=>Promise.resolve().then(()=>{
133
+ const { bff } = api.getNormalizedConfig();
134
+ if (bff?.crossProject !== true) return;
135
+ return isBuild ? generate() : compileApi().then(()=>generate());
118
136
  });
119
- };
120
- const handleCrossProjectInvocation = async (isBuild = false)=>{
121
- const { bff } = api.getNormalizedConfig();
122
- if (bff?.crossProject) {
123
- if (!isBuild) await compileApi();
124
- await generate();
125
- }
126
- };
127
137
  return {
128
138
  compileApi,
129
139
  generate,
@@ -1,24 +1,19 @@
1
- import { applyOptionsChain, fs } from "@modern-js/utils";
1
+ import { applyOptionsChain, fs, upath } from "@modern-js/utils";
2
2
  import { transform } from "@swc/core";
3
- import path from "path";
4
3
  const RELEASE_IDENTITY_GLOBAL_NAMES = [
5
4
  'ULTRAMODERN_BUILD_MARKER',
6
5
  'ULTRAMODERN_SOURCE_REVISION',
7
6
  'ULTRAMODERN_RELEASE_VERSION'
8
7
  ];
9
- const collectJavaScriptFiles = async (directory)=>{
10
- const entries = await fs.readdir(directory, {
11
- withFileTypes: true
12
- });
13
- const nested = await Promise.all(entries.map(async (entry)=>{
14
- const absolutePath = path.join(directory, entry.name);
15
- if (entry.isDirectory()) return collectJavaScriptFiles(absolutePath);
16
- return /\.(?:c|m)?js$/u.test(entry.name) ? [
17
- absolutePath
18
- ] : [];
19
- }));
20
- return nested.flat();
21
- };
8
+ const collectJavaScriptFiles = (directory)=>Promise.resolve().then(()=>fs.readdir(directory, {
9
+ withFileTypes: true
10
+ })).then((entries)=>Promise.all(entries.map((entry)=>{
11
+ const absolutePath = upath.join(directory, entry.name);
12
+ if (entry.isDirectory()) return collectJavaScriptFiles(absolutePath);
13
+ return /\.(?:c|m)?js$/u.test(entry.name) ? [
14
+ absolutePath
15
+ ] : [];
16
+ })).then((nested)=>nested.flat()));
22
17
  const serializeServerGlobalVars = (options)=>{
23
18
  const globalVars = applyOptionsChain({}, options, {
24
19
  env: 'server',
@@ -40,37 +35,38 @@ const serializeServerGlobalVars = (options)=>{
40
35
  ];
41
36
  }));
42
37
  };
43
- const transformServerGlobalVars = async (outputDirectories, globalVars)=>{
44
- if (0 === Object.keys(globalVars).length) return;
45
- const existingDirectories = (await Promise.all(outputDirectories.map(async (directory)=>await fs.pathExists(directory) ? directory : void 0))).filter((directory)=>Boolean(directory));
46
- const files = (await Promise.all(existingDirectories.map(collectJavaScriptFiles))).flat();
47
- const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`void ${globalVars[name]};`).join('')}\n` : '';
48
- await Promise.all(files.map(async (filename)=>{
49
- const source = await fs.readFile(filename, 'utf8');
50
- const sourceMapFilename = `${filename}.map`;
51
- const sourceMapExists = await fs.pathExists(sourceMapFilename);
52
- const inputSourceMap = sourceMapExists ? await fs.readFile(sourceMapFilename, 'utf8') : void 0;
53
- const result = await transform(`${releaseIdentityBanner}${source}`, {
54
- filename,
55
- inputSourceMap,
56
- sourceMaps: sourceMapExists,
57
- jsc: {
58
- parser: {
59
- syntax: "ecmascript"
60
- },
61
- preserveAllComments: true,
62
- target: 'es2022',
63
- transform: {
64
- optimizer: {
65
- globals: {
66
- vars: globalVars
67
- }
68
- }
69
- }
70
- }
71
- });
72
- await fs.writeFile(filename, result.code);
73
- if (sourceMapExists && result.map) await fs.writeFile(sourceMapFilename, result.map);
74
- }));
75
- };
38
+ const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resolve().then(()=>{
39
+ if (0 === Object.keys(globalVars).length) return;
40
+ return Promise.all(outputDirectories.map((directory)=>fs.pathExists(directory).then((directoryExists)=>directoryExists ? directory : void 0))).then((existingDirectories)=>{
41
+ const filesPromise = Promise.all(existingDirectories.filter((directory)=>void 0 !== directory).map(collectJavaScriptFiles)).then((files)=>files.flat());
42
+ const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`void ${globalVars[name]};`).join('')}\n` : '';
43
+ return filesPromise.then((files)=>Promise.all(files.map((filename)=>fs.readFile(filename, 'utf8').then((source)=>{
44
+ const sourceMapFilename = `${filename}.map`;
45
+ return fs.pathExists(sourceMapFilename).then((sourceMapExists)=>{
46
+ const inputSourceMapPromise = sourceMapExists ? fs.readFile(sourceMapFilename, 'utf8') : Promise.resolve(void 0);
47
+ return inputSourceMapPromise.then((inputSourceMap)=>transform(`${releaseIdentityBanner}${source}`, {
48
+ filename,
49
+ inputSourceMap,
50
+ sourceMaps: sourceMapExists,
51
+ jsc: {
52
+ parser: {
53
+ syntax: "ecmascript"
54
+ },
55
+ preserveAllComments: true,
56
+ target: 'es2022',
57
+ transform: {
58
+ optimizer: {
59
+ globals: {
60
+ vars: globalVars
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }).then((result)=>fs.writeFile(filename, result.code).then(()=>{
66
+ if (sourceMapExists && void 0 !== result.map && result.map.length > 0) return fs.writeFile(sourceMapFilename, result.map);
67
+ })));
68
+ });
69
+ }))));
70
+ }).then(()=>void 0);
71
+ });
76
72
  export { serializeServerGlobalVars, transformServerGlobalVars };
package/dist/esm/cli.mjs CHANGED
@@ -128,4 +128,6 @@ const bffPlugin = ()=>({
128
128
  });
129
129
  }
130
130
  });
131
+ const cli = bffPlugin;
132
+ export default cli;
131
133
  export { bffPlugin };
@@ -4,6 +4,7 @@ import path from "path";
4
4
  import { generateEffectClientCode, resolveEffectEntryFile } from "./utils/effectClientGenerator.mjs";
5
5
  const EFFECT_BFF_WORKER_RUNTIME_QUERY = 'modern-bff-runtime';
6
6
  const EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY = 'modern-bff-runtime-source';
7
+ const createErrorModule = (message)=>`throw new Error(${JSON.stringify(message)});`;
7
8
  async function transformEffectRuntimeSource(source, filename) {
8
9
  const swc = await import("@swc/core");
9
10
  const result = await swc.transform(source, {
@@ -25,7 +26,7 @@ async function transformEffectRuntimeSource(source, filename) {
25
26
  function createEffectWorkerRuntimeWrapper(resourcePath) {
26
27
  const sourceRequest = `${resourcePath}?${EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY}`;
27
28
  return `import * as effectBffModule from ${JSON.stringify(sourceRequest)};
28
- import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge';
29
+ import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge/dispatcher';
29
30
 
30
31
  export const __modern_create_effect_bff_dispatcher = options =>
31
32
  createEffectBffEdgeDispatcher({
@@ -66,13 +67,13 @@ async function loader(source) {
66
67
  onDependency: (dependency)=>this.addDependency(dependency)
67
68
  });
68
69
  if (code) return void callback(void 0, code);
69
- callback(void 0, `throw new Error('Failed to generate Effect client for ${resourcePath}')`);
70
+ callback(void 0, createErrorModule(`Failed to generate Effect client for ${resourcePath}`));
70
71
  return;
71
72
  }
72
73
  const warning = `The file ${resourcePath} is not allowed to be imported in src directory, only API definition files are allowed.`;
73
74
  if (!draftOptions.existLambda) {
74
75
  logger.warn(warning);
75
- callback(null, `throw new Error('${warning}')`);
76
+ callback(null, createErrorModule(warning));
76
77
  return;
77
78
  }
78
79
  const options = {
@@ -89,14 +90,14 @@ async function loader(source) {
89
90
  const { lambdaDir } = draftOptions;
90
91
  if (!resourcePath.startsWith(lambdaDir)) {
91
92
  logger.warn(warning);
92
- callback(null, `throw new Error('${warning}')`);
93
+ callback(null, createErrorModule(warning));
93
94
  return;
94
95
  }
95
96
  if (draftOptions.fetcher) options.fetcher = draftOptions.fetcher;
96
97
  if (draftOptions.requestCreator) options.requestCreator = draftOptions.requestCreator;
97
98
  options.requireResolve = require.resolve;
98
99
  const result = await generateClient(options);
99
- callback(void 0, result.isOk ? result.value : `throw new Error('${result.value}')`);
100
+ callback(void 0, result.isOk ? result.value : createErrorModule(result.value));
100
101
  }
101
102
  const src_loader = loader;
102
103
  export default src_loader;
@@ -2,22 +2,27 @@ import { logger } from "@modern-js/utils";
2
2
  import { HttpApi } from "effect/unstable/httpapi";
3
3
  import { resolveAdapterCrossProjectPolicy } from "../../../utils/crossProjectServerPolicy.mjs";
4
4
  import { collectEffectEndpoints, extractHttpApiFromModule, toOperationContractSources } from "../endpoint-contracts.mjs";
5
- async function resolveEffectAdapterCrossProjectPolicy(api, prefix, mod) {
6
- let contractSources = [];
7
- if (mod) try {
8
- const effectApi = await extractHttpApiFromModule(mod, HttpApi.isHttpApi);
9
- if (effectApi) {
10
- const reflect = (apiValue, handlers)=>HttpApi.reflect(apiValue, {
5
+ function resolveEffectAdapterCrossProjectPolicy(api, prefix, mod) {
6
+ const contractSourcesPromise = null === mod ? Promise.resolve([]) : Promise.resolve().then(()=>extractHttpApiFromModule(mod, HttpApi.isHttpApi)).then((effectApi)=>{
7
+ if (null !== effectApi) {
8
+ const reflect = (apiValue, handlers)=>{
9
+ if (!HttpApi.isHttpApi(apiValue)) throw new Error('[BFF][Effect] Endpoint reflection received a non-HttpApi value.');
10
+ HttpApi.reflect(apiValue, {
11
11
  onGroup: handlers.onGroup ?? (()=>{}),
12
12
  onEndpoint: handlers.onEndpoint
13
13
  });
14
- contractSources = toOperationContractSources(collectEffectEndpoints(reflect, effectApi, prefix));
14
+ };
15
+ return toOperationContractSources(collectEffectEndpoints(reflect, effectApi, prefix));
15
16
  }
16
- } catch (error) {
17
+ return [];
18
+ }).catch((error)=>{
17
19
  logger.warn(`[BFF][Effect] Failed reflect HttpApi endpoints cross-project policy: ${String(error)}`);
18
- }
19
- const policy = resolveAdapterCrossProjectPolicy(api, contractSources);
20
- if (policy?.enabled && 0 === contractSources.length) logger.warn('[BFF][Effect] Cross-project policy enabled but no HttpApi endpoints could be reflected; requests fail operation-contract matching unless allowUnknownOperations is enabled.');
21
- return policy;
20
+ return [];
21
+ });
22
+ return contractSourcesPromise.then((contractSources)=>{
23
+ const policy = resolveAdapterCrossProjectPolicy(api, contractSources);
24
+ if (policy?.enabled === true && 0 === contractSources.length) logger.warn('[BFF][Effect] Cross-project policy enabled but no HttpApi endpoints could be reflected; requests fail operation-contract matching unless allowUnknownOperations is enabled.');
25
+ return policy;
26
+ });
22
27
  }
23
28
  export { resolveEffectAdapterCrossProjectPolicy };