@bleedingdev/modern-js-plugin-bff 3.8.1-ultramodern.5 → 3.8.2-ultramodern.10

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.
@@ -163,6 +163,7 @@ const createBffGenerator = (api)=>{
163
163
  httpMethodDecider,
164
164
  relativeDistPath,
165
165
  relativeApiPath,
166
+ apiFiles: apiRouter.getApiFiles(),
166
167
  bffRuntimeFramework,
167
168
  effectEntry: bff?.effect?.entry,
168
169
  effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
@@ -44,7 +44,6 @@ __webpack_require__.d(__webpack_exports__, {
44
44
  PLUGIN_DIR: ()=>PLUGIN_DIR,
45
45
  RUNTIME_DIR: ()=>RUNTIME_DIR,
46
46
  TYPE_PREFIX: ()=>TYPE_PREFIX,
47
- copyFiles: ()=>copyFiles,
48
47
  createFileDetails: ()=>createFileDetails,
49
48
  posixJoin: ()=>posixJoin,
50
49
  readDirectoryFiles: ()=>readDirectoryFiles,
@@ -85,29 +84,18 @@ function createFileDetails(options) {
85
84
  exportKey: toPosixPath(external_path_default().join(parsedPath.dir, parsedPath.name))
86
85
  };
87
86
  }
88
- async function readDirectoryFiles(appDirectory, directory, relativeDistPath) {
87
+ async function readDirectoryFiles(appDirectory, directory, relativeDistPath, apiFiles) {
89
88
  const filesList = [];
90
- async function readFiles(currentPath) {
91
- const entries = await utils_namespaceObject.fs.readdir(currentPath, {
92
- withFileTypes: true
93
- });
94
- for (const entry of entries){
95
- if ('_app.ts' === entry.name) continue;
96
- const resourcePath = external_path_default().join(currentPath, entry.name);
97
- if (entry.isDirectory()) await readFiles(resourcePath);
98
- else {
99
- const source = await utils_namespaceObject.fs.readFile(resourcePath, 'utf8');
100
- filesList.push(createFileDetails({
101
- appDirectory,
102
- baseDirectory: directory,
103
- resourcePath,
104
- source,
105
- relativeDistPath
106
- }));
107
- }
108
- }
89
+ for (const resourcePath of apiFiles){
90
+ const source = await utils_namespaceObject.fs.readFile(resourcePath, 'utf8');
91
+ filesList.push(createFileDetails({
92
+ appDirectory,
93
+ baseDirectory: directory,
94
+ resourcePath,
95
+ source,
96
+ relativeDistPath
97
+ }));
109
98
  }
110
- await readFiles(directory);
111
99
  return filesList;
112
100
  }
113
101
  async function writeTargetFile(absTargetDir, content) {
@@ -116,9 +104,6 @@ async function writeTargetFile(absTargetDir, content) {
116
104
  });
117
105
  await utils_namespaceObject.fs.writeFile(absTargetDir, content);
118
106
  }
119
- async function copyFiles(from, to) {
120
- if (await utils_namespaceObject.fs.pathExists(from)) await utils_namespaceObject.fs.copy(toPosixPath(from), toPosixPath(to));
121
- }
122
107
  exports.API_DIR = __webpack_exports__.API_DIR;
123
108
  exports.CLIENT_DIR = __webpack_exports__.CLIENT_DIR;
124
109
  exports.EXPORT_PREFIX = __webpack_exports__.EXPORT_PREFIX;
@@ -126,7 +111,6 @@ exports.GENERATED_RUNTIME_DIRS = __webpack_exports__.GENERATED_RUNTIME_DIRS;
126
111
  exports.PLUGIN_DIR = __webpack_exports__.PLUGIN_DIR;
127
112
  exports.RUNTIME_DIR = __webpack_exports__.RUNTIME_DIR;
128
113
  exports.TYPE_PREFIX = __webpack_exports__.TYPE_PREFIX;
129
- exports.copyFiles = __webpack_exports__.copyFiles;
130
114
  exports.createFileDetails = __webpack_exports__.createFileDetails;
131
115
  exports.posixJoin = __webpack_exports__.posixJoin;
132
116
  exports.readDirectoryFiles = __webpack_exports__.readDirectoryFiles;
@@ -140,7 +124,6 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
140
124
  "PLUGIN_DIR",
141
125
  "RUNTIME_DIR",
142
126
  "TYPE_PREFIX",
143
- "copyFiles",
144
127
  "createFileDetails",
145
128
  "posixJoin",
146
129
  "readDirectoryFiles",
@@ -47,12 +47,13 @@ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_na
47
47
  const external_effectClientGenerator_js_namespaceObject = require("../effectClientGenerator.js");
48
48
  const external_files_js_namespaceObject = require("./files.js");
49
49
  const external_package_json_js_namespaceObject = require("./package-json.js");
50
+ const external_type_facade_js_namespaceObject = require("./type-facade.js");
50
51
  const external_write_package_js_namespaceObject = require("./write-package.js");
51
52
  async function clientGenerator(draftOptions) {
52
53
  const generatedClientDir = external_path_default().resolve(draftOptions.appDir, draftOptions.relativeDistPath, external_files_js_namespaceObject.CLIENT_DIR);
53
54
  await utils_namespaceObject.fs.remove(generatedClientDir);
54
55
  const requestId = (0, external_package_json_js_namespaceObject.getPackageName)(draftOptions.appDir) || process.env.npm_package_name;
55
- const lambdaSourceList = draftOptions.existLambda ? await (0, external_files_js_namespaceObject.readDirectoryFiles)(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath) : [];
56
+ const lambdaSourceList = draftOptions.existLambda ? await (0, external_files_js_namespaceObject.readDirectoryFiles)(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath, draftOptions.apiFiles) : [];
56
57
  const generatedSourceList = [
57
58
  ...lambdaSourceList
58
59
  ];
@@ -77,12 +78,17 @@ async function clientGenerator(draftOptions) {
77
78
  const result = await (0, bff_core_namespaceObject.generateClient)(options);
78
79
  return result;
79
80
  };
81
+ const writeClientTypeFacade = async (source, clientCode)=>{
82
+ if (!await utils_namespaceObject.fs.pathExists(external_path_default().resolve(source.relativeTargetDistDir))) throw new external_type_facade_js_namespaceObject.MissingClientDeclarationError(source.resourcePath, source.relativeTargetDistDir);
83
+ const clientTypesFile = source.targetDir.replace(/\.js$/, '.d.ts');
84
+ await (0, external_files_js_namespaceObject.writeTargetFile)(external_path_default().resolve(clientTypesFile), (0, external_type_facade_js_namespaceObject.buildClientTypeFacade)(clientTypesFile, source.relativeTargetDistDir, external_type_facade_js_namespaceObject.DEFAULT_EXPORT_RE.test(clientCode), true));
85
+ };
80
86
  try {
81
87
  for (const source of lambdaSourceList){
82
88
  const code = await getClitentCode(source.resourcePath, source.source);
83
89
  if (code?.value) {
84
90
  await (0, external_files_js_namespaceObject.writeTargetFile)(source.absTargetDir, code.value);
85
- await (0, external_files_js_namespaceObject.copyFiles)(source.relativeTargetDistDir, source.targetDir.replace("js", 'd.ts'));
91
+ await writeClientTypeFacade(source, code.value);
86
92
  }
87
93
  }
88
94
  if ('effect' === draftOptions.bffRuntimeFramework) {
@@ -121,6 +127,7 @@ async function clientGenerator(draftOptions) {
121
127
  }
122
128
  utils_namespaceObject.logger.info("Client bundle generate succeed");
123
129
  } catch (error) {
130
+ if (error instanceof external_type_facade_js_namespaceObject.MissingClientDeclarationError) throw error;
124
131
  utils_namespaceObject.logger.error(`Client bundle generate failed: ${error}`);
125
132
  }
126
133
  if (generatedSourceList.length > 0) await (0, external_write_package_js_namespaceObject.writeClientModuleBoundary)(draftOptions.appDir, draftOptions.relativeDistPath);
@@ -37,8 +37,9 @@ var __webpack_require__ = {};
37
37
  var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
+ MissingClientDeclarationError: ()=>external_type_facade_js_namespaceObject.MissingClientDeclarationError,
41
+ buildClientTypeFacade: ()=>external_type_facade_js_namespaceObject.buildClientTypeFacade,
40
42
  clientGenerator: ()=>external_generate_js_namespaceObject.clientGenerator,
41
- copyFiles: ()=>external_files_js_namespaceObject.copyFiles,
42
43
  createFileDetails: ()=>external_files_js_namespaceObject.createFileDetails,
43
44
  default: ()=>external_generate_js_default(),
44
45
  getClientPackageName: ()=>external_package_json_js_namespaceObject.getClientPackageName,
@@ -53,9 +54,11 @@ const external_files_js_namespaceObject = require("./files.js");
53
54
  const external_generate_js_namespaceObject = require("./generate.js");
54
55
  var external_generate_js_default = /*#__PURE__*/ __webpack_require__.n(external_generate_js_namespaceObject);
55
56
  const external_package_json_js_namespaceObject = require("./package-json.js");
57
+ const external_type_facade_js_namespaceObject = require("./type-facade.js");
56
58
  const external_write_package_js_namespaceObject = require("./write-package.js");
59
+ exports.MissingClientDeclarationError = __webpack_exports__.MissingClientDeclarationError;
60
+ exports.buildClientTypeFacade = __webpack_exports__.buildClientTypeFacade;
57
61
  exports.clientGenerator = __webpack_exports__.clientGenerator;
58
- exports.copyFiles = __webpack_exports__.copyFiles;
59
62
  exports.createFileDetails = __webpack_exports__.createFileDetails;
60
63
  exports["default"] = __webpack_exports__["default"];
61
64
  exports.getClientPackageName = __webpack_exports__.getClientPackageName;
@@ -66,8 +69,9 @@ exports.setPackage = __webpack_exports__.setPackage;
66
69
  exports.writeClientModuleBoundary = __webpack_exports__.writeClientModuleBoundary;
67
70
  exports.writeTargetFile = __webpack_exports__.writeTargetFile;
68
71
  for(var __rspack_i in __webpack_exports__)if (-1 === [
72
+ "MissingClientDeclarationError",
73
+ "buildClientTypeFacade",
69
74
  "clientGenerator",
70
- "copyFiles",
71
75
  "createFileDetails",
72
76
  "default",
73
77
  "getClientPackageName",
@@ -0,0 +1,74 @@
1
+ "use strict";
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, getters, values)=>{
14
+ var define = (defs, kind)=>{
15
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
16
+ enumerable: true,
17
+ [kind]: defs[key]
18
+ });
19
+ };
20
+ define(getters, "get");
21
+ define(values, "value");
22
+ };
23
+ })();
24
+ (()=>{
25
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
26
+ })();
27
+ (()=>{
28
+ __webpack_require__.r = (exports1)=>{
29
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
30
+ value: 'Module'
31
+ });
32
+ Object.defineProperty(exports1, '__esModule', {
33
+ value: true
34
+ });
35
+ };
36
+ })();
37
+ var __webpack_exports__ = {};
38
+ __webpack_require__.r(__webpack_exports__);
39
+ __webpack_require__.d(__webpack_exports__, {
40
+ DEFAULT_EXPORT_RE: ()=>DEFAULT_EXPORT_RE,
41
+ MissingClientDeclarationError: ()=>MissingClientDeclarationError,
42
+ buildClientTypeFacade: ()=>buildClientTypeFacade
43
+ });
44
+ const external_path_namespaceObject = require("path");
45
+ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
46
+ const external_files_js_namespaceObject = require("./files.js");
47
+ const DEFAULT_EXPORT_RE = /(^|[\s;])export\s+default\b/;
48
+ class MissingClientDeclarationError extends Error {
49
+ constructor(resourcePath, expectedDeclaration){
50
+ super(`[plugin-bff] No declaration was emitted for "${resourcePath}", so the published client type for it cannot be generated (expected "${expectedDeclaration}"). Enable "declaration": true in the server tsconfig used by this app.`);
51
+ this.name = 'MissingClientDeclarationError';
52
+ }
53
+ }
54
+ function buildClientTypeFacade(clientTypesFile, originTypesFile, hasDefaultExport, esm = false) {
55
+ const originNoExt = originTypesFile.replace(/\.d\.ts$/, '');
56
+ let specifier = (0, external_files_js_namespaceObject.toPosixPath)(external_path_default().relative(external_path_default().dirname(clientTypesFile), originNoExt));
57
+ if (!specifier.startsWith('.')) specifier = `./${specifier}`;
58
+ if (esm) specifier = `${specifier}.js`;
59
+ const lines = [];
60
+ if (hasDefaultExport) lines.push(`export { default } from '${specifier}';`);
61
+ lines.push(`export * from '${specifier}';`);
62
+ return `${lines.join('\n')}\n`;
63
+ }
64
+ exports.DEFAULT_EXPORT_RE = __webpack_exports__.DEFAULT_EXPORT_RE;
65
+ exports.MissingClientDeclarationError = __webpack_exports__.MissingClientDeclarationError;
66
+ exports.buildClientTypeFacade = __webpack_exports__.buildClientTypeFacade;
67
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
68
+ "DEFAULT_EXPORT_RE",
69
+ "MissingClientDeclarationError",
70
+ "buildClientTypeFacade"
71
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
72
+ Object.defineProperty(exports, '__esModule', {
73
+ value: true
74
+ });
@@ -62,7 +62,8 @@ async function setPackage(files, appDirectory, relativeDistPath) {
62
62
  const addFiles = [
63
63
  (0, external_files_js_namespaceObject.posixJoin)(relativeDistPath, external_files_js_namespaceObject.CLIENT_DIR, '**', '*'),
64
64
  (0, external_files_js_namespaceObject.posixJoin)(relativeDistPath, external_files_js_namespaceObject.RUNTIME_DIR, '**', '*'),
65
- (0, external_files_js_namespaceObject.posixJoin)(relativeDistPath, external_files_js_namespaceObject.PLUGIN_DIR, '**', '*')
65
+ (0, external_files_js_namespaceObject.posixJoin)(relativeDistPath, external_files_js_namespaceObject.PLUGIN_DIR, '**', '*'),
66
+ (0, external_files_js_namespaceObject.posixJoin)(relativeDistPath, '**', '*.d.ts')
66
67
  ];
67
68
  const typesVersions = {
68
69
  '*': sortedFiles.reduce((acc, file)=>{
@@ -119,6 +119,7 @@ const createBffGenerator = (api)=>{
119
119
  httpMethodDecider,
120
120
  relativeDistPath,
121
121
  relativeApiPath,
122
+ apiFiles: apiRouter.getApiFiles(),
122
123
  bffRuntimeFramework,
123
124
  effectEntry: bff?.effect?.entry,
124
125
  effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
@@ -31,29 +31,18 @@ function createFileDetails(options) {
31
31
  exportKey: toPosixPath(path.join(parsedPath.dir, parsedPath.name))
32
32
  };
33
33
  }
34
- async function readDirectoryFiles(appDirectory, directory, relativeDistPath) {
34
+ async function readDirectoryFiles(appDirectory, directory, relativeDistPath, apiFiles) {
35
35
  const filesList = [];
36
- async function readFiles(currentPath) {
37
- const entries = await fs.readdir(currentPath, {
38
- withFileTypes: true
39
- });
40
- for (const entry of entries){
41
- if ('_app.ts' === entry.name) continue;
42
- const resourcePath = path.join(currentPath, entry.name);
43
- if (entry.isDirectory()) await readFiles(resourcePath);
44
- else {
45
- const source = await fs.readFile(resourcePath, 'utf8');
46
- filesList.push(createFileDetails({
47
- appDirectory,
48
- baseDirectory: directory,
49
- resourcePath,
50
- source,
51
- relativeDistPath
52
- }));
53
- }
54
- }
36
+ for (const resourcePath of apiFiles){
37
+ const source = await fs.readFile(resourcePath, 'utf8');
38
+ filesList.push(createFileDetails({
39
+ appDirectory,
40
+ baseDirectory: directory,
41
+ resourcePath,
42
+ source,
43
+ relativeDistPath
44
+ }));
55
45
  }
56
- await readFiles(directory);
57
46
  return filesList;
58
47
  }
59
48
  async function writeTargetFile(absTargetDir, content) {
@@ -62,7 +51,4 @@ async function writeTargetFile(absTargetDir, content) {
62
51
  });
63
52
  await fs.writeFile(absTargetDir, content);
64
53
  }
65
- async function copyFiles(from, to) {
66
- if (await fs.pathExists(from)) await fs.copy(toPosixPath(from), toPosixPath(to));
67
- }
68
- export { API_DIR, CLIENT_DIR, EXPORT_PREFIX, GENERATED_RUNTIME_DIRS, PLUGIN_DIR, RUNTIME_DIR, TYPE_PREFIX, copyFiles, createFileDetails, posixJoin, readDirectoryFiles, toPosixPath, writeTargetFile };
54
+ export { API_DIR, CLIENT_DIR, EXPORT_PREFIX, GENERATED_RUNTIME_DIRS, PLUGIN_DIR, RUNTIME_DIR, TYPE_PREFIX, createFileDetails, posixJoin, readDirectoryFiles, toPosixPath, writeTargetFile };
@@ -2,14 +2,15 @@ import { generateClient } from "@modern-js/bff-core";
2
2
  import { fs, logger } from "@modern-js/utils";
3
3
  import path from "path";
4
4
  import { generateEffectClient, resolveEffectEntryFile } from "../effectClientGenerator.mjs";
5
- import { CLIENT_DIR, copyFiles, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
5
+ import { CLIENT_DIR, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
6
6
  import { getPackageName } from "./package-json.mjs";
7
+ import { DEFAULT_EXPORT_RE, MissingClientDeclarationError, buildClientTypeFacade } from "./type-facade.mjs";
7
8
  import { setPackage, writeClientModuleBoundary } from "./write-package.mjs";
8
9
  async function clientGenerator(draftOptions) {
9
10
  const generatedClientDir = path.resolve(draftOptions.appDir, draftOptions.relativeDistPath, CLIENT_DIR);
10
11
  await fs.remove(generatedClientDir);
11
12
  const requestId = getPackageName(draftOptions.appDir) || process.env.npm_package_name;
12
- const lambdaSourceList = draftOptions.existLambda ? await readDirectoryFiles(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath) : [];
13
+ const lambdaSourceList = draftOptions.existLambda ? await readDirectoryFiles(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath, draftOptions.apiFiles) : [];
13
14
  const generatedSourceList = [
14
15
  ...lambdaSourceList
15
16
  ];
@@ -34,12 +35,17 @@ async function clientGenerator(draftOptions) {
34
35
  const result = await generateClient(options);
35
36
  return result;
36
37
  };
38
+ const writeClientTypeFacade = async (source, clientCode)=>{
39
+ if (!await fs.pathExists(path.resolve(source.relativeTargetDistDir))) throw new MissingClientDeclarationError(source.resourcePath, source.relativeTargetDistDir);
40
+ const clientTypesFile = source.targetDir.replace(/\.js$/, '.d.ts');
41
+ await writeTargetFile(path.resolve(clientTypesFile), buildClientTypeFacade(clientTypesFile, source.relativeTargetDistDir, DEFAULT_EXPORT_RE.test(clientCode), true));
42
+ };
37
43
  try {
38
44
  for (const source of lambdaSourceList){
39
45
  const code = await getClitentCode(source.resourcePath, source.source);
40
46
  if (code?.value) {
41
47
  await writeTargetFile(source.absTargetDir, code.value);
42
- await copyFiles(source.relativeTargetDistDir, source.targetDir.replace("js", 'd.ts'));
48
+ await writeClientTypeFacade(source, code.value);
43
49
  }
44
50
  }
45
51
  if ('effect' === draftOptions.bffRuntimeFramework) {
@@ -78,6 +84,7 @@ async function clientGenerator(draftOptions) {
78
84
  }
79
85
  logger.info("Client bundle generate succeed");
80
86
  } catch (error) {
87
+ if (error instanceof MissingClientDeclarationError) throw error;
81
88
  logger.error(`Client bundle generate failed: ${error}`);
82
89
  }
83
90
  if (generatedSourceList.length > 0) await writeClientModuleBoundary(draftOptions.appDir, draftOptions.relativeDistPath);
@@ -1,4 +1,5 @@
1
- export { copyFiles, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
1
+ export { createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
2
2
  export { clientGenerator, default } from "./generate.mjs";
3
3
  export { getClientPackageName, getPackageName, mergePackageJson } from "./package-json.mjs";
4
+ export { MissingClientDeclarationError, buildClientTypeFacade } from "./type-facade.mjs";
4
5
  export { setPackage, writeClientModuleBoundary } from "./write-package.mjs";
@@ -0,0 +1,20 @@
1
+ import path from "path";
2
+ import { toPosixPath } from "./files.mjs";
3
+ const DEFAULT_EXPORT_RE = /(^|[\s;])export\s+default\b/;
4
+ class MissingClientDeclarationError extends Error {
5
+ constructor(resourcePath, expectedDeclaration){
6
+ super(`[plugin-bff] No declaration was emitted for "${resourcePath}", so the published client type for it cannot be generated (expected "${expectedDeclaration}"). Enable "declaration": true in the server tsconfig used by this app.`);
7
+ this.name = 'MissingClientDeclarationError';
8
+ }
9
+ }
10
+ function buildClientTypeFacade(clientTypesFile, originTypesFile, hasDefaultExport, esm = false) {
11
+ const originNoExt = originTypesFile.replace(/\.d\.ts$/, '');
12
+ let specifier = toPosixPath(path.relative(path.dirname(clientTypesFile), originNoExt));
13
+ if (!specifier.startsWith('.')) specifier = `./${specifier}`;
14
+ if (esm) specifier = `${specifier}.js`;
15
+ const lines = [];
16
+ if (hasDefaultExport) lines.push(`export { default } from '${specifier}';`);
17
+ lines.push(`export * from '${specifier}';`);
18
+ return `${lines.join('\n')}\n`;
19
+ }
20
+ export { DEFAULT_EXPORT_RE, MissingClientDeclarationError, buildClientTypeFacade };
@@ -19,7 +19,8 @@ async function setPackage(files, appDirectory, relativeDistPath) {
19
19
  const addFiles = [
20
20
  posixJoin(relativeDistPath, CLIENT_DIR, '**', '*'),
21
21
  posixJoin(relativeDistPath, RUNTIME_DIR, '**', '*'),
22
- posixJoin(relativeDistPath, PLUGIN_DIR, '**', '*')
22
+ posixJoin(relativeDistPath, PLUGIN_DIR, '**', '*'),
23
+ posixJoin(relativeDistPath, '**', '*.d.ts')
23
24
  ];
24
25
  const typesVersions = {
25
26
  '*': sortedFiles.reduce((acc, file)=>{
@@ -120,6 +120,7 @@ const createBffGenerator = (api)=>{
120
120
  httpMethodDecider,
121
121
  relativeDistPath,
122
122
  relativeApiPath,
123
+ apiFiles: apiRouter.getApiFiles(),
123
124
  bffRuntimeFramework,
124
125
  effectEntry: bff?.effect?.entry,
125
126
  effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
@@ -32,29 +32,18 @@ function createFileDetails(options) {
32
32
  exportKey: toPosixPath(path.join(parsedPath.dir, parsedPath.name))
33
33
  };
34
34
  }
35
- async function readDirectoryFiles(appDirectory, directory, relativeDistPath) {
35
+ async function readDirectoryFiles(appDirectory, directory, relativeDistPath, apiFiles) {
36
36
  const filesList = [];
37
- async function readFiles(currentPath) {
38
- const entries = await fs.readdir(currentPath, {
39
- withFileTypes: true
40
- });
41
- for (const entry of entries){
42
- if ('_app.ts' === entry.name) continue;
43
- const resourcePath = path.join(currentPath, entry.name);
44
- if (entry.isDirectory()) await readFiles(resourcePath);
45
- else {
46
- const source = await fs.readFile(resourcePath, 'utf8');
47
- filesList.push(createFileDetails({
48
- appDirectory,
49
- baseDirectory: directory,
50
- resourcePath,
51
- source,
52
- relativeDistPath
53
- }));
54
- }
55
- }
37
+ for (const resourcePath of apiFiles){
38
+ const source = await fs.readFile(resourcePath, 'utf8');
39
+ filesList.push(createFileDetails({
40
+ appDirectory,
41
+ baseDirectory: directory,
42
+ resourcePath,
43
+ source,
44
+ relativeDistPath
45
+ }));
56
46
  }
57
- await readFiles(directory);
58
47
  return filesList;
59
48
  }
60
49
  async function writeTargetFile(absTargetDir, content) {
@@ -63,7 +52,4 @@ async function writeTargetFile(absTargetDir, content) {
63
52
  });
64
53
  await fs.writeFile(absTargetDir, content);
65
54
  }
66
- async function copyFiles(from, to) {
67
- if (await fs.pathExists(from)) await fs.copy(toPosixPath(from), toPosixPath(to));
68
- }
69
- export { API_DIR, CLIENT_DIR, EXPORT_PREFIX, GENERATED_RUNTIME_DIRS, PLUGIN_DIR, RUNTIME_DIR, TYPE_PREFIX, copyFiles, createFileDetails, posixJoin, readDirectoryFiles, toPosixPath, writeTargetFile };
55
+ export { API_DIR, CLIENT_DIR, EXPORT_PREFIX, GENERATED_RUNTIME_DIRS, PLUGIN_DIR, RUNTIME_DIR, TYPE_PREFIX, createFileDetails, posixJoin, readDirectoryFiles, toPosixPath, writeTargetFile };
@@ -3,14 +3,15 @@ import { generateClient } from "@modern-js/bff-core";
3
3
  import { fs, logger } from "@modern-js/utils";
4
4
  import path from "path";
5
5
  import { generateEffectClient, resolveEffectEntryFile } from "../effectClientGenerator.mjs";
6
- import { CLIENT_DIR, copyFiles, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
6
+ import { CLIENT_DIR, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
7
7
  import { getPackageName } from "./package-json.mjs";
8
+ import { DEFAULT_EXPORT_RE, MissingClientDeclarationError, buildClientTypeFacade } from "./type-facade.mjs";
8
9
  import { setPackage, writeClientModuleBoundary } from "./write-package.mjs";
9
10
  async function clientGenerator(draftOptions) {
10
11
  const generatedClientDir = path.resolve(draftOptions.appDir, draftOptions.relativeDistPath, CLIENT_DIR);
11
12
  await fs.remove(generatedClientDir);
12
13
  const requestId = getPackageName(draftOptions.appDir) || process.env.npm_package_name;
13
- const lambdaSourceList = draftOptions.existLambda ? await readDirectoryFiles(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath) : [];
14
+ const lambdaSourceList = draftOptions.existLambda ? await readDirectoryFiles(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath, draftOptions.apiFiles) : [];
14
15
  const generatedSourceList = [
15
16
  ...lambdaSourceList
16
17
  ];
@@ -35,12 +36,17 @@ async function clientGenerator(draftOptions) {
35
36
  const result = await generateClient(options);
36
37
  return result;
37
38
  };
39
+ const writeClientTypeFacade = async (source, clientCode)=>{
40
+ if (!await fs.pathExists(path.resolve(source.relativeTargetDistDir))) throw new MissingClientDeclarationError(source.resourcePath, source.relativeTargetDistDir);
41
+ const clientTypesFile = source.targetDir.replace(/\.js$/, '.d.ts');
42
+ await writeTargetFile(path.resolve(clientTypesFile), buildClientTypeFacade(clientTypesFile, source.relativeTargetDistDir, DEFAULT_EXPORT_RE.test(clientCode), true));
43
+ };
38
44
  try {
39
45
  for (const source of lambdaSourceList){
40
46
  const code = await getClitentCode(source.resourcePath, source.source);
41
47
  if (code?.value) {
42
48
  await writeTargetFile(source.absTargetDir, code.value);
43
- await copyFiles(source.relativeTargetDistDir, source.targetDir.replace("js", 'd.ts'));
49
+ await writeClientTypeFacade(source, code.value);
44
50
  }
45
51
  }
46
52
  if ('effect' === draftOptions.bffRuntimeFramework) {
@@ -79,6 +85,7 @@ async function clientGenerator(draftOptions) {
79
85
  }
80
86
  logger.info("Client bundle generate succeed");
81
87
  } catch (error) {
88
+ if (error instanceof MissingClientDeclarationError) throw error;
82
89
  logger.error(`Client bundle generate failed: ${error}`);
83
90
  }
84
91
  if (generatedSourceList.length > 0) await writeClientModuleBoundary(draftOptions.appDir, draftOptions.relativeDistPath);
@@ -1,5 +1,6 @@
1
1
  import "node:module";
2
- export { copyFiles, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
2
+ export { createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
3
3
  export { clientGenerator, default } from "./generate.mjs";
4
4
  export { getClientPackageName, getPackageName, mergePackageJson } from "./package-json.mjs";
5
+ export { MissingClientDeclarationError, buildClientTypeFacade } from "./type-facade.mjs";
5
6
  export { setPackage, writeClientModuleBoundary } from "./write-package.mjs";
@@ -0,0 +1,21 @@
1
+ import "node:module";
2
+ import path from "path";
3
+ import { toPosixPath } from "./files.mjs";
4
+ const DEFAULT_EXPORT_RE = /(^|[\s;])export\s+default\b/;
5
+ class MissingClientDeclarationError extends Error {
6
+ constructor(resourcePath, expectedDeclaration){
7
+ super(`[plugin-bff] No declaration was emitted for "${resourcePath}", so the published client type for it cannot be generated (expected "${expectedDeclaration}"). Enable "declaration": true in the server tsconfig used by this app.`);
8
+ this.name = 'MissingClientDeclarationError';
9
+ }
10
+ }
11
+ function buildClientTypeFacade(clientTypesFile, originTypesFile, hasDefaultExport, esm = false) {
12
+ const originNoExt = originTypesFile.replace(/\.d\.ts$/, '');
13
+ let specifier = toPosixPath(path.relative(path.dirname(clientTypesFile), originNoExt));
14
+ if (!specifier.startsWith('.')) specifier = `./${specifier}`;
15
+ if (esm) specifier = `${specifier}.js`;
16
+ const lines = [];
17
+ if (hasDefaultExport) lines.push(`export { default } from '${specifier}';`);
18
+ lines.push(`export * from '${specifier}';`);
19
+ return `${lines.join('\n')}\n`;
20
+ }
21
+ export { DEFAULT_EXPORT_RE, MissingClientDeclarationError, buildClientTypeFacade };
@@ -20,7 +20,8 @@ async function setPackage(files, appDirectory, relativeDistPath) {
20
20
  const addFiles = [
21
21
  posixJoin(relativeDistPath, CLIENT_DIR, '**', '*'),
22
22
  posixJoin(relativeDistPath, RUNTIME_DIR, '**', '*'),
23
- posixJoin(relativeDistPath, PLUGIN_DIR, '**', '*')
23
+ posixJoin(relativeDistPath, PLUGIN_DIR, '**', '*'),
24
+ posixJoin(relativeDistPath, '**', '*.d.ts')
24
25
  ];
25
26
  const typesVersions = {
26
27
  '*': sortedFiles.reduce((acc, file)=>{
@@ -23,6 +23,15 @@ export declare function createFileDetails(options: {
23
23
  source: string;
24
24
  relativeDistPath: string;
25
25
  }): FileDetails;
26
- export declare function readDirectoryFiles(appDirectory: string, directory: string, relativeDistPath: string): Promise<FileDetails[]>;
26
+ /**
27
+ * Turn the API files ApiRouter resolved into client-generation inputs.
28
+ *
29
+ * `apiFiles` are absolute paths produced with the very same `API_FILE_RULES`
30
+ * the runtime router uses, which keeps the client generator and the router in
31
+ * agreement about what an API module is. A bare recursive read of `directory`
32
+ * also swept in whatever else happened to sit next to the sources — emitted
33
+ * `.d.ts` and compiled `.js`, tests, private `_`-prefixed modules — and handed
34
+ * them to `generateClient`.
35
+ */
36
+ export declare function readDirectoryFiles(appDirectory: string, directory: string, relativeDistPath: string, apiFiles: string[]): Promise<FileDetails[]>;
27
37
  export declare function writeTargetFile(absTargetDir: string, content: string): Promise<void>;
28
- export declare function copyFiles(from: string, to: string): Promise<void>;
@@ -10,6 +10,14 @@ export type APILoaderOptions = {
10
10
  httpMethodDecider?: HttpMethodDecider;
11
11
  relativeDistPath: string;
12
12
  relativeApiPath: string;
13
+ /**
14
+ * Absolute paths of the valid API files, resolved by ApiRouter with the same
15
+ * `API_FILE_RULES` the runtime router uses. Passing them in keeps the client
16
+ * generator and the router in agreement about what an API module is, so
17
+ * stray artifacts next to the sources (compiled `.d.ts`/`.js`, tests,
18
+ * private files) never reach `generateClient`.
19
+ */
20
+ apiFiles: string[];
13
21
  bffRuntimeFramework?: 'hono' | 'effect';
14
22
  effectEntry?: string;
15
23
  effectDataPlatformBatch?: {
@@ -1,7 +1,8 @@
1
1
  export type { FileDetails } from './files';
2
- export { copyFiles, createFileDetails, readDirectoryFiles, writeTargetFile, } from './files';
2
+ export { createFileDetails, readDirectoryFiles, writeTargetFile, } from './files';
3
3
  export type { APILoaderOptions } from './generate';
4
4
  export { clientGenerator, default } from './generate';
5
5
  export type { PackageJsonLike } from './package-json';
6
6
  export { getClientPackageName, getPackageName, mergePackageJson, } from './package-json';
7
+ export { buildClientTypeFacade, MissingClientDeclarationError, } from './type-facade';
7
8
  export { setPackage, writeClientModuleBoundary } from './write-package';
@@ -0,0 +1,24 @@
1
+ export declare const DEFAULT_EXPORT_RE: RegExp;
2
+ /**
3
+ * Raised when the handler declaration the client facade must re-export was
4
+ * never emitted. `setPackage` unconditionally publishes `types` entries for
5
+ * every generated client, so a missing declaration means the published package
6
+ * advertises a type surface it cannot resolve. Failing the build is the only
7
+ * honest outcome: the previous behaviour (copy the declaration if it happens to
8
+ * exist, otherwise carry on) shipped tarballs whose clients silently degraded
9
+ * to `any` in consumer projects.
10
+ */
11
+ export declare class MissingClientDeclarationError extends Error {
12
+ constructor(resourcePath: string, expectedDeclaration: string);
13
+ }
14
+ /**
15
+ * Build the `.d.ts` that sits next to a generated client module.
16
+ *
17
+ * The published client re-exports the handler's own declaration instead of
18
+ * copying it. A verbatim copy landed the `.d.ts` one directory shallower than
19
+ * the compiler emitted it (`dist/client/*` vs `dist/<lambda>/*`), breaking
20
+ * every relative specifier inside. A facade leaves the original declarations in
21
+ * place (relative refs intact, and published via `**\/*.d.ts`) and only points
22
+ * at them.
23
+ */
24
+ export declare function buildClientTypeFacade(clientTypesFile: string, originTypesFile: string, hasDefaultExport: boolean, esm?: boolean): string;
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.8.1-ultramodern.5",
20
+ "version": "3.8.2-ultramodern.10",
21
21
  "types": "./dist/types/cli.d.ts",
22
22
  "main": "./dist/cjs/cli.js",
23
23
  "exports": {
@@ -169,12 +169,12 @@
169
169
  }
170
170
  },
171
171
  "dependencies": {
172
- "@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.8.1-ultramodern.5",
173
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.8.1-ultramodern.5",
174
- "@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.8.1-ultramodern.5",
175
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.1-ultramodern.5",
176
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.8.1-ultramodern.5",
177
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.1-ultramodern.5",
172
+ "@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.8.2-ultramodern.10",
173
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.8.2-ultramodern.10",
174
+ "@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.8.2-ultramodern.10",
175
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.2-ultramodern.10",
176
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.8.2-ultramodern.10",
177
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.2-ultramodern.10",
178
178
  "@module-federation/runtime": "2.8.2",
179
179
  "@opentelemetry/api": "1.9.1",
180
180
  "@opentelemetry/api-logs": "0.220.0",
@@ -205,12 +205,12 @@
205
205
  },
206
206
  "devDependencies": {
207
207
  "@effect/opentelemetry": "4.0.0-beta.107",
208
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.8.1-ultramodern.5",
209
- "@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.8.1-ultramodern.5",
210
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.1-ultramodern.5",
211
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.8.1-ultramodern.5",
212
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.1-ultramodern.5",
213
- "@rsbuild/core": "2.1.5",
208
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.8.2-ultramodern.10",
209
+ "@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.8.2-ultramodern.10",
210
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.2-ultramodern.10",
211
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.8.2-ultramodern.10",
212
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.2-ultramodern.10",
213
+ "@rsbuild/core": "2.2.0-rc.0",
214
214
  "@rslib/core": "0.23.2",
215
215
  "@scripts/rstest-config": "2.66.0",
216
216
  "@types/node": "^26.2.0",