Skip to content

Commit 43d1c82

Browse files
author
Corentin Mors
authored
Make a tsc plugin for esbuild (#252)
This includes: - using Brotli to compress final builds - embed pem files from nsm attestation in code - make a tsc plugin for esbuild
1 parent f0229e9 commit 43d1c82

File tree

6 files changed

+212
-26
lines changed

6 files changed

+212
-26
lines changed

.github/workflows/manual-test-release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
- run: yarn workspaces focus --all --production
3636
# package final binaries
3737
- run: |
38-
yarn dlx @yao-pkg/pkg@5.11.1 ./dist -t node18-${{ matrix.settings.target }} -o bundle/dcli-${{ matrix.settings.target }}${{ matrix.settings.extension }} -C GZip "--public" "--public-packages" "tslib,thirty-two,node-hkdf-sync,vows" "--no-bytecode"
38+
yarn dlx @yao-pkg/pkg@5.11.1 ./dist -t node18-${{ matrix.settings.target }} -o bundle/dcli-${{ matrix.settings.target }}${{ matrix.settings.extension }} -C Brotli "--public" "--public-packages" "tslib,thirty-two,node-hkdf-sync,vows" "--no-bytecode"
3939
4040
- name: Archive binary artifact
4141
uses: actions/upload-artifact@v4

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
- run: yarn workspaces focus --all --production
3939
# package final binaries
4040
- run: |
41-
yarn dlx @yao-pkg/pkg@5.11.1 ./dist -t node18-${{ matrix.settings.target }} -o bundle/dcli-${{ matrix.settings.target }}${{ matrix.settings.extension }} -C GZip "--public" "--public-packages" "tslib,thirty-two,node-hkdf-sync,vows" "--no-bytecode"
41+
yarn dlx @yao-pkg/pkg@5.11.1 ./dist -t node18-${{ matrix.settings.target }} -o bundle/dcli-${{ matrix.settings.target }}${{ matrix.settings.extension }} -C Brotli "--public" "--public-packages" "tslib,thirty-two,node-hkdf-sync,vows" "--no-bytecode"
4242
4343
- name: Archive binary artifact
4444
uses: actions/upload-artifact@v4

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"contributors": [],
4242
"license": "Apache-2.0",
4343
"nativeDependencies": {
44-
"@dashlane/nsm-attestation": "*",
4544
"better-sqlite3": "*",
4645
"@json2csv/plainjs": "*",
4746
"@json2csv/transforms": "*",
@@ -52,6 +51,7 @@
5251
"node-mac-auth": "*"
5352
},
5453
"devDependencies": {
54+
"@aivenio/tsc-output-parser": "^2.1.1",
5555
"@types/async": "^3.2.24",
5656
"@types/better-sqlite3": "^7.6.10",
5757
"@types/chai": "^4.3.16",
@@ -67,14 +67,15 @@
6767
"eslint": "^8.57.0",
6868
"eslint-config-prettier": "^9.1.0",
6969
"eslint-plugin-import": "^2.29.1",
70+
"execa": "^9.1.0",
7071
"husky": "^9.0.11",
7172
"mocha": "^10.4.0",
7273
"prettier": "^3.2.5",
7374
"ts-node": "^10.9.2",
7475
"typescript": "^5.4.5"
7576
},
7677
"dependencies": {
77-
"@dashlane/nsm-attestation": "^1.0.1",
78+
"@dashlane/nsm-attestation": "^1.0.2",
7879
"@inquirer/prompts": "^5.0.5",
7980
"@json2csv/plainjs": "^7.0.6",
8081
"@json2csv/transforms": "^7.0.6",

scripts/build.js

+85-14
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,103 @@ import os from 'os';
44
import fs from 'fs';
55
import path from 'path';
66
import process from 'process';
7-
import childProcess from 'child_process';
87
import esbuild from 'esbuild';
98
import packageJSON from '../package.json' assert { type: 'json' };
109
import { fileURLToPath } from 'url';
10+
import { $ } from "execa";
11+
import tscOutputParser from '@aivenio/tsc-output-parser';
1112

1213
const platform = os.platform();
1314
const __filename = fileURLToPath(import.meta.url);
1415
const __dirname = path.dirname(__filename);
1516

17+
const pemReadFilePlugin = {
18+
name: 'base64-plugin',
19+
setup(build) {
20+
build.onLoad({ filter: /\.js$/ }, async (args) => {
21+
let contents = fs.readFileSync(args.path, 'utf8');
22+
23+
const regex = /await\s+fs\.promises\.readFile\(\s*path\.resolve\(__dirname,\s*['"`](.*\.pem)['"`]\s*\)\)/g;
24+
let match;
25+
26+
while ((match = regex.exec(contents)) !== null) {
27+
const pemFilePath = path.resolve(path.dirname(args.path), match[1]);
28+
const pemContents = fs.readFileSync(pemFilePath, 'utf8');
29+
const base64Contents = Buffer.from(pemContents).toString('base64');
30+
contents = contents.replace(match[0], `await Promise.resolve(Buffer.from("${base64Contents}", 'base64').toString())`);
31+
}
32+
33+
return {
34+
contents,
35+
loader: 'js',
36+
};
37+
});
38+
},
39+
};
40+
41+
const tscDiagnosticToEsbuild = async (
42+
diagnostic,
43+
) => {
44+
const lineText =
45+
await $`sed -n ${diagnostic.value.cursor.value.line}p ${diagnostic.value.path.value}`;
46+
47+
const [firstLine, rest] = diagnostic.value.message.value.split("\n", 2);
48+
49+
return {
50+
location: {
51+
column: diagnostic.value.cursor.value.col - 1,
52+
line: diagnostic.value.cursor.value.line,
53+
file: diagnostic.value.path.value,
54+
lineText: lineText.stdout,
55+
},
56+
notes: rest && rest.trim().length > 0 ? [{ text: rest }] : [],
57+
text: `${firstLine} [${diagnostic.value.tsError.value.errorString}]`,
58+
};
59+
};
60+
61+
const checkTypesPlugin = () => {
62+
return {
63+
name: 'check-types',
64+
setup(build) {
65+
build.onEnd(async (result) => {
66+
if (result.errors.length > 0) {
67+
return;
68+
}
69+
70+
const buildArgs = ['--noEmit', '-p', './tsconfig.build.json', '--pretty', 'false'];
71+
try {
72+
await $('tsc', buildArgs);
73+
} catch (err) {
74+
const tscOutput = tscOutputParser.parse(err.stdout);
75+
const messages = await Promise.all(tscOutput.map(output => tscDiagnosticToEsbuild(output)));
76+
const formatted = await esbuild.formatMessages(
77+
messages,
78+
{
79+
kind: 'error',
80+
color: true,
81+
terminalWidth: 100,
82+
}
83+
);
84+
console.log(formatted.join('\n'));
85+
process.exit(1);
86+
}
87+
});
88+
},
89+
};
90+
};
91+
1692
async function main(argv = process.argv) {
1793
argv = argv.slice(2);
1894
const projectRoot = path.join(__dirname, '..');
19-
const buildPath = path.join(projectRoot, 'build');
95+
const srcPath = path.join(projectRoot, 'src');
2096
const distPath = path.join(projectRoot, 'dist');
2197
const gitPath = process.env.GIT_DIR ?? path.join(projectRoot, '.git');
2298
await fs.promises.rm(distPath, {
2399
recursive: true,
24100
force: true,
25101
});
26-
const buildArgs = ['-p', './tsconfig.build.json', ...argv];
27-
console.error('Running tsc:');
28-
console.error(['tsc', ...buildArgs].join(' '));
29-
childProcess.execFileSync('tsc', buildArgs, {
30-
stdio: ['inherit', 'inherit', 'inherit'],
31-
windowsHide: true,
32-
encoding: 'utf-8',
33-
shell: platform === 'win32' ? true : false,
102+
await fs.promises.mkdir(distPath, {
103+
recursive: true,
34104
});
35105
// This collects the build metadata and adds it to the build folder so that dynamic imports to it will resolve correctly.
36106
let gitHead = process.env.COMMIT_HASH;
@@ -52,17 +122,17 @@ async function main(argv = process.argv) {
52122
console.error('Writing build metadata (build.json):');
53123
console.error(buildJSON);
54124
await fs.promises.writeFile(
55-
path.join(buildPath, 'build.json'),
125+
path.join(distPath, 'build.json'),
56126
JSON.stringify(buildJSON, null, 2),
57127
);
58128
// This specifies import paths that is left as an external require
59129
// This is kept to packages that have a native binding
60130
const externalDependencies = Object.keys(packageJSON.nativeDependencies ?? {});
61131
const esbuildOptions = {
62132
entryPoints: [
63-
path.join(buildPath, 'index.js'),
133+
'src/index.ts'
64134
],
65-
sourceRoot: buildPath,
135+
sourceRoot: srcPath,
66136
bundle: true,
67137
platform: 'node',
68138
format: 'cjs',
@@ -74,7 +144,8 @@ async function main(argv = process.argv) {
74144
minify: true,
75145
keepNames: true,
76146
outfile: path.join(distPath, 'index.cjs'),
77-
metafile: true
147+
metafile: true,
148+
plugins: [checkTypesPlugin(), pemReadFilePlugin],
78149
};
79150
console.error('Running esbuild:');
80151
console.error(esbuildOptions);

tsconfig.build.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"extends": "./tsconfig.json",
33
"compilerOptions": {
44
"rootDir": "./src",
5-
"noEmit": false,
5+
"noEmit": true,
66
"stripInternal": true
77
},
88
"exclude": ["./documentation/**/*", "./scripts/**/*"]

0 commit comments

Comments
 (0)