@travetto/compiler 3.0.0-rc.1 → 3.0.0-rc.12
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/README.md +8 -5
- package/__index__.ts +3 -0
- package/bin/trv.js +96 -0
- package/package.json +32 -15
- package/src/compiler.ts +147 -149
- package/src/state.ts +211 -0
- package/src/util.ts +143 -0
- package/support/compiler-entry.ts +2 -0
- package/support/launcher.ts +160 -0
- package/support/transpile.ts +185 -0
- package/tsconfig.trv.json +23 -0
- package/LICENSE +0 -21
- package/index.ts +0 -3
- package/src/host.ts +0 -142
- package/src/transformer.ts +0 -75
- package/support/dynamic.compiler.ts +0 -72
- package/support/phase.init.ts +0 -13
- package/support/phase.reset.ts +0 -10
package/src/state.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
|
|
4
|
+
import { path, ManifestModuleUtil, ManifestModule, ManifestModuleFileType, ManifestRoot, WatchEvent } from '@travetto/manifest';
|
|
5
|
+
|
|
6
|
+
import { CompilerUtil } from './util';
|
|
7
|
+
import { TranspileUtil } from '../support/transpile';
|
|
8
|
+
|
|
9
|
+
const validFile = (type: ManifestModuleFileType): boolean => type === 'ts' || type === 'package-json' || type === 'js';
|
|
10
|
+
|
|
11
|
+
export class CompilerState {
|
|
12
|
+
|
|
13
|
+
#inputFiles: Set<string>;
|
|
14
|
+
#inputToSource = new Map<string, string>();
|
|
15
|
+
#stagedOutputToOutput = new Map<string, string>();
|
|
16
|
+
#inputToOutput = new Map<string, string | undefined>();
|
|
17
|
+
#inputDirectoryToSource = new Map<string, string>();
|
|
18
|
+
#sourceInputOutput = new Map<string, { source: string, input: string, stagedOutput?: string, output?: string, module: ManifestModule }>();
|
|
19
|
+
|
|
20
|
+
#sourceContents = new Map<string, string | undefined>();
|
|
21
|
+
#sourceFileObjects = new Map<string, ts.SourceFile>();
|
|
22
|
+
#sourceHashes = new Map<string, number>();
|
|
23
|
+
|
|
24
|
+
#manifest: ManifestRoot;
|
|
25
|
+
#modules: ManifestModule[];
|
|
26
|
+
#transformers: string[];
|
|
27
|
+
|
|
28
|
+
constructor(manifest: ManifestRoot) {
|
|
29
|
+
this.#manifest = manifest;
|
|
30
|
+
this.#modules = Object.values(this.#manifest.modules);
|
|
31
|
+
this.#inputFiles = new Set(this.#modules.flatMap(
|
|
32
|
+
x => [
|
|
33
|
+
...x.files.bin ?? [],
|
|
34
|
+
...x.files.src ?? [],
|
|
35
|
+
...x.files.support ?? [],
|
|
36
|
+
...x.files.doc ?? [],
|
|
37
|
+
...x.files.test ?? [],
|
|
38
|
+
...x.files.$index ?? [],
|
|
39
|
+
...x.files.$package ?? []
|
|
40
|
+
]
|
|
41
|
+
.filter(([file, type]) => validFile(type) || type === 'typings')
|
|
42
|
+
.map(([f]) => this.registerInput(x, f))
|
|
43
|
+
));
|
|
44
|
+
|
|
45
|
+
this.#transformers = this.#modules.flatMap(
|
|
46
|
+
x => (x.files.$transformer ?? []).map(([f]) =>
|
|
47
|
+
path.resolve(manifest.workspacePath, x.sourceFolder, f)
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getCompilerOptions(): Promise<ts.CompilerOptions> {
|
|
53
|
+
return {
|
|
54
|
+
...await TranspileUtil.getCompilerOptions(this.#manifest),
|
|
55
|
+
outDir: this.#manifest.workspacePath, // Force to root
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
resolveInput(file: string): string {
|
|
60
|
+
return this.#sourceInputOutput.get(file)!.input;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
registerInput(module: ManifestModule, moduleFile: string): string {
|
|
64
|
+
const relativeInput = `${module.outputFolder}/${moduleFile}`;
|
|
65
|
+
const sourceFile = path.toPosix(path.resolve(this.#manifest.workspacePath, module.sourceFolder, moduleFile));
|
|
66
|
+
const sourceFolder = path.dirname(sourceFile);
|
|
67
|
+
const inputFile = path.resolve(this.#manifest.workspacePath, '##', relativeInput); // Ensure input is isolated
|
|
68
|
+
const inputFolder = path.dirname(inputFile);
|
|
69
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
70
|
+
const outputFile = fileType === 'typings' ?
|
|
71
|
+
undefined :
|
|
72
|
+
path.resolve(
|
|
73
|
+
this.#manifest.workspacePath,
|
|
74
|
+
this.#manifest.outputFolder,
|
|
75
|
+
CompilerUtil.inputToOutput(relativeInput)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Rewrite stagedOutput to final output form
|
|
79
|
+
const stagedOutputFile = CompilerUtil.inputToOutput(inputFile);
|
|
80
|
+
|
|
81
|
+
this.#inputToSource.set(inputFile, sourceFile);
|
|
82
|
+
this.#sourceInputOutput.set(sourceFile, { source: sourceFile, input: inputFile, stagedOutput: stagedOutputFile, output: outputFile, module });
|
|
83
|
+
this.#inputToOutput.set(inputFile, outputFile);
|
|
84
|
+
this.#inputDirectoryToSource.set(inputFolder, sourceFolder);
|
|
85
|
+
|
|
86
|
+
if (stagedOutputFile) {
|
|
87
|
+
this.#stagedOutputToOutput.set(stagedOutputFile, outputFile!);
|
|
88
|
+
this.#stagedOutputToOutput.set(`${stagedOutputFile}.map`, `${outputFile!}.map`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return inputFile;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
removeInput(inputFile: string): void {
|
|
95
|
+
const source = this.#inputToSource.get(inputFile)!;
|
|
96
|
+
const { stagedOutput } = this.#sourceInputOutput.get(source)!;
|
|
97
|
+
this.#stagedOutputToOutput.delete(stagedOutput!);
|
|
98
|
+
this.#sourceInputOutput.delete(source);
|
|
99
|
+
this.#inputToSource.delete(inputFile);
|
|
100
|
+
this.#inputToOutput.delete(inputFile);
|
|
101
|
+
this.#inputFiles.delete(inputFile);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
resetInputSource(inputFile: string): void {
|
|
105
|
+
this.#sourceFileObjects.delete(inputFile);
|
|
106
|
+
this.#sourceContents.delete(inputFile);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get modules(): ManifestModule[] {
|
|
110
|
+
return this.#modules;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get transformers(): string[] {
|
|
114
|
+
return this.#transformers;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getAllFiles(): string[] {
|
|
118
|
+
return [...this.#inputFiles];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Build watcher
|
|
122
|
+
getWatcher(handler: {
|
|
123
|
+
create: (inputFile: string) => void;
|
|
124
|
+
update: (inputFile: string) => void;
|
|
125
|
+
delete: (outputFile: string) => void;
|
|
126
|
+
}): (ev: WatchEvent, folder: string) => void {
|
|
127
|
+
const mods = Object.fromEntries(this.modules.map(x => [path.resolve(this.#manifest.workspacePath, x.sourceFolder), x]));
|
|
128
|
+
return ({ file: sourceFile, action }: WatchEvent, folder: string): void => {
|
|
129
|
+
const mod = mods[folder];
|
|
130
|
+
const moduleFile = sourceFile.includes(mod.sourceFolder) ? sourceFile.split(`${mod.sourceFolder}/`)[1] : sourceFile;
|
|
131
|
+
switch (action) {
|
|
132
|
+
case 'create': {
|
|
133
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
134
|
+
if (validFile(fileType)) {
|
|
135
|
+
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
136
|
+
const input = this.registerInput(mod, moduleFile);
|
|
137
|
+
this.#sourceHashes.set(sourceFile, hash);
|
|
138
|
+
handler.create(input);
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'update': {
|
|
143
|
+
const io = this.#sourceInputOutput.get(sourceFile);
|
|
144
|
+
if (io) {
|
|
145
|
+
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
146
|
+
if (this.#sourceHashes.get(sourceFile) !== hash) {
|
|
147
|
+
this.resetInputSource(io.input);
|
|
148
|
+
this.#sourceHashes.set(sourceFile, hash);
|
|
149
|
+
handler.update(io.input);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'delete': {
|
|
155
|
+
const io = this.#sourceInputOutput.get(sourceFile);
|
|
156
|
+
if (io) {
|
|
157
|
+
this.removeInput(io.input);
|
|
158
|
+
if (io.output) {
|
|
159
|
+
handler.delete(io.output);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ts.CompilerHost
|
|
168
|
+
getCompilerHost(options: ts.CompilerOptions): ts.CompilerHost {
|
|
169
|
+
const host: ts.CompilerHost = {
|
|
170
|
+
getCanonicalFileName: (file: string): string => file,
|
|
171
|
+
getCurrentDirectory: path.cwd,
|
|
172
|
+
getDefaultLibFileName: (opts: ts.CompilerOptions): string => ts.getDefaultLibFileName(opts),
|
|
173
|
+
getNewLine: (): string => ts.sys.newLine,
|
|
174
|
+
useCaseSensitiveFileNames: (): boolean => ts.sys.useCaseSensitiveFileNames,
|
|
175
|
+
getDefaultLibLocation: (): string => path.dirname(ts.getDefaultLibFilePath(options)),
|
|
176
|
+
fileExists: (inputFile: string): boolean => this.#inputToSource.has(inputFile) || ts.sys.fileExists(inputFile),
|
|
177
|
+
directoryExists: (inputFolder: string): boolean => this.#inputDirectoryToSource.has(inputFolder) || ts.sys.directoryExists(inputFolder),
|
|
178
|
+
readFile: (inputFile: string): string | undefined => {
|
|
179
|
+
const res = this.#sourceContents.get(inputFile) ?? ts.sys.readFile(this.#inputToSource.get(inputFile) ?? inputFile);
|
|
180
|
+
this.#sourceContents.set(inputFile, res);
|
|
181
|
+
return res;
|
|
182
|
+
},
|
|
183
|
+
writeFile: (
|
|
184
|
+
outputFile: string,
|
|
185
|
+
text: string,
|
|
186
|
+
bom: boolean,
|
|
187
|
+
onError?: (message: string) => void,
|
|
188
|
+
sourceFiles?: readonly ts.SourceFile[],
|
|
189
|
+
data?: ts.WriteFileCallbackData
|
|
190
|
+
): void => {
|
|
191
|
+
if (outputFile.endsWith('package.json')) {
|
|
192
|
+
text = CompilerUtil.rewritePackageJSON(this.#manifest, text, options);
|
|
193
|
+
} else if (!options.inlineSourceMap && options.sourceMap && outputFile.endsWith('.map')) {
|
|
194
|
+
text = CompilerUtil.rewriteSourceMap(this.#manifest.workspacePath, text, f => this.#sourceInputOutput.get(this.#inputToSource.get(f)!));
|
|
195
|
+
} else if (options.inlineSourceMap && CompilerUtil.isSourceMapUrlPosData(data)) {
|
|
196
|
+
text = CompilerUtil.rewriteInlineSourceMap(this.#manifest.workspacePath, text, f => this.#sourceInputOutput.get(this.#inputToSource.get(f)!), data);
|
|
197
|
+
}
|
|
198
|
+
outputFile = this.#stagedOutputToOutput.get(outputFile) ?? outputFile;
|
|
199
|
+
ts.sys.writeFile(outputFile, text, bom);
|
|
200
|
+
},
|
|
201
|
+
getSourceFile: (inputFile: string, language: ts.ScriptTarget, __onErr?: unknown): ts.SourceFile => {
|
|
202
|
+
if (!this.#sourceFileObjects.has(inputFile)) {
|
|
203
|
+
const content = host.readFile(inputFile)!;
|
|
204
|
+
this.#sourceFileObjects.set(inputFile, ts.createSourceFile(inputFile, content ?? '', language));
|
|
205
|
+
}
|
|
206
|
+
return this.#sourceFileObjects.get(inputFile)!;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
return host;
|
|
210
|
+
}
|
|
211
|
+
}
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { ManifestRoot, Package, path } from '@travetto/manifest';
|
|
4
|
+
|
|
5
|
+
type InputToSource = (inputFile: string) => ({ source: string } | undefined);
|
|
6
|
+
export type FileWatchEvent = { type: 'create' | 'delete' | 'update', path: string };
|
|
7
|
+
|
|
8
|
+
const nativeCwd = process.cwd();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Standard utilities for compiler
|
|
12
|
+
*/
|
|
13
|
+
export class CompilerUtil {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Map input file to output format, generally converting ts extensions to js
|
|
17
|
+
* @param file
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
static inputToOutput(file: string): string {
|
|
21
|
+
return file.replace(/[.][tj]s$/, '.js');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Determines if write callback data has sourcemap information
|
|
26
|
+
* @param data
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
static isSourceMapUrlPosData(data?: ts.WriteFileCallbackData): data is { sourceMapUrlPos: number } {
|
|
30
|
+
return data !== undefined && data !== null && typeof data === 'object' && ('sourceMapUrlPos' in data);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Rewrite's sourcemap locations to real folders
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
static rewriteSourceMap(root: string, text: string, inputToSource: InputToSource): string {
|
|
38
|
+
const data: { sourceRoot: string, sources: string[] } = JSON.parse(text);
|
|
39
|
+
const src = path.resolve(data.sourceRoot, data.sources[0]);
|
|
40
|
+
|
|
41
|
+
const { source: file } = inputToSource(src) ?? {};
|
|
42
|
+
if (file) {
|
|
43
|
+
data.sourceRoot = root;
|
|
44
|
+
data.sources = [file];
|
|
45
|
+
text = JSON.stringify(data);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Rewrite's inline sourcemap locations to real folders
|
|
53
|
+
* @param text
|
|
54
|
+
* @param inputToSource
|
|
55
|
+
* @param writeData
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
static rewriteInlineSourceMap(
|
|
59
|
+
root: string,
|
|
60
|
+
text: string,
|
|
61
|
+
inputToSource: InputToSource,
|
|
62
|
+
{ sourceMapUrlPos }: ts.WriteFileCallbackData & { sourceMapUrlPos: number }
|
|
63
|
+
): string {
|
|
64
|
+
const sourceMapUrl = text.substring(sourceMapUrlPos);
|
|
65
|
+
const [prefix, sourceMapData] = sourceMapUrl.split('base64,');
|
|
66
|
+
const rewritten = this.rewriteSourceMap(root, Buffer.from(sourceMapData, 'base64url').toString('utf8'), inputToSource);
|
|
67
|
+
return [
|
|
68
|
+
text.substring(0, sourceMapUrlPos),
|
|
69
|
+
prefix,
|
|
70
|
+
'base64,',
|
|
71
|
+
Buffer.from(rewritten, 'utf8').toString('base64url')
|
|
72
|
+
].join('');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Rewrites the package.json to target .js files instead of .ts files, and pins versions
|
|
77
|
+
* @param manifest
|
|
78
|
+
* @param file
|
|
79
|
+
* @param text
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
static rewritePackageJSON(manifest: ManifestRoot, text: string, opts: ts.CompilerOptions): string {
|
|
83
|
+
const pkg: Package = JSON.parse(text);
|
|
84
|
+
if (pkg.files) {
|
|
85
|
+
pkg.files = pkg.files.map(x => this.inputToOutput(x));
|
|
86
|
+
}
|
|
87
|
+
if (pkg.main) {
|
|
88
|
+
pkg.main = this.inputToOutput(pkg.main);
|
|
89
|
+
}
|
|
90
|
+
pkg.type = opts.module !== ts.ModuleKind.CommonJS ? 'module' : 'commonjs';
|
|
91
|
+
for (const key of ['devDependencies', 'dependencies', 'peerDependencies'] as const) {
|
|
92
|
+
if (key in pkg) {
|
|
93
|
+
for (const dep of Object.keys(pkg[key] ?? {})) {
|
|
94
|
+
if (dep in manifest.modules) {
|
|
95
|
+
pkg[key]![dep] = manifest.modules[dep].version;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return JSON.stringify(pkg, null, 2);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Build transpilation error
|
|
105
|
+
* @param filename The name of the file
|
|
106
|
+
* @param diagnostics The diagnostic errors
|
|
107
|
+
*/
|
|
108
|
+
static buildTranspileError(filename: string, diagnostics: Error | readonly ts.Diagnostic[]): Error {
|
|
109
|
+
if (diagnostics instanceof Error) {
|
|
110
|
+
return diagnostics;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const errors: string[] = diagnostics.slice(0, 5).map(diag => {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
115
|
+
const message = ts.flattenDiagnosticMessageText(diag.messageText, '\n');
|
|
116
|
+
if (diag.file) {
|
|
117
|
+
const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start!);
|
|
118
|
+
return ` @ ${diag.file.fileName.replace(nativeCwd, '.')}(${line + 1}, ${character + 1}): ${message}`;
|
|
119
|
+
} else {
|
|
120
|
+
return ` ${message}`;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (diagnostics.length > 5) {
|
|
125
|
+
errors.push(`${diagnostics.length - 5} more ...`);
|
|
126
|
+
}
|
|
127
|
+
return new Error(`Transpiling ${filename.replace(nativeCwd, '.')} failed: \n${errors.join('\n')}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Naive hashing
|
|
132
|
+
*/
|
|
133
|
+
static naiveHash(text: string): number {
|
|
134
|
+
let hash = 5381;
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < text.length; i++) {
|
|
137
|
+
// eslint-disable-next-line no-bitwise
|
|
138
|
+
hash = (hash * 33) ^ text.charCodeAt(i);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return Math.abs(hash);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { Module } from 'module';
|
|
4
|
+
|
|
5
|
+
import type { ManifestContext } from '@travetto/manifest';
|
|
6
|
+
import { TranspileUtil } from './transpile';
|
|
7
|
+
|
|
8
|
+
const SOURCE_SEED = ['package.json', 'index.ts', '__index__.ts', 'src', 'support', 'bin'];
|
|
9
|
+
const PRECOMPILE_MODS = ['@travetto/terminal', '@travetto/manifest', '@travetto/transformer', '@travetto/compiler'];
|
|
10
|
+
|
|
11
|
+
const importManifest = (ctx: ManifestContext): Promise<typeof import('@travetto/manifest')> =>
|
|
12
|
+
import(path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/manifest/__index__.js'));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Recompile folder if stale
|
|
16
|
+
*/
|
|
17
|
+
async function compileIfStale(ctx: ManifestContext, scope: string, mod: string, seed: string[]): Promise<string[]> {
|
|
18
|
+
const files = await TranspileUtil.getModuleSources(ctx, mod, seed);
|
|
19
|
+
const changes = files.filter(x => x.stale).map(x => x.input);
|
|
20
|
+
const out: string[] = [];
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
await TranspileUtil.withLogger(scope, { args: [mod], basic: false }, async log => {
|
|
24
|
+
if (files.some(f => f.stale)) {
|
|
25
|
+
log('debug', 'Starting');
|
|
26
|
+
for (const file of files.filter(x => x.stale)) {
|
|
27
|
+
await TranspileUtil.transpileFile(ctx, file.input, file.output);
|
|
28
|
+
}
|
|
29
|
+
if (changes.length) {
|
|
30
|
+
out.push(...changes.map(x => `${mod}/${x}`));
|
|
31
|
+
log('debug', `Source changed: ${changes.join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
log('debug', 'Completed');
|
|
34
|
+
} else {
|
|
35
|
+
log('debug', 'Skipped');
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error(err);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Run the compiler
|
|
46
|
+
*/
|
|
47
|
+
export async function compile(ctx: ManifestContext, op?: 'watch' | 'build'): Promise<void> {
|
|
48
|
+
let changes = 0;
|
|
49
|
+
|
|
50
|
+
await TranspileUtil.withLogger('precompile', async () => {
|
|
51
|
+
for (const mod of PRECOMPILE_MODS) {
|
|
52
|
+
changes += (await compileIfStale(ctx, 'precompile', mod, SOURCE_SEED)).length;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const { ManifestUtil, ManifestDeltaUtil } = await importManifest(ctx);
|
|
57
|
+
|
|
58
|
+
const manifest = await TranspileUtil.withLogger('manifest', async () => ManifestUtil.buildManifest(ctx));
|
|
59
|
+
|
|
60
|
+
await TranspileUtil.withLogger('transformers', async () => {
|
|
61
|
+
for (const mod of Object.values(manifest.modules).filter(m => m.files.$transformer?.length)) {
|
|
62
|
+
changes += (await compileIfStale(ctx, 'transformers', mod.name, ['package.json', ...mod.files.$transformer!.map(x => x[0])])).length;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const delta = await TranspileUtil.withLogger('delta', async log => {
|
|
67
|
+
if (changes) {
|
|
68
|
+
log('debug', 'Skipping, everything changed');
|
|
69
|
+
return [{ type: 'changed', file: '*', module: ctx.mainModule } as const];
|
|
70
|
+
} else {
|
|
71
|
+
return ManifestDeltaUtil.produceDelta(ctx, manifest);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (changes) {
|
|
76
|
+
await fs.rm(path.resolve(ctx.workspacePath, ctx.outputFolder), { recursive: true, force: true });
|
|
77
|
+
TranspileUtil.log('reset', [], 'info', 'Clearing output due to compiler changes');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Write manifest
|
|
81
|
+
await TranspileUtil.withLogger('manifest', async log => {
|
|
82
|
+
await ManifestUtil.writeManifest(ctx, manifest);
|
|
83
|
+
log('debug', `Wrote manifest ${ctx.mainModule}`);
|
|
84
|
+
|
|
85
|
+
// Update all manifests
|
|
86
|
+
if (delta.length && ctx.monoRepo && !ctx.mainFolder) {
|
|
87
|
+
const names: string[] = [];
|
|
88
|
+
const mods = Object.values(manifest.modules).filter(x => x.local && x.name !== ctx.mainModule);
|
|
89
|
+
for (const mod of mods) {
|
|
90
|
+
await ManifestUtil.rewriteManifest(path.resolve(ctx.workspacePath, mod.sourceFolder));
|
|
91
|
+
names.push(mod.name);
|
|
92
|
+
}
|
|
93
|
+
log('debug', `Changes triggered ${delta.map(x => `${x.type}:${x.module}:${x.file}`)}`);
|
|
94
|
+
log('debug', `Rewrote monorepo manifests [changes=${delta.length}] ${names.join(', ')}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await TranspileUtil.withLogger('compile', { args: [], basic: false }, async log => {
|
|
99
|
+
const changed = delta.filter(x => x.type === 'added' || x.type === 'changed');
|
|
100
|
+
log('debug', `Started action=${op} changed=${changed.map(x => `${x.module}/${x.file}`)}`);
|
|
101
|
+
if (changed.length || op === 'watch') {
|
|
102
|
+
await TranspileUtil.runCompiler(ctx, manifest, changed, op === 'watch');
|
|
103
|
+
log('debug', 'Finished');
|
|
104
|
+
} else {
|
|
105
|
+
log('debug', 'Skipped');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (op === 'build') {
|
|
110
|
+
TranspileUtil.log('build', [], 'info', 'Successfully built');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Export manifests
|
|
116
|
+
*/
|
|
117
|
+
export async function exportManifest(ctx: ManifestContext, output?: string, env = 'dev'): Promise<string | undefined> {
|
|
118
|
+
const { ManifestUtil } = await importManifest(ctx);
|
|
119
|
+
const manifest = await ManifestUtil.buildManifest(ctx);
|
|
120
|
+
|
|
121
|
+
// If in prod mode, only include std modules
|
|
122
|
+
if (/^prod/i.test(env)) {
|
|
123
|
+
manifest.modules = Object.fromEntries(
|
|
124
|
+
Object.values(manifest.modules)
|
|
125
|
+
.filter(x => x.profiles.includes('std'))
|
|
126
|
+
.map(m => [m.name, m])
|
|
127
|
+
);
|
|
128
|
+
// Mark output folder/workspace path as portable
|
|
129
|
+
manifest.outputFolder = '';
|
|
130
|
+
manifest.workspacePath = '';
|
|
131
|
+
}
|
|
132
|
+
if (output) {
|
|
133
|
+
if (!output.endsWith('.json')) {
|
|
134
|
+
output = path.resolve(output, 'manifest.json');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await TranspileUtil.writeTextFile(output, JSON.stringify(manifest));
|
|
138
|
+
TranspileUtil.log('manifest', [], 'info', `Wrote manifest ${output}`);
|
|
139
|
+
return output;
|
|
140
|
+
} else {
|
|
141
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function launchMain(ctx: ManifestContext): Promise<void> {
|
|
147
|
+
// Rewriting node_path
|
|
148
|
+
const nodeOut = path.resolve(ctx.workspacePath, ctx.outputFolder, 'node_modules');
|
|
149
|
+
const og = process.env.NODE_PATH;
|
|
150
|
+
process.env.NODE_PATH = [nodeOut, og].join(path.delimiter);
|
|
151
|
+
// @ts-expect-error
|
|
152
|
+
Module._initPaths();
|
|
153
|
+
process.env.NODE_PATH = og; // Restore
|
|
154
|
+
|
|
155
|
+
process.env.TRV_MANIFEST = path.resolve(nodeOut, ctx.mainModule);
|
|
156
|
+
|
|
157
|
+
// TODO: Externalize somehow?
|
|
158
|
+
const cliMain = path.join(nodeOut, '@travetto/cli/support/cli.js');
|
|
159
|
+
return await import(cliMain);
|
|
160
|
+
}
|