-
Notifications
You must be signed in to change notification settings - Fork 263
Expand file tree
/
Copy pathcodemaker.ts
More file actions
188 lines (164 loc) · 5.05 KB
/
codemaker.ts
File metadata and controls
188 lines (164 loc) · 5.05 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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import * as util from 'util';
import * as caseutils from './case-utils';
import FileBuffer from './filebuff';
/**
* Multi-file text writer with some code-generation features.
*/
export class CodeMaker {
/**
* The indentation level of the file.
*/
public indentation = 4;
private currIndent = 0;
private currentFile?: FileBuffer;
private readonly files = new Array<FileBuffer>();
private readonly excludes = new Array<string>();
public get currentIndentLength(): number {
return this.currIndent * this.indentation;
}
/**
* Formats an block open statement.
*/
public openBlockFormatter: (s?: string) => string = (s) => `${s} {`;
/**
* Formats a block close statement.
*/
public closeBlockFormatter: (s?: string) => string | false = () => '}';
/**
* Saves all the files created in this code maker.
* @param rootDir The root directory for all saved files.
* @returns A sorted list of all the files saved (absolute paths).
*/
public async save(rootDir: string) {
const paths = this.files
.filter((file) => !this.excludes.includes(file.filePath))
.map((file) => file.save(rootDir));
return (await Promise.all(paths)).sort();
}
/**
* Sets the name of the current file we are working with.
* Note that this doesn't really create a new file (files are only created when save() is called.
* Use `closeFile` to close this file.
* @param filePath The relative path of the new file.
*/
public openFile(filePath: string) {
if (this.currentFile) {
throw new Error(
`Cannot open file ${filePath} without closing the previous file ${this.currentFile.filePath}`,
);
}
this.currentFile = new FileBuffer(filePath);
}
/**
* Indicates that we finished generating the current file.
* @param filePath The relative file path (must be the same as one passed to openFile)
*/
public closeFile(filePath: string) {
if (!this.currentFile) {
throw new Error(`Cannot close file ${filePath}. It was never opened`);
}
if (this.currentFile.filePath !== filePath) {
throw new Error(
`Cannot close file ${filePath}. The currently opened file is ${this.currentFile.filePath}`,
);
}
this.files.push(this.currentFile);
this.currentFile = undefined;
}
/**
* Emits a line into the currently opened file.
* Line is emitted with the current level of indentation.
* If no arguments are provided, an empty new line is emitted.
* @param fmt String format arguments (passed to `util.format`)
* @param args String arguments
*/
public line(fmt?: string, ...args: string[]) {
if (!this.currentFile) {
throw new Error('Cannot emit source lines without openning a file');
}
if (fmt) {
fmt = this.makeIndent() + fmt;
this.currentFile.write(util.format(fmt, ...args));
}
this.currentFile.write('\n');
}
/**
* Same as `open`.
*/
public indent(textBefore?: string) {
this.open(textBefore);
}
/**
* Same as `close`.
*/
public unindent(textAfter?: string | false) {
this.close(textAfter);
}
/**
* Increases the indentation level by `indentation` spaces for the next line.
* @param textBefore Text to emit before the newline (i.e. block open).
*/
public open(textBefore?: string) {
this.line(textBefore);
this.currIndent++;
}
/**
* Decreases the indentation level by `indentation` for the next line.
* @param textAfter Text to emit in the line after indentation was decreased.
* If `false` no line will be emitted at all, but the indent
* counter will be decremented.
*/
public close(textAfter?: string | false) {
this.currIndent--;
if (textAfter !== false) {
this.line(textAfter);
}
}
/**
* Opens a code block. The formatting of the block is determined by `openBlockFormatter`.
* @param text The text to pass to the formatter.
*/
public openBlock(text: string) {
this.open(this.openBlockFormatter(text));
}
/**
* Closes a code block. The formatting of the block is determined by `closeBlockFormatter`.
* @param text The text to pass to the formatter.
*/
public closeBlock(text?: string) {
this.close(this.closeBlockFormatter(text));
}
/**
* Adds a file to the exclude list. This means this file will not be saved during save().
* @param filePath The relative path of the file.
*/
public exclude(filePath: string) {
this.excludes.push(filePath);
}
/**
* convertsStringToCamelCase
*/
public toCamelCase(...args: string[]) {
return caseutils.toCamelCase(...args);
}
/**
* ConvertsStringToPascalCase
*/
public toPascalCase(...args: string[]) {
return caseutils.toPascalCase(...args);
}
/**
* convert_string_to_snake_case
* @param sep Separator (defaults to '_')
*/
public toSnakeCase(s: string, sep = '_') {
return caseutils.toSnakeCase(s, sep);
}
private makeIndent(): string {
const length = this.currentIndentLength;
if (length <= 0) {
return '';
}
return ' '.repeat(length);
}
}