2020-03-17 14:16:18 +08:00

101 lines
3.0 KiB
TypeScript

import {
ChunkContent,
ChunkType,
CodeGeneratorError,
CodeGeneratorFunction,
ICodeBuilder,
ICodeChunk,
} from '../types';
export default class Builder implements ICodeBuilder {
private chunkDefinitions: ICodeChunk[] = [];
private generators: { [key: string]: CodeGeneratorFunction<ChunkContent> } = {
[ChunkType.STRING]: (str: string) => str, // no-op for string chunks
[ChunkType.JSON]: (json: object) => 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.
*/
public 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[] = [];
while (unprocessedChunks.length > 0) {
let indexToRemove = 0;
for (let index = 0; index < unprocessedChunks.length; index++) {
if (unprocessedChunks[index].linkAfter.length <= 0) {
indexToRemove = index;
break;
}
}
if (unprocessedChunks[indexToRemove].linkAfter.length > 0) {
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');
}
unprocessedChunks.splice(indexToRemove, 1);
unprocessedChunks.forEach(
// remove the processed chunk from all the linkAfter arrays from the remaining chunks
ch => (ch.linkAfter = ch.linkAfter.filter(after => after !== name)),
);
}
return resultingString.join('\n');
}
public 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),
);
}
}