@lark-apaas/fullstack-rspack-preset 1.0.55 → 1.0.56-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.
package/lib/index.js CHANGED
@@ -41,10 +41,14 @@ const normalize_base_path_1 = require("./utils/normalize-base-path");
41
41
  * - CLIENT_DEV_PORT: 客户端开发端口 (默认: 8080)
42
42
  */
43
43
  function createFullstackRspackConfig(overrides = {}) {
44
- // 1. 自动加载 .env 文件(同步加载,确保环境变量立即可用)
44
+ // 优先级 shell > .env.local > .env,对齐 Vite/Next.js 约定。
45
+ // dotenv.config 默认 override:false(先到先得),先 .env.local 让它优先,
46
+ // 再 .env 兜底;shell env 已在 process.env 中,两次 config 都不会覆盖。
45
47
  try {
46
48
  // eslint-disable-next-line @typescript-eslint/no-var-requires
47
- require('dotenv').config();
49
+ const dotenv = require('dotenv');
50
+ dotenv.config({ path: '.env.local' });
51
+ dotenv.config({ path: '.env' });
48
52
  }
49
53
  catch (e) {
50
54
  // dotenv 是可选依赖,如果没有安装也不影响使用
package/lib/preset.js CHANGED
@@ -10,12 +10,12 @@ const core_1 = __importDefault(require("@rspack/core"));
10
10
  const devtool_kits_1 = require("@lark-apaas/devtool-kits");
11
11
  const plugin_react_refresh_1 = __importDefault(require("@rspack/plugin-react-refresh"));
12
12
  const dev_server_listener_1 = require("./utils/dev-server-listener");
13
+ const local_dev_1 = require("./utils/local-dev");
13
14
  const route_parser_plugin_1 = __importDefault(require("./rspack-plugins/route-parser-plugin"));
14
15
  const slardar_performance_monitor_plugin_1 = __importDefault(require("./rspack-plugins/slardar-performance-monitor-plugin"));
15
16
  const view_context_injection_plugin_1 = __importDefault(require("./rspack-plugins/view-context-injection-plugin"));
16
17
  const og_meta_injection_plugin_1 = __importDefault(require("./rspack-plugins/og-meta-injection-plugin"));
17
18
  const runtime_injection_plugin_1 = __importDefault(require("./rspack-plugins/runtime-injection-plugin"));
18
- const basename_injection_plugin_1 = __importDefault(require("./rspack-plugins/basename-injection-plugin"));
19
19
  const css_legacy_plugin_1 = __importDefault(require("./rspack-plugins/css-legacy-plugin"));
20
20
  const polyfill_plugin_1 = __importDefault(require("./rspack-plugins/polyfill-plugin"));
21
21
  const static_assets_plugin_1 = __importDefault(require("./rspack-plugins/static-assets-plugin"));
@@ -74,6 +74,33 @@ function createRecommendRspackConfig(options) {
74
74
  }
75
75
  }
76
76
  catch { /* ignore */ }
77
+ // 本地 dev 链路兜底:将 {clientBasePath}/__runtime__ 反代到公网沙箱,
78
+ // 由 SDK 而非用户应用承担 cookie / x-larkgw-suda-webuser 注入。
79
+ const localDev = (0, local_dev_1.isLocalDev)();
80
+ const sandboxBase = process.env.SANDBOX_PUBLIC_URL;
81
+ const sandboxCookie = process.env.SANDBOX_COOKIE || '';
82
+ let localDevWebUserHeader = '';
83
+ if (process.env.SUDA_WEBUSER) {
84
+ try {
85
+ localDevWebUserHeader = encodeURIComponent(JSON.stringify(JSON.parse(process.env.SUDA_WEBUSER)));
86
+ }
87
+ catch { /* ignore */ }
88
+ }
89
+ // 沙箱(SANDBOX_PUBLIC_URL)上 /__runtime__/* 的路由本身就期望带 /app/<appId> 前缀
90
+ // (跟生产网关 miaoda.feishu-boe.cn 的行为一致),所以直接透传完整路径,不做 pathRewrite。
91
+ const localDevRuntimeProxyEntries = localDev && sandboxBase
92
+ ? [{
93
+ context: [`${clientBasePath}/__runtime__`],
94
+ target: sandboxBase,
95
+ changeOrigin: true,
96
+ secure: true,
97
+ headers: {
98
+ ...(sandboxCookie ? { cookie: sandboxCookie } : {}),
99
+ 'accept-encoding': 'identity',
100
+ ...(localDevWebUserHeader ? { 'x-larkgw-suda-webuser': localDevWebUserHeader } : {}),
101
+ },
102
+ }]
103
+ : [];
77
104
  return {
78
105
  mode: isDev ? 'development' : 'production',
79
106
  cache: false,
@@ -194,10 +221,6 @@ function createRecommendRspackConfig(options) {
194
221
  plugins: [
195
222
  // 运行时注入插件 - 自动将 @lark-apaas/client-toolkit/runtime 注入到所有入口之前
196
223
  new runtime_injection_plugin_1.default(),
197
- // basename 运行时注入 - 改写入口文件 process.env.CLIENT_BASE_PATH 为运行时读 window.__BASENAME__
198
- // 必须在 DefinePlugin 之前生效(plugin 内部 enforce:'pre')
199
- // 匹配 client/src/ 根层级入口文件(index.tsx / main.tsx 等),不影响 SDK 内部代码
200
- new basename_injection_plugin_1.default({ envBasePath: clientBasePath }),
201
224
  new core_1.default.BannerPlugin({
202
225
  banner: `window.__RELEASE_COMMIT_ID__ = '${releaseId}';`,
203
226
  raw: true,
@@ -376,6 +399,7 @@ function createRecommendRspackConfig(options) {
376
399
  (0, dev_server_listener_1.listenHmrTiming)(server);
377
400
  },
378
401
  proxy: [
402
+ ...localDevRuntimeProxyEntries,
379
403
  {
380
404
  context: [`${clientBasePath}/api`],
381
405
  target: `http://localhost:${serverPort}`,
@@ -68,25 +68,19 @@ class SourceMapUploadPlugin {
68
68
  if (!fs.existsSync(destinationDir)) {
69
69
  fs.mkdirSync(destinationDir, { recursive: true });
70
70
  }
71
- // Move the file; fallback to copy+delete on cross-filesystem (EXDEV)
71
+ // The asset source may be a Buffer or a string.
72
+ const source = sourceMap.source.source();
73
+ const sourceBuffer = Buffer.isBuffer(source) ? source : Buffer.from(source, 'utf-8');
74
+ fs.writeFileSync(destinationPath, sourceBuffer);
75
+ console.log(`SourceMapUploadPlugin: Consolidated ${assetName} to ${destinationPath}`);
76
+ // Delete the original sourcemap file.
72
77
  try {
73
- fs.renameSync(originalPath, destinationPath);
78
+ fs.unlinkSync(originalPath);
79
+ console.log(`SourceMapUploadPlugin: Deleted original sourcemap ${originalPath}`);
74
80
  }
75
- catch (renameError) {
76
- if (renameError.code === 'EXDEV') {
77
- fs.copyFileSync(originalPath, destinationPath);
78
- try {
79
- fs.unlinkSync(originalPath);
80
- }
81
- catch (deleteError) {
82
- console.warn(`SourceMapUploadPlugin: Failed to delete original sourcemap ${originalPath}.`, deleteError);
83
- }
84
- }
85
- else {
86
- throw renameError;
87
- }
81
+ catch (deleteError) {
82
+ console.warn(`SourceMapUploadPlugin: Failed to delete original sourcemap ${originalPath}.`, deleteError);
88
83
  }
89
- console.log(`SourceMapUploadPlugin: Consolidated ${assetName} to ${destinationPath}`);
90
84
  }
91
85
  catch (error) {
92
86
  const errorMessage = `SourceMapUploadPlugin: Failed to consolidate ${assetName}.`;
@@ -79,6 +79,5 @@ function getViewContextScriptContent() {
79
79
  if (appInfo.name) {
80
80
  window._appInfo = appInfo;
81
81
  }
82
- window.__BASENAME__ = "{{basename}}";
83
82
  `;
84
83
  }
@@ -0,0 +1 @@
1
+ export declare function isLocalDev(): boolean;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isLocalDev = isLocalDev;
4
+ function isLocalDev() {
5
+ if (process.env.NODE_ENV === 'production')
6
+ return false;
7
+ const flag = process.env.MIAODA_LOCAL_DEV;
8
+ return flag === '1' || flag === 'true';
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-rspack-preset",
3
- "version": "1.0.55",
3
+ "version": "1.0.56-alpha.0",
4
4
  "files": [
5
5
  "lib",
6
6
  "patches",
@@ -1,2 +0,0 @@
1
- declare function _exports(source: any): any;
2
- export = _exports;
@@ -1,23 +0,0 @@
1
- "use strict";
2
- /**
3
- * Basename Injection Loader
4
- *
5
- * 仅由 BasenameInjectionPlugin 在入口文件上挂载,做字符串替换:
6
- * process.env.CLIENT_BASE_PATH
7
- * → ((typeof window !== "undefined" && window.__BASENAME__) || "<envBasePath>")
8
- *
9
- * loader 阶段天然早于 DefinePlugin 的表达式替换(rspack source-transform → parsing → DefinePlugin 流水线顺序)。
10
- * 替换后的字符串里没有 process.env.X 形式,DefinePlugin 不会再二次替换。
11
- */
12
- module.exports = function basenameInjectionLoader(source) {
13
- const options = (typeof this.getOptions === 'function' ? this.getOptions() : this.query) || {};
14
- const envBasePath = options.envBasePath || '';
15
- if (typeof source !== 'string' || !source.includes('process.env.CLIENT_BASE_PATH')) {
16
- return source;
17
- }
18
- const fallback = JSON.stringify(envBasePath);
19
- const runtimeExpr = '((typeof window !== "undefined" && window.__BASENAME__) || ' +
20
- fallback +
21
- ')';
22
- return source.replace(/\bprocess\.env\.CLIENT_BASE_PATH\b/g, runtimeExpr);
23
- };
@@ -1,31 +0,0 @@
1
- /**
2
- * Basename Injection Plugin for Rspack
3
- *
4
- * 在 module rules 最前面注入一个 loader,仅匹配应用入口文件,
5
- * 把入口里的 process.env.CLIENT_BASE_PATH
6
- * 改写为运行时读 window.__BASENAME__(fallback 到构建时常量)。
7
- *
8
- * 让 BrowserRouter basename 运行时动态化,实现同一份 bundle 跑在不同 basename 下
9
- * (默认域名 `/app/<appId>`、自定义域名 `/` 或 `/<alias>`)。
10
- *
11
- * 入口文件识别:在 entryOption hook(配置已完全解析合并)中从 entry 提取业务入口路径,
12
- * 过滤掉 node_modules 包名(runtime 注入、polyfill 等非业务入口)和 Module Federation
13
- * 远程模块,生成精确匹配正则。兜底到 client/src/index.{tsx,ts,jsx,js} 正则。
14
- *
15
- * 关键约束:
16
- * - 只匹配入口文件,不影响 SDK 内部代码(如 client-toolkit-lite 仍读编译期常量)
17
- * - loader 阶段天然早于 DefinePlugin 的表达式替换(rspack 内部 source-transform → parsing → DefinePlugin 流水线顺序)
18
- * enforce:'pre' 是用来跟 babel-loader / swc-loader 等其他 loader 抢先,避免被它们先处理掉表达式
19
- * - 兜底常量来自构建时 process.env.CLIENT_BASE_PATH,保证降级运行
20
- */
21
- import type { Compiler } from '@rspack/core';
22
- export interface BasenameInjectionPluginOptions {
23
- /** 编译时兜底 basename,通常等于 process.env.CLIENT_BASE_PATH */
24
- envBasePath?: string;
25
- }
26
- export declare class BasenameInjectionPlugin {
27
- private readonly envBasePath;
28
- constructor(options?: BasenameInjectionPluginOptions);
29
- apply(compiler: Compiler): void;
30
- }
31
- export default BasenameInjectionPlugin;
@@ -1,115 +0,0 @@
1
- "use strict";
2
- /**
3
- * Basename Injection Plugin for Rspack
4
- *
5
- * 在 module rules 最前面注入一个 loader,仅匹配应用入口文件,
6
- * 把入口里的 process.env.CLIENT_BASE_PATH
7
- * 改写为运行时读 window.__BASENAME__(fallback 到构建时常量)。
8
- *
9
- * 让 BrowserRouter basename 运行时动态化,实现同一份 bundle 跑在不同 basename 下
10
- * (默认域名 `/app/<appId>`、自定义域名 `/` 或 `/<alias>`)。
11
- *
12
- * 入口文件识别:在 entryOption hook(配置已完全解析合并)中从 entry 提取业务入口路径,
13
- * 过滤掉 node_modules 包名(runtime 注入、polyfill 等非业务入口)和 Module Federation
14
- * 远程模块,生成精确匹配正则。兜底到 client/src/index.{tsx,ts,jsx,js} 正则。
15
- *
16
- * 关键约束:
17
- * - 只匹配入口文件,不影响 SDK 内部代码(如 client-toolkit-lite 仍读编译期常量)
18
- * - loader 阶段天然早于 DefinePlugin 的表达式替换(rspack 内部 source-transform → parsing → DefinePlugin 流水线顺序)
19
- * enforce:'pre' 是用来跟 babel-loader / swc-loader 等其他 loader 抢先,避免被它们先处理掉表达式
20
- * - 兜底常量来自构建时 process.env.CLIENT_BASE_PATH,保证降级运行
21
- */
22
- var __importDefault = (this && this.__importDefault) || function (mod) {
23
- return (mod && mod.__esModule) ? mod : { "default": mod };
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.BasenameInjectionPlugin = void 0;
27
- const path_1 = __importDefault(require("path"));
28
- const PLUGIN_NAME = 'BasenameInjectionPlugin';
29
- const FALLBACK_ENTRY_REGEX = /[/\\]client[/\\]src[/\\]index\.(tsx?|jsx?)$/;
30
- /**
31
- * 判断 import 路径是否为本地业务文件(相对路径或绝对路径),
32
- * 排除 node_modules 包名(runtime 注入、polyfill、HMR client 等)。
33
- */
34
- function isLocalFile(imp) {
35
- return imp.startsWith('.') || imp.startsWith('/');
36
- }
37
- /**
38
- * 从 entry 配置中提取所有本地业务入口文件的绝对路径。
39
- *
40
- * entry 结构(entryOption hook 阶段已规范化):
41
- * { main: { import: ['@lark-apaas/client-toolkit/runtime', './client/src/index.tsx'] } }
42
- *
43
- * 过滤规则:
44
- * - 跳过非本地路径(node_modules 包名,如 runtime 注入、polyfill、HMR client)
45
- * - 跳过 node_modules 内的文件(Module Federation 远程模块等)
46
- */
47
- function resolveEntryFiles(entry, context) {
48
- const files = [];
49
- for (const descriptor of Object.values(entry)) {
50
- const imports = descriptor.import;
51
- if (!Array.isArray(imports))
52
- continue;
53
- for (const imp of imports) {
54
- if (!isLocalFile(imp))
55
- continue;
56
- const resolved = path_1.default.resolve(context, imp);
57
- if (resolved.includes('node_modules'))
58
- continue;
59
- files.push(resolved);
60
- }
61
- }
62
- return files;
63
- }
64
- /**
65
- * 构建入口文件匹配正则:精确匹配 resolved 路径,无路径时兜底默认正则。
66
- * 路径分隔符统一用 [/\\] 匹配,避免跨平台问题(rspack 传给 test 的路径分隔符不确定)。
67
- */
68
- function buildEntryTest(entryFiles) {
69
- if (entryFiles.length === 0)
70
- return FALLBACK_ENTRY_REGEX;
71
- const escaped = entryFiles.map(f => {
72
- return path_1.default.normalize(f)
73
- .split(path_1.default.sep) // 按平台分隔符拆段
74
- .map(seg => seg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) // 每段转义正则特殊字符
75
- .join('[/\\\\]'); // 用 [/\\] 重新拼接
76
- });
77
- return new RegExp(`(${escaped.join('|')})$`);
78
- }
79
- class BasenameInjectionPlugin {
80
- constructor(options = {}) {
81
- this.envBasePath = options.envBasePath ?? process.env.CLIENT_BASE_PATH ?? '';
82
- }
83
- apply(compiler) {
84
- const loaderPath = path_1.default.resolve(__dirname, './basename-injection-loader.js');
85
- const envBasePath = this.envBasePath;
86
- // entryOption hook:配置已完全解析合并(包括异步 entry 函数的返回值),
87
- // 是读 entry 最安全的时机。参考 RuntimeInjectionPlugin 的做法。
88
- compiler.hooks.entryOption.tap(PLUGIN_NAME, (context, entry) => {
89
- // 动态 entry(函数形式)无法在此阶段解析,兜底到默认正则
90
- const entryFiles = (typeof entry !== 'function')
91
- ? resolveEntryFiles(entry, context)
92
- : [];
93
- const entryTest = buildEntryTest(entryFiles);
94
- if (!compiler.options.module) {
95
- compiler.options.module = { rules: [] };
96
- }
97
- const rules = compiler.options.module.rules || [];
98
- compiler.options.module.rules = [
99
- {
100
- test: entryTest,
101
- enforce: 'pre',
102
- use: [
103
- {
104
- loader: loaderPath,
105
- options: { envBasePath },
106
- },
107
- ],
108
- },
109
- ...rules,
110
- ];
111
- });
112
- }
113
- }
114
- exports.BasenameInjectionPlugin = BasenameInjectionPlugin;
115
- exports.default = BasenameInjectionPlugin;