-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Expand file tree
/
Copy pathCodeBuilder.ts
More file actions
99 lines (83 loc) · 3.02 KB
/
CodeBuilder.ts
File metadata and controls
99 lines (83 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import {
ChunkContent,
ChunkType,
CodeGeneratorError,
CodeGeneratorFunction,
ICodeBuilder,
ICodeChunk,
} from '../types';
export class CodeBuilder implements ICodeBuilder {
private chunkDefinitions: ICodeChunk[] = [];
private generators: { [key: string]: CodeGeneratorFunction<ChunkContent> } = {
[ChunkType.STRING]: (str: string) => str, // no-op for string chunks
[ChunkType.JSON]: (json: Record<string, unknown>) => JSON.stringify(json), // stringify json to string
};
constructor(chunkDefinitions: ICodeChunk[] = []) {
this.chunkDefinitions = chunkDefinitions;
}
/**
* Links all chunks together based on their requirements. Returns an array
* of ordered chunk names which need to be compiled and glued together.
*/
link(chunkDefinitions: ICodeChunk[] = []): string {
const chunks = chunkDefinitions || this.chunkDefinitions;
if (chunks.length <= 0) {
return '';
}
const unprocessedChunks = chunks.map((chunk) => {
return {
name: chunk.name,
type: chunk.type,
content: chunk.content,
linkAfter: this.cleanupInvalidChunks(chunk.linkAfter, chunks),
};
});
const resultingString: string[] = [];
const processed = new Set<string>();
while (unprocessedChunks.length > 0) {
let indexToRemove = -1;
for (let index = 0; index < unprocessedChunks.length; index++) {
if (unprocessedChunks[index].linkAfter.length === 0) {
indexToRemove = index;
break;
}
}
if (indexToRemove === -1) {
throw new CodeGeneratorError(
'Operation aborted. Reason: cyclic dependency between chunks.',
);
}
const { type, content, name } = unprocessedChunks[indexToRemove];
const compiledContent = this.generateByType(type, content);
if (compiledContent) {
resultingString.push(`${compiledContent}\n`);
}
processed.add(name);
unprocessedChunks.splice(indexToRemove, 1);
unprocessedChunks.forEach((ch) => {
ch.linkAfter = ch.linkAfter.filter((after) => !processed.has(after));
});
}
return resultingString.join('\n');
}
generateByType(type: string, content: unknown): string {
if (!content) {
return '';
}
if (Array.isArray(content)) {
return content.map((contentItem) => this.generateByType(type, contentItem)).join('\n');
}
if (!this.generators[type]) {
throw new Error(
`Attempted to generate unknown type ${type}. Please register a generator for this type in builder/index.ts`,
);
}
return this.generators[type](content);
}
// remove invalid chunks (which did not end up being created) from the linkAfter fields
// one use-case is when you want to remove the import plugin
private cleanupInvalidChunks(linkAfter: string[], chunks: ICodeChunk[]) {
return linkAfter.filter((chunkName) => chunks.some((chunk) => chunk.name === chunkName));
}
}
export default CodeBuilder;