xshell 1.3.44 → 1.3.46
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/builder.d.ts +4 -1
- package/builder.js +56 -7
- package/package.json +1 -1
- package/tsconfig.json +2 -2
- package/xlint.js +38 -43
package/builder.d.ts
CHANGED
|
@@ -60,6 +60,9 @@ type Assets = {
|
|
|
60
60
|
productions?: (string | AssetOption)[];
|
|
61
61
|
devs?: (string | AssetOption)[];
|
|
62
62
|
};
|
|
63
|
+
/** 从 tsconfig.json 中解析 paths 配置为 resolve alias
|
|
64
|
+
文件不存在或无 paths alias 配置时返回 null */
|
|
65
|
+
export declare function get_resolve_alias(fpd_root: string): Promise<Record<string, string> | null>;
|
|
63
66
|
export interface BundlerOptions {
|
|
64
67
|
dependencies?: DependencyId[];
|
|
65
68
|
source_map?: boolean;
|
|
@@ -153,7 +156,7 @@ export declare class Bundler {
|
|
|
153
156
|
- expose?: `false` 入口模块所有导出的属性赋值到全局对象 globalThis 上
|
|
154
157
|
- single_chunk?: `true` 输出为单个文件,将依赖也打包到其中,不要生成多个 chunk
|
|
155
158
|
- dynamic_import?: `true` 对于 await import('...') 要不要打包外部模块到输出文件中
|
|
156
|
-
- resolve_alias?: 配置 resolve alias
|
|
159
|
+
- resolve_alias?: 配置 resolve alias,会自动从项目 tsconfig.json 的 paths 中读取,用户配置作为覆盖
|
|
157
160
|
- resolve_fallback?: 配置 resolve fallback
|
|
158
161
|
- assets_stats?: `true` 打印输出的文件信息
|
|
159
162
|
- analyzer?: `false` 启用 WebpackBundleAnalyzer 插件分析构建产物大小
|
package/builder.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
2
|
import { not_empty } from "./prototype.js";
|
|
3
3
|
import { noprint } from "./process.js";
|
|
4
|
-
import { Lock, filter_values } from "./utils.js";
|
|
5
|
-
import { fcopy, fmkdir, fwrite, print_info, ramdisk } from "./file.js";
|
|
4
|
+
import { Lock, check, filter_values } from "./utils.js";
|
|
5
|
+
import { fcopy, fmkdir, fwrite, print_info, ramdisk, fread_lines } from "./file.js";
|
|
6
6
|
import { path } from "./path.js";
|
|
7
7
|
const get_target = (production) => production ? 'production' : 'development';
|
|
8
8
|
function get_react_js(production, src, map) {
|
|
@@ -112,6 +112,51 @@ function get_vendor_asset_out(asset) {
|
|
|
112
112
|
? `vendors/${asset}`
|
|
113
113
|
: asset.out || asset.src;
|
|
114
114
|
}
|
|
115
|
+
/** get_resolve_alias 的缓存,null 表示文件不存在或无 paths 配置 */
|
|
116
|
+
const resolve_alias_cache = new Map();
|
|
117
|
+
/** 从 tsconfig.json 中解析 paths 配置为 resolve alias
|
|
118
|
+
文件不存在或无 paths alias 配置时返回 null */
|
|
119
|
+
export async function get_resolve_alias(fpd_root) {
|
|
120
|
+
const cached = resolve_alias_cache.get(fpd_root);
|
|
121
|
+
if (cached !== undefined)
|
|
122
|
+
return cached;
|
|
123
|
+
const fp = `${fpd_root}tsconfig.json`;
|
|
124
|
+
let lines;
|
|
125
|
+
try {
|
|
126
|
+
lines = await fread_lines(fp);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error.code === 'ENOENT') {
|
|
130
|
+
resolve_alias_cache.set(fpd_root, null);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
// 移除 // 行注释
|
|
136
|
+
const { compilerOptions } = JSON.parse(lines
|
|
137
|
+
.filter(line => !line.trimStart().startsWith('//'))
|
|
138
|
+
.join_lines());
|
|
139
|
+
const paths = compilerOptions?.paths;
|
|
140
|
+
if (!paths) {
|
|
141
|
+
resolve_alias_cache.set(fpd_root, null);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const result = Object.fromEntries(Object.entries(paths).map(([key, values]) => {
|
|
145
|
+
// 检查 value 数组只有一项
|
|
146
|
+
check(values.length === 1, `paths 的 value 数组必须只有一项: ${key}`);
|
|
147
|
+
const value = values[0];
|
|
148
|
+
// key 和 value 都以 /* 结尾时,去掉 *
|
|
149
|
+
if (key.endsWith('/*') && value.endsWith('/*'))
|
|
150
|
+
return [
|
|
151
|
+
key.slice(0, -1),
|
|
152
|
+
fpd_root + value.slice(0, -1).strip_if_start('./')
|
|
153
|
+
];
|
|
154
|
+
else // 是某个具体文件
|
|
155
|
+
return [key, fpd_root + value.strip_if_start('./')];
|
|
156
|
+
}));
|
|
157
|
+
resolve_alias_cache.set(fpd_root, result);
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
115
160
|
export class Bundler {
|
|
116
161
|
name;
|
|
117
162
|
target;
|
|
@@ -177,7 +222,7 @@ export class Bundler {
|
|
|
177
222
|
- expose?: `false` 入口模块所有导出的属性赋值到全局对象 globalThis 上
|
|
178
223
|
- single_chunk?: `true` 输出为单个文件,将依赖也打包到其中,不要生成多个 chunk
|
|
179
224
|
- dynamic_import?: `true` 对于 await import('...') 要不要打包外部模块到输出文件中
|
|
180
|
-
- resolve_alias?: 配置 resolve alias
|
|
225
|
+
- resolve_alias?: 配置 resolve alias,会自动从项目 tsconfig.json 的 paths 中读取,用户配置作为覆盖
|
|
181
226
|
- resolve_fallback?: 配置 resolve fallback
|
|
182
227
|
- assets_stats?: `true` 打印输出的文件信息
|
|
183
228
|
- analyzer?: `false` 启用 WebpackBundleAnalyzer 插件分析构建产物大小
|
|
@@ -201,6 +246,7 @@ export class Bundler {
|
|
|
201
246
|
throw new Error('expose 和 commonjs2 不能同时启用');
|
|
202
247
|
}
|
|
203
248
|
async build(print = true) {
|
|
249
|
+
const { fpd_root } = this;
|
|
204
250
|
if (!this.lcompiler)
|
|
205
251
|
await (this.lcompiler = new Lock()).request(async () => {
|
|
206
252
|
const { default: Webpack } = await import('webpack');
|
|
@@ -232,7 +278,7 @@ export class Bundler {
|
|
|
232
278
|
name: this.name,
|
|
233
279
|
mode: get_target(this.production),
|
|
234
280
|
devtool: this.source_map ? 'source-map' : false,
|
|
235
|
-
context:
|
|
281
|
+
context: fpd_root,
|
|
236
282
|
entry: this.entry,
|
|
237
283
|
experiments: {
|
|
238
284
|
outputModule: output_module,
|
|
@@ -286,7 +332,10 @@ export class Bundler {
|
|
|
286
332
|
extensionAlias: {
|
|
287
333
|
'.js': ['.js', '.ts', '.tsx']
|
|
288
334
|
},
|
|
289
|
-
|
|
335
|
+
alias: filter_values({
|
|
336
|
+
...await get_resolve_alias(fpd_root),
|
|
337
|
+
...this.resolve_alias
|
|
338
|
+
}),
|
|
290
339
|
// modules: [
|
|
291
340
|
// '',
|
|
292
341
|
// ],
|
|
@@ -319,7 +368,7 @@ export class Bundler {
|
|
|
319
368
|
loader: get_loader('ts-loader'),
|
|
320
369
|
// https://github.com/TypeStrong/ts-loader
|
|
321
370
|
options: {
|
|
322
|
-
configFile: `${
|
|
371
|
+
configFile: `${fpd_root}tsconfig.json`,
|
|
323
372
|
onlyCompileBundledFiles: true,
|
|
324
373
|
transpileOnly: !this.dts,
|
|
325
374
|
compilerOptions: {
|
|
@@ -438,7 +487,7 @@ export class Bundler {
|
|
|
438
487
|
},
|
|
439
488
|
stats: {
|
|
440
489
|
colors: true,
|
|
441
|
-
context:
|
|
490
|
+
context: fpd_root,
|
|
442
491
|
entrypoints: false,
|
|
443
492
|
errors: true,
|
|
444
493
|
errorDetails: true,
|
package/package.json
CHANGED
package/tsconfig.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
"compilerOptions": {
|
|
7
7
|
// --- module
|
|
8
|
-
"module": "ESNext",
|
|
8
|
+
"module": "ESNext",
|
|
9
9
|
"moduleResolution": "Bundler",
|
|
10
10
|
"allowSyntheticDefaultImports": true,
|
|
11
11
|
"esModuleInterop": false,
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"noImplicitOverride": true,
|
|
58
58
|
"noUnusedLocals": false,
|
|
59
59
|
"noUnusedParameters": false,
|
|
60
|
-
"skipLibCheck": true
|
|
60
|
+
"skipLibCheck": true
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
package/xlint.js
CHANGED
|
@@ -5,7 +5,7 @@ import TSParser from '@typescript-eslint/parser';
|
|
|
5
5
|
import ts_plugin from '@typescript-eslint/eslint-plugin';
|
|
6
6
|
import react_plugin from 'eslint-plugin-react';
|
|
7
7
|
import stylistic_plugin from '@stylistic/eslint-plugin';
|
|
8
|
-
import { check } from "./utils.js";
|
|
8
|
+
import { array_equals, check } from "./utils.js";
|
|
9
9
|
import { path } from "./path.js";
|
|
10
10
|
// 用 ast explorer 选 esprima 来看
|
|
11
11
|
// 或者 https://explorer.eslint.org/
|
|
@@ -703,18 +703,6 @@ export const xlint_plugin = {
|
|
|
703
703
|
'Program:exit'() {
|
|
704
704
|
if (need_fix_source || imports.length <= 1)
|
|
705
705
|
return;
|
|
706
|
-
const { sourceCode: source } = context;
|
|
707
|
-
const { body } = source.ast;
|
|
708
|
-
const first = imports[0].node;
|
|
709
|
-
const ifirst = body.indexOf(first);
|
|
710
|
-
if (body.indexOf(imports.last.node, ifirst + 1) - ifirst !== imports.length - 1) {
|
|
711
|
-
context.report({
|
|
712
|
-
// @ts-ignore
|
|
713
|
-
message: 'import 语句不连续,中间有其他语句,无法自动排序修复,需手动修复',
|
|
714
|
-
node: first
|
|
715
|
-
});
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
706
|
// 检查顺序并修复: 样式 xxx.sass < nodejs builtin < package < alias < subpath
|
|
719
707
|
// 如果为样式,则放前面,其他的就按照数组来
|
|
720
708
|
let imports_ = imports.toSorted((a, b) => {
|
|
@@ -724,40 +712,47 @@ export const xlint_plugin = {
|
|
|
724
712
|
return a.ialias - b.ialias;
|
|
725
713
|
return imtypes.indexOf(a.type) - imtypes.indexOf(b.type);
|
|
726
714
|
});
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
715
|
+
if (array_equals(imports, imports_))
|
|
716
|
+
return;
|
|
717
|
+
const { sourceCode: source } = context;
|
|
718
|
+
const { body } = source.ast;
|
|
719
|
+
const first = imports[0].node;
|
|
720
|
+
const ifirst = body.indexOf(first);
|
|
721
|
+
if (body.indexOf(imports.last.node, ifirst + 1) - ifirst !== imports.length - 1) {
|
|
732
722
|
context.report({
|
|
733
723
|
// @ts-ignore
|
|
734
|
-
message:
|
|
735
|
-
node:
|
|
736
|
-
fix(fixer) {
|
|
737
|
-
let _start = Number.MAX_SAFE_INTEGER, _end = 0;
|
|
738
|
-
let type;
|
|
739
|
-
// 收集所有排序后的 import 文本
|
|
740
|
-
const texts = imports_.map((im, i) => {
|
|
741
|
-
const [start, end] = get_range_with_comments(source, im.node);
|
|
742
|
-
if (start < _start)
|
|
743
|
-
_start = start;
|
|
744
|
-
if (end > _end)
|
|
745
|
-
_end = end;
|
|
746
|
-
let text = source.text.slice(start, end);
|
|
747
|
-
// 分组之间空一行
|
|
748
|
-
if (im.type !== type) {
|
|
749
|
-
if (type)
|
|
750
|
-
text = '\n' + text;
|
|
751
|
-
type = im.type;
|
|
752
|
-
}
|
|
753
|
-
return text;
|
|
754
|
-
});
|
|
755
|
-
// 一次性替换所有 import
|
|
756
|
-
return fixer.replaceTextRange([_start, _end], texts.join_lines(false));
|
|
757
|
-
}
|
|
724
|
+
message: 'import 顺序不对,且 import 语句不连续,中间有其他语句,无法自动排序修复,需手动修复',
|
|
725
|
+
node: first
|
|
758
726
|
});
|
|
759
|
-
|
|
727
|
+
return;
|
|
760
728
|
}
|
|
729
|
+
context.report({
|
|
730
|
+
// @ts-ignore
|
|
731
|
+
message: 'import 顺序不对',
|
|
732
|
+
node: imports[0].node,
|
|
733
|
+
fix(fixer) {
|
|
734
|
+
let _start = Number.MAX_SAFE_INTEGER, _end = 0;
|
|
735
|
+
let type;
|
|
736
|
+
// 收集所有排序后的 import 文本
|
|
737
|
+
const texts = imports_.map((im, i) => {
|
|
738
|
+
const [start, end] = get_range_with_comments(source, im.node);
|
|
739
|
+
if (start < _start)
|
|
740
|
+
_start = start;
|
|
741
|
+
if (end > _end)
|
|
742
|
+
_end = end;
|
|
743
|
+
let text = source.text.slice(start, end);
|
|
744
|
+
// 分组之间空一行
|
|
745
|
+
if (im.type !== type) {
|
|
746
|
+
if (type)
|
|
747
|
+
text = '\n' + text;
|
|
748
|
+
type = im.type;
|
|
749
|
+
}
|
|
750
|
+
return text;
|
|
751
|
+
});
|
|
752
|
+
// 一次性替换所有 import
|
|
753
|
+
return fixer.replaceTextRange([_start, _end], texts.join_lines(false));
|
|
754
|
+
}
|
|
755
|
+
});
|
|
761
756
|
}
|
|
762
757
|
};
|
|
763
758
|
}
|