Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/lib/tsb/index.ts
4772 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import Vinyl from 'vinyl';
7
import through from 'through';
8
import * as builder from './builder.ts';
9
import ts from 'typescript';
10
import { Readable, Writable, Duplex } from 'stream';
11
import { dirname } from 'path';
12
import { strings } from './utils.ts';
13
import { readFileSync, statSync } from 'fs';
14
import log from 'fancy-log';
15
import { ESBuildTranspiler, type ITranspiler, TscTranspiler } from './transpiler.ts';
16
import colors from 'ansi-colors';
17
18
export interface IncrementalCompiler {
19
(token?: any): Readable & Writable;
20
src(opts?: { cwd?: string; base?: string }): Readable;
21
}
22
23
class EmptyDuplex extends Duplex {
24
_write(_chunk: any, _encoding: string, callback: (err?: Error) => void): void { callback(); }
25
_read() { this.push(null); }
26
}
27
28
function createNullCompiler(): IncrementalCompiler {
29
const result: IncrementalCompiler = function () { return new EmptyDuplex(); };
30
result.src = () => new EmptyDuplex();
31
return result;
32
}
33
34
const _defaultOnError = (err: string) => console.log(JSON.stringify(err, null, 4));
35
36
export function create(
37
projectPath: string,
38
existingOptions: Partial<ts.CompilerOptions>,
39
config: { verbose?: boolean; transpileOnly?: boolean; transpileOnlyIncludesDts?: boolean; transpileWithEsbuild?: boolean },
40
onError: (message: string) => void = _defaultOnError
41
): IncrementalCompiler {
42
43
function printDiagnostic(diag: ts.Diagnostic | Error): void {
44
45
if (diag instanceof Error) {
46
onError(diag.message);
47
} else if (!diag.file || !diag.start) {
48
onError(ts.flattenDiagnosticMessageText(diag.messageText, '\n'));
49
} else {
50
const lineAndCh = diag.file.getLineAndCharacterOfPosition(diag.start);
51
onError(strings.format('{0}({1},{2}): {3}',
52
diag.file.fileName,
53
lineAndCh.line + 1,
54
lineAndCh.character + 1,
55
ts.flattenDiagnosticMessageText(diag.messageText, '\n'))
56
);
57
}
58
}
59
60
const parsed = ts.readConfigFile(projectPath, ts.sys.readFile);
61
if (parsed.error) {
62
printDiagnostic(parsed.error);
63
return createNullCompiler();
64
}
65
66
const cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname(projectPath), existingOptions);
67
if (cmdLine.errors.length > 0) {
68
cmdLine.errors.forEach(printDiagnostic);
69
return createNullCompiler();
70
}
71
72
function logFn(topic: string, message: string): void {
73
if (config.verbose) {
74
log(colors.cyan(topic), message);
75
}
76
}
77
78
// FULL COMPILE stream doing transpile, syntax and semantic diagnostics
79
function createCompileStream(builder: builder.ITypeScriptBuilder, token?: builder.CancellationToken): Readable & Writable {
80
81
return through(function (this: through.ThroughStream, file: Vinyl) {
82
// give the file to the compiler
83
if (file.isStream()) {
84
this.emit('error', 'no support for streams');
85
return;
86
}
87
builder.file(file);
88
89
}, function (this: { queue(a: any): void }) {
90
// start the compilation process
91
builder.build(
92
file => this.queue(file),
93
printDiagnostic,
94
token
95
).catch(e => console.error(e)).then(() => this.queue(null));
96
});
97
}
98
99
// TRANSPILE ONLY stream doing just TS to JS conversion
100
function createTranspileStream(transpiler: ITranspiler): Readable & Writable {
101
return through(function (this: through.ThroughStream & { queue(a: any): void }, file: Vinyl) {
102
// give the file to the compiler
103
if (file.isStream()) {
104
this.emit('error', 'no support for streams');
105
return;
106
}
107
if (!file.contents) {
108
return;
109
}
110
if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) {
111
return;
112
}
113
114
if (!transpiler.onOutfile) {
115
transpiler.onOutfile = file => this.queue(file);
116
}
117
118
transpiler.transpile(file);
119
120
}, function (this: { queue(a: any): void }) {
121
transpiler.join().then(() => {
122
this.queue(null);
123
transpiler.onOutfile = undefined;
124
});
125
});
126
}
127
128
129
let result: IncrementalCompiler;
130
if (config.transpileOnly) {
131
const transpiler = !config.transpileWithEsbuild
132
? new TscTranspiler(logFn, printDiagnostic, projectPath, cmdLine)
133
: new ESBuildTranspiler(logFn, printDiagnostic, projectPath, cmdLine);
134
result = (() => createTranspileStream(transpiler)) as IncrementalCompiler;
135
} else {
136
const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine);
137
result = ((token: builder.CancellationToken) => createCompileStream(_builder, token)) as IncrementalCompiler;
138
}
139
140
result.src = (opts?: { cwd?: string; base?: string }) => {
141
let _pos = 0;
142
const _fileNames = cmdLine.fileNames.slice(0);
143
return new class extends Readable {
144
constructor() {
145
super({ objectMode: true });
146
}
147
_read() {
148
let more: boolean = true;
149
let path: string;
150
for (; more && _pos < _fileNames.length; _pos++) {
151
path = _fileNames[_pos];
152
more = this.push(new Vinyl({
153
path,
154
contents: readFileSync(path),
155
stat: statSync(path),
156
cwd: opts && opts.cwd,
157
base: opts && opts.base || dirname(projectPath)
158
}));
159
}
160
if (_pos >= _fileNames.length) {
161
this.push(null);
162
}
163
}
164
};
165
};
166
167
return result as IncrementalCompiler;
168
}
169
170