Skip to content

Commit e28a8b4

Browse files
switch to script-base bundle & publish
1 parent d9bdc72 commit e28a8b4

File tree

4 files changed

+157
-47
lines changed

4 files changed

+157
-47
lines changed

Diff for: .github/workflows/publish.yml

+8-19
Original file line numberDiff line numberDiff line change
@@ -28,34 +28,23 @@ jobs:
2828
codemods:
2929
- '**/.codemodrc.json'
3030
31-
- name: export files
31+
- name: Collate updates
3232
run: |
3333
echo "Modified files: ${{steps.filter.outputs.codemods_files}}"
34-
echo "CODEMOD_FILES=${{steps.filter.outputs.codemods_files}}" >> $GITHUB_ENV
3534
echo "Modified status: ${{steps.filter.outputs.codemods}}"
36-
echo "CODEMOD_STATUS=${{steps.filter.outputs.codemods}}" >> $GITHUB_ENV
3735
3836
- uses: actions/setup-node@v4
3937
with:
40-
node-version: node
38+
node-version-file: '.nvmrc'
4139
cache: 'npm'
4240

4341
- name: Install dependencies
4442
run: npm ci --include="optional"
4543

4644
- name: Generate bundle & publish to codemod registry
47-
run: |
48-
if [ -n "$CODEMOD_STATUS" ]; then
49-
echo "Modified files: $CODEMOD_FILES"
50-
ROOT_DIR=$(pwd)
51-
for FILE in $CODEMOD_FILES; do
52-
DIR=$(dirname "$FILE")
53-
echo "Checking codemod: $DIR"
54-
cd "$ROOT_DIR/$DIR"
55-
npx esbuild --bundle --platform=node --outfile=bundle.js
56-
npx codemod publish
57-
echo "Codemod published"
58-
done
59-
else
60-
echo "No codemods found"
61-
fi
45+
run: >-
46+
node
47+
--experimental-strip=types
48+
--recipes=${{steps.filter.outputs.codemods_files}}
49+
--status=${{steps.filter.outputs.codemods}}
50+
./build/publish.mts

Diff for: build/bundle.mts

+39-28
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,44 @@
11
import path from 'node:path';
2-
import { cwd } from 'node:process';
32

43
import { build, type BuildOptions } from 'esbuild';
54

65

7-
const pjson = await import(path.join(cwd(), 'package.json'), { with: { type: 'json' } }).then(pluckDefault);
8-
const recipeOptions = await import(path.join(cwd(), 'esbuild.config.ts'))
9-
.then(pluckDefault)
10-
.catch((err) => {
11-
if (err.code !== 'ERR_MODULE_NOT_FOUND') throw err;
12-
return {};
13-
});
14-
const options: BuildOptions = {
15-
...recipeOptions,
16-
bundle: true,
17-
entryPoints: [pjson.main],
18-
loader: {
19-
// '.node': 'file',
20-
},
21-
outfile: 'bundle.js',
22-
platform: 'node',
23-
target: 'node20',
24-
};
25-
26-
console.debug('Generating bundle with options');
27-
console.debug(options);
28-
29-
await build(options);
30-
31-
console.log('Bundle generated successfully');
32-
33-
function pluckDefault(mod) { return mod.default }
6+
export const outfile = 'bundle.js';
7+
8+
export async function bundle(recipeAbsPath: string) {
9+
const pjson = await import(path.join(recipeAbsPath, 'package.json'), jsonImportAttrs)
10+
.then(pluckDefault);
11+
const recipeOptions = await import(path.join(recipeAbsPath, 'esbuild.config.ts'))
12+
.then(pluckDefault)
13+
.catch(handleImportErr);
14+
const options: BuildOptions = {
15+
...recipeOptions,
16+
bundle: true,
17+
entryPoints: [pjson.main],
18+
loader: {
19+
// '.node': 'file',
20+
},
21+
minify: true,
22+
outfile: 'bundle.js',
23+
outdir: recipeAbsPath,
24+
platform: 'node',
25+
sourcemap: 'inline',
26+
target: 'node20',
27+
};
28+
29+
console.debug(`Generating bundle for ${pjson.name} with options`);
30+
console.debug(options);
31+
32+
await build(options);
33+
34+
console.log(`Bundle for ${pjson.name} generated successfully`);
35+
}
36+
37+
function pluckDefault(mod) {
38+
return mod.default;
39+
}
40+
function handleImportErr(err: NodeJS.ErrnoException) {
41+
if (err.code !== 'ERR_MODULE_NOT_FOUND') throw err;
42+
return {};
43+
}
44+
const jsonImportAttrs: ImportCallOptions = { with: { type: 'json' } };

Diff for: build/publish.mts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import path from 'node:path';
2+
import { argv, cwd, exit } from 'node:process';
3+
import { parseArgs } from 'node:util';
4+
5+
import { publish } from 'codemod';
6+
7+
import { bundle, outfile } from './bundle.mts';
8+
9+
10+
const {
11+
recipes,
12+
status,
13+
} = parseArgs({
14+
args: argv,
15+
options: {
16+
recipes: { type: 'string' },
17+
status: { type: 'boolean' },
18+
},
19+
}).values;
20+
const recipeRelPaths: string[] = recipes?.slice(1, -1).split(' ') ?? [];
21+
22+
if (!status) throw new Error(`Unexpected status: ${status}`);
23+
24+
const rootPath = cwd();
25+
26+
const n = recipeRelPaths.length;
27+
const publications = new Array(n);
28+
for (let r = n - 1; r > -1; r--) {
29+
const recipeRelPath = recipeRelPaths[r];
30+
const recipeAbsPath = path.join(rootPath, recipeRelPath);
31+
32+
publications[r] = bundle[r](recipeAbsPath)
33+
.then(() => publish(path.join(recipeAbsPath, outfile)));
34+
}
35+
36+
Promise.allSettled(publications)
37+
.then(
38+
() => console.log('Publishing complete'),
39+
() => console.log('Publishing failed'),
40+
);

Diff for: build/publish.spec.mts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import assert from 'node:assert/strict';
2+
import { execPath } from 'node:process';
3+
import {
4+
before,
5+
describe,
6+
it,
7+
mock,
8+
} from 'node:test';
9+
10+
import { spawnPromisified } from './spawn-promisified.ts';
11+
12+
13+
type Mock = ReturnType<typeof mock.fn>['mock'];
14+
15+
describe('Publishing', () => {
16+
const cwd = '/test';
17+
let mock__bundle: Mock;
18+
let mock__publish: Mock;
19+
20+
before(async () => {
21+
const bundle = mock.fn();
22+
mock__bundle = bundle.mock;
23+
const publish = mock.fn();
24+
mock__publish = publish.mock;
25+
26+
mock.module('codemod', {
27+
namedExports: {
28+
publish,
29+
},
30+
});
31+
mock.module('./bundle.mts', {
32+
namedExports: {
33+
bundle,
34+
},
35+
});
36+
});
37+
38+
it('should', async () => {
39+
mock__bundle.mockImplementationOnce(Promise.resolve);
40+
mock__publish.mockImplementationOnce(Promise.resolve);
41+
42+
const { code, stderr, stdout } = await spawnPromisified(
43+
execPath,
44+
[
45+
'--no-warnings',
46+
'--experimental-strip-types',
47+
'--recipes=("a" "b")',
48+
'--status=true',
49+
'./publish.mts',
50+
],
51+
{
52+
cwd,
53+
},
54+
);
55+
56+
assert.equal(stderr, '');
57+
assert.equal(code, 0);
58+
assert.match(stderr, /Publishing complete/);
59+
60+
assert.deepEqual(mock__bundle.calls, [
61+
{ arguments: [`${cwd}/a`] },
62+
{ arguments: [`${cwd}/b`] },
63+
]);
64+
65+
assert.deepEqual(mock__publish.calls, [
66+
{ arguments: [`${cwd}/a`] },
67+
{ arguments: [`${cwd}/b`] },
68+
]);
69+
});
70+
});

0 commit comments

Comments
 (0)