ranuts 0.1.0-alpha → 0.1.0-alpha.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.
- package/assets/img/sort/bubble.gif +0 -0
- package/assets/img/sort/complexity.png +0 -0
- package/assets/img/sort/select.gif +0 -0
- package/assets/img/sort/sort.png +0 -0
- package/assets/img/tree/balanceTree.png +0 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.js +1078 -44
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +1 -1
- package/dist/index.umd.cjs.map +1 -1
- package/dist/src/file/fileInfo.d.ts +7 -0
- package/dist/src/file/fs.d.ts +2 -0
- package/dist/src/file/readDir.d.ts +6 -0
- package/dist/src/file/readFile.d.ts +8 -0
- package/dist/src/file/watchFile.d.ts +8 -0
- package/dist/src/file/writeFile.d.ts +8 -0
- package/dist/src/mode/subscribe.d.ts +16 -0
- package/dist/src/ranlog/behavior.d.ts +1 -0
- package/dist/src/ranlog/env.d.ts +5 -0
- package/dist/src/ranlog/error.d.ts +2 -0
- package/dist/src/ranlog/index.d.ts +26 -0
- package/dist/src/ranlog/performance.d.ts +21 -0
- package/dist/src/ranlog/report.d.ts +7 -0
- package/dist/src/ranlog/request.d.ts +18 -0
- package/dist/src/ranlog/utils.d.ts +19 -0
- package/dist/src/utils/filterObj.d.ts +8 -0
- package/dist/src/utils/str2Xml.d.ts +8 -0
- package/dist/src/vnode/h.d.ts +6 -0
- package/dist/src/vnode/hooks.d.ts +23 -0
- package/dist/src/vnode/htmlDomApi.d.ts +33 -0
- package/dist/src/vnode/init.d.ts +2 -0
- package/dist/src/vnode/is.d.ts +5 -0
- package/dist/src/vnode/modules/attributes.d.ts +8 -0
- package/dist/src/vnode/modules/class.d.ts +8 -0
- package/dist/src/vnode/modules/index.d.ts +7 -0
- package/dist/src/vnode/modules/listeners.d.ts +14 -0
- package/dist/src/vnode/modules/props.d.ts +8 -0
- package/dist/src/vnode/modules/style.d.ts +14 -0
- package/dist/src/vnode/vnode.d.ts +31 -0
- package/package.json +32 -9
- package/readme.md +1 -0
- package/src/astParser/Parser.ts +654 -0
- package/src/astParser/Tokenizer.ts +447 -0
- package/src/astParser/nodeTypes.ts +194 -0
- package/src/astParser/utils.ts +27 -0
- package/src/babel/parser.ts +10 -0
- package/src/colors/fmt.ts +29 -0
- package/src/colors/isColorSupported.ts +13 -0
- package/src/file/appendFile.ts +29 -0
- package/src/file/fileInfo.ts +22 -0
- package/src/file/fs.ts +9 -0
- package/src/file/readDir.ts +18 -0
- package/src/file/readFile.ts +26 -0
- package/src/file/watchFile.ts +31 -0
- package/src/file/writeFile.ts +38 -0
- package/src/mode/subscribe.ts +89 -0
- package/src/ran/commit.ts +0 -0
- package/src/ran/dom.ts +0 -0
- package/src/ran/hooks.ts +0 -0
- package/src/ran/reconcile.ts +0 -0
- package/src/ran/schedule.ts +0 -0
- package/src/ranlog/behavior.ts +5 -0
- package/src/ranlog/console.ts +16 -0
- package/src/ranlog/env.ts +29 -0
- package/src/ranlog/error.ts +11 -0
- package/src/ranlog/performance.ts +65 -0
- package/src/ranlog/report.ts +33 -0
- package/src/ranlog/request.ts +60 -0
- package/src/ranlog/utils.ts +92 -0
- package/src/ranpack/ast/Declaration.ts +120 -0
- package/src/ranpack/ast/Node.ts +7 -0
- package/src/ranpack/ast/Reference.ts +32 -0
- package/src/ranpack/ast/Scope.ts +80 -0
- package/src/ranpack/bundle.ts +50 -0
- package/src/ranpack/graph.ts +143 -0
- package/src/ranpack/module.ts +431 -0
- package/src/ranpack/moduleLoader.ts +73 -0
- package/src/ranpack/plugins.ts +70 -0
- package/src/ranpack/statement.ts +66 -0
- package/src/ranpack/utils/buildScope.ts +77 -0
- package/src/ranpack/utils/findReference.ts +36 -0
- package/src/ranpack/utils/isFunctionDeclaration.ts +41 -0
- package/src/ranpack/utils/makeLegalIdentifier.ts +18 -0
- package/src/ranpack/utils/object.ts +8 -0
- package/src/ranpack/utils/resolve.ts +32 -0
- package/src/ranpack/utils/walk.ts +66 -0
- package/src/ranpack/ws.ts +269 -0
- package/src/server/connectType.ts +9 -0
- package/src/server/encodeUrl.ts +46 -0
- package/src/server/escapeHtml.ts +46 -0
- package/src/server/get.ts +36 -0
- package/src/server/jitter.ts +77 -0
- package/src/server/mimeType.ts +858 -0
- package/src/server/paresUrl.ts +40 -0
- package/src/server/send.ts +89 -0
- package/src/server/server.ts +67 -0
- package/src/server/status.ts +191 -0
- package/src/server/traverse.ts +54 -0
- package/src/server/websocket.ts +200 -0
- package/src/sort/bubble.ts +21 -0
- package/src/sort/bucket.ts +11 -0
- package/src/sort/count.ts +10 -0
- package/src/sort/heap.ts +10 -0
- package/src/sort/insert.ts +20 -0
- package/src/sort/merge.ts +35 -0
- package/src/sort/quick.ts +50 -0
- package/src/sort/radix.ts +11 -0
- package/src/sort/randomArray.ts +21 -0
- package/src/sort/select.ts +23 -0
- package/src/sort/shell.ts +22 -0
- package/src/utils/compose.ts +37 -0
- package/src/utils/filterObj.ts +21 -0
- package/src/utils/mergeObj.ts +12 -0
- package/src/utils/startTask.ts +15 -0
- package/src/utils/str2Xml.ts +26 -0
- package/src/utils/taskEnd.ts +14 -0
- package/src/vnode/h.ts +108 -0
- package/src/vnode/hooks.ts +25 -0
- package/src/vnode/htmlDomApi.ts +199 -0
- package/src/vnode/init.ts +429 -0
- package/src/vnode/is.ts +17 -0
- package/src/vnode/modules/attributes.ts +70 -0
- package/src/vnode/modules/class.ts +46 -0
- package/src/vnode/modules/listeners.ts +123 -0
- package/src/vnode/modules/props.ts +41 -0
- package/src/vnode/modules/style.ts +118 -0
- package/src/vnode/vnode.ts +65 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { keys, values } from '../utils/object';
|
|
2
|
+
import type { Module } from '../module';
|
|
3
|
+
import type { Statement } from '../statement';
|
|
4
|
+
import { Reference } from './Reference';
|
|
5
|
+
|
|
6
|
+
export class Declaration {
|
|
7
|
+
isFunctionDeclaration: boolean = false;
|
|
8
|
+
functionNode: any;
|
|
9
|
+
statement: Statement | null;
|
|
10
|
+
name: string | null = null;
|
|
11
|
+
isParam: boolean = false;
|
|
12
|
+
isUsed: boolean = false;
|
|
13
|
+
isReassigned: boolean = false;
|
|
14
|
+
constructor(node: any, isParam: boolean, statement: Statement | null) {
|
|
15
|
+
// 考虑函数和变量声明两种情况
|
|
16
|
+
if (node) {
|
|
17
|
+
if (node.type === 'FunctionDeclaration') {
|
|
18
|
+
this.isFunctionDeclaration = true;
|
|
19
|
+
this.functionNode = node;
|
|
20
|
+
} else if (
|
|
21
|
+
node.type === 'VariableDeclarator' &&
|
|
22
|
+
node.init &&
|
|
23
|
+
/FunctionExpression/.test(node.init.type)
|
|
24
|
+
) {
|
|
25
|
+
this.isFunctionDeclaration = true;
|
|
26
|
+
this.functionNode = node.init;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
this.statement = statement;
|
|
30
|
+
this.isParam = isParam;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
addReference(reference: Reference):void {
|
|
34
|
+
reference.declaration = this;
|
|
35
|
+
this.name = reference.name;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
use():void {
|
|
39
|
+
// 标记该节点被使用
|
|
40
|
+
this.isUsed = true;
|
|
41
|
+
// 对应的 statement 节点也应该被标记
|
|
42
|
+
if (this.statement) {
|
|
43
|
+
this.statement.mark();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 另外,你可以加上 render 方法,便于后续代码生成的步骤
|
|
47
|
+
render():string | null {
|
|
48
|
+
return this.name;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class SyntheticDefaultDeclaration extends Declaration {
|
|
53
|
+
original: Declaration | null;
|
|
54
|
+
exportName: string | null;
|
|
55
|
+
constructor(node: any, name: string, statement: Statement) {
|
|
56
|
+
super(node, false, statement);
|
|
57
|
+
this.original = null;
|
|
58
|
+
this.exportName = '';
|
|
59
|
+
this.name = name;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
render():string | null {
|
|
63
|
+
return this.original?.render() || this.name;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
bind(declaration: Declaration):void {
|
|
67
|
+
this.original = declaration;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class SyntheticNamespaceDeclaration extends Declaration {
|
|
72
|
+
module: Module;
|
|
73
|
+
originals: Record<string, Declaration> = {};
|
|
74
|
+
needsNamespaceBlock: boolean = false;
|
|
75
|
+
constructor(module: Module) {
|
|
76
|
+
super(null, false, null);
|
|
77
|
+
this.module = module;
|
|
78
|
+
module.getExports().forEach((name) => {
|
|
79
|
+
const declaration = module.traceExport(name);
|
|
80
|
+
if (declaration) {
|
|
81
|
+
this.originals[name] = declaration;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
addReference(reference: Reference): void {
|
|
87
|
+
if (!this.needsNamespaceBlock) {
|
|
88
|
+
this.needsNamespaceBlock = true;
|
|
89
|
+
}
|
|
90
|
+
if (reference.objectPaths.length) {
|
|
91
|
+
const ref = reference.objectPaths.shift();
|
|
92
|
+
reference.name = ref.name;
|
|
93
|
+
reference.start = ref.start;
|
|
94
|
+
reference.end = ref.end;
|
|
95
|
+
}
|
|
96
|
+
values(this.originals).forEach((declaration) => {
|
|
97
|
+
declaration.addReference(reference);
|
|
98
|
+
});
|
|
99
|
+
// 必须放在最后执行,因为要将 reference 和当前的 SyntheticNamespaceDeclaration 绑定
|
|
100
|
+
super.addReference(reference);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
renderBlock(intentString = ' '):string {
|
|
104
|
+
const members = keys(this.originals)
|
|
105
|
+
.map((name: string) => {
|
|
106
|
+
const originDeclaration = this.originals[name];
|
|
107
|
+
return `${intentString}${name}: ${originDeclaration.render()}`;
|
|
108
|
+
})
|
|
109
|
+
.join(',\n');
|
|
110
|
+
return `const ${this.render()} = Object.freeze({\n${members}\n});`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
use():void {
|
|
114
|
+
for (const original of values(this.originals)) {
|
|
115
|
+
original.use();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { Reference };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Statement } from '../statement';
|
|
2
|
+
import type { Scope } from './Scope';
|
|
3
|
+
import type { Declaration } from './Declaration';
|
|
4
|
+
/**
|
|
5
|
+
* @description: 记录其它节点与 Declaration 节点的引用关系
|
|
6
|
+
*/
|
|
7
|
+
export class Reference {
|
|
8
|
+
node: any;
|
|
9
|
+
scope: Scope;
|
|
10
|
+
statement: Statement;
|
|
11
|
+
// declaration 信息在构建依赖图的部分补充
|
|
12
|
+
declaration: Declaration | null = null;
|
|
13
|
+
name: string;
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
objectPaths: any[] = [];
|
|
17
|
+
constructor(node: any, scope: Scope, statement: Statement) {
|
|
18
|
+
this.node = node;
|
|
19
|
+
this.scope = scope;
|
|
20
|
+
this.statement = statement;
|
|
21
|
+
this.start = node.start;
|
|
22
|
+
this.end = node.end;
|
|
23
|
+
let root = node;
|
|
24
|
+
this.objectPaths = [];
|
|
25
|
+
while (root.type === 'MemberExpression') {
|
|
26
|
+
this.objectPaths.unshift(root.property);
|
|
27
|
+
root = root.object;
|
|
28
|
+
}
|
|
29
|
+
this.objectPaths.unshift(root);
|
|
30
|
+
this.name = root.name;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Statement } from '../statement';
|
|
2
|
+
import { keys } from '../utils/object';
|
|
3
|
+
import { Declaration } from './Declaration';
|
|
4
|
+
|
|
5
|
+
interface ScopeOptions {
|
|
6
|
+
parent?: Scope;
|
|
7
|
+
paramNodes?: any[];
|
|
8
|
+
block?: boolean;
|
|
9
|
+
statement: Statement;
|
|
10
|
+
isTopLevel?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @description: 封装作用域相关的基本信息
|
|
14
|
+
* @return {*}
|
|
15
|
+
*/
|
|
16
|
+
export class Scope {
|
|
17
|
+
// 父作用域
|
|
18
|
+
parent?: Scope;
|
|
19
|
+
// 如果是函数作用域,则需要参数节点
|
|
20
|
+
paramNodes: any[];
|
|
21
|
+
// 是否为块级作用域
|
|
22
|
+
isBlockScope?: boolean;
|
|
23
|
+
// 作用域对应的语句节点
|
|
24
|
+
statement: Statement;
|
|
25
|
+
// 变量/函数 声明节点,为 Scope 的核心数据
|
|
26
|
+
declarations: Record<string, Declaration> = {};
|
|
27
|
+
constructor(options: ScopeOptions) {
|
|
28
|
+
const { parent, paramNodes, block, statement } = options;
|
|
29
|
+
this.parent = parent;
|
|
30
|
+
this.paramNodes = paramNodes || [];
|
|
31
|
+
this.statement = statement;
|
|
32
|
+
this.isBlockScope = !!block;
|
|
33
|
+
this.paramNodes.forEach(
|
|
34
|
+
(node) =>
|
|
35
|
+
(this.declarations[node.name] = new Declaration(
|
|
36
|
+
node,
|
|
37
|
+
true,
|
|
38
|
+
this.statement
|
|
39
|
+
))
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
addDeclaration(node: any, isBlockDeclaration: boolean):void {
|
|
44
|
+
// block scope & var, 向上追溯,直到顶层作用域
|
|
45
|
+
if (this.isBlockScope && !isBlockDeclaration && this.parent) {
|
|
46
|
+
this.parent.addDeclaration(node, isBlockDeclaration);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// 否则在当前作用域新建声明节点(Declaration)
|
|
50
|
+
// 变量声明 函数声明
|
|
51
|
+
// if (
|
|
52
|
+
// node.type === 'VariableDeclaration' ||
|
|
53
|
+
// node.type === 'FunctionDeclaration'
|
|
54
|
+
// ) {
|
|
55
|
+
const key = node.id && node.id.name;
|
|
56
|
+
this.declarations[key] = new Declaration(node, false, this.statement);
|
|
57
|
+
// }
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @description: 遍历声明节点(Declaration)
|
|
61
|
+
* @param {function} fn
|
|
62
|
+
* @return {*}
|
|
63
|
+
*/
|
|
64
|
+
eachDeclaration(fn: (name: string, dec: Declaration) => void):void {
|
|
65
|
+
keys(this.declarations).forEach((key) => {
|
|
66
|
+
fn(key, this.declarations[key]);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
contains(name: string): Declaration {
|
|
71
|
+
return this.findDeclaration(name);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
findDeclaration(name: string): Declaration {
|
|
75
|
+
return (
|
|
76
|
+
this.declarations[name] ||
|
|
77
|
+
(this.parent && this.parent.findDeclaration(name))
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as MagicString from 'magic-string';
|
|
2
|
+
import type { Module } from './module';
|
|
3
|
+
import { Graph } from './graph';
|
|
4
|
+
|
|
5
|
+
interface BundleOptions {
|
|
6
|
+
entry: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class Bundle {
|
|
10
|
+
graph: Graph;
|
|
11
|
+
constructor(options: BundleOptions) {
|
|
12
|
+
// 初始化模块依赖图对象
|
|
13
|
+
this.graph = new Graph({
|
|
14
|
+
entry: options.entry,
|
|
15
|
+
bundle: this
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async build():Promise<void> {
|
|
20
|
+
// 模块打包逻辑,完成所有的 AST 相关操作
|
|
21
|
+
return await this.graph.build();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getModuleById(id: string):Module {
|
|
25
|
+
return this.graph.getModuleById(id);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
addModule(module: Module):void {
|
|
29
|
+
return this.graph.addModule(module);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @description: 代码生成逻辑,拼接模块 AST 节点,产出代码
|
|
33
|
+
* @return {*}
|
|
34
|
+
*/
|
|
35
|
+
render(): { code: string; map: MagicString.SourceMap } {
|
|
36
|
+
const msBundle = new MagicString.Bundle({ separator: '\n' });
|
|
37
|
+
|
|
38
|
+
this.graph.orderedModules.forEach((module) => {
|
|
39
|
+
msBundle.addSource({
|
|
40
|
+
content: module.render()
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const map = msBundle.generateMap({ includeContent: true });
|
|
45
|
+
return {
|
|
46
|
+
code: msBundle.toString(),
|
|
47
|
+
map
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import type { Module } from './module';
|
|
3
|
+
import type { Statement } from './statement';
|
|
4
|
+
import { ModuleLoader } from './moduleLoader';
|
|
5
|
+
import type { Bundle } from './bundle';
|
|
6
|
+
import { keys } from './utils/object';
|
|
7
|
+
|
|
8
|
+
interface GraphOptions {
|
|
9
|
+
entry: string;
|
|
10
|
+
bundle: Bundle;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @description: 模块依赖图对象的实现
|
|
15
|
+
* @return {*}
|
|
16
|
+
*/
|
|
17
|
+
export class Graph {
|
|
18
|
+
entryPath: string;
|
|
19
|
+
basedir: string;
|
|
20
|
+
statements: Statement[] = [];
|
|
21
|
+
moduleLoader: ModuleLoader;
|
|
22
|
+
modules: Module[] = [];
|
|
23
|
+
moduleById: Record<string, Module> = {};
|
|
24
|
+
resolveIds: Record<string, string> = {};
|
|
25
|
+
orderedModules: Module[] = [];
|
|
26
|
+
bundle: Bundle;
|
|
27
|
+
constructor(options: GraphOptions) {
|
|
28
|
+
const { entry, bundle } = options;
|
|
29
|
+
this.entryPath = resolve(entry);
|
|
30
|
+
this.basedir = dirname(this.entryPath);
|
|
31
|
+
this.bundle = bundle;
|
|
32
|
+
// 初始化模块加载器对象
|
|
33
|
+
this.moduleLoader = new ModuleLoader(bundle);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async build(): Promise<void> {
|
|
37
|
+
// 1. 获取并解析模块信息,返回入口模块对象
|
|
38
|
+
const entryModule = await this.moduleLoader.fetchModule(
|
|
39
|
+
this.entryPath,
|
|
40
|
+
'',
|
|
41
|
+
true
|
|
42
|
+
);
|
|
43
|
+
// 2. 构建依赖关系图
|
|
44
|
+
this.modules.forEach((module) => module.bind());
|
|
45
|
+
// 3. 模块拓扑排序
|
|
46
|
+
this.orderedModules = this.sortModules(entryModule!);
|
|
47
|
+
// 4. Tree Shaking,标记需要包含的语句
|
|
48
|
+
entryModule!.getExports().forEach((name) => {
|
|
49
|
+
const declaration = entryModule!.traceExport(name);
|
|
50
|
+
declaration!.use();
|
|
51
|
+
});
|
|
52
|
+
// 5. 处理命名冲突
|
|
53
|
+
this.handleNameConflict();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
handleNameConflict(): void {
|
|
57
|
+
const used: Record<string, true> = {};
|
|
58
|
+
|
|
59
|
+
function getSafeName(name: string) {
|
|
60
|
+
let safeName = name;
|
|
61
|
+
let count = 1;
|
|
62
|
+
while (used[safeName]) {
|
|
63
|
+
safeName = `${name}$${count++}`;
|
|
64
|
+
}
|
|
65
|
+
used[safeName] = true;
|
|
66
|
+
return safeName;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.modules.forEach((module) => {
|
|
70
|
+
keys(module.declarations).forEach((name) => {
|
|
71
|
+
const declaration = module.declarations[name];
|
|
72
|
+
declaration.name = getSafeName(declaration.name!);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getModuleById(id: string): Module {
|
|
78
|
+
return this.moduleById[id];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
addModule(module: Module): void {
|
|
82
|
+
if (!this.moduleById[module.id]) {
|
|
83
|
+
this.moduleById[module.id] = module;
|
|
84
|
+
this.modules.push(module);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
sortModules(entryModule: Module): Module[] {
|
|
89
|
+
const orderedModules: Module[] = [];
|
|
90
|
+
const analysedModule: Record<string, boolean> = {};
|
|
91
|
+
const parent: Record<string, string> = {};
|
|
92
|
+
const cyclePathList: string[][] = [];
|
|
93
|
+
// 回溯,用来定位循环依赖
|
|
94
|
+
function getCyclePath(id: string, parentId: string): string[] {
|
|
95
|
+
const paths = [id];
|
|
96
|
+
let currentId = parentId;
|
|
97
|
+
while (currentId !== id) {
|
|
98
|
+
paths.push(currentId);
|
|
99
|
+
// 向前回溯
|
|
100
|
+
currentId = parent[currentId];
|
|
101
|
+
}
|
|
102
|
+
paths.push(paths[0]);
|
|
103
|
+
return paths.reverse();
|
|
104
|
+
}
|
|
105
|
+
// 拓扑排序核心逻辑,基于依赖图的后序遍历完成
|
|
106
|
+
function analyzeModule(module: Module) {
|
|
107
|
+
if (analysedModule[module.id]) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for (const dependency of module.dependencyModules) {
|
|
111
|
+
// 检测到循环依赖
|
|
112
|
+
// 如果某个模块没有被记录到 analysedModule 中,
|
|
113
|
+
// 则表示它的依赖模块并没有分析完,在这个前提下中
|
|
114
|
+
// 如果再次遍历到这个模块,说明已经出现了循环依赖,
|
|
115
|
+
// 1. 不为入口模块
|
|
116
|
+
if (parent[dependency.id]) {
|
|
117
|
+
// 2. 依赖模块还没有分析结束
|
|
118
|
+
if (!analysedModule[dependency.id]) {
|
|
119
|
+
cyclePathList.push(getCyclePath(dependency.id, module.id));
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
parent[dependency.id] = module.id;
|
|
124
|
+
analyzeModule(dependency);
|
|
125
|
+
}
|
|
126
|
+
// 由于 analyzeModule 函数中采用后序的方式来遍历依赖
|
|
127
|
+
// 也就是说一旦某个模块被记录到 analysedModule 表中
|
|
128
|
+
// 那么也就意味着其所有的依赖模块已经被遍历完成了:
|
|
129
|
+
analysedModule[module.id] = true;
|
|
130
|
+
orderedModules.push(module);
|
|
131
|
+
}
|
|
132
|
+
// 从入口模块开始分析
|
|
133
|
+
analyzeModule(entryModule);
|
|
134
|
+
// 如果有循环依赖,则打印循环依赖信息
|
|
135
|
+
if (cyclePathList.length) {
|
|
136
|
+
cyclePathList.forEach((paths) => {
|
|
137
|
+
console.log(paths);
|
|
138
|
+
});
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
return orderedModules;
|
|
142
|
+
}
|
|
143
|
+
}
|