@lark-apaas/fullstack-presets 0.1.1 → 1.0.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.
|
@@ -57,6 +57,17 @@ exports.default = {
|
|
|
57
57
|
'no-constant-binary-expression': 'off', // 不强制使用常量二进制表达式
|
|
58
58
|
// React Refresh 相关 - 开发时不影响渲染的检测
|
|
59
59
|
'react-refresh/only-export-components': 'off',
|
|
60
|
+
'no-restricted-imports': [
|
|
61
|
+
'error',
|
|
62
|
+
{
|
|
63
|
+
paths: [
|
|
64
|
+
{
|
|
65
|
+
name: 'next/link',
|
|
66
|
+
message: "Importing from 'next/link' is prohibited. Please use the `Link` component from 'react-router-dom' for navigation.",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
60
71
|
'no-restricted-syntax': [
|
|
61
72
|
'error',
|
|
62
73
|
// 限制使用 fetch(推荐使用生成的 API Client)
|
|
@@ -86,6 +97,21 @@ exports.default = {
|
|
|
86
97
|
message: "Please don't use confirm. It may conflict with window.confirm BOM method, use `Dialog` component instead for better user experience and consistency",
|
|
87
98
|
selector: "CallExpression[callee.name='confirm']",
|
|
88
99
|
},
|
|
100
|
+
// 禁用`window.location.href`赋值使用,可以读取
|
|
101
|
+
{
|
|
102
|
+
selector: 'AssignmentExpression[left.object.object.name="window"][left.object.property.name="location"][left.property.name="href"]',
|
|
103
|
+
message: "Please don't use `window.location.href` to navigate. Use `useNavigate` hook from 'react-router-dom' instead.",
|
|
104
|
+
},
|
|
105
|
+
// 禁止`location.href`赋值使用,可以读取
|
|
106
|
+
{
|
|
107
|
+
selector: 'AssignmentExpression[left.object.name="location"][left.property.name="href"]',
|
|
108
|
+
message: "Please don't use `location.href` to navigate. Use `useNavigate` hook from 'react-router-dom' instead.",
|
|
109
|
+
},
|
|
110
|
+
// SelectItem组件的value属性值不能为空字符串
|
|
111
|
+
{
|
|
112
|
+
message: 'The `value` attribute of the `SelectItem` component cannot be an empty string.',
|
|
113
|
+
selector: 'JSXOpeningElement[name.name="SelectItem"]:has(JSXAttribute[name.name="value"][value.value=""]), JSXOpeningElement[name.name="SelectItem"]:has(JSXAttribute[name.name="value"][value.expression.value=""])',
|
|
114
|
+
},
|
|
89
115
|
],
|
|
90
116
|
},
|
|
91
117
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type Configuration } from '@rspack/core';
|
|
2
|
-
export declare function createRecommendRspackConfig(options: {
|
|
3
|
-
enableReactRefresh?: boolean;
|
|
4
|
-
isDev?: boolean;
|
|
5
|
-
/** 是否需要插件解析路由 */
|
|
6
|
-
needRoutes?: boolean;
|
|
7
|
-
/** 运行态模式,默认为全栈模式 */
|
|
8
|
-
runtimeMode?: string;
|
|
9
|
-
}): Configuration;
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createRecommendRspackConfig = createRecommendRspackConfig;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const core_1 = __importDefault(require("@rspack/core"));
|
|
9
|
-
const dev_server_listener_1 = require("../utils/dev-server-listener");
|
|
10
|
-
const RouteParserPlugin = require('../rspack-plugins/route-parser-plugin');
|
|
11
|
-
const SlardarPerformanceMonitorPlugin = require('../rspack-plugins/slardar-performance-monitor-plugin');
|
|
12
|
-
function createRecommendRspackConfig(options) {
|
|
13
|
-
const { enableReactRefresh = false, isDev = true, needRoutes = true, runtimeMode = 'fullstack', } = options;
|
|
14
|
-
return {
|
|
15
|
-
experiments: {
|
|
16
|
-
css: true,
|
|
17
|
-
},
|
|
18
|
-
module: {
|
|
19
|
-
rules: [
|
|
20
|
-
{
|
|
21
|
-
test: /\.svg$/,
|
|
22
|
-
type: 'asset',
|
|
23
|
-
parser: {
|
|
24
|
-
dataUrlCondition: {
|
|
25
|
-
maxSize: 10 * 1024, // 对应vite的assetsInlineLimit
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
test: /\.(png|jpe?g|gif|webp|ico)$/,
|
|
31
|
-
type: 'asset',
|
|
32
|
-
parser: {
|
|
33
|
-
dataUrlCondition: {
|
|
34
|
-
maxSize: 10 * 1024, // 对应vite的assetsInlineLimit
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
test: /\.css$/,
|
|
40
|
-
use: [
|
|
41
|
-
{
|
|
42
|
-
loader: 'postcss-loader',
|
|
43
|
-
options: {
|
|
44
|
-
postcssOptions: {
|
|
45
|
-
plugins: [
|
|
46
|
-
[
|
|
47
|
-
'postcss-import',
|
|
48
|
-
{
|
|
49
|
-
resolve: (id, _basedir) => {
|
|
50
|
-
// 只有dev环境需要打包选中精调所需的一些预置样式, prod环境则不打包
|
|
51
|
-
if (id === '@/inspector.dev.css') {
|
|
52
|
-
return isDev
|
|
53
|
-
? path_1.default.join(_basedir, '/inspector.dev.css')
|
|
54
|
-
: [];
|
|
55
|
-
}
|
|
56
|
-
return id;
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
type: 'css',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
test: /\.tsx?$/,
|
|
69
|
-
use: [
|
|
70
|
-
{
|
|
71
|
-
loader: 'builtin:swc-loader',
|
|
72
|
-
options: {
|
|
73
|
-
/**
|
|
74
|
-
* @type {import('@swc/core').JscConfig}
|
|
75
|
-
*/
|
|
76
|
-
jsc: {
|
|
77
|
-
parser: {
|
|
78
|
-
syntax: 'typescript',
|
|
79
|
-
jsx: true,
|
|
80
|
-
},
|
|
81
|
-
transform: {
|
|
82
|
-
react: {
|
|
83
|
-
runtime: 'automatic',
|
|
84
|
-
...(isDev
|
|
85
|
-
? {
|
|
86
|
-
importSource: path_1.default.dirname(require.resolve('@lark-apaas/miaoda-inspector-jsx-runtime')),
|
|
87
|
-
}
|
|
88
|
-
: {}),
|
|
89
|
-
development: isDev,
|
|
90
|
-
refresh: enableReactRefresh,
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
...(isDev
|
|
97
|
-
? [require.resolve('@lark-apaas/miaoda-inspector-babel-plugin')]
|
|
98
|
-
: []),
|
|
99
|
-
],
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
},
|
|
103
|
-
plugins: [
|
|
104
|
-
// 针对 clsx/echarts 等包,让其默认好用
|
|
105
|
-
new core_1.default.NormalModuleReplacementPlugin(/^(clsx|echarts)$/, function (resource) {
|
|
106
|
-
if (!resource.context.endsWith('module-alias')) {
|
|
107
|
-
if (resource.request.endsWith('echarts')) {
|
|
108
|
-
resource.request = require.resolve('../module-alias/echarts.js');
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
resource.request = require.resolve('../module-alias/clsx.js');
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}),
|
|
115
|
-
new core_1.default.DefinePlugin({
|
|
116
|
-
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
|
117
|
-
'process.env.CWD': JSON.stringify(process.cwd()),
|
|
118
|
-
'process.env.runtimeMode': JSON.stringify(runtimeMode),
|
|
119
|
-
}),
|
|
120
|
-
new core_1.default.optimize.LimitChunkCountPlugin({
|
|
121
|
-
maxChunks: 1,
|
|
122
|
-
}),
|
|
123
|
-
// 全栈模式下,增加性能监控上报脚本注入
|
|
124
|
-
runtimeMode === 'fullstack'
|
|
125
|
-
? new SlardarPerformanceMonitorPlugin()
|
|
126
|
-
: undefined,
|
|
127
|
-
// 开发环境下,解析路由
|
|
128
|
-
isDev &&
|
|
129
|
-
needRoutes &&
|
|
130
|
-
new RouteParserPlugin({
|
|
131
|
-
appPath: './client/src/app.tsx',
|
|
132
|
-
outputPath: path_1.default.resolve(__dirname, 'dist/client/routes.json'),
|
|
133
|
-
}),
|
|
134
|
-
],
|
|
135
|
-
optimization: isDev
|
|
136
|
-
? {}
|
|
137
|
-
: {
|
|
138
|
-
moduleIds: 'deterministic',
|
|
139
|
-
concatenateModules: true,
|
|
140
|
-
minimize: true, // 对应vite的minify配置
|
|
141
|
-
minimizer: [
|
|
142
|
-
new core_1.default.SwcJsMinimizerRspackPlugin({
|
|
143
|
-
minimizerOptions: {
|
|
144
|
-
// 保持不压缩
|
|
145
|
-
minify: !isDev,
|
|
146
|
-
mangle: !isDev,
|
|
147
|
-
format: {
|
|
148
|
-
beautify: isDev,
|
|
149
|
-
comments: false,
|
|
150
|
-
},
|
|
151
|
-
compress: {
|
|
152
|
-
keep_classnames: true,
|
|
153
|
-
keep_fnames: true,
|
|
154
|
-
keep_fargs: !isDev,
|
|
155
|
-
unused: true,
|
|
156
|
-
dead_code: true,
|
|
157
|
-
drop_debugger: true,
|
|
158
|
-
// FIXME: 先临时开始 console.log
|
|
159
|
-
// drop_console: !isDev,
|
|
160
|
-
const_to_let: !isDev,
|
|
161
|
-
booleans_as_integers: !isDev,
|
|
162
|
-
booleans: !isDev,
|
|
163
|
-
// maybe unsafe
|
|
164
|
-
reduce_funcs: !isDev,
|
|
165
|
-
reduce_vars: !isDev,
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
}),
|
|
169
|
-
new core_1.default.LightningCssMinimizerRspackPlugin(),
|
|
170
|
-
],
|
|
171
|
-
sideEffects: true,
|
|
172
|
-
usedExports: true,
|
|
173
|
-
innerGraph: true,
|
|
174
|
-
providedExports: true,
|
|
175
|
-
mergeDuplicateChunks: true,
|
|
176
|
-
splitChunks: false, // 禁用代码分割,保持单文件输出
|
|
177
|
-
},
|
|
178
|
-
devtool: isDev ? 'source-map' : false, // 对应vite的sourcemap配置
|
|
179
|
-
devServer: {
|
|
180
|
-
headers: req => {
|
|
181
|
-
// 获取请求的Origin头
|
|
182
|
-
const requestOrigin = req.headers.origin ?? '';
|
|
183
|
-
// 定义允许的域名白名单
|
|
184
|
-
const allowedOrigins = [
|
|
185
|
-
'https://miaoda.feishu.cn',
|
|
186
|
-
'https://miaoda.feishu-boe.cn',
|
|
187
|
-
'https://miaoda.feishu-pre.cn',
|
|
188
|
-
];
|
|
189
|
-
// 检查请求的Origin是否在允许的列表中
|
|
190
|
-
const allowedOrigin = allowedOrigins.includes(requestOrigin)
|
|
191
|
-
? requestOrigin
|
|
192
|
-
: allowedOrigins[0]; // 默认返回第一个允许的域名
|
|
193
|
-
return {
|
|
194
|
-
'Access-Control-Allow-Origin': allowedOrigin,
|
|
195
|
-
};
|
|
196
|
-
},
|
|
197
|
-
onListening(server) {
|
|
198
|
-
if (runtimeMode === 'fullstack') {
|
|
199
|
-
(0, dev_server_listener_1.listenHmrTiming)(server);
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
client: {
|
|
203
|
-
overlay: {
|
|
204
|
-
errors: false,
|
|
205
|
-
warnings: false,
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.listenHmrTiming = listenHmrTiming;
|
|
37
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
/**
|
|
40
|
-
* 监听 HMR 编译时间
|
|
41
|
-
* @param server
|
|
42
|
-
*/
|
|
43
|
-
function listenHmrTiming(server) {
|
|
44
|
-
const compiler = server.compiler;
|
|
45
|
-
const ws = server.webSocketServer;
|
|
46
|
-
let start = 0;
|
|
47
|
-
let changedFiles = new Set();
|
|
48
|
-
// 监听文件变更
|
|
49
|
-
compiler.hooks.watchRun.tap('HmrTiming', (comp) => {
|
|
50
|
-
const modifiedFiles = comp.modifiedFiles;
|
|
51
|
-
if (modifiedFiles && modifiedFiles.size > 0) {
|
|
52
|
-
changedFiles = new Set(modifiedFiles);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
changedFiles.clear();
|
|
56
|
-
}
|
|
57
|
-
start = Date.now();
|
|
58
|
-
});
|
|
59
|
-
// 编译完成
|
|
60
|
-
compiler.hooks.done.tap('HmrTiming', () => {
|
|
61
|
-
const duration = Date.now() - start;
|
|
62
|
-
// 过滤有效文件 - 只保留实际的项目文件
|
|
63
|
-
const validFiles = [...changedFiles]
|
|
64
|
-
.map(f => f.replace(process.cwd(), '').replace(/\\/g, '/'))
|
|
65
|
-
.filter(filePath => {
|
|
66
|
-
// 过滤无效文件
|
|
67
|
-
if (!filePath || filePath.length === 0)
|
|
68
|
-
return false;
|
|
69
|
-
// 过滤 node_modules 中的文件
|
|
70
|
-
if (filePath.includes('/node_modules/'))
|
|
71
|
-
return false;
|
|
72
|
-
// 只保留支持的文件类型
|
|
73
|
-
const validExtensions = ['.js', '.jsx', '.ts', '.tsx', '.svelte', '.css', '.json', '.html'];
|
|
74
|
-
const hasValidExtension = validExtensions.some(ext => filePath.toLowerCase().endsWith(ext));
|
|
75
|
-
if (!hasValidExtension)
|
|
76
|
-
return false;
|
|
77
|
-
return true;
|
|
78
|
-
});
|
|
79
|
-
// 获取文件统计信息
|
|
80
|
-
const fileStats = validFiles.map(filePath => {
|
|
81
|
-
const fullPath = process.cwd() + filePath;
|
|
82
|
-
try {
|
|
83
|
-
const stats = fs.statSync(fullPath);
|
|
84
|
-
return {
|
|
85
|
-
path: filePath,
|
|
86
|
-
size: stats.size,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
return {
|
|
91
|
-
path: filePath,
|
|
92
|
-
size: 0,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
// 计算总文件大小(B)
|
|
97
|
-
const totalSize = fileStats.reduce((sum, file) => sum + file.size, 0);
|
|
98
|
-
const payload = {
|
|
99
|
-
duration,
|
|
100
|
-
fileCount: validFiles.length,
|
|
101
|
-
fileTotalSize: totalSize,
|
|
102
|
-
};
|
|
103
|
-
// 推送到浏览器端
|
|
104
|
-
for (const client of ws.clients) {
|
|
105
|
-
if (client.readyState === 1) {
|
|
106
|
-
client.send(JSON.stringify({
|
|
107
|
-
type: 'hmr-timing',
|
|
108
|
-
data: payload,
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|