Skip to content

Commit be345af

Browse files
committed
build-npm/deno: use TS to find all *.ts files
Motivation: Remove custom code for finding *.ts files and reuse code from typescript library
1 parent 612abd3 commit be345af

File tree

3 files changed

+119
-118
lines changed

3 files changed

+119
-118
lines changed

Diff for: resources/build-deno.ts

+23-25
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,34 @@ import ts from 'typescript';
55

66
import { changeExtensionInImportPaths } from './change-extension-in-import-paths.js';
77
import { inlineInvariant } from './inline-invariant.js';
8-
import { readdirRecursive, showDirStats, writeGeneratedFile } from './utils.js';
8+
import { readTSConfig, showDirStats, writeGeneratedFile } from './utils.js';
99

1010
fs.rmSync('./denoDist', { recursive: true, force: true });
1111
fs.mkdirSync('./denoDist');
1212

13-
const srcFiles = readdirRecursive('./src', { ignoreDir: /^__.*__$/ });
14-
for (const filepath of srcFiles) {
15-
if (filepath.endsWith('.ts')) {
16-
const srcPath = path.join('./src', filepath);
17-
18-
const sourceFile = ts.createSourceFile(
19-
srcPath,
20-
fs.readFileSync(srcPath, 'utf-8'),
21-
ts.ScriptTarget.Latest,
22-
);
23-
24-
const transformed = ts.transform(sourceFile, [
25-
changeExtensionInImportPaths({ extension: '.ts' }),
26-
inlineInvariant,
27-
]);
28-
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
29-
const newContent = printer.printBundle(
30-
ts.factory.createBundle(transformed.transformed),
31-
);
32-
33-
transformed.dispose();
34-
35-
const destPath = path.join('./denoDist', filepath);
36-
writeGeneratedFile(destPath, newContent);
13+
const tsProgram = ts.createProgram(['src/index.ts'], readTSConfig());
14+
for (const sourceFile of tsProgram.getSourceFiles()) {
15+
if (
16+
tsProgram.isSourceFileFromExternalLibrary(sourceFile) ||
17+
tsProgram.isSourceFileDefaultLibrary(sourceFile)
18+
) {
19+
continue;
3720
}
21+
22+
const transformed = ts.transform(sourceFile, [
23+
changeExtensionInImportPaths({ extension: '.ts' }),
24+
inlineInvariant,
25+
]);
26+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
27+
const newContent = printer.printBundle(
28+
ts.factory.createBundle(transformed.transformed),
29+
);
30+
31+
transformed.dispose();
32+
33+
const filepath = path.relative('./src', sourceFile.fileName);
34+
const destPath = path.join('./denoDist', filepath);
35+
writeGeneratedFile(destPath, newContent);
3836
}
3937

4038
fs.copyFileSync('./LICENSE', './denoDist/LICENSE');

Diff for: resources/build-npm.ts

+46-60
Original file line numberDiff line numberDiff line change
@@ -6,70 +6,24 @@ import ts from 'typescript';
66

77
import { inlineInvariant } from './inline-invariant.js';
88
import {
9-
localRepoPath,
10-
readdirRecursive,
119
readPackageJSON,
10+
readTSConfig,
1211
showDirStats,
1312
writeGeneratedFile,
1413
} from './utils.js';
1514

16-
fs.rmSync('./npmDist', { recursive: true, force: true });
17-
fs.mkdirSync('./npmDist');
18-
19-
// Based on https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#getting-the-dts-from-a-javascript-file
20-
const tsConfigPath = localRepoPath('tsconfig.json');
21-
const { config: tsConfig, error: tsConfigError } = ts.parseConfigFileTextToJson(
22-
tsConfigPath,
23-
fs.readFileSync(tsConfigPath, 'utf-8'),
24-
);
25-
assert(tsConfigError === undefined, 'Fail to parse config: ' + tsConfigError);
26-
assert(
27-
tsConfig.compilerOptions,
28-
'"tsconfig.json" should have `compilerOptions`',
29-
);
30-
31-
const { options: tsOptions, errors: tsOptionsErrors } =
32-
ts.convertCompilerOptionsFromJson(
33-
{
34-
...tsConfig.compilerOptions,
35-
module: 'es2020',
36-
noEmit: false,
37-
declaration: true,
38-
declarationDir: './npmDist',
39-
outDir: './npmDist',
40-
},
41-
process.cwd(),
42-
);
43-
44-
assert(
45-
tsOptionsErrors.length === 0,
46-
'Fail to parse options: ' + tsOptionsErrors,
47-
);
48-
49-
const tsHost = ts.createCompilerHost(tsOptions);
50-
tsHost.writeFile = writeGeneratedFile;
51-
52-
const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost);
53-
const tsResult = tsProgram.emit(undefined, undefined, undefined, undefined, {
54-
after: [inlineInvariant],
55-
});
56-
assert(
57-
!tsResult.emitSkipped,
58-
'Fail to generate `*.d.ts` files, please run `npm run check`',
59-
);
15+
buildPackage();
16+
showDirStats('./npmDist');
6017

61-
fs.copyFileSync('./LICENSE', './npmDist/LICENSE');
62-
fs.copyFileSync('./README.md', './npmDist/README.md');
18+
function buildPackage() {
19+
fs.rmSync('./npmDist', { recursive: true, force: true });
20+
fs.mkdirSync('./npmDist');
6321

64-
// Should be done as the last step so only valid packages can be published
65-
writeGeneratedFile(
66-
'./npmDist/package.json',
67-
JSON.stringify(buildPackageJSON()),
68-
);
22+
fs.copyFileSync('./LICENSE', './npmDist/LICENSE');
23+
fs.copyFileSync('./README.md', './npmDist/README.md');
6924

70-
showDirStats('./npmDist');
25+
const { emittedTSFiles } = emitTSFiles('./npmDist');
7126

72-
function buildPackageJSON() {
7327
const packageJSON = readPackageJSON();
7428

7529
delete packageJSON.private;
@@ -97,10 +51,10 @@ function buildPackageJSON() {
9751

9852
packageJSON.exports = {};
9953

100-
for (const filepath of readdirRecursive('./src', { ignoreDir: /^__.*__$/ })) {
101-
if (path.basename(filepath) === 'index.ts') {
102-
const key = path.dirname(filepath);
103-
packageJSON.exports[key] = filepath.replace(/\.ts$/, '.js');
54+
for (const filepath of emittedTSFiles) {
55+
if (path.basename(filepath) === 'index.js') {
56+
const relativePath = './' + path.relative('./npmDist', filepath);
57+
packageJSON.exports[path.dirname(relativePath)] = relativePath;
10458
}
10559
}
10660

@@ -136,5 +90,37 @@ function buildPackageJSON() {
13690
);
13791
}
13892

139-
return packageJSON;
93+
// Should be done as the last step so only valid packages can be published
94+
writeGeneratedFile('./npmDist/package.json', JSON.stringify(packageJSON));
95+
}
96+
97+
// Based on https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#getting-the-dts-from-a-javascript-file
98+
function emitTSFiles(outDir: string): {
99+
emittedTSFiles: ReadonlyArray<string>;
100+
} {
101+
const tsOptions = readTSConfig({
102+
module: 'es2020',
103+
noEmit: false,
104+
declaration: true,
105+
declarationDir: outDir,
106+
outDir,
107+
listEmittedFiles: true,
108+
});
109+
110+
const tsHost = ts.createCompilerHost(tsOptions);
111+
tsHost.writeFile = writeGeneratedFile;
112+
113+
const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost);
114+
const tsResult = tsProgram.emit(undefined, undefined, undefined, undefined, {
115+
after: [inlineInvariant],
116+
});
117+
assert(
118+
!tsResult.emitSkipped,
119+
'Fail to generate `*.d.ts` files, please run `npm run check`',
120+
);
121+
122+
assert(tsResult.emittedFiles != null);
123+
return {
124+
emittedTSFiles: tsResult.emittedFiles.sort((a, b) => a.localeCompare(b)),
125+
};
140126
}

Diff for: resources/utils.ts

+50-33
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import os from 'node:os';
55
import path from 'node:path';
66

77
import prettier from 'prettier';
8+
import ts from 'typescript';
89

910
export function localRepoPath(...paths: ReadonlyArray<string>): string {
1011
const repoDir = new URL('..', import.meta.url).pathname;
@@ -109,30 +110,21 @@ function spawn(
109110
childProcess.spawnSync(command, args, { stdio: 'inherit', ...options });
110111
}
111112

112-
export function readdirRecursive(
113-
dirPath: string,
114-
opts: { ignoreDir?: RegExp } = {},
115-
): Array<string> {
116-
const { ignoreDir } = opts;
117-
const result = [];
118-
for (const dirent of fs.readdirSync(dirPath, { withFileTypes: true })) {
119-
const name = dirent.name;
120-
if (!dirent.isDirectory()) {
121-
result.push(dirent.name);
122-
continue;
123-
}
124-
125-
if (ignoreDir?.test(name)) {
126-
continue;
113+
function* readdirRecursive(dirPath: string): Generator<{
114+
name: string;
115+
filepath: string;
116+
stats: fs.Stats;
117+
}> {
118+
for (const name of fs.readdirSync(dirPath)) {
119+
const filepath = path.join(dirPath, name);
120+
const stats = fs.lstatSync(filepath);
121+
122+
if (stats.isDirectory()) {
123+
yield* readdirRecursive(filepath);
124+
} else {
125+
yield { name, filepath, stats };
127126
}
128-
const list = readdirRecursive(path.join(dirPath, name), opts).map((f) =>
129-
path.join(name, f),
130-
);
131-
result.push(...list);
132127
}
133-
134-
result.sort((a, b) => a.localeCompare(b));
135-
return result.map((filepath) => './' + filepath);
136128
}
137129

138130
export function showDirStats(dirPath: string): void {
@@ -141,18 +133,14 @@ export function showDirStats(dirPath: string): void {
141133
} = {};
142134
let totalSize = 0;
143135

144-
for (const filepath of readdirRecursive(dirPath)) {
145-
const name = filepath.split(path.sep).at(-1);
146-
assert(name != null);
147-
const [base, ...splitExt] = name.split('.');
148-
const ext = splitExt.join('.');
136+
for (const { name, filepath, stats } of readdirRecursive(dirPath)) {
137+
const ext = name.split('.').slice(1).join('.');
138+
const filetype = ext ? '*.' + ext : name;
149139

150-
const filetype = ext ? '*.' + ext : base;
151-
fileTypes[filetype] = fileTypes[filetype] || { filepaths: [], size: 0 };
140+
fileTypes[filetype] = fileTypes[filetype] ?? { filepaths: [], size: 0 };
152141

153-
const { size } = fs.lstatSync(path.join(dirPath, filepath));
154-
totalSize += size;
155-
fileTypes[filetype].size += size;
142+
totalSize += stats.size;
143+
fileTypes[filetype].size += stats.size;
156144
fileTypes[filetype].filepaths.push(filepath);
157145
}
158146

@@ -163,7 +151,8 @@ export function showDirStats(dirPath: string): void {
163151
if (numFiles > 1) {
164152
stats.push([filetype + ' x' + numFiles, typeStats.size]);
165153
} else {
166-
stats.push([typeStats.filepaths[0], typeStats.size]);
154+
const relativePath = path.relative(dirPath, typeStats.filepaths[0]);
155+
stats.push([relativePath, typeStats.size]);
167156
}
168157
}
169158
stats.sort((a, b) => b[1] - a[1]);
@@ -218,3 +207,31 @@ export function readPackageJSON(
218207
const filepath = path.join(dirPath, 'package.json');
219208
return JSON.parse(fs.readFileSync(filepath, 'utf-8'));
220209
}
210+
211+
export function readTSConfig(overrides?: any): ts.CompilerOptions {
212+
const tsConfigPath = localRepoPath('tsconfig.json');
213+
214+
const { config: tsConfig, error: tsConfigError } =
215+
ts.parseConfigFileTextToJson(
216+
tsConfigPath,
217+
fs.readFileSync(tsConfigPath, 'utf-8'),
218+
);
219+
assert(tsConfigError === undefined, 'Fail to parse config: ' + tsConfigError);
220+
assert(
221+
tsConfig.compilerOptions,
222+
'"tsconfig.json" should have `compilerOptions`',
223+
);
224+
225+
const { options: tsOptions, errors: tsOptionsErrors } =
226+
ts.convertCompilerOptionsFromJson(
227+
{ ...tsConfig.compilerOptions, ...overrides },
228+
process.cwd(),
229+
);
230+
231+
assert(
232+
tsOptionsErrors.length === 0,
233+
'Fail to parse options: ' + tsOptionsErrors,
234+
);
235+
236+
return tsOptions;
237+
}

0 commit comments

Comments
 (0)