@modern-js/plugin-bff 2.69.5 → 3.0.0-alpha.0

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 (76) hide show
  1. package/dist/cjs/cli.js +256 -272
  2. package/dist/cjs/constants.js +43 -34
  3. package/dist/cjs/index.js +55 -19
  4. package/dist/cjs/loader.js +69 -65
  5. package/dist/cjs/runtime/create-request/index.js +39 -29
  6. package/dist/cjs/runtime/hono/adapter.js +128 -126
  7. package/dist/cjs/runtime/hono/index.js +78 -30
  8. package/dist/cjs/runtime/hono/operators.js +64 -67
  9. package/dist/cjs/server.js +159 -165
  10. package/dist/cjs/utils/clientGenerator.js +204 -206
  11. package/dist/cjs/utils/createHonoRoutes.js +128 -144
  12. package/dist/cjs/utils/crossProjectApiPlugin.js +91 -81
  13. package/dist/cjs/utils/pluginGenerator.js +66 -54
  14. package/dist/cjs/utils/runtimeGenerator.js +67 -45
  15. package/dist/esm/cli.mjs +214 -0
  16. package/dist/esm/constants.mjs +11 -0
  17. package/dist/esm/loader.mjs +39 -0
  18. package/dist/esm/runtime/create-request/{index.js → index.mjs} +1 -5
  19. package/dist/esm/runtime/hono/adapter.mjs +95 -0
  20. package/dist/{esm-node/runtime/hono/index.js → esm/runtime/hono/index.mjs} +2 -4
  21. package/dist/esm/runtime/hono/operators.mjs +31 -0
  22. package/dist/esm/server.mjs +122 -0
  23. package/dist/esm/utils/clientGenerator.mjs +175 -0
  24. package/dist/esm/utils/createHonoRoutes.mjs +91 -0
  25. package/dist/esm/utils/crossProjectApiPlugin.mjs +34 -0
  26. package/dist/esm/utils/pluginGenerator.mjs +29 -0
  27. package/dist/esm/utils/runtimeGenerator.mjs +43 -0
  28. package/dist/esm-node/cli.mjs +214 -0
  29. package/dist/esm-node/constants.mjs +11 -0
  30. package/dist/esm-node/index.mjs +1 -0
  31. package/dist/esm-node/loader.mjs +39 -0
  32. package/dist/esm-node/runtime/create-request/{index.js → index.mjs} +1 -5
  33. package/dist/esm-node/runtime/hono/adapter.mjs +95 -0
  34. package/dist/{esm/runtime/hono/index.js → esm-node/runtime/hono/index.mjs} +3 -5
  35. package/dist/esm-node/runtime/hono/operators.mjs +31 -0
  36. package/dist/esm-node/server.mjs +122 -0
  37. package/dist/esm-node/utils/clientGenerator.mjs +175 -0
  38. package/dist/esm-node/utils/createHonoRoutes.mjs +91 -0
  39. package/dist/esm-node/utils/crossProjectApiPlugin.mjs +34 -0
  40. package/dist/esm-node/utils/pluginGenerator.mjs +29 -0
  41. package/dist/esm-node/utils/runtimeGenerator.mjs +43 -0
  42. package/dist/types/loader.d.ts +2 -2
  43. package/dist/types/runtime/hono/adapter.d.ts +3 -3
  44. package/dist/types/server.d.ts +2 -2
  45. package/dist/types/utils/runtimeGenerator.d.ts +2 -1
  46. package/package.json +53 -31
  47. package/rslib.config.mts +4 -0
  48. package/dist/cjs/helper.js +0 -48
  49. package/dist/esm/cli.js +0 -425
  50. package/dist/esm/constants.js +0 -14
  51. package/dist/esm/helper.js +0 -13
  52. package/dist/esm/index.js +0 -1
  53. package/dist/esm/loader.js +0 -75
  54. package/dist/esm/runtime/hono/adapter.js +0 -243
  55. package/dist/esm/runtime/hono/operators.js +0 -79
  56. package/dist/esm/server.js +0 -258
  57. package/dist/esm/utils/clientGenerator.js +0 -517
  58. package/dist/esm/utils/createHonoRoutes.js +0 -319
  59. package/dist/esm/utils/crossProjectApiPlugin.js +0 -49
  60. package/dist/esm/utils/pluginGenerator.js +0 -94
  61. package/dist/esm/utils/runtimeGenerator.js +0 -55
  62. package/dist/esm-node/cli.js +0 -246
  63. package/dist/esm-node/constants.js +0 -14
  64. package/dist/esm-node/helper.js +0 -14
  65. package/dist/esm-node/loader.js +0 -49
  66. package/dist/esm-node/runtime/hono/adapter.js +0 -103
  67. package/dist/esm-node/runtime/hono/operators.js +0 -46
  68. package/dist/esm-node/server.js +0 -142
  69. package/dist/esm-node/utils/clientGenerator.js +0 -192
  70. package/dist/esm-node/utils/createHonoRoutes.js +0 -120
  71. package/dist/esm-node/utils/crossProjectApiPlugin.js +0 -47
  72. package/dist/esm-node/utils/pluginGenerator.js +0 -31
  73. package/dist/esm-node/utils/runtimeGenerator.js +0 -35
  74. package/dist/types/helper.d.ts +0 -2
  75. package/types.d.ts +0 -3
  76. /package/dist/{esm-node/index.js → esm/index.mjs} +0 -0
@@ -1,53 +1,68 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var runtimeGenerator_exports = {};
30
- __export(runtimeGenerator_exports, {
31
- default: () => runtimeGenerator_default
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
12
+ (()=>{
13
+ __webpack_require__.d = (exports1, definition)=>{
14
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
15
+ enumerable: true,
16
+ get: definition[key]
17
+ });
18
+ };
19
+ })();
20
+ (()=>{
21
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
22
+ })();
23
+ (()=>{
24
+ __webpack_require__.r = (exports1)=>{
25
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
26
+ value: 'Module'
27
+ });
28
+ Object.defineProperty(exports1, '__esModule', {
29
+ value: true
30
+ });
31
+ };
32
+ })();
33
+ var __webpack_exports__ = {};
34
+ __webpack_require__.r(__webpack_exports__);
35
+ __webpack_require__.d(__webpack_exports__, {
36
+ default: ()=>utils_runtimeGenerator
32
37
  });
33
- module.exports = __toCommonJS(runtimeGenerator_exports);
34
- var import_path = __toESM(require("path"));
35
- var import_utils = require("@modern-js/utils");
36
- async function runtimeGenerator({ runtime, appDirectory, relativeDistPath }) {
37
- const pluginDir = import_path.default.resolve(appDirectory, `./${relativeDistPath}`, "runtime");
38
- const source = `import { configure as _configure } from '${runtime}'
38
+ const external_path_namespaceObject = require("path");
39
+ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
40
+ const utils_namespaceObject = require("@modern-js/utils");
41
+ const getPackageName = (appDirectory)=>{
42
+ try {
43
+ const packageJsonPath = external_path_default().resolve(appDirectory, './package.json');
44
+ const packageJson = require(packageJsonPath);
45
+ return packageJson.name;
46
+ } catch (error) {
47
+ return;
48
+ }
49
+ };
50
+ async function runtimeGenerator({ runtime, appDirectory, relativeDistPath, packageName }) {
51
+ const pluginDir = external_path_default().resolve(appDirectory, `./${relativeDistPath}`, 'runtime');
52
+ const requestId = packageName || getPackageName(appDirectory) || process.env.npm_package_name || 'default';
53
+ const source = `import { configure as _configure } from '${runtime}'
39
54
  const configure = (options) => {
40
55
  return _configure({
41
56
  ...options,
42
- requestId: '${process.env.npm_package_name}',
57
+ requestId: '${requestId}',
43
58
  });
44
59
  }
45
60
  export { configure }
46
61
  `;
47
- const pluginPath = import_path.default.join(pluginDir, "index.js");
48
- await import_utils.fs.ensureFile(pluginPath);
49
- await import_utils.fs.writeFile(pluginPath, source);
50
- const tsSource = `type IOptions<F = typeof fetch> = {
62
+ const pluginPath = external_path_default().join(pluginDir, 'index.js');
63
+ await utils_namespaceObject.fs.ensureFile(pluginPath);
64
+ await utils_namespaceObject.fs.writeFile(pluginPath, source);
65
+ const tsSource = `type IOptions<F = typeof fetch> = {
51
66
  request?: F;
52
67
  interceptor?: (request: F) => F;
53
68
  allowedHeaders?: string[];
@@ -58,8 +73,15 @@ async function runtimeGenerator({ runtime, appDirectory, relativeDistPath }) {
58
73
  requestId?: string;
59
74
  };
60
75
  export declare const configure: (options: IOptions) => void;`;
61
- const pluginTypePath = import_path.default.join(pluginDir, "index.d.ts");
62
- await import_utils.fs.ensureFile(pluginTypePath);
63
- await import_utils.fs.writeFile(pluginTypePath, tsSource);
76
+ const pluginTypePath = external_path_default().join(pluginDir, 'index.d.ts');
77
+ await utils_namespaceObject.fs.ensureFile(pluginTypePath);
78
+ await utils_namespaceObject.fs.writeFile(pluginTypePath, tsSource);
64
79
  }
65
- var runtimeGenerator_default = runtimeGenerator;
80
+ const utils_runtimeGenerator = runtimeGenerator;
81
+ exports["default"] = __webpack_exports__["default"];
82
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
83
+ "default"
84
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
85
+ Object.defineProperty(exports, '__esModule', {
86
+ value: true
87
+ });
@@ -0,0 +1,214 @@
1
+ import path from "path";
2
+ import { ApiRouter } from "@modern-js/bff-core";
3
+ import { compile } from "@modern-js/server-utils";
4
+ import { API_DIR, DEFAULT_API_PREFIX, SHARED_DIR, fs, normalizeOutputPath } from "@modern-js/utils";
5
+ import clientGenerator from "./utils/clientGenerator";
6
+ import pluginGenerator from "./utils/pluginGenerator";
7
+ import runtimeGenerator from "./utils/runtimeGenerator";
8
+ const TS_CONFIG_FILENAME = 'tsconfig.json';
9
+ const RUNTIME_CREATE_REQUEST = '@modern-js/plugin-bff/client';
10
+ const RUNTIME_HONO = '@modern-js/plugin-bff/server';
11
+ const bffPlugin = ()=>({
12
+ name: '@modern-js/plugin-bff',
13
+ setup: (api)=>{
14
+ const compileApi = async ()=>{
15
+ const { appDirectory, distDirectory, apiDirectory, sharedDirectory, moduleType } = api.getAppContext();
16
+ const modernConfig = api.getNormalizedConfig();
17
+ const distDir = path.resolve(distDirectory);
18
+ const apiDir = apiDirectory || path.resolve(appDirectory, API_DIR);
19
+ const sharedDir = sharedDirectory || path.resolve(appDirectory, SHARED_DIR);
20
+ const tsconfigPath = path.resolve(appDirectory, TS_CONFIG_FILENAME);
21
+ const sourceDirs = [];
22
+ if (await fs.pathExists(apiDir)) sourceDirs.push(apiDir);
23
+ if (await fs.pathExists(sharedDir)) sourceDirs.push(sharedDir);
24
+ const { server } = modernConfig;
25
+ const { alias } = modernConfig.source;
26
+ const { alias: resolveAlias } = modernConfig.resolve;
27
+ const { babel } = modernConfig.tools;
28
+ if (sourceDirs.length > 0) {
29
+ const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
30
+ await compile(appDirectory, {
31
+ server,
32
+ alias: combinedAlias,
33
+ babelConfig: babel
34
+ }, {
35
+ sourceDirs,
36
+ distDir,
37
+ tsconfigPath,
38
+ moduleType,
39
+ throwErrorInsteadOfExit: true
40
+ });
41
+ }
42
+ };
43
+ const generator = async ()=>{
44
+ const { appDirectory, apiDirectory, lambdaDirectory, port } = api.getAppContext();
45
+ const modernConfig = api.getNormalizedConfig();
46
+ const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
47
+ const { bff } = modernConfig || {};
48
+ const prefix = bff?.prefix || DEFAULT_API_PREFIX;
49
+ const httpMethodDecider = bff?.httpMethodDecider;
50
+ const apiRouter = new ApiRouter({
51
+ apiDir: apiDirectory,
52
+ appDir: appDirectory,
53
+ lambdaDir: lambdaDirectory,
54
+ prefix,
55
+ httpMethodDecider,
56
+ isBuild: true
57
+ });
58
+ const lambdaDir = apiRouter.getLambdaDir();
59
+ const existLambda = apiRouter.isExistLambda();
60
+ const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
61
+ const relativeApiPath = path.relative(appDirectory, apiDirectory);
62
+ const relativeLambdaPath = path.relative(appDirectory, lambdaDir);
63
+ await pluginGenerator({
64
+ prefix,
65
+ appDirectory,
66
+ relativeDistPath,
67
+ relativeApiPath,
68
+ relativeLambdaPath
69
+ });
70
+ await clientGenerator({
71
+ prefix,
72
+ appDir: appDirectory,
73
+ apiDir: apiDirectory,
74
+ lambdaDir,
75
+ existLambda,
76
+ port,
77
+ requestCreator: bff?.requestCreator,
78
+ httpMethodDecider,
79
+ relativeDistPath,
80
+ relativeApiPath
81
+ });
82
+ await runtimeGenerator({
83
+ runtime,
84
+ appDirectory,
85
+ relativeDistPath
86
+ });
87
+ };
88
+ const handleCrossProjectInvocation = async (isBuild = false)=>{
89
+ const { bff } = api.getNormalizedConfig();
90
+ if (bff?.crossProject) {
91
+ if (!isBuild) await compileApi();
92
+ await generator();
93
+ }
94
+ };
95
+ const isHono = ()=>{
96
+ const { bffRuntimeFramework } = api.getAppContext();
97
+ return 'hono' === bffRuntimeFramework;
98
+ };
99
+ const createCompressConfig = (devServer, prefix)=>{
100
+ if (!devServer || 'object' != typeof devServer || Array.isArray(devServer)) return;
101
+ const { compress } = devServer;
102
+ if (void 0 === compress || true === compress) return {
103
+ filter: (req)=>!req.url?.includes(prefix)
104
+ };
105
+ if (false === compress) return false;
106
+ return compress;
107
+ };
108
+ api.config(async ()=>{
109
+ const honoRuntimePath = isHono() ? {
110
+ [RUNTIME_HONO]: RUNTIME_HONO
111
+ } : void 0;
112
+ const devServer = api.getConfig()?.tools?.devServer;
113
+ const prefix = api.getConfig()?.bff?.prefix || DEFAULT_API_PREFIX;
114
+ const compress = createCompressConfig(devServer, prefix);
115
+ return {
116
+ tools: {
117
+ devServer: {
118
+ compress
119
+ },
120
+ bundlerChain: (chain, { CHAIN_ID, isServer })=>{
121
+ const { port, appDirectory, apiDirectory, lambdaDirectory } = api.getAppContext();
122
+ const modernConfig = api.getNormalizedConfig();
123
+ const { bff } = modernConfig || {};
124
+ const prefix = bff?.prefix || DEFAULT_API_PREFIX;
125
+ const httpMethodDecider = bff?.httpMethodDecider;
126
+ const apiRouter = new ApiRouter({
127
+ apiDir: apiDirectory,
128
+ appDir: appDirectory,
129
+ lambdaDir: lambdaDirectory,
130
+ prefix,
131
+ httpMethodDecider,
132
+ isBuild: true
133
+ });
134
+ const lambdaDir = apiRouter.getLambdaDir();
135
+ const existLambda = apiRouter.isExistLambda();
136
+ const apiRegexp = new RegExp(normalizeOutputPath(`${apiDirectory}${path.sep}.*(.[tj]s)$`));
137
+ const name = isServer ? 'server' : 'client';
138
+ const loaderPath = require.resolve('./loader');
139
+ chain.module.rule(CHAIN_ID.RULE.JS).exclude.add(apiRegexp);
140
+ chain.module.rule('js-bff-api').test(apiRegexp).use('custom-loader').loader(loaderPath.replace(/\\/g, '/')).options({
141
+ prefix,
142
+ appDir: appDirectory,
143
+ apiDir: apiDirectory,
144
+ lambdaDir,
145
+ existLambda,
146
+ port,
147
+ target: name,
148
+ requestCreator: bff?.requestCreator,
149
+ httpMethodDecider
150
+ });
151
+ }
152
+ },
153
+ output: {
154
+ externals: honoRuntimePath
155
+ }
156
+ };
157
+ });
158
+ api.modifyServerRoutes(({ routes })=>{
159
+ const modernConfig = api.getNormalizedConfig();
160
+ const { bff } = modernConfig || {};
161
+ const prefix = bff?.prefix || '/api';
162
+ const prefixList = [];
163
+ if (Array.isArray(prefix)) prefixList.push(...prefix);
164
+ else prefixList.push(prefix);
165
+ const apiServerRoutes = prefixList.map((pre)=>({
166
+ urlPath: pre,
167
+ isApi: true,
168
+ entryPath: '',
169
+ isSPA: false,
170
+ isSSR: false
171
+ }));
172
+ if (!isHono() && bff?.enableHandleWeb) return {
173
+ routes: routes.map((route)=>({
174
+ ...route,
175
+ isApi: true
176
+ })).concat(apiServerRoutes)
177
+ };
178
+ return {
179
+ routes: routes.concat(apiServerRoutes)
180
+ };
181
+ });
182
+ api._internalServerPlugins(({ plugins })=>{
183
+ plugins.push({
184
+ name: '@modern-js/plugin-bff/server-plugin'
185
+ });
186
+ return {
187
+ plugins
188
+ };
189
+ });
190
+ api.onBeforeDev(async ()=>{
191
+ await handleCrossProjectInvocation();
192
+ });
193
+ api.onAfterBuild(async ()=>{
194
+ await compileApi();
195
+ await handleCrossProjectInvocation(true);
196
+ });
197
+ api.addWatchFiles(async ()=>{
198
+ const appContext = api.getAppContext();
199
+ const config = api.getNormalizedConfig();
200
+ if (config?.bff?.crossProject) return [
201
+ appContext.apiDirectory
202
+ ];
203
+ return [];
204
+ });
205
+ api.onFileChanged(async (e)=>{
206
+ const { filename, eventType, isPrivate } = e;
207
+ const { appDirectory, apiDirectory } = api.getAppContext();
208
+ const relativeApiPath = path.relative(appDirectory, apiDirectory);
209
+ if (!isPrivate && ('change' === eventType || 'unlink' === eventType) && filename.startsWith(`${relativeApiPath}/`) && (filename.endsWith('.ts') || filename.endsWith('.js'))) await handleCrossProjectInvocation();
210
+ });
211
+ }
212
+ });
213
+ const cli = bffPlugin;
214
+ export { bffPlugin, cli as default };
@@ -0,0 +1,11 @@
1
+ const API_APP_NAME = '_app';
2
+ const BUILD_FILES = [
3
+ '**/*.[tj]sx?',
4
+ '!**/*.test.jsx?',
5
+ '!**/*.test.tsx?',
6
+ '!**/*.spec.jsx?',
7
+ '!**/*.spec.tsx?',
8
+ '!__tests__/*.tsx?',
9
+ '!__tests__/*.jsx?'
10
+ ];
11
+ export { API_APP_NAME, BUILD_FILES };
@@ -0,0 +1,39 @@
1
+ import { generateClient } from "@modern-js/bff-core";
2
+ import { logger } from "@modern-js/utils";
3
+ async function loader(source) {
4
+ this.cacheable();
5
+ const { resourcePath } = this;
6
+ delete require.cache[resourcePath];
7
+ const callback = this.async();
8
+ const draftOptions = this.getOptions();
9
+ const warning = `The file ${resourcePath} is not allowd to be imported in src directory, only API definition files are allowed.`;
10
+ if (!draftOptions.existLambda) {
11
+ logger.warn(warning);
12
+ callback(null, `throw new Error('${warning}')`);
13
+ return;
14
+ }
15
+ const options = {
16
+ prefix: Array.isArray(draftOptions.prefix) ? draftOptions.prefix[0] : draftOptions.prefix,
17
+ appDir: draftOptions.appDir,
18
+ apiDir: draftOptions.apiDir,
19
+ lambdaDir: draftOptions.lambdaDir,
20
+ target: draftOptions.target,
21
+ port: Number(draftOptions.port),
22
+ source,
23
+ resourcePath,
24
+ httpMethodDecider: draftOptions.httpMethodDecider
25
+ };
26
+ const { lambdaDir } = draftOptions;
27
+ if (!resourcePath.startsWith(lambdaDir)) {
28
+ logger.warn(warning);
29
+ callback(null, `throw new Error('${warning}')`);
30
+ return;
31
+ }
32
+ if (draftOptions.fetcher) options.fetcher = draftOptions.fetcher;
33
+ if (draftOptions.requestCreator) options.requestCreator = draftOptions.requestCreator;
34
+ options.requireResolve = require.resolve;
35
+ const result = await generateClient(options);
36
+ callback(void 0, result.isOk ? result.value : `throw new Error('${result.value}')`);
37
+ }
38
+ const src_loader = loader;
39
+ export { src_loader as default };
@@ -1,6 +1,2 @@
1
1
  import { configure, createRequest, createUploader } from "@modern-js/create-request";
2
- export {
3
- configure,
4
- createRequest,
5
- createUploader
6
- };
2
+ export { configure, createRequest, createUploader };
@@ -0,0 +1,95 @@
1
+ import { Hono } from "@modern-js/server-core";
2
+ import { isProd, logger } from "@modern-js/utils";
3
+ import createHonoRoutes from "../../utils/createHonoRoutes";
4
+ const before = [
5
+ 'custom-server-hook',
6
+ 'custom-server-middleware',
7
+ 'render'
8
+ ];
9
+ class HonoAdapter {
10
+ wrapInArray(handler) {
11
+ if (Array.isArray(handler)) return handler;
12
+ return [
13
+ handler
14
+ ];
15
+ }
16
+ constructor(api){
17
+ this.apiMiddleware = [];
18
+ this.apiServer = null;
19
+ this.isHono = true;
20
+ this.setHandlers = async ()=>{
21
+ if (!this.isHono) return;
22
+ const { apiHandlerInfos } = this.api.getServerContext();
23
+ const honoHandlers = createHonoRoutes(apiHandlerInfos);
24
+ this.apiMiddleware = honoHandlers.map(({ path, method, handler })=>({
25
+ name: 'hono-bff-api',
26
+ path,
27
+ method,
28
+ handler,
29
+ order: 'post',
30
+ before
31
+ }));
32
+ };
33
+ this.registerApiRoutes = async ()=>{
34
+ if (!this.isHono) return;
35
+ this.apiServer = new Hono();
36
+ this.apiMiddleware.forEach(({ path = '*', method = 'all', handler })=>{
37
+ const handlers = this.wrapInArray(handler);
38
+ if (0 === handlers.length) return;
39
+ const firstHandler = handlers[0];
40
+ const restHandlers = handlers.slice(1);
41
+ const m = method;
42
+ const server = this.apiServer;
43
+ if (!server) return;
44
+ const register = server[m];
45
+ register.call(server, path, firstHandler, ...restHandlers);
46
+ });
47
+ this.apiServer.onError(async (err, c)=>{
48
+ try {
49
+ const serverConfig = this.api.getServerConfig();
50
+ const onErrorHandler = serverConfig?.onError;
51
+ if (onErrorHandler) {
52
+ const result = await onErrorHandler(err, c);
53
+ if (result instanceof Response) return result;
54
+ } else logger.error(err);
55
+ } catch (configError) {
56
+ logger.error(`Error in serverConfig.onError handler: ${configError}`);
57
+ }
58
+ return c.json({
59
+ message: err?.message || '[BFF] Internal Server Error'
60
+ }, err?.status || 500);
61
+ });
62
+ };
63
+ this.registerMiddleware = async (options)=>{
64
+ const { prefix } = options;
65
+ const { bffRuntimeFramework } = this.api.getServerContext();
66
+ if ('hono' !== bffRuntimeFramework) {
67
+ this.isHono = false;
68
+ return;
69
+ }
70
+ const { middlewares: globalMiddlewares } = this.api.getServerContext();
71
+ await this.setHandlers();
72
+ if (isProd()) globalMiddlewares.push(...this.apiMiddleware);
73
+ else {
74
+ await this.registerApiRoutes();
75
+ const dynamicApiMiddleware = {
76
+ name: 'dynamic-bff-handler',
77
+ path: `${prefix}/*`,
78
+ method: 'all',
79
+ order: 'post',
80
+ before,
81
+ handler: async (c, next)=>{
82
+ if (this.apiServer) {
83
+ const response = await this.apiServer.fetch(c.req.raw, c.env);
84
+ if (404 !== response.status) return new Response(response.body, response);
85
+ }
86
+ await next();
87
+ }
88
+ };
89
+ globalMiddlewares.push(dynamicApiMiddleware);
90
+ }
91
+ };
92
+ this.api = api;
93
+ }
94
+ }
95
+ export { HonoAdapter };
@@ -1,6 +1,4 @@
1
- export * from "@modern-js/bff-core";
2
1
  import { useHonoContext } from "@modern-js/server-core";
2
+ export * from "@modern-js/bff-core";
3
3
  export * from "./operators";
4
- export {
5
- useHonoContext
6
- };
4
+ export { useHonoContext };
@@ -0,0 +1,31 @@
1
+ import { useHonoContext } from "@modern-js/server-core";
2
+ const Pipe = (func)=>({
3
+ name: 'pipe',
4
+ async execute (executeHelper, next) {
5
+ const { inputs } = executeHelper;
6
+ const ctx = useHonoContext();
7
+ const { res } = ctx;
8
+ if ('function' == typeof func) {
9
+ let isPiped = true;
10
+ const end = (value)=>{
11
+ isPiped = false;
12
+ if ('function' == typeof value) return void value(res);
13
+ return value;
14
+ };
15
+ const output = await func(inputs, end);
16
+ if (!isPiped) if (output) return executeHelper.result = output;
17
+ else return;
18
+ executeHelper.inputs = output;
19
+ await next();
20
+ }
21
+ }
22
+ });
23
+ const Middleware = (middleware)=>({
24
+ name: 'middleware',
25
+ metadata (helper) {
26
+ const middlewares = helper.getMetadata('pipe') || [];
27
+ middlewares.push(middleware);
28
+ helper.setMetadata('middleware', middlewares);
29
+ }
30
+ });
31
+ export { Middleware, Pipe };
@@ -0,0 +1,122 @@
1
+ import path from "path";
2
+ import { ApiRouter } from "@modern-js/bff-core";
3
+ import { API_DIR, isFunction, isProd, isWebOnly, requireExistModule } from "@modern-js/utils";
4
+ import { API_APP_NAME } from "./constants";
5
+ import { HonoAdapter } from "./runtime/hono/adapter";
6
+ class Storage {
7
+ reset() {
8
+ this.middlewares = [];
9
+ }
10
+ constructor(){
11
+ this.middlewares = [];
12
+ }
13
+ }
14
+ const createTransformAPI = (storage)=>({
15
+ addMiddleware (fn) {
16
+ storage.middlewares.push(fn);
17
+ }
18
+ });
19
+ const server = ()=>({
20
+ name: '@modern-js/plugin-bff',
21
+ setup: (api)=>{
22
+ const storage = new Storage();
23
+ const transformAPI = createTransformAPI(storage);
24
+ let apiAppPath = '';
25
+ let apiRouter;
26
+ const honoAdapter = new HonoAdapter(api);
27
+ api.onPrepare(async ()=>{
28
+ const appContext = api.getServerContext();
29
+ const { appDirectory, distDirectory, render } = appContext;
30
+ const root = isProd() ? distDirectory : appDirectory;
31
+ const apiPath = path.resolve(root || process.cwd(), API_DIR);
32
+ apiAppPath = path.resolve(apiPath, API_APP_NAME);
33
+ const apiMod = await requireExistModule(apiAppPath);
34
+ if (apiMod && 'function' == typeof apiMod) apiMod(transformAPI);
35
+ const { middlewares } = storage;
36
+ api.updateServerContext({
37
+ ...appContext,
38
+ apiMiddlewares: middlewares
39
+ });
40
+ const config = api.getServerConfig();
41
+ const prefix = config?.bff?.prefix || '/api';
42
+ const enableHandleWeb = config?.bff?.enableHandleWeb;
43
+ const httpMethodDecider = config?.bff?.httpMethodDecider;
44
+ const { distDirectory: pwd, middlewares: globalMiddlewares } = api.getServerContext();
45
+ const webOnly = await isWebOnly();
46
+ let handler;
47
+ if (webOnly) handler = async (c, next)=>{
48
+ c.body('');
49
+ await next();
50
+ };
51
+ else {
52
+ const runner = api.getHooks();
53
+ const renderHandler = enableHandleWeb ? render : null;
54
+ handler = await runner.prepareApiServer.call({
55
+ pwd: pwd,
56
+ prefix,
57
+ render: renderHandler,
58
+ httpMethodDecider
59
+ });
60
+ }
61
+ if (handler && isFunction(handler)) globalMiddlewares.push({
62
+ name: 'bind-bff',
63
+ handler: (c, next)=>{
64
+ if (!c.req.path.startsWith(prefix) && !enableHandleWeb) return next();
65
+ return handler(c, next);
66
+ },
67
+ order: 'post',
68
+ before: [
69
+ 'custom-server-hook',
70
+ 'custom-server-middleware',
71
+ 'render'
72
+ ]
73
+ });
74
+ honoAdapter.registerMiddleware({
75
+ prefix,
76
+ enableHandleWeb
77
+ });
78
+ });
79
+ api.onReset(async ({ event })=>{
80
+ storage.reset();
81
+ const appContext = api.getServerContext();
82
+ const newApiModule = await requireExistModule(apiAppPath);
83
+ if (newApiModule && 'function' == typeof newApiModule) newApiModule(transformAPI);
84
+ const { middlewares } = storage;
85
+ api.updateServerContext({
86
+ ...appContext,
87
+ apiMiddlewares: middlewares
88
+ });
89
+ if ('file-change' === event.type) {
90
+ const apiHandlerInfos = await apiRouter.getApiHandlers();
91
+ const appContext = api.getServerContext();
92
+ api.updateServerContext({
93
+ ...appContext,
94
+ apiHandlerInfos
95
+ });
96
+ await honoAdapter.setHandlers();
97
+ await honoAdapter.registerApiRoutes();
98
+ }
99
+ });
100
+ api.prepareApiServer(async (input, next)=>{
101
+ const { pwd, prefix, httpMethodDecider } = input;
102
+ const apiDir = path.resolve(pwd, API_DIR);
103
+ const appContext = api.getServerContext();
104
+ const { apiDirectory, lambdaDirectory } = appContext;
105
+ apiRouter = new ApiRouter({
106
+ appDir: pwd,
107
+ apiDir: apiDirectory || apiDir,
108
+ lambdaDir: lambdaDirectory,
109
+ prefix,
110
+ httpMethodDecider
111
+ });
112
+ const apiHandlerInfos = await apiRouter.getApiHandlers();
113
+ api.updateServerContext({
114
+ ...appContext,
115
+ apiRouter,
116
+ apiHandlerInfos
117
+ });
118
+ return next(input);
119
+ });
120
+ }
121
+ });
122
+ export { server as default };