Skip to content

feat: align dev tooling with actions/typescript-action template (Scope B)#126

Merged
ahanoff merged 7 commits into
mainfrom
align-tooling-with-typescript-action-template
Jun 19, 2026
Merged

feat: align dev tooling with actions/typescript-action template (Scope B)#126
ahanoff merged 7 commits into
mainfrom
align-tooling-with-typescript-action-template

Conversation

@ahanoff

@ahanoff ahanoff commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

What

Align the repo's dev tooling with the official actions/typescript-action template, Scope B (medium): dep version bumps + Jest 30 + ESLint 10 flat config migration. Keeps @vercel/ncc bundler, CommonJS output, @actions/core v1, and the existing tsconfig target — those are deferred to a future Scope C migration.

Why

The official template has moved to ESLint 10 (flat config) and Jest 30. Staying on ESLint 8 / Jest 29 leaves the repo on unsupported major versions and makes it harder to consume template updates.

Changes

Dependencies

Package From To
eslint ^8.51.0 ^10.0.0
jest ^29.7.0 ^30.2.0
@types/jest ^29.5.6 ^30.0.0
typescript ^5.2.2 ^5.9.3
@types/node ^24.5.2 ^25.2.3
prettier 3.2.5 ^3.8.1
ts-jest ^29.1.1 ^29.4.6
eslint-plugin-jest ^27.4.2 ^29.15.0
eslint-plugin-prettier ^5.0.1 ^5.5.5

Added: @eslint/compat, @eslint/eslintrc, @eslint/js, @typescript-eslint/eslint-plugin, eslint-config-prettier

Removed: eslint-plugin-github (not in template), jest-circus (default in Jest 30), js-yaml (not used in source)

Tooling

  • eslint.config.mjs (flat config) replaces .eslintrc.json + .eslintignore
  • jest.config.js simplified: preset: 'ts-jest', dropped explicit jest-circus/runner
  • Scripts renamed to match template: formatformat:write, format-checkformat:check, lint now scans . (whole project)
  • Prettier run on all files (was previously **/*.ts only) — touches README.md, action.yml, tsconfig.json, renovate configs, CHANGELOG.md

Source (driven by stricter lint rules)

  • src/main.ts: removed unused catch (e) param; catch (error: any)catch (error) + instanceof Error narrowing (no as any)
  • src/card.ts + src/__tests__/card.test.ts: Array<T>T[]; extracted empty-string commit case into its own test (jest/no-conditional-expect)

Version

2.1.02.2.0 (minor: developer tooling changed, action runtime behavior unchanged)

Bundle size note

dist/main/index.js grew 1,633,205 → 1,712,720 bytes (+79KB / 4.8%). Cause: TypeScript 5.9 emits additional ESM↔CJS interop helpers (__exportStar, __createBinding, __defineGetter/__defineSetter/__lookupGetter/__lookupSetter, __moduleExports, __namespace) and the ncc 0.38.0 → 0.38.4 runtime preamble is slightly larger. All production deps are byte-identical — no runtime code was added.

Verification

Check Result
npm run lint ✅ clean
npm run build (tsc) ✅ clean
npm test (Jest 30) ✅ 28/28 pass
npm run package (ncc) ✅ bundles
npm run all ✅ full pipeline passes

Out of scope (deferred to future Scope C PR)

  • @actions/core v1 → v3 (breaking API changes)
  • @vercel/nccrollup bundler swap
  • "type": "module" ESM migration
  • tsconfig.jsonES2022 / NodeNext

ahanoff added 7 commits June 20, 2026 02:13
…plate

Bump versions to match the official actions/typescript-action template where possible without breaking changes:

- eslint: 8.51.0 -> 10.0.0 (with new @eslint/js, @eslint/compat, @eslint/eslintrc)

- jest: 29.7.0 -> 30.2.0; ts-jest: 29.1.1 -> 29.4.6; @types/jest: 29.5.6 -> 30.0.0

- typescript: 5.2.2 -> 5.9.3; @types/node: 24.5.2 -> 25.2.3

- prettier: 3.2.5 -> 3.8.1; eslint-plugin-prettier: 5.0.1 -> 5.5.5

- eslint-plugin-jest: 27.4.2 -> 29.15.0

- add @typescript-eslint/eslint-plugin@8.56.0 and eslint-config-prettier@10.1.8

- drop eslint-plugin-github, jest-circus (default in Jest 30), js-yaml (unused)

- rename format scripts to format:write / format:check; lint now scans '.' instead of 'src/**/*.ts'

- bump version to 2.2.0
Replace .eslintrc.json + .eslintignore with eslint.config.mjs to match the actions/typescript-action template and ESLint v10 requirements.

- extends eslint:recommended, @typescript-eslint/recommended, jest/recommended, prettier/recommended

- preserves the existing project-specific TypeScript rules (no-explicit-any, explicit-member-accessibility, etc.)

- drops parserOptions.project since no type-aware rules are used (also speeds up lint)
- switch to ts-jest preset

- drop explicit testRunner: 'jest-circus/runner' (default since Jest 27)

- tighten testMatch to **/__tests__/**/*.test.ts
- remove unused 'catch (e)' parameter (use optional catch binding)

- replace 'catch (error: any)' with 'catch (error)' plus instanceof Error narrowing

- both flagged by @typescript-eslint/no-unused-vars and no-explicit-any under the new flat config
- replace Array<T> with T[] in WebhookBody interface and test type assertions (@typescript-eslint/array-type)

- extract empty-string commit message case into its own test (jest/no-conditional-expect)
Output of 'prettier --write .' which now formats every file instead of only *.ts. Touched files: renovate configs, CHANGELOG, README, action.yml, tsconfig.json.
Regenerate dist/main/index.js via 'npm run package' after the source and tooling changes above. Bundle size grew ~79KB / 4.8% purely from TypeScript 5.9 emitting additional ESM/CJS interop helpers (__exportStar, __createBinding, accessor helpers) and a slightly larger ncc runtime preamble; production dependencies are unchanged.
@ahanoff ahanoff changed the title Align dev tooling with actions/typescript-action template (Scope B) feat: align dev tooling with actions/typescript-action template (Scope B) Jun 19, 2026
@ahanoff ahanoff merged commit fadc34f into main Jun 19, 2026
4 checks passed
@ahanoff ahanoff deleted the align-tooling-with-typescript-action-template branch June 19, 2026 18:15
ahanoff added a commit that referenced this pull request Jun 22, 2026
…core@3) (#129)

## What

Complete the template alignment by migrating from CJS+ncc to ESM+rollup.
This is **Scope C** — the remaining work after [PR #126 (Scope
B)](#126) which
covered ESLint flat config + Jest 30 + dep version bumps.

## Breaking changes

| Area | Before | After |
|---|---|---|
| Module system | CommonJS | **ESM** (`"type": "module"`) |
| Bundler | `@vercel/ncc` → `dist/main/index.js` (CJS) | **rollup** →
`dist/index.js` (ESM) |
| `@actions/core` | `^1.10.1` (CJS) | **`^3.0.1`** (pure ESM) |
| `@actions/github` | `^6.0.0` (CJS, http-client v2 conflict) |
**`^9.0.0`** (pure ESM, eliminates conflict) |
| tsconfig | `es6` / `commonjs` | **`ES2022` / `NodeNext`** |
| Action entry | `dist/main/index.js` | `dist/index.js` |
| Version | 2.2.0 | **3.0.0** |

**For action consumers**: no change needed. Same inputs, same outputs,
same `node24` runtime. The only visible difference is the `main:` path
in action.yml.

## Why

Stay current with the official
[actions/typescript-action](https://github.com/actions/typescript-action)
template, which is now ESM-only:
- `@actions/core@3` is pure ESM (no `require()` support)
- `@actions/github@9` is pure ESM
- Template uses rollup, not ncc
- All new `@actions/*` releases will be ESM-only

## How

### Rollup bundler
- `rollup-plugin-esbuild` for TypeScript transpilation (uses `transform`
hook — `@rollup/plugin-typescript` v11/v12 use `resolveId`+`load` hooks
which silently skip entry points, a known issue)
- `@rollup/plugin-commonjs` converts CJS deps (adaptivecards stack) to
ESM at build time
- `@rollup/plugin-json` — **critical**: adaptivecards-templating's
source does `require('./../package.json')` which rollup cannot handle
natively
- `@rollup/plugin-node-resolve` for module resolution

### Source changes (minimal)
- 2 relative imports get `.js` extensions for NodeNext: `'./card'` →
`'./card.js'`
- No other source changes — `@actions/core@3` APIs (`getInput`, `info`,
`warning`, `error`, `setFailed`) are byte-identical to v1

### Jest ESM mode
- `export default` config (was `module.exports`)
- `extensionsToTreatAsEsm: ['.ts']`, `useESM: true`, `ts-jest-resolver`
- `NODE_OPTIONS=--experimental-vm-modules` in test script

## Compatibility research

Before implementing, investigated all runtime deps for ESM
compatibility:

| Package | Format | Bundles via commonjs plugin? |
|---|---|---|
| `adaptive-expressions` | CJS-only | ✅ |
| `adaptivecards` | CJS-only (broken for native ESM import — [issue
#9155](microsoft/AdaptiveCards#9155)) | ✅
(commonjs plugin sidesteps the bug) |
| `adaptivecards-templating` | CJS-only + `require('./../package.json')`
| ✅ + `@rollup/plugin-json` |
| `cockatiel` | Dual (has `module` field) | ✅ (rollup picks ESM
automatically) |

## Bundle size

| Metric | ncc (before) | rollup (after) |
|---|---|---|
| `dist/index.js` | 1.7 MB (minified CJS) | 3.8 MB (ESM + sourcemap) |
| Build time | 4.0s | 2.1s |

Larger because rollup doesn't minify by default (template doesn't
either). Functionally irrelevant for GitHub Actions runtime. Can add
minification later if desired.

## Verification

| Check | Result |
|---|---|
| `npm run lint` | ✅ clean |
| `npm run build` (`tsc --noEmit`) | ✅ clean |
| `npm test` (Jest 30 ESM mode) | ✅ 28/28 pass in 0.43s |
| `npm run package` (rollup) | ✅ builds in 2.1s |
| `npm run all` | ✅ full pipeline passes |

## Deviation from template

| Template uses | We use | Reason |
|---|---|---|
| `@rollup/plugin-typescript` | `rollup-plugin-esbuild` | Plugin's
`resolveId`+`load` hooks don't intercept entry points (confirmed broken
in v11.0.0–v12.3.0 with rollup 4.x). esbuild uses `transform` hook which
works reliably. Type checking handled by `tsc --noEmit`. |
| `rollup.config.ts` | `rollup.config.mjs` | Eliminates need for
`--configPlugin` flag (which would require `@rollup/plugin-typescript`
we dropped). Config is a plain object, doesn't need TypeScript types. |
| No `@actions/github` | `@actions/github@9` | Our action needs Octokit
for workflow/job status queries. |

## Not in scope

- Minification (rollup output is unminified, matching template behavior)
- `@github/local-action` for local testing (can add separately)
- Coverage badge generation (can add separately)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant