Skip to content

Commit 372347b

Browse files
committed
checkpoint: successful initial emit!
1 parent 3a724d3 commit 372347b

1 file changed

Lines changed: 128 additions & 154 deletions

File tree

tools/tsicklecompiler/index.ts

Lines changed: 128 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,171 @@
11

22
import * as path from 'path';
3-
import { promises as fs } from 'fs';
3+
import * as fs from 'fs';
4+
import { promises as asyncfs } from 'fs';
45
import ts from 'typescript';
56
import * as tsickle from 'tsickle';
6-
import { loadTsickleHostConfig, TsickleHostConfig } from './tsicklehost';
7-
import { CompilerHost, loadTsConfigFromPath } from './compilerhost';
87

98
const DEBUG = true;
109

11-
async function main() {
12-
const cwd = process.cwd()
13-
const execroot = process.env.JS_BINARY__EXECROOT || '.';
10+
function getExecRoot(): string {
11+
return process.env.JS_BINARY__EXECROOT || '.';
12+
}
13+
14+
function getBazelBinDir(): string {
15+
const binDir = process.env.BAZEL_BINDIR!;
16+
return path.join(getExecRoot(), binDir);
17+
}
18+
19+
function getOutDir(execRoot: string): string {
1420
const pkgdir = process.env.BAZEL_PACKAGE!;
1521
const bindir = process.env.BAZEL_BINDIR!;
16-
const outdir = path.join(execroot, bindir, pkgdir);
17-
// const outdir = path.join(execroot, pkgdir);
18-
const tsickleConfigFile = process.env.TSICKLE_CONFIG_FILE!;
19-
const tsickleHostConfig = loadTsickleHostConfig(tsickleConfigFile);
20-
// const tsConfigFile = `${execroot}/${process.env.TS_CONFIG_FILE!}`;
21-
const tsConfigFile = `tsconfig.json`;
22+
return path.join(execRoot, bindir, pkgdir);
23+
}
2224

23-
const args = process.argv.slice(2);
25+
async function listFiles(dir: string): Promise<string[]> {
26+
return asyncfs.readdir(dir, { recursive: true });
27+
}
2428

25-
if (DEBUG) {
26-
// console.log('env:', process.env);
27-
console.log('pwd:', cwd);
28-
console.log('args:', args);
29-
console.log('tsickleHostConfig:', tsickleHostConfig);
30-
for (const filename of await listFiles(execroot)) {
31-
if (filename.indexOf('.aspect_rules_js') >= 0) {
32-
continue;
33-
}
34-
if (filename.indexOf('tsconfig.json') < 0) {
35-
continue;
36-
}
37-
console.log(`{execroot}/${filename}`);
29+
async function listExecrootFiles() {
30+
const execroot = process.env.JS_BINARY__EXECROOT || '.';
31+
for (const filename of await listFiles(execroot)) {
32+
if (filename.indexOf('.aspect_rules_js') >= 0) {
33+
continue;
3834
}
35+
if (filename.indexOf('tsconfig.json') < 0) {
36+
continue;
37+
}
38+
console.log(`{execroot}/${filename}`);
3939
}
40+
}
4041

41-
const inputFiles = args.map(arg => `${execroot}/${arg}`);
42-
42+
function getInputFiles(execRoot: string, args: string[]): string[] {
43+
const inputFiles = args.map(arg => `${execRoot}/${arg}`);
4344
if (inputFiles.length === 0) {
4445
console.error('Usage: tsicklecompiler <input.ts> [input2.ts] ...');
4546
process.exit(1);
4647
}
48+
return inputFiles;
49+
}
4750

48-
let compilerOptions: ts.CompilerOptions = {
51+
function getTsCompilerOptions(): ts.CompilerOptions {
52+
return {
4953
target: ts.ScriptTarget.ES2017,
5054
module: ts.ModuleKind.CommonJS,
51-
outDir: outdir,
55+
outDir: getBazelBinDir(),
56+
rootDir: getExecRoot(),
5257
declaration: true,
5358
strict: true
5459
};
60+
}
5561

56-
if (!tsConfigFile) {
57-
throw new Error(`tsconfig.json file not set`);
58-
}
59-
60-
if (false) {
61-
const { parsedConfig } = loadTsConfigFromPath(tsConfigFile);
62-
compilerOptions = parsedConfig.options;
63-
compilerOptions.outDir = outdir;
64-
compilerOptions.module = ts.ModuleKind.CommonJS;
65-
compilerOptions.target = ts.ScriptTarget.ES5;
62+
/**
63+
* Determine the lowest-level common parent directory of the given list of files.
64+
*/
65+
function getCommonParentDirectory(fileNames: string[]): string {
66+
const pathSplitter = /[\/\\]+/;
67+
const commonParent = fileNames[0].split(pathSplitter);
68+
for (let i = 1; i < fileNames.length; i++) {
69+
const thisPath = fileNames[i].split(pathSplitter);
70+
let j = 0;
71+
while (thisPath[j] === commonParent[j]) {
72+
j++;
73+
}
74+
commonParent.length = j; // Truncate without copying the array
6675
}
67-
if (tsickleHostConfig) {
68-
await runTsickle(tsickleHostConfig, compilerOptions, inputFiles);
76+
if (commonParent.length === 0) {
77+
return '/';
6978
} else {
70-
await runVanillaTsc(compilerOptions, inputFiles);
79+
return commonParent.join(path.sep);
7180
}
7281
}
7382

74-
async function runTsickle(
75-
tsickleHostConfig: TsickleHostConfig,
76-
compilerOptions: ts.CompilerOptions,
77-
inputFiles: string[],
78-
) {
79-
const delegate = ts.createCompilerHost(compilerOptions);
80-
const host = new CompilerHost(tsickleHostConfig, compilerOptions, delegate);
81-
82-
const program = ts.createProgram(inputFiles, compilerOptions, host);
83-
const diagnostics = ts.getPreEmitDiagnostics(program);
84-
if (diagnostics.length > 0) {
85-
diagnostics.forEach(diagnostic => {
86-
if (diagnostic.file) {
87-
const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
88-
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
89-
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
90-
} else {
91-
console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
92-
}
93-
});
94-
process.exit(12);
95-
}
96-
97-
let targetSourceFile: ts.SourceFile | undefined = undefined;
98-
for (const sf of program.getSourceFiles()) {
99-
console.log('program source file:', sf.fileName);
100-
targetSourceFile = sf;
101-
}
102-
103-
const emittedFiles: string[] = [];
104-
const result: tsickle.EmitResult = tsickle.emit(program, host, (fileName, data) => {
105-
emittedFiles.push(fileName);
106-
host.writeFile(fileName, data, false);
107-
}, targetSourceFile);
108-
109-
if (result.diagnostics.length > 0) {
110-
diagnostics.forEach(diagnostic => {
111-
if (diagnostic.file) {
112-
const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
113-
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
114-
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
115-
} else {
116-
console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
117-
}
118-
});
119-
}
120-
// const emittedFiles: string[] = [];
121-
// const emitResult = program.emit(undefined, (fileName, data) => {
122-
// emittedFiles.push(fileName);
123-
// host.writeFile(fileName, data, false);
124-
// });
125-
126-
// const diagnostics = ts
127-
// .getPreEmitDiagnostics(program)
128-
// .concat(emitResult.diagnostics);
129-
130-
// diagnostics.forEach(diagnostic => {
131-
// if (diagnostic.file) {
132-
// const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
133-
// const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
134-
// console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
135-
// } else {
136-
// console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
137-
// }
138-
// });
83+
async function main() {
84+
const execRoot = getExecRoot();
85+
const outDir = getOutDir(execRoot);
86+
const args = process.argv.slice(2);
87+
const inputFiles = getInputFiles(execRoot, args);
88+
// const inputFiles = args;
89+
const compilerOptions = getTsCompilerOptions();
13990

14091
if (DEBUG) {
141-
if (emittedFiles.length > 0) {
142-
console.log('Emitted files:');
143-
emittedFiles.forEach(file => console.log(` ${file}`));
144-
} else {
145-
console.log('No files were emitted');
146-
}
92+
const cwd = process.cwd()
93+
// console.log('env:', process.env);
94+
console.log('pwd:', cwd);
95+
console.log('args:', args);
96+
console.log('files:', inputFiles);
97+
console.log('compilerOptions:', compilerOptions);
14798
}
14899

149-
// const exitCode = emitResult.emitSkipped ? 1 : 0;
150-
// process.exit(exitCode);
151-
}
152-
153-
async function runVanillaTsc(
154-
compilerOptions: ts.CompilerOptions,
155-
inputFiles: string[],
156-
) {
157-
const host = ts.createCompilerHost(compilerOptions);
158-
const program = ts.createProgram(inputFiles, compilerOptions, host);
159-
160-
const emittedFiles: string[] = [];
161-
const emitResult = program.emit(undefined, (fileName, data) => {
162-
emittedFiles.push(fileName);
163-
host.writeFile(fileName, data, false);
100+
const result = run(compilerOptions, inputFiles, (filePath: string, contents: string) => {
101+
console.log('emitted file:', filePath);
102+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
103+
fs.writeFileSync(filePath, contents, { encoding: 'utf-8' });
164104
});
165105

166-
const diagnostics = ts
167-
.getPreEmitDiagnostics(program)
168-
.concat(emitResult.diagnostics);
169-
170-
diagnostics.forEach(diagnostic => {
171-
if (diagnostic.file) {
172-
const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
173-
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
174-
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
175-
} else {
176-
console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
177-
}
178-
});
179-
180-
if (DEBUG) {
181-
if (emittedFiles.length > 0) {
182-
console.log('Emitted files:');
183-
emittedFiles.forEach(file => console.log(` ${file}`));
184-
} else {
185-
console.log('No files were emitted');
186-
}
106+
if (result.diagnostics.length) {
107+
console.error(ts.formatDiagnostics(result.diagnostics, ts.createCompilerHost(compilerOptions)));
108+
return 1;
187109
}
188110

189-
const exitCode = emitResult.emitSkipped ? 1 : 0;
190-
process.exit(exitCode);
111+
// if (settings.externsPath) {
112+
// fs.mkdirSync(path.dirname(settings.externsPath), { recursive: true });
113+
// fs.writeFileSync(
114+
// settings.externsPath,
115+
// tsickle.getGeneratedExterns(result.externs, config.options.rootDir || ''));
116+
// }
117+
118+
return 0;
119+
191120
}
192121

193-
async function listFiles(dir: string): Promise<string[]> {
194-
return fs.readdir(dir, { recursive: true });
122+
function run(
123+
options: ts.CompilerOptions,
124+
absoluteFileNames: string[],
125+
writeFile: ts.WriteFileCallback,
126+
): tsickle.EmitResult {
127+
// Use absolute paths to determine what files to process since files may be imported using
128+
// relative or absolute paths
129+
// const absoluteFileNames = fileNames.map(i => path.resolve(i));
130+
131+
const compilerHost = ts.createCompilerHost(options);
132+
const program = ts.createProgram(absoluteFileNames, options, compilerHost);
133+
const filesToProcess = new Set(absoluteFileNames);
134+
const rootModulePath = options.rootDir || getCommonParentDirectory(absoluteFileNames);
135+
const transformerHost: tsickle.TsickleHost = {
136+
rootDirsRelative: (f: string) => f,
137+
shouldSkipTsickleProcessing: (fileName: string) => {
138+
return !filesToProcess.has(path.resolve(fileName));
139+
},
140+
shouldIgnoreWarningsForPath: (fileName: string) => false,
141+
pathToModuleName: (context, fileName) =>
142+
tsickle.pathToModuleName(rootModulePath, context, fileName),
143+
fileNameToModuleId: (fileName) => path.relative(rootModulePath, fileName),
144+
googmodule: true,
145+
transformDecorators: true,
146+
transformTypesToClosure: true,
147+
typeBlackListPaths: new Set(),
148+
untyped: false,
149+
logWarning: (warning) =>
150+
console.error(ts.formatDiagnostics([warning], compilerHost)),
151+
options,
152+
generateExtraSuppressions: true,
153+
// transformDynamicImport: 'nodejs',
154+
moduleResolutionHost: compilerHost,
155+
};
156+
const diagnostics = ts.getPreEmitDiagnostics(program);
157+
if (diagnostics.length > 0) {
158+
return {
159+
tsMigrationExportsShimFiles: new Map(),
160+
diagnostics,
161+
modulesManifest: new tsickle.ModulesManifest(),
162+
externs: {},
163+
emitSkipped: true,
164+
emittedFiles: [],
165+
// fileSummaries: new Map(),
166+
};
167+
}
168+
return tsickle.emit(program, transformerHost, writeFile);
195169
}
196170

197171
void main()

0 commit comments

Comments
 (0)