|
1 | 1 |
|
2 | 2 | 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'; |
4 | 5 | import ts from 'typescript'; |
5 | 6 | import * as tsickle from 'tsickle'; |
6 | | -import { loadTsickleHostConfig, TsickleHostConfig } from './tsicklehost'; |
7 | | -import { CompilerHost, loadTsConfigFromPath } from './compilerhost'; |
8 | 7 |
|
9 | 8 | const DEBUG = true; |
10 | 9 |
|
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 { |
14 | 20 | const pkgdir = process.env.BAZEL_PACKAGE!; |
15 | 21 | 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 | +} |
22 | 24 |
|
23 | | - const args = process.argv.slice(2); |
| 25 | +async function listFiles(dir: string): Promise<string[]> { |
| 26 | + return asyncfs.readdir(dir, { recursive: true }); |
| 27 | +} |
24 | 28 |
|
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; |
38 | 34 | } |
| 35 | + if (filename.indexOf('tsconfig.json') < 0) { |
| 36 | + continue; |
| 37 | + } |
| 38 | + console.log(`{execroot}/${filename}`); |
39 | 39 | } |
| 40 | +} |
40 | 41 |
|
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}`); |
43 | 44 | if (inputFiles.length === 0) { |
44 | 45 | console.error('Usage: tsicklecompiler <input.ts> [input2.ts] ...'); |
45 | 46 | process.exit(1); |
46 | 47 | } |
| 48 | + return inputFiles; |
| 49 | +} |
47 | 50 |
|
48 | | - let compilerOptions: ts.CompilerOptions = { |
| 51 | +function getTsCompilerOptions(): ts.CompilerOptions { |
| 52 | + return { |
49 | 53 | target: ts.ScriptTarget.ES2017, |
50 | 54 | module: ts.ModuleKind.CommonJS, |
51 | | - outDir: outdir, |
| 55 | + outDir: getBazelBinDir(), |
| 56 | + rootDir: getExecRoot(), |
52 | 57 | declaration: true, |
53 | 58 | strict: true |
54 | 59 | }; |
| 60 | +} |
55 | 61 |
|
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 |
66 | 75 | } |
67 | | - if (tsickleHostConfig) { |
68 | | - await runTsickle(tsickleHostConfig, compilerOptions, inputFiles); |
| 76 | + if (commonParent.length === 0) { |
| 77 | + return '/'; |
69 | 78 | } else { |
70 | | - await runVanillaTsc(compilerOptions, inputFiles); |
| 79 | + return commonParent.join(path.sep); |
71 | 80 | } |
72 | 81 | } |
73 | 82 |
|
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(); |
139 | 90 |
|
140 | 91 | 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); |
147 | 98 | } |
148 | 99 |
|
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' }); |
164 | 104 | }); |
165 | 105 |
|
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; |
187 | 109 | } |
188 | 110 |
|
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 | + |
191 | 120 | } |
192 | 121 |
|
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); |
195 | 169 | } |
196 | 170 |
|
197 | 171 | void main() |
0 commit comments