@lark-apaas/fullstack-presets 1.0.13 → 1.1.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/README.md +2 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +13 -1
- package/lib/recommend/eslint/eslint-client.js +1 -1
- package/lib/recommend/rspack/index.d.ts +9 -0
- package/lib/recommend/rspack/index.js +210 -0
- package/lib/simple/recommend/eslint/eslint-client.d.ts +29 -0
- package/lib/simple/recommend/eslint/eslint-client.js +140 -0
- package/lib/simple/recommend/eslint/eslint-server.d.ts +53 -0
- package/lib/simple/recommend/eslint/eslint-server.js +73 -0
- package/lib/simple/recommend/eslint/index.d.ts +37 -0
- package/lib/simple/recommend/eslint/index.js +30 -0
- package/lib/simple/recommend/stylelint/index.d.ts +5 -0
- package/lib/simple/recommend/stylelint/index.js +8 -0
- package/lib/simple/recommend/tailwind/createTailwindPreset.d.ts +8 -0
- package/lib/simple/recommend/tailwind/createTailwindPreset.js +63 -0
- package/lib/simple/tsconfig/tsconfig.app.json +30 -0
- package/lib/simple/tsconfig/tsconfig.node.json +25 -0
- package/lib/utils/dev-server-listener.d.ts +5 -0
- package/lib/utils/dev-server-listener.js +113 -0
- package/package.json +1 -1
package/README.md
ADDED
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
export { createTailwindPreset } from './recommend/tailwind/createTailwindPreset';
|
|
2
2
|
export { eslintPresets } from './recommend/eslint';
|
|
3
3
|
export { stylelintPresets } from './recommend/stylelint';
|
|
4
|
+
/**
|
|
5
|
+
* NestJs 精简版本配置(妙搭专用)
|
|
6
|
+
*/
|
|
7
|
+
export { createTailwindPreset as createTailwindPresetOfSimple } from './simple/recommend/tailwind/createTailwindPreset';
|
|
8
|
+
export { eslintPresets as eslintPresetsOfSimple } from './simple/recommend/eslint';
|
|
9
|
+
export { stylelintPresets as stylelintPresetsOfSimple } from './simple/recommend/stylelint';
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.stylelintPresets = exports.eslintPresets = exports.createTailwindPreset = void 0;
|
|
3
|
+
exports.stylelintPresetsOfSimple = exports.eslintPresetsOfSimple = exports.createTailwindPresetOfSimple = exports.stylelintPresets = exports.eslintPresets = exports.createTailwindPreset = void 0;
|
|
4
4
|
// Tailwind 配置
|
|
5
5
|
var createTailwindPreset_1 = require("./recommend/tailwind/createTailwindPreset");
|
|
6
6
|
Object.defineProperty(exports, "createTailwindPreset", { enumerable: true, get: function () { return createTailwindPreset_1.createTailwindPreset; } });
|
|
@@ -10,3 +10,15 @@ Object.defineProperty(exports, "eslintPresets", { enumerable: true, get: functio
|
|
|
10
10
|
// Stylelint 配置
|
|
11
11
|
var stylelint_1 = require("./recommend/stylelint");
|
|
12
12
|
Object.defineProperty(exports, "stylelintPresets", { enumerable: true, get: function () { return stylelint_1.stylelintPresets; } });
|
|
13
|
+
/**
|
|
14
|
+
* NestJs 精简版本配置(妙搭专用)
|
|
15
|
+
*/
|
|
16
|
+
// Tailwind 配置
|
|
17
|
+
var createTailwindPreset_2 = require("./simple/recommend/tailwind/createTailwindPreset");
|
|
18
|
+
Object.defineProperty(exports, "createTailwindPresetOfSimple", { enumerable: true, get: function () { return createTailwindPreset_2.createTailwindPreset; } });
|
|
19
|
+
// Eslint 配置
|
|
20
|
+
var eslint_2 = require("./simple/recommend/eslint");
|
|
21
|
+
Object.defineProperty(exports, "eslintPresetsOfSimple", { enumerable: true, get: function () { return eslint_2.eslintPresets; } });
|
|
22
|
+
// Stylelint 配置
|
|
23
|
+
var stylelint_2 = require("./simple/recommend/stylelint");
|
|
24
|
+
Object.defineProperty(exports, "stylelintPresetsOfSimple", { enumerable: true, get: function () { return stylelint_2.stylelintPresets; } });
|
|
@@ -132,7 +132,7 @@ exports.default = {
|
|
|
132
132
|
},
|
|
133
133
|
// 禁止使用 text-accent
|
|
134
134
|
{
|
|
135
|
-
selector: 'JSXAttribute[name.name="className"][value.value=/text-accent/]',
|
|
135
|
+
selector: 'JSXAttribute[name.name="className"][value.value=/(^|\\s)text-accent(\\s|$)/]',
|
|
136
136
|
message: 'Classname "text-accent" would cause visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
137
137
|
},
|
|
138
138
|
],
|
|
@@ -0,0 +1,9 @@
|
|
|
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;
|
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
name: string;
|
|
3
|
+
languageOptions: {
|
|
4
|
+
ecmaVersion: number;
|
|
5
|
+
parser: any;
|
|
6
|
+
parserOptions: {
|
|
7
|
+
ecmaFeatures: {
|
|
8
|
+
jsx: boolean;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
globals: any;
|
|
12
|
+
};
|
|
13
|
+
plugins: {
|
|
14
|
+
'react-hooks': any;
|
|
15
|
+
import: any;
|
|
16
|
+
};
|
|
17
|
+
settings: {
|
|
18
|
+
'import/resolver': {
|
|
19
|
+
node: {
|
|
20
|
+
extensions: string[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
'import/parsers': {
|
|
24
|
+
'@typescript-eslint/parser': string[];
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
rules: any;
|
|
28
|
+
};
|
|
29
|
+
export default _default;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals = require('globals');
|
|
4
|
+
const reactHooks = require('eslint-plugin-react-hooks');
|
|
5
|
+
const tseslint = require('typescript-eslint');
|
|
6
|
+
const importPlugin = require('eslint-plugin-import');
|
|
7
|
+
exports.default = {
|
|
8
|
+
name: 'fullstack-presets/client-recommend',
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2020,
|
|
11
|
+
parser: tseslint.parser,
|
|
12
|
+
parserOptions: {
|
|
13
|
+
ecmaFeatures: {
|
|
14
|
+
jsx: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
globals: {
|
|
18
|
+
...globals.browser,
|
|
19
|
+
...globals.node,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
plugins: {
|
|
23
|
+
'react-hooks': reactHooks,
|
|
24
|
+
import: importPlugin,
|
|
25
|
+
},
|
|
26
|
+
settings: {
|
|
27
|
+
'import/resolver': {
|
|
28
|
+
node: {
|
|
29
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
'import/parsers': {
|
|
33
|
+
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
rules: {
|
|
37
|
+
// React Hooks 推荐规则
|
|
38
|
+
...reactHooks.configs.recommended.rules,
|
|
39
|
+
// TypeScript 规则
|
|
40
|
+
'@typescript-eslint/no-unused-vars': 'off', // 未使用变量检查关闭
|
|
41
|
+
'@typescript-eslint/no-explicit-any': 'off', // 允许使用 any 类型
|
|
42
|
+
'@typescript-eslint/no-empty-interface': 'off', // 允许空接口
|
|
43
|
+
'@typescript-eslint/no-empty-object-type': 'off', // 允许空对象类型
|
|
44
|
+
'@typescript-eslint/no-redeclare': 'error', // 禁止重复声明
|
|
45
|
+
// React 规则
|
|
46
|
+
'react/jsx-no-undef-props': 'off', // 不检查未定义的 props
|
|
47
|
+
'react/no-unknown-property': 'off', // 允许未知属性
|
|
48
|
+
'react-hooks/exhaustive-deps': 'off', // 不强制检查依赖数组
|
|
49
|
+
// JavaScript 基础规则
|
|
50
|
+
'no-undef': 'off', // 禁止使用未声明的变量
|
|
51
|
+
'no-console': 'off', // 允许使用 console
|
|
52
|
+
'prefer-const': 'off', // 不强制使用 const
|
|
53
|
+
// Import 规则
|
|
54
|
+
'import/no-unresolved': 'error', // 检查导入路径是否存在
|
|
55
|
+
// 其他规则
|
|
56
|
+
'no-constant-binary-expression': 'off', // 不强制使用常量二进制表达式
|
|
57
|
+
// React Refresh 相关 - 开发时不影响渲染的检测
|
|
58
|
+
'react-refresh/only-export-components': 'off',
|
|
59
|
+
'no-restricted-imports': [
|
|
60
|
+
'error',
|
|
61
|
+
{
|
|
62
|
+
paths: [
|
|
63
|
+
{
|
|
64
|
+
name: 'next/link',
|
|
65
|
+
message: "Importing from 'next/link' is prohibited. Please use the `Link` component from 'react-router-dom' for navigation.",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
'no-restricted-syntax': [
|
|
71
|
+
'error',
|
|
72
|
+
// 限制使用 fetch(推荐使用生成的 API Client)
|
|
73
|
+
{
|
|
74
|
+
message: "Please don't use fetch, use Generated API Client instead",
|
|
75
|
+
selector: "CallExpression[callee.name='fetch'][arguments.0.type='Literal'][arguments.0.value=/^\\u002F/]",
|
|
76
|
+
},
|
|
77
|
+
// 避免使用 console.log,推荐使用 logger 库
|
|
78
|
+
{
|
|
79
|
+
selector: "CallExpression[callee.object.name='console'][callee.property.name=/^(log|warn|info|debug|trace)$/]",
|
|
80
|
+
message: 'Avoid using console.log, console.warn, etc. Use `@lark-apaas/client-toolkit/logger` instead.',
|
|
81
|
+
},
|
|
82
|
+
// 禁用BOM方法alert和confirm,及不推荐使用同名方法
|
|
83
|
+
{
|
|
84
|
+
message: "Please don't use window.alert, use `Dialog` component instead for better user experience and consistency",
|
|
85
|
+
selector: "MemberExpression[object.name='window'][property.name='alert']",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
message: "Please don't use window.confirm, use `Dialog` component instead for better user experience and consistency",
|
|
89
|
+
selector: "MemberExpression[object.name='window'][property.name='confirm']",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
message: "Please don't use alert. It may conflict with window.alert BOM method, use `Dialog` component instead for better user experience and consistency",
|
|
93
|
+
selector: "CallExpression[callee.name='alert']",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
message: "Please don't use confirm. It may conflict with window.confirm BOM method, use `Dialog` component instead for better user experience and consistency",
|
|
97
|
+
selector: "CallExpression[callee.name='confirm']",
|
|
98
|
+
},
|
|
99
|
+
// 禁用`window.location.href`赋值使用,可以读取
|
|
100
|
+
{
|
|
101
|
+
selector: 'AssignmentExpression[left.object.object.name="window"][left.object.property.name="location"][left.property.name="href"]',
|
|
102
|
+
message: "Please don't use `window.location.href` to navigate. Use `useNavigate` hook from 'react-router-dom' instead.",
|
|
103
|
+
},
|
|
104
|
+
// 禁止`location.href`赋值使用,可以读取
|
|
105
|
+
{
|
|
106
|
+
selector: 'AssignmentExpression[left.object.name="location"][left.property.name="href"]',
|
|
107
|
+
message: "Please don't use `location.href` to navigate. Use `useNavigate` hook from 'react-router-dom' instead.",
|
|
108
|
+
},
|
|
109
|
+
// SelectItem组件的value属性值不能为空字符串
|
|
110
|
+
{
|
|
111
|
+
message: 'The `value` attribute of the `SelectItem` component cannot be an empty string.',
|
|
112
|
+
selector: 'JSXOpeningElement[name.name="SelectItem"]:has(JSXAttribute[name.name="value"][value.value=""]), JSXOpeningElement[name.name="SelectItem"]:has(JSXAttribute[name.name="value"][value.expression.value=""])',
|
|
113
|
+
},
|
|
114
|
+
// 禁止a标签href使用相对路径
|
|
115
|
+
{
|
|
116
|
+
selector: "JSXOpeningElement[name.name='a']:has(JSXAttribute[name.name='href'][value.value=/^(?!https?:|\\u002F\\u002F|mailto:|tel:|#).+/])",
|
|
117
|
+
message: "Please don't use relative paths in <a> tags. Use NavLink from 'react-router-dom' instead.",
|
|
118
|
+
},
|
|
119
|
+
// 禁止 variant 为 outline|link|ghost 的 Button 使用 text-white
|
|
120
|
+
{
|
|
121
|
+
selector: 'JSXElement[openingElement.name.name="Button"]' +
|
|
122
|
+
':has(JSXAttribute[name.name="variant"][value.value=/^(outline|link|ghost)$/])' +
|
|
123
|
+
':has(JSXAttribute[name.name="className"][value.value=/text-white/])',
|
|
124
|
+
message: 'Button with variant="outline|link|ghost" should not use "text-white" className. This causes visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
125
|
+
},
|
|
126
|
+
// 禁止在 Button 上组合 text-primary-foreground 与 bg-background 并用的情况
|
|
127
|
+
{
|
|
128
|
+
selector: 'JSXElement[openingElement.name.name="Button"]' +
|
|
129
|
+
':has(JSXAttribute[name.name="className"][value.value=/text-primary-foreground/])' +
|
|
130
|
+
':has(JSXAttribute[name.name="className"][value.value=/bg-background/])',
|
|
131
|
+
message: 'Button should not use "text-primary-foreground" and "bg-background" className. This causes visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
132
|
+
},
|
|
133
|
+
// 禁止使用 text-accent
|
|
134
|
+
{
|
|
135
|
+
selector: 'JSXAttribute[name.name="className"][value.value=/(^|\\s)text-accent(\\s|$)/]',
|
|
136
|
+
message: 'Classname "text-accent" would cause visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
name: string;
|
|
3
|
+
languageOptions: {
|
|
4
|
+
ecmaVersion: number;
|
|
5
|
+
sourceType: string;
|
|
6
|
+
parser: any;
|
|
7
|
+
globals: any;
|
|
8
|
+
};
|
|
9
|
+
plugins: {
|
|
10
|
+
import: any;
|
|
11
|
+
};
|
|
12
|
+
settings: {
|
|
13
|
+
'import/resolver': {
|
|
14
|
+
node: {
|
|
15
|
+
extensions: string[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
'import/parsers': {
|
|
19
|
+
'@typescript-eslint/parser': string[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
rules: {
|
|
23
|
+
'@typescript-eslint/no-unused-vars': string;
|
|
24
|
+
'@typescript-eslint/no-explicit-any': string;
|
|
25
|
+
'@typescript-eslint/no-empty-interface': string;
|
|
26
|
+
'@typescript-eslint/no-empty-object-type': string;
|
|
27
|
+
'@typescript-eslint/no-redeclare': string;
|
|
28
|
+
'@typescript-eslint/no-extraneous-class': string;
|
|
29
|
+
'no-undef': string;
|
|
30
|
+
'no-console': string;
|
|
31
|
+
'prefer-const': string;
|
|
32
|
+
'import/no-unresolved': string;
|
|
33
|
+
'import/no-extraneous-dependencies': string;
|
|
34
|
+
'no-restricted-imports': (string | {
|
|
35
|
+
patterns: {
|
|
36
|
+
group: string[];
|
|
37
|
+
message: string;
|
|
38
|
+
}[];
|
|
39
|
+
})[];
|
|
40
|
+
'@darraghor/nestjs-typed/injectable-should-be-provided': (string | {
|
|
41
|
+
src: string[];
|
|
42
|
+
})[];
|
|
43
|
+
'@darraghor/nestjs-typed/api-property-returning-array-should-set-array': string;
|
|
44
|
+
'@darraghor/nestjs-typed/api-property-matches-property-optionality': string;
|
|
45
|
+
'@darraghor/nestjs-typed/all-properties-have-explicit-defined': string;
|
|
46
|
+
'@darraghor/nestjs-typed/controllers-should-supply-api-tags': string;
|
|
47
|
+
'@darraghor/nestjs-typed/api-method-should-specify-api-response': string;
|
|
48
|
+
'@darraghor/nestjs-typed/api-method-should-specify-api-operation': string;
|
|
49
|
+
'@darraghor/nestjs-typed/api-enum-property-best-practices': string;
|
|
50
|
+
'@darraghor/nestjs-typed/all-properties-are-whitelisted': string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export default _default;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals = require('globals');
|
|
4
|
+
const tseslint = require('typescript-eslint');
|
|
5
|
+
const importPlugin = require('eslint-plugin-import');
|
|
6
|
+
exports.default = {
|
|
7
|
+
name: 'fullstack-presets/server-recommend',
|
|
8
|
+
languageOptions: {
|
|
9
|
+
ecmaVersion: 2022,
|
|
10
|
+
sourceType: 'module',
|
|
11
|
+
parser: tseslint.parser,
|
|
12
|
+
globals: {
|
|
13
|
+
...globals.node,
|
|
14
|
+
NodeJS: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
plugins: {
|
|
18
|
+
import: importPlugin,
|
|
19
|
+
},
|
|
20
|
+
settings: {
|
|
21
|
+
'import/resolver': {
|
|
22
|
+
node: {
|
|
23
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
'import/parsers': {
|
|
27
|
+
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
rules: {
|
|
31
|
+
// TypeScript 规则
|
|
32
|
+
'@typescript-eslint/no-unused-vars': 'off',
|
|
33
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
34
|
+
'@typescript-eslint/no-empty-interface': 'off',
|
|
35
|
+
'@typescript-eslint/no-empty-object-type': 'off',
|
|
36
|
+
'@typescript-eslint/no-redeclare': 'error',
|
|
37
|
+
'@typescript-eslint/no-extraneous-class': 'off',
|
|
38
|
+
// JavaScript 基础规则
|
|
39
|
+
'no-undef': 'off',
|
|
40
|
+
'no-console': 'error',
|
|
41
|
+
'prefer-const': 'off',
|
|
42
|
+
// Import 规则
|
|
43
|
+
'import/no-unresolved': 'error',
|
|
44
|
+
'import/no-extraneous-dependencies': 'error',
|
|
45
|
+
// 禁止服务端导入客户端专用包
|
|
46
|
+
'no-restricted-imports': [
|
|
47
|
+
'error',
|
|
48
|
+
{
|
|
49
|
+
patterns: [
|
|
50
|
+
{
|
|
51
|
+
group: ['@lark-apaas/client-toolkit', '@lark-apaas/client-toolkit/*'],
|
|
52
|
+
message: 'This toolkit is for client-side use only and must not be imported or used in server-side code.',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
'@darraghor/nestjs-typed/injectable-should-be-provided': [
|
|
58
|
+
'error',
|
|
59
|
+
{
|
|
60
|
+
src: ['./server/**/*.ts'],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
'@darraghor/nestjs-typed/api-property-returning-array-should-set-array': 'off',
|
|
64
|
+
'@darraghor/nestjs-typed/api-property-matches-property-optionality': 'off',
|
|
65
|
+
'@darraghor/nestjs-typed/all-properties-have-explicit-defined': 'off',
|
|
66
|
+
// 关闭 Swagger 相关规则
|
|
67
|
+
'@darraghor/nestjs-typed/controllers-should-supply-api-tags': 'off',
|
|
68
|
+
'@darraghor/nestjs-typed/api-method-should-specify-api-response': 'off',
|
|
69
|
+
'@darraghor/nestjs-typed/api-method-should-specify-api-operation': 'off',
|
|
70
|
+
'@darraghor/nestjs-typed/api-enum-property-best-practices': 'off',
|
|
71
|
+
'@darraghor/nestjs-typed/all-properties-are-whitelisted': 'off',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const eslintPresets: {
|
|
2
|
+
client: ({
|
|
3
|
+
readonly rules: Readonly<import("eslint").Linter.RulesRecord>;
|
|
4
|
+
} | import("typescript-eslint/dist/compatibility-types").CompatibleConfig | {
|
|
5
|
+
name: string;
|
|
6
|
+
languageOptions: {
|
|
7
|
+
ecmaVersion: number;
|
|
8
|
+
parser: any;
|
|
9
|
+
parserOptions: {
|
|
10
|
+
ecmaFeatures: {
|
|
11
|
+
jsx: boolean;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
globals: any;
|
|
15
|
+
};
|
|
16
|
+
plugins: {
|
|
17
|
+
'react-hooks': any;
|
|
18
|
+
import: any;
|
|
19
|
+
};
|
|
20
|
+
settings: {
|
|
21
|
+
'import/resolver': {
|
|
22
|
+
node: {
|
|
23
|
+
extensions: string[];
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
'import/parsers': {
|
|
27
|
+
'@typescript-eslint/parser': string[];
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
rules: any;
|
|
31
|
+
} | {
|
|
32
|
+
ignores: string[];
|
|
33
|
+
})[];
|
|
34
|
+
server: ({
|
|
35
|
+
readonly rules: Readonly<import("eslint").Linter.RulesRecord>;
|
|
36
|
+
} | import("typescript-eslint/dist/compatibility-types").CompatibleConfig)[];
|
|
37
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
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.eslintPresets = void 0;
|
|
7
|
+
const eslint_client_1 = __importDefault(require("./eslint-client"));
|
|
8
|
+
const eslint_server_1 = __importDefault(require("./eslint-server"));
|
|
9
|
+
const js_1 = __importDefault(require("@eslint/js"));
|
|
10
|
+
const typescript_eslint_1 = __importDefault(require("typescript-eslint"));
|
|
11
|
+
const eslint_plugin_nestjs_typed_1 = __importDefault(require("@darraghor/eslint-plugin-nestjs-typed"));
|
|
12
|
+
const globalIgnoreServerPatterns = ['server/database/.introspect/**']; // 全局 eslint server ignore
|
|
13
|
+
const globalIgnoreClientPatterns = ['dist', 'node_modules', 'client/src/api/gen']; // 全局 eslint client Ignore
|
|
14
|
+
// 只导出纯净的规则配置,不包含基础配置
|
|
15
|
+
// 用户需要在项目配置中自己添加基础配置(eslintJs, tseslint, nestjs 等)
|
|
16
|
+
exports.eslintPresets = {
|
|
17
|
+
client: [
|
|
18
|
+
{ ignores: globalIgnoreClientPatterns },
|
|
19
|
+
js_1.default.configs.recommended,
|
|
20
|
+
...typescript_eslint_1.default.configs.recommended,
|
|
21
|
+
eslint_client_1.default
|
|
22
|
+
],
|
|
23
|
+
server: [
|
|
24
|
+
{ ignores: globalIgnoreServerPatterns },
|
|
25
|
+
js_1.default.configs.recommended,
|
|
26
|
+
...typescript_eslint_1.default.configs.recommended,
|
|
27
|
+
...eslint_plugin_nestjs_typed_1.default.configs.flatRecommended,
|
|
28
|
+
eslint_server_1.default
|
|
29
|
+
],
|
|
30
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
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.createTailwindPreset = createTailwindPreset;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const defaultPackageName = '@lark-apaas/client-toolkit';
|
|
39
|
+
function createTailwindPreset(options = {}) {
|
|
40
|
+
// 默认补充扫描外网包依赖
|
|
41
|
+
const { packageName = defaultPackageName } = options;
|
|
42
|
+
const pkgPath = getToolKitPath(packageName);
|
|
43
|
+
return {
|
|
44
|
+
darkMode: 'class',
|
|
45
|
+
content: [pkgPath].filter(Boolean),
|
|
46
|
+
prefix: '',
|
|
47
|
+
plugins: [
|
|
48
|
+
require('tailwindcss-animate'),
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getToolKitPath(packageName) {
|
|
53
|
+
let pkgPath = '';
|
|
54
|
+
if (packageName) {
|
|
55
|
+
try {
|
|
56
|
+
pkgPath = path.join(path.dirname(require.resolve(packageName)), '/**/*.js');
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
console.warn(`Failed to resolve package ${packageName}, use default path`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return pkgPath;
|
|
63
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": false,
|
|
19
|
+
"noUnusedLocals": false,
|
|
20
|
+
"noUnusedParameters": false,
|
|
21
|
+
"noImplicitAny": false,
|
|
22
|
+
"noFallthroughCasesInSwitch": false,
|
|
23
|
+
|
|
24
|
+
"sourceMap": true,
|
|
25
|
+
"allowJs": true,
|
|
26
|
+
"strictNullChecks": false,
|
|
27
|
+
|
|
28
|
+
"incremental": false,
|
|
29
|
+
},
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"emitDecoratorMetadata": true,
|
|
12
|
+
"importHelpers": true,
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"noEmit": false,
|
|
15
|
+
|
|
16
|
+
/* Linting */
|
|
17
|
+
"strict": false,
|
|
18
|
+
"composite": true,
|
|
19
|
+
"declaration": true,
|
|
20
|
+
"removeComments": false,
|
|
21
|
+
|
|
22
|
+
"types": ["node"]
|
|
23
|
+
},
|
|
24
|
+
"ts-node": { "require": ["tsconfig-paths/register"] }
|
|
25
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
}
|