Skip to content

Commit df7a657

Browse files
authored
Bundle CLI to eliminate dependency conflicts (#134)
1 parent 0d22a18 commit df7a657

7 files changed

Lines changed: 53 additions & 4 deletions

File tree

‎.github/workflows/deploy-preview.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
- name: Prepare release
4040
run: |-
4141
cp package.json LICENSE README.md build/
42+
npm --prefix build pkg delete dependencies
4243
cd build
4344
4445
- name: Publish preview

‎.github/workflows/deploy-release.yaml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
npm run graphql-codegen &&
1414
sed -i -e "s~\"version\": \"0.0.0-dev\"~\"version\": \"${GITHUB_REF##*/}\"~" package.json
1515
prepare-script: >-
16-
cp package.json LICENSE README.md build/
16+
cp package.json LICENSE README.md build/ &&
17+
npm --prefix build pkg delete dependencies
1718
secrets:
1819
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

‎eslint.config.mjs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ export default defineConfig(
2929
files: ['tsup.config.ts'],
3030
rules: {
3131
'import/no-default-export': 'off',
32+
'import-x/no-default-export': 'off',
33+
'object-shorthand': 'off',
34+
'func-names': 'off',
3235
},
3336
}
3437
);

‎package.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
"is-unicode-supported": "^2.1.0",
6767
"jsep": "^1.4.0",
6868
"minimatch": "^10.0.3",
69-
"node-emoji": "^2.2.0",
7069
"open": "^10.2.0",
7170
"prompts": "^2.4.2",
7271
"recast": "^0.23.11",

‎src/application/project/code/transformation/javascript/typeErasureCodemod.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ export class TypeErasureCodemod implements Codemod<File, TypeErasureCodemodOptio
9393
],
9494
ast: true,
9595
configFile: false,
96+
// Skip browserslist config resolution. Without this, Babel's
97+
// partial-config pipeline calls into @babel/helper-compilation-targets,
98+
// which requires browserslist + baseline-browser-mapping (~170KB
99+
// of code we never use since we don't pass any browser targets).
100+
browserslistConfigFile: false,
96101
});
97102

98103
return {

‎src/infrastructure/application/cli/io/formatting.ts‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@ import isUnicodeSupported from 'is-unicode-supported';
44
import {render as renderMarkdown} from '@croct/md-lite/rendering.js';
55
import terminalLink from 'terminal-link';
66
import {unescape as unescapeMarkdown} from '@croct/md-lite/parsing.js';
7-
import {strip} from 'node-emoji';
87
import type {Semantics} from '@/application/cli/io/output';
98

9+
// Strips both unicode pictographs and `:shortcode:` markers from a string.
10+
// Replaces node-emoji's strip(), which pulls a 275KB emoji dataset for the
11+
// same effect when the terminal can't render unicode.
12+
const EMOJI_PICTOGRAPH = /\p{Extended_Pictographic}(️|‍\p{Extended_Pictographic})*/gu;
13+
const EMOJI_SHORTCODE = /:[\w+-]+:/g;
14+
15+
function stripEmoji(value: string): string {
16+
return value.replace(EMOJI_PICTOGRAPH, '').replace(EMOJI_SHORTCODE, '');
17+
}
18+
1019
const unicodeSupport = isUnicodeSupported();
1120

1221
export const colors: Record<Semantics, ForegroundColorName> = {
@@ -43,7 +52,7 @@ export function format(message: string, options: FormatingOptions = {}): string
4352
let result = options.basic === true ? message : render(message);
4453

4554
if (!unicodeSupport) {
46-
result = strip(result);
55+
result = stripEmoji(result);
4756
}
4857

4958
if (options.text !== undefined) {

‎tsup.config.ts‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,47 @@
1+
import type {Plugin} from 'esbuild';
12
import {defineConfig} from 'tsup';
23

4+
// Modules whose code paths are unreachable in our usage. Resolving them to
5+
// an empty module saves ~700KB:
6+
// - `@babel/preset-typescript`: loaded by @babel/core for `.cts` configs
7+
// (we always pass configFile: false).
8+
// - `esprima`: recast's tokenizer fallback when AST has no `tokens`; our
9+
// `@babel/parser`-based recast parser always provides them.
10+
// - `browserslist`: required by @babel/helper-compilation-targets; never
11+
// invoked since we set `browserslistConfigFile: false`.
12+
const namespace = 'stub-unreachable-modules';
13+
const pattern = /^@babel\/preset-typescript(\/.*)?$|^esprima$|^browserslist$/;
14+
15+
const stubUnreachableModules: Plugin = {
16+
name: namespace,
17+
setup(build) {
18+
build.onResolve(
19+
{filter: pattern},
20+
args => ({path: args.path, namespace}),
21+
);
22+
build.onLoad(
23+
{filter: /.*/, namespace},
24+
() => ({contents: 'module.exports = {};', loader: 'js'}),
25+
);
26+
},
27+
};
28+
329
export default defineConfig({
430
entry: {
531
index: 'src/program.ts',
632
},
733
format: 'esm',
834
target: 'esnext',
35+
platform: 'node',
936
clean: true,
1037
sourcemap: false,
1138
minify: true,
1239
outDir: 'build',
1340
treeshake: true,
41+
// Bundle every dependency into the output so consumers don't get
42+
// anything installed transitively (avoids version conflicts in host projects).
43+
noExternal: [/.*/],
44+
esbuildPlugins: [stubUnreachableModules],
1445
banner: ({format}) => {
1546
if (format === 'esm') {
1647
return ({

0 commit comments

Comments
 (0)