@travetto/compiler 3.0.0-rc.4 → 3.0.0-rc.7
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/transpile.d.ts +32 -0
- package/bin/transpile.js +227 -0
- package/bin/trv.js +81 -0
- package/package.json +30 -15
- package/src/compiler.ts +147 -147
- package/src/state.ts +225 -0
- package/src/util.ts +202 -0
- package/support/bin/compiler-bootstrap.ts +151 -0
- package/support/bin/utils.ts +116 -0
- package/support/main.output.ts +11 -0
- package/tsconfig.trv.json +22 -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/compiler.ts
CHANGED
|
@@ -1,187 +1,187 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { ModuleManager } from '@travetto/boot/src/internal/module';
|
|
8
|
-
import { Dynamic } from '@travetto/base/src/internal/dynamic';
|
|
9
|
-
import { TranspileUtil } from '@travetto/boot/src/internal/transpile-util';
|
|
5
|
+
import { ManifestState } from '@travetto/manifest';
|
|
6
|
+
import { GlobalTerminal, TerminalProgressEvent } from '@travetto/terminal';
|
|
10
7
|
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { CompilerUtil } from './util';
|
|
9
|
+
import { CompilerState } from './state';
|
|
13
10
|
|
|
14
|
-
type
|
|
15
|
-
|
|
11
|
+
export type TransformerProvider = {
|
|
12
|
+
init(checker: ts.TypeChecker): void;
|
|
13
|
+
get(): ts.CustomTransformers | undefined;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type EmitError = Error | readonly ts.Diagnostic[];
|
|
17
|
+
type Emitter = (file: string, newProgram?: boolean) => EmitError | undefined;
|
|
18
|
+
type EmitEvent = { file: string, i: number, total: number, err?: EmitError };
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
|
-
* Compilation
|
|
21
|
+
* Compilation support
|
|
19
22
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
export class Compiler {
|
|
24
|
+
|
|
25
|
+
#bootTsconfig: string;
|
|
26
|
+
#state: CompilerState;
|
|
27
|
+
#transformers: string[];
|
|
28
|
+
|
|
29
|
+
init(manifestState: ManifestState): this {
|
|
30
|
+
this.#state = new CompilerState(manifestState);
|
|
31
|
+
this.#bootTsconfig = this.#state.resolveModuleFile('@travetto/compiler', 'tsconfig.trv.json');
|
|
32
|
+
|
|
33
|
+
this.#transformers = this.state.modules.flatMap(
|
|
34
|
+
x => (x.files.support ?? [])
|
|
35
|
+
.filter(([f, type]) => type === 'ts' && f.startsWith('support/transformer.'))
|
|
36
|
+
.map(([f]) =>
|
|
37
|
+
path.resolve(
|
|
38
|
+
this.#state.manifest.workspacePath,
|
|
39
|
+
this.#state.manifest.compilerFolder,
|
|
40
|
+
x.output,
|
|
41
|
+
f.replace(/[.][tj]s$/, '.js')
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
);
|
|
22
45
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#emitter = new EventEmitter();
|
|
26
|
-
#host = new SourceHost();
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
27
48
|
|
|
28
|
-
|
|
49
|
+
get state(): CompilerState {
|
|
50
|
+
return this.#state;
|
|
51
|
+
}
|
|
29
52
|
|
|
30
53
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @param forFile If this file is new, force a recompilation
|
|
54
|
+
* Watches local modules
|
|
34
55
|
*/
|
|
35
|
-
#
|
|
56
|
+
async #watchLocalModules(emit: Emitter): Promise<() => Promise<void>> {
|
|
57
|
+
const folders = this.state.modules.filter(x => x.local).map(x => x.source);
|
|
58
|
+
const emitWithError = (file: string): void => {
|
|
59
|
+
const err = emit(file, true);
|
|
60
|
+
if (err) {
|
|
61
|
+
console.error(CompilerUtil.buildTranspileError(file, err));
|
|
62
|
+
} else {
|
|
63
|
+
console.error('Compiled', file.split('node_modules/')[1]);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const watcher = this.state.getWatcher({
|
|
67
|
+
create: (inputFile) => emitWithError(inputFile),
|
|
68
|
+
update: (inputFile) => emitWithError(inputFile),
|
|
69
|
+
delete: (outputFile) => fs.unlink(outputFile).catch(() => { })
|
|
70
|
+
});
|
|
71
|
+
return CompilerUtil.fileWatcher(folders, watcher);
|
|
72
|
+
}
|
|
36
73
|
|
|
37
|
-
|
|
74
|
+
async createTransformerProvider(): Promise<TransformerProvider> {
|
|
75
|
+
const { TransformerManager } = await import('@travetto/transformer');
|
|
76
|
+
return TransformerManager.create(this.#transformers, this.state.manifest);
|
|
77
|
+
}
|
|
38
78
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
async writeRawFile(file: string, contents: string, mode?: string): Promise<void> {
|
|
80
|
+
const outFile = path.resolve(
|
|
81
|
+
this.#state.manifest.workspacePath,
|
|
82
|
+
this.#state.manifest.outputFolder,
|
|
83
|
+
file
|
|
84
|
+
);
|
|
85
|
+
await fs.mkdir(path.dirname(outFile), { recursive: true });
|
|
86
|
+
await fs.writeFile(outFile, contents, { encoding: 'utf8', mode });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async outputInit(): Promise<void> {
|
|
90
|
+
// Write manifest
|
|
91
|
+
await this.writeRawFile(this.#state.manifest.manifestFile, JSON.stringify(this.state.manifest));
|
|
92
|
+
// TODO: This needs to be isolated, just like in the bootstrap
|
|
93
|
+
await this.writeRawFile('trv', '#!/bin/sh\nnode node_modules/@travetto/cli/support/main.cli.js $@\n', '755');
|
|
94
|
+
await this.writeRawFile('trv.cmd', 'node node_modules/@travetto/cli/support/main.cli.js %*\n', '755');
|
|
53
95
|
}
|
|
54
96
|
|
|
55
97
|
/**
|
|
56
|
-
*
|
|
98
|
+
* Compile in a single pass, only emitting dirty files
|
|
57
99
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
100
|
+
async getCompiler(): Promise<Emitter> {
|
|
101
|
+
let program: ts.Program;
|
|
102
|
+
|
|
103
|
+
const transformers = await this.createTransformerProvider();
|
|
104
|
+
const options = await CompilerUtil.getCompilerOptions(
|
|
105
|
+
path.resolve(
|
|
106
|
+
this.#state.manifest.workspacePath,
|
|
107
|
+
this.#state.manifest.outputFolder,
|
|
108
|
+
),
|
|
109
|
+
this.#bootTsconfig,
|
|
110
|
+
this.#state.manifest.workspacePath
|
|
111
|
+
);
|
|
112
|
+
const host = this.state.getCompilerHost(options);
|
|
113
|
+
|
|
114
|
+
const emit = (file: string, needsNewProgram = program === undefined): EmitError | undefined => {
|
|
115
|
+
if (needsNewProgram) {
|
|
116
|
+
program = ts.createProgram({ rootNames: this.#state.getAllFiles(), host, options, oldProgram: program });
|
|
117
|
+
transformers.init(program.getTypeChecker());
|
|
118
|
+
}
|
|
62
119
|
try {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
const result = prog.emit(
|
|
66
|
-
prog.getSourceFile(filename),
|
|
67
|
-
undefined,
|
|
68
|
-
undefined,
|
|
69
|
-
false,
|
|
70
|
-
this.#transformerManager.getTransformers()
|
|
120
|
+
const result = program.emit(
|
|
121
|
+
program.getSourceFile(file)!, host.writeFile, undefined, false, transformers.get()
|
|
71
122
|
);
|
|
72
123
|
|
|
73
|
-
|
|
124
|
+
if (result.diagnostics?.length) {
|
|
125
|
+
return result.diagnostics;
|
|
126
|
+
}
|
|
74
127
|
} catch (err) {
|
|
75
|
-
if (
|
|
128
|
+
if (err instanceof Error) {
|
|
129
|
+
return err;
|
|
130
|
+
} else {
|
|
76
131
|
throw err;
|
|
77
132
|
}
|
|
78
|
-
const errContent = TranspileUtil.transpileError(filename, err);
|
|
79
|
-
this.#host.contents.set(filename, errContent);
|
|
80
133
|
}
|
|
81
|
-
|
|
82
|
-
} else {
|
|
83
|
-
this.#host.fetchFile(filename);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return this.#host.contents.get(filename)!;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get program
|
|
91
|
-
* @private
|
|
92
|
-
*/
|
|
93
|
-
getProgram(): ts.Program {
|
|
94
|
-
return this.#getProgram();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Initialize the compiler
|
|
99
|
-
*/
|
|
100
|
-
async init(): Promise<void> {
|
|
101
|
-
if (this.active) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const start = Date.now();
|
|
106
|
-
this.active = true;
|
|
107
|
-
|
|
108
|
-
if (!EnvUtil.isReadonly()) {
|
|
109
|
-
await this.#transformerManager.init();
|
|
110
|
-
// Enhance transpilation, with custom transformations
|
|
111
|
-
ModuleManager.setTranspiler(tsf => this.#transpile(tsf));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
ModuleManager.onUnload((f, unlink) => this.#host.unload(f, unlink)); // Remove source
|
|
115
|
-
|
|
116
|
-
// Update source map support to read from transpiler cache
|
|
117
|
-
sourceMapSupport.install({
|
|
118
|
-
retrieveFile: p => this.#host.contents.get(PathUtil.toUnixTs(p))!
|
|
119
|
-
});
|
|
134
|
+
};
|
|
120
135
|
|
|
121
|
-
|
|
136
|
+
return emit;
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
/**
|
|
125
|
-
*
|
|
140
|
+
* Emit all files as a stream
|
|
126
141
|
*/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
142
|
+
async * emit(files: string[], emitter: Emitter): AsyncIterable<EmitEvent> {
|
|
143
|
+
let i = 0;
|
|
144
|
+
const manifest = this.#state.manifest;
|
|
145
|
+
for (const file of files) {
|
|
146
|
+
const err = emitter(file);
|
|
147
|
+
const outputFile = file
|
|
148
|
+
.replace(/[.]ts$/, '.js')
|
|
149
|
+
.replace(manifest.compilerFolder, manifest.outputFolder);
|
|
150
|
+
yield { file: outputFile, i: i += 1, err, total: files.length };
|
|
132
151
|
}
|
|
133
|
-
ModuleManager.clearUnloadHandlers();
|
|
134
|
-
SourceIndex.reset();
|
|
135
|
-
this.active = false;
|
|
136
152
|
}
|
|
137
153
|
|
|
138
154
|
/**
|
|
139
|
-
*
|
|
155
|
+
* Run the compiler
|
|
140
156
|
*/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
this
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
async run(watch?: boolean): Promise<void> {
|
|
158
|
+
await this.outputInit();
|
|
159
|
+
const emitter = await this.getCompiler();
|
|
160
|
+
let failed = false;
|
|
161
|
+
|
|
162
|
+
const resolveEmittedFile = ({ file, total, i, err }: EmitEvent): TerminalProgressEvent => {
|
|
163
|
+
if (err) {
|
|
164
|
+
failed = true;
|
|
165
|
+
console.error(CompilerUtil.buildTranspileError(file, err));
|
|
166
|
+
}
|
|
167
|
+
return { idx: i, total, text: `Compiling [%idx/%total] -- ${file.split('node_modules/')[1]}` };
|
|
168
|
+
};
|
|
153
169
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
added(filename: string): void {
|
|
158
|
-
if (filename in require.cache) { // if already loaded
|
|
159
|
-
ModuleManager.unload(filename);
|
|
170
|
+
let files = this.state.getDirtyFiles();
|
|
171
|
+
if (!watch && !files.length) {
|
|
172
|
+
files = this.state.getAllFiles();
|
|
160
173
|
}
|
|
161
|
-
// Load Synchronously
|
|
162
|
-
require(filename);
|
|
163
|
-
this.notify('added', filename);
|
|
164
|
-
}
|
|
165
174
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
175
|
+
if (files.length) {
|
|
176
|
+
await GlobalTerminal.trackProgress(this.emit(files, emitter), resolveEmittedFile, { position: 'bottom' });
|
|
177
|
+
if (failed) {
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
173
181
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
changed(filename: string): void {
|
|
178
|
-
if (this.#host.hashChanged(filename)) {
|
|
179
|
-
ModuleManager.unload(filename);
|
|
180
|
-
// Load Synchronously
|
|
181
|
-
require(filename);
|
|
182
|
-
this.notify('changed', filename);
|
|
182
|
+
if (watch) {
|
|
183
|
+
await this.#watchLocalModules(emitter);
|
|
184
|
+
await new Promise(r => setTimeout(r, 1000 * 60 * 60 * 24));
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export const Compiler = new $Compiler();
|
|
187
|
+
}
|
package/src/state.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { mkdirSync, readFileSync, writeFile } from 'fs';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
path,
|
|
6
|
+
ManifestModuleUtil, ManifestDelta, ManifestModule,
|
|
7
|
+
ManifestModuleFileType, ManifestRoot, ManifestState
|
|
8
|
+
} from '@travetto/manifest';
|
|
9
|
+
|
|
10
|
+
import { CompilerUtil, FileWatchEvent } from './util';
|
|
11
|
+
|
|
12
|
+
const validFile = (type: ManifestModuleFileType): boolean => type === 'ts' || type === 'package-json' || type === 'js';
|
|
13
|
+
|
|
14
|
+
export class CompilerState {
|
|
15
|
+
|
|
16
|
+
#inputFiles: Set<string>;
|
|
17
|
+
#relativeInputToSource = new Map<string, { source: string, module: ManifestModule }>();
|
|
18
|
+
#inputToSource = new Map<string, string>();
|
|
19
|
+
#inputToOutput = new Map<string, string | undefined>();
|
|
20
|
+
#inputDirectoryToSource = new Map<string, string>();
|
|
21
|
+
#sourceInputOutput = new Map<string, { input: string, output?: string, relativeInput: string, module: ManifestModule }>();
|
|
22
|
+
|
|
23
|
+
#sourceContents = new Map<string, string | undefined>();
|
|
24
|
+
#sourceFileObjects = new Map<string, ts.SourceFile>();
|
|
25
|
+
#sourceHashes = new Map<string, number>();
|
|
26
|
+
|
|
27
|
+
#manifest: ManifestRoot;
|
|
28
|
+
#delta: ManifestDelta;
|
|
29
|
+
#modules: ManifestModule[];
|
|
30
|
+
|
|
31
|
+
constructor({ manifest, delta }: ManifestState) {
|
|
32
|
+
this.#manifest = manifest;
|
|
33
|
+
this.#delta = delta;
|
|
34
|
+
this.#modules = Object.values(this.#manifest.modules);
|
|
35
|
+
this.#inputFiles = new Set(this.#modules.flatMap(
|
|
36
|
+
x => [
|
|
37
|
+
...x.files.bin ?? [],
|
|
38
|
+
...x.files.src ?? [],
|
|
39
|
+
...x.files.support ?? [],
|
|
40
|
+
...x.files.doc ?? [],
|
|
41
|
+
...x.files.test ?? [],
|
|
42
|
+
...x.files.$index ?? [],
|
|
43
|
+
...x.files.$package ?? []
|
|
44
|
+
]
|
|
45
|
+
.filter(([file, type]) => validFile(type) || type === 'typings')
|
|
46
|
+
.map(([f]) => this.registerInput(x, f))
|
|
47
|
+
));
|
|
48
|
+
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
registerInput(module: ManifestModule, moduleFile: string): string {
|
|
53
|
+
const relativeInput = `${module.output}/${moduleFile}`;
|
|
54
|
+
const sourceFile = `${module.source}/${moduleFile}`;
|
|
55
|
+
const sourceFolder = path.dirname(sourceFile);
|
|
56
|
+
const inputFile = path.resolve(relativeInput);
|
|
57
|
+
const inputFolder = path.dirname(inputFile);
|
|
58
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
59
|
+
const outputFile = fileType === 'typings' ?
|
|
60
|
+
undefined :
|
|
61
|
+
path.resolve(
|
|
62
|
+
this.#manifest.workspacePath,
|
|
63
|
+
this.#manifest.outputFolder,
|
|
64
|
+
(fileType === 'ts' ? relativeInput.replace(/[.]ts$/, '.js') : relativeInput)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
this.#inputToSource.set(inputFile, sourceFile);
|
|
68
|
+
this.#sourceInputOutput.set(sourceFile, { input: inputFile, output: outputFile, relativeInput, module });
|
|
69
|
+
this.#inputToOutput.set(inputFile, outputFile);
|
|
70
|
+
this.#inputDirectoryToSource.set(inputFolder, sourceFolder);
|
|
71
|
+
this.#relativeInputToSource.set(relativeInput, { source: sourceFile, module });
|
|
72
|
+
|
|
73
|
+
return inputFile;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
removeInput(inputFile: string): void {
|
|
77
|
+
const source = this.#inputToSource.get(inputFile)!;
|
|
78
|
+
const { relativeInput } = this.#sourceInputOutput.get(source)!;
|
|
79
|
+
this.#sourceInputOutput.delete(source);
|
|
80
|
+
this.#inputToSource.delete(inputFile);
|
|
81
|
+
this.#inputToOutput.delete(inputFile);
|
|
82
|
+
this.#relativeInputToSource.delete(relativeInput);
|
|
83
|
+
this.#inputFiles.delete(inputFile);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
resetInputSource(inputFile: string): void {
|
|
87
|
+
this.#sourceFileObjects.delete(inputFile);
|
|
88
|
+
this.#sourceContents.delete(inputFile);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get manifest(): ManifestRoot {
|
|
92
|
+
return this.#manifest;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get modules(): ManifestModule[] {
|
|
96
|
+
return this.#modules;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getDirtyFiles(): string[] {
|
|
100
|
+
if (this.#delta && Object.keys(this.#delta).length) { // If we have any changes
|
|
101
|
+
const files: string[] = [];
|
|
102
|
+
for (const [modName, events] of Object.entries(this.#delta)) {
|
|
103
|
+
const mod = this.#manifest.modules[modName];
|
|
104
|
+
for (const { file } of events) {
|
|
105
|
+
const fileType = ManifestModuleUtil.getFileType(file);
|
|
106
|
+
if (validFile(fileType)) {
|
|
107
|
+
files.push(path.resolve(mod.output, file));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return files;
|
|
112
|
+
} else {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getAllFiles(): string[] {
|
|
118
|
+
return [...this.#inputFiles];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
resolveModuleFile(module: string, file: string): string {
|
|
122
|
+
return `${this.modules.find(m => m.name === module)!.source}/${file}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Build watcher
|
|
126
|
+
getWatcher(handler: {
|
|
127
|
+
create: (inputFile: string) => void;
|
|
128
|
+
update: (inputFile: string) => void;
|
|
129
|
+
delete: (outputFile: string) => void;
|
|
130
|
+
}): (ev: FileWatchEvent, folder: string) => void {
|
|
131
|
+
const mods = Object.fromEntries(this.modules.map(x => [x.source, x]));
|
|
132
|
+
return ({ path: sourceFile, type }: FileWatchEvent, folder: string): void => {
|
|
133
|
+
const mod = mods[folder];
|
|
134
|
+
const moduleFile = sourceFile.replace(`${mod.source}/`, '');
|
|
135
|
+
switch (type) {
|
|
136
|
+
case 'create': {
|
|
137
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
138
|
+
if (validFile(fileType)) {
|
|
139
|
+
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
140
|
+
const input = this.registerInput(mod, moduleFile);
|
|
141
|
+
this.#sourceHashes.set(sourceFile, hash);
|
|
142
|
+
handler.create(input);
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case 'update': {
|
|
147
|
+
const io = this.#sourceInputOutput.get(sourceFile);
|
|
148
|
+
if (io) {
|
|
149
|
+
const hash = CompilerUtil.naiveHash(readFileSync(sourceFile, 'utf8'));
|
|
150
|
+
if (this.#sourceHashes.get(sourceFile) !== hash) {
|
|
151
|
+
this.resetInputSource(io.input);
|
|
152
|
+
this.#sourceHashes.set(sourceFile, hash);
|
|
153
|
+
handler.update(io.input);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case 'delete': {
|
|
159
|
+
const io = this.#sourceInputOutput.get(sourceFile);
|
|
160
|
+
if (io) {
|
|
161
|
+
this.removeInput(io.input);
|
|
162
|
+
if (io.output) {
|
|
163
|
+
handler.delete(io.output);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Update manifest on every change
|
|
170
|
+
writeFile(
|
|
171
|
+
path.resolve(
|
|
172
|
+
this.#manifest.workspacePath,
|
|
173
|
+
this.#manifest.outputFolder,
|
|
174
|
+
this.#manifest.manifestFile
|
|
175
|
+
),
|
|
176
|
+
JSON.stringify(this.#manifest),
|
|
177
|
+
() => { });
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ts.CompilerHost
|
|
182
|
+
getCompilerHost(options: ts.CompilerOptions): ts.CompilerHost {
|
|
183
|
+
const host: ts.CompilerHost = {
|
|
184
|
+
getCanonicalFileName: (file: string): string => file,
|
|
185
|
+
getCurrentDirectory: path.cwd,
|
|
186
|
+
getDefaultLibFileName: (opts: ts.CompilerOptions): string => ts.getDefaultLibFileName(opts),
|
|
187
|
+
getNewLine: (): string => ts.sys.newLine,
|
|
188
|
+
useCaseSensitiveFileNames: (): boolean => ts.sys.useCaseSensitiveFileNames,
|
|
189
|
+
getDefaultLibLocation: (): string => path.dirname(ts.getDefaultLibFilePath(options)),
|
|
190
|
+
fileExists: (inputFile: string): boolean => this.#inputToSource.has(inputFile) || ts.sys.fileExists(inputFile),
|
|
191
|
+
directoryExists: (inputFolder: string): boolean => this.#inputDirectoryToSource.has(inputFolder) || ts.sys.directoryExists(inputFolder),
|
|
192
|
+
readFile: (inputFile: string): string | undefined => {
|
|
193
|
+
const res = this.#sourceContents.get(inputFile) ?? ts.sys.readFile(this.#inputToSource.get(inputFile) ?? inputFile);
|
|
194
|
+
this.#sourceContents.set(inputFile, res);
|
|
195
|
+
return res;
|
|
196
|
+
},
|
|
197
|
+
writeFile: (
|
|
198
|
+
outputFile: string,
|
|
199
|
+
text: string,
|
|
200
|
+
bom: boolean,
|
|
201
|
+
onError?: (message: string) => void,
|
|
202
|
+
sourceFiles?: readonly ts.SourceFile[],
|
|
203
|
+
data?: ts.WriteFileCallbackData
|
|
204
|
+
): void => {
|
|
205
|
+
mkdirSync(path.dirname(outputFile), { recursive: true });
|
|
206
|
+
if (outputFile.endsWith('package.json')) {
|
|
207
|
+
text = CompilerUtil.rewritePackageJSON(this.manifest, text, options);
|
|
208
|
+
} else if (!options.inlineSourceMap && options.sourceMap && outputFile.endsWith('.map')) {
|
|
209
|
+
text = CompilerUtil.rewriteSourceMap(text, f => this.#relativeInputToSource.get(f));
|
|
210
|
+
} else if (options.inlineSourceMap && CompilerUtil.isSourceMapUrlPosData(data)) {
|
|
211
|
+
text = CompilerUtil.rewriteInlineSourceMap(text, f => this.#relativeInputToSource.get(f), data);
|
|
212
|
+
}
|
|
213
|
+
ts.sys.writeFile(outputFile, text, bom);
|
|
214
|
+
},
|
|
215
|
+
getSourceFile: (inputFile: string, language: ts.ScriptTarget, __onErr?: unknown): ts.SourceFile => {
|
|
216
|
+
if (!this.#sourceFileObjects.has(inputFile)) {
|
|
217
|
+
const content = host.readFile(inputFile)!;
|
|
218
|
+
this.#sourceFileObjects.set(inputFile, ts.createSourceFile(inputFile, content ?? '', language));
|
|
219
|
+
}
|
|
220
|
+
return this.#sourceFileObjects.get(inputFile)!;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
return host;
|
|
224
|
+
}
|
|
225
|
+
}
|