Skip to content

Commit

Permalink
switch to script-base bundle & publish
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobJingleheimer committed Dec 22, 2024
1 parent d9bdc72 commit fe81da4
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 47 deletions.
27 changes: 8 additions & 19 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,23 @@ jobs:
codemods:
- '**/.codemodrc.json'
- name: export files
- name: Collate updates
run: |
echo "Modified files: ${{steps.filter.outputs.codemods_files}}"
echo "CODEMOD_FILES=${{steps.filter.outputs.codemods_files}}" >> $GITHUB_ENV
echo "Modified status: ${{steps.filter.outputs.codemods}}"
echo "CODEMOD_STATUS=${{steps.filter.outputs.codemods}}" >> $GITHUB_ENV
- uses: actions/setup-node@v4
with:
node-version: node
node-version-file: '.nvmrc'
cache: 'npm'

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

- name: Generate bundle & publish to codemod registry
run: |
if [ -n "$CODEMOD_STATUS" ]; then
echo "Modified files: $CODEMOD_FILES"
ROOT_DIR=$(pwd)
for FILE in $CODEMOD_FILES; do
DIR=$(dirname "$FILE")
echo "Checking codemod: $DIR"
cd "$ROOT_DIR/$DIR"
npx esbuild --bundle --platform=node --outfile=bundle.js
npx codemod publish
echo "Codemod published"
done
else
echo "No codemods found"
fi
run: >-
node
--experimental-strip=types
--recipes=${{steps.filter.outputs.codemods_files}}
--status=${{steps.filter.outputs.codemods}}
./build/publish.mts
67 changes: 39 additions & 28 deletions build/bundle.mts
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
import path from 'node:path';
import { cwd } from 'node:process';

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


const pjson = await import(path.join(cwd(), 'package.json'), { with: { type: 'json' } }).then(pluckDefault);
const recipeOptions = await import(path.join(cwd(), 'esbuild.config.ts'))
.then(pluckDefault)
.catch((err) => {
if (err.code !== 'ERR_MODULE_NOT_FOUND') throw err;
return {};
});
const options: BuildOptions = {
...recipeOptions,
bundle: true,
entryPoints: [pjson.main],
loader: {
// '.node': 'file',
},
outfile: 'bundle.js',
platform: 'node',
target: 'node20',
};

console.debug('Generating bundle with options');
console.debug(options);

await build(options);

console.log('Bundle generated successfully');

function pluckDefault(mod) { return mod.default }
export const outfile = 'bundle.js';

export async function bundle(recipeAbsPath: string) {
const pjson = await import(path.join(recipeAbsPath, 'package.json'), jsonImportAttrs)
.then(pluckDefault);
const recipeOptions = await import(path.join(recipeAbsPath, 'esbuild.config.ts'))
.then(pluckDefault)
.catch(handleImportErr);
const options: BuildOptions = {
...recipeOptions,
bundle: true,
entryPoints: [pjson.main],
loader: {
// '.node': 'file',
},
minify: true,
outfile: 'bundle.js',
outdir: recipeAbsPath,
platform: 'node',
sourcemap: 'inline',
target: 'node20',
};

console.debug(`Generating bundle for ${pjson.name} with options`);
console.debug(options);

await build(options);

console.log(`Bundle for ${pjson.name} generated successfully`);
}

function pluckDefault(mod) {
return mod.default;
}
function handleImportErr(err: NodeJS.ErrnoException) {
if (err.code !== 'ERR_MODULE_NOT_FOUND') throw err;
return {};
}
const jsonImportAttrs: ImportCallOptions = { with: { type: 'json' } };
40 changes: 40 additions & 0 deletions build/publish.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import path from 'node:path';
import { argv, cwd, exit } from 'node:process';
import { parseArgs } from 'node:util';

import { publish } from 'codemod';

import { bundle, outfile } from './bundle.mts';


const {
recipes,
status,
} = parseArgs({
args: argv,
options: {
recipes: { type: 'string' },
status: { type: 'boolean' },
},
}).values;
const recipeRelPaths: string[] = recipes?.slice(1, -1).split(' ') ?? [];

if (!status) throw new Error(`Unexpected status: ${status}`);

const rootPath = cwd();

const n = recipeRelPaths.length;
const publications = new Array(n);
for (let r = n - 1; r > -1; r--) {
const recipeRelPath = recipeRelPaths[r];
const recipeAbsPath = path.join(rootPath, recipeRelPath);

publications[r] = bundle[r](recipeAbsPath)
.then(() => publish(path.join(recipeAbsPath, outfile)));
}

Promise.allSettled(publications)
.then(
() => console.log('Publishing complete'),
() => console.log('Publishing failed'),
);
69 changes: 69 additions & 0 deletions build/publish.spec.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import assert from 'node:assert/strict';
import { execPath } from 'node:process';
import {
before,
describe,
it,
mock,
} from 'node:test';

import { spawnPromisified } from './spawn-promisified.ts';


type Mock = ReturnType<typeof mock.fn>['mock'];

describe('Publishing', () => {
const cwd = '/test';
let mock__bundle: Mock;
let mock__publish: Mock;

before(async () => {
const bundle = mock.fn();
mock__bundle = bundle.mock;
const publish = mock.fn();
mock__publish = publish.mock;

mock.module('codemod', {
namedExports: {
publish,
},
});
mock.module('./bundle.mts', {
namedExports: {
bundle,
},
});
});

it('should', async () => {
mock__bundle.mockImplementationOnce(Promise.resolve);
mock__publish.mockImplementationOnce(Promise.resolve);

const { code, stderr, stdout } = await spawnPromisified(
execPath,
[
'--no-warnings',
'--experimental-strip-types',
'--recipes=("a" "b")',
'--status=true',
'./publish.mts',
],
{
cwd,
},
);

assert.equal(stderr, '');
assert.equal(code, 0);

assert.deepEqual(mock__bundle.calls, [
{ arguments: [`${cwd}/a`] },
{ arguments: [`${cwd}/b`] },
]);

assert.deepEqual(mock__publish.calls, [
{ arguments: [`${cwd}/a`] },
{ arguments: [`${cwd}/b`] },
]);
});
});

0 comments on commit fe81da4

Please sign in to comment.