chore: migrate to the magic tooling stack - #24
Conversation
`@expo/config-plugins` is not a declared dependency — it arrives through `expo`. Yarn's flat node_modules made the deep import resolve anyway; pnpm's isolated layout does not, so the build fails with TS2307. The type is re-exported as `AndroidConfig.Resources.ResourceXML` from the entry point the rest of the file already imports. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
- pnpm 11.17.0 pinned through `packageManager`; yarn.lock deleted, pnpm-lock added. Yarn's `resolutions` become pnpm `overrides` in pnpm-workspace.yaml, which is where pnpm 11 reads settings from — the `pnpm` key in package.json is ignored now. - eslint, prettier and the generated `.eslintrc.js` are gone. oxlint 1.75.0 and oxfmt 0.60.0 (both pinned exactly) plus magic-oxlint-config, magic-oxfmt-config, magic-tsconfig and magic-codemods take their place. - tsconfig.json extends magic-tsconfig/base.json, re-declaring CommonJS emit: Expo `require`s a config plugin, so the bundler-oriented `module: Preserve` from the shared base would ship ESM and break every consumer's prebuild. tsconfig.build.json is the emit-only variant, so `typecheck` can cover the tests without the tests reaching the published tarball. - `expo-module build`, `expo-module test`, `expo-module prepare` and `expo-module prepublishOnly` are replaced by `tsc` and `jest` directly. expo-module-scripts@3 routes every one of those through a wrapper that runs `yarn exec -- npx …` whenever yarn is on PATH and the user agent is not yarn, which is exactly the pnpm case — and yarn then refuses on the `packageManager: pnpm@…` field. `prepare` additionally ran `expo-module configure`, which re-creates the deleted `.eslintrc.js` from a template on every install. `expo-module prepublishOnly` shells out to the `proofread` bin, a transitive dependency that pnpm does not link. The jest preset and `expo-module clean` still come from the package. - @types/node and @types/jest are now direct devDependencies; pnpm does not expose transitive ones to the type checker. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
`base` is the right variant: this package is plain Node TypeScript that runs inside Expo's prebuild, with no React or JSX anywhere. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
`unicorn/filename-case` is on in magic-oxlint-config. Applied with `magic-kebab --write`: 11 renames, 21 import specifiers rewritten, no conflicts and nothing needing review. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
magic-tsconfig's base turns `incremental` on, and the build info file lands
next to the config rather than inside `outDir`. `pnpm run clean && pnpm run
build` therefore emitted nothing at all — tsc still believed the output it had
just deleted was up to date, and `require("./build")` died on the first missing
module. Off for the emit config; `typecheck` keeps it.
Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
- `typescript/consistent-type-imports` and `func-style` autofixed (two passes,
as the magic README warns).
- `.release-it.cjs` filtered changelog commits with `/^chore\(release\)/`;
`String#startsWith` says the same thing without a regex.
- `no-template-curly-in-string` is off for `.release-it.cjs` alone: `${version}`
there is release-it's own template syntax and has to reach it uninterpolated,
so a real template literal would be the bug.
Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
Mostly package.json key sorting, which oxfmt does by default. CHANGELOG.md is excluded: @release-it/conventional-changelog rewrites it on every release with `*` bullets, and oxfmt rewrites those to `-`, so the release workflow's own bump PR would fail the format check it just created. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
- CI calls the shared `GSTJ/magic/.github/workflows/ci.yml`, which installs with pnpm and runs lint, format and typecheck; build, test and the `npm pack --dry-run` tarball guard stay as this repo's own inputs. - The `validate` job exists only to carry the name "Lint, test and build". master's ruleset requires that exact status check, a called workflow reports as "<caller job> / <called job>" and can never produce it, and changing the ruleset is a repository setting, not a change this branch can make. - release.yml installs and runs everything through pnpm, and now checks format and typecheck too — it used to lean on `expo-module lint`, which no longer exists. Publishing is untouched: release-it still shells out to npm. - .nvmrc pins node 24, which both workflows read, replacing the version that was duplicated in each. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
magic-oxlint-config, magic-oxfmt-config, magic-tsconfig and magic-codemods all shipped a 1.1.0 that fixes what the 1.0.0 migrations reported. The pnpm 11 release quarantine still applies — the 1.1.0 tarballs are hours old — so `minimumReleaseAgeExclude` moves to the new versions rather than being dropped. Delete those four lines once the packages age past the three-day window. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
oxfmt: magic-oxfmt-config now ignores `**/CHANGELOG.md` itself, so the local spread that re-declared it is gone and the config is the plain re-export the README asks for. tsconfig: magic-tsconfig no longer sets `incremental`, so `"incremental": false` in the build config guards against nothing. `*.tsbuildinfo` leaves .gitignore with it — `tsc --noEmit` and `tsc -p tsconfig.build.json` both write none now, confirmed after deleting the stale root one. oxlint: `extends` still drops the preset's `ignorePatterns` on oxlint 1.75.0 with magic-oxlint-config 1.1.0, so the re-declared list could not just be deleted — checked with `--print-config` and by linting a probe file under `**/generated/**`, which went from ignored to four diagnostics the moment the line came out. `extendConfig` flattens instead of extending, which carries the ignore list by construction and leaves nothing local to drift. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
magic-oxlint-config 1.1.0 pins `typescript/consistent-type-definitions` to "type"; 1.0.0 shipped the rule with oxlint's default option, which asks for the opposite. `--fix` did the rewrite, `oxfmt` added the terminating semicolon it left off. Nothing imports this as an interface — no declaration merging, no `implements` — so the alias is a straight swap. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
Updated to the magic 1.1.0 roundThree commits on top of the migration: the bump, the workarounds it retires, and the one code change 1.1.0 forces. Bumped — Removed
Kept, but rewritten — the So instead of deleting the line, this config moved to New violation — Verification — from a deleted
|
magic-oxlint-config, magic-oxfmt-config and magic-tsconfig only; magic-codemods stays on 1.1.0, which is still latest. The pnpm 11 quarantine needs both versions excluded for the one install that rewrites the lockfile — pnpm verifies the committed lockfile before it resolves anything, so leaving 1.1.0 out fails on the versions being left behind. The @1.1.0 lines are gone again after that install; magic-codemods keeps its own, since 1.1.0 published today too. Nothing else moved: the lockfile diff is 15 lines each way, all of them the three specifiers and their resolutions. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
Both configs are already the shapes 1.2.0 documents, so this is comments only. The oxlint one cited `--print-config` as the check that `extends` drops `ignorePatterns`; 1.2.0 says that command renders an `extends` config pre-merge and is not a valid audit of one. The reason survives it: oxlint has no per-override ignore, which is why `extends` is undocumented now and why `extendConfig` is what this repo uses. The oxfmt one notes why `withoutIgnorePatterns` does not apply here — this changelog is generated by @release-it/conventional-changelog, so the preset's `**/CHANGELOG.md` ignore is the wanted behaviour. Claude-Session: https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1
|
Bumped to magic-oxlint-config / magic-oxfmt-config / magic-tsconfig 1.2.0. magic-codemods stays on 1.1.0 (still latest). Both quarantine versions had to be listed in Nothing to unwind from the 1.2.0 fixes: this isn't a Next app, so Green: install --frozen-lockfile, build, lint (0), format --check, typecheck, 5/5 tests. |
`--report-unused-disable-directives` reports at *warning* severity, and oxlint exits 0 on warnings, so the one thing that flag exists to catch was the one thing that couldn't fail CI. Every rule in the shared preset is at `error`, so this is the only diagnostic `--deny-warnings` changes — verified by running both forms against a file with a stale directive: exit 0 without it, exit 1 with it. This repo just spent a day on a lint step that found things and exited 0 anyway, which is why it gets closed here rather than left for later. The flag belongs in the shared script in GSTJ/magic for the same reason; until it is there, this is a one-line local deviation.
Moves this package onto the shared GSTJ tooling stack: pnpm, oxlint, oxfmt,
magic-tsconfig, and the reusable CI workflow.
Stack changes
yarn.lockpackageManager) +pnpm-lock.yamlexpo-module lint(eslint-config-universe)oxlint@1.75.0+magic-oxlint-config/baseoxfmt@0.60.0+magic-oxfmt-configexpo-module-scripts/tsconfig.pluginmagic-tsconfig/base.jsonGSTJ/magic/.github/workflows/ci.yml@mainScripts are the standard set:
lint,lint:fix,format,format:fix,typecheck, plus this repo'sbuild,test,cleanandrelease.expo-module-scripts is no longer the runner
expo-module build,test,prepareandprepublishOnlyall route through awrapper that runs
yarn exec -- npx …whenever yarn is on PATH and the useragent is not yarn — exactly the pnpm case, and yarn then refuses on the
packageManager: pnpm@…field. Two more reasons not to keep them:prepareranexpo-module configure, which re-creates the deleted.eslintrc.jsfrom a template on every install.prepublishOnlyshells out to theproofreadbin, a transitive dependencythat pnpm does not link into
node_modules/.bin.So
buildistsc --project tsconfig.build.jsonandtestisjest, which iswhat those wrappers were doing. The package stays:
expo-module cleanand thejest preset in
jest.config.jsstill come from it.Notable refactors
ResourceXMLcame from a deep import into@expo/config-plugins, whichis not a declared dependency — it arrives through
expo. Yarn's flatnode_modulesmade that resolve; pnpm's does not. It now comes fromAndroidConfig.Resourceson the entry point the file already imported.with
magic-kebab --write. No conflicts, nothing skipped, nothing flagged forreview.
git log --followstill reaches back through the renames.tsconfig.json(typecheck) covers the tests;tsconfig.build.jsonis the emit config that excludes them. Both re-declareCommonJS: Expo
requires a config plugin, and the shared base isbundler-oriented (
module: Preserve), which would emit ESM and break everyconsumer's prebuild.
incrementalis off for the emit config — its build-infofile lands outside
outDir, soclean && buildwas emitting nothing at alland
require("./build")died on the first missing module..release-it.cjsfilters changelog commits withString#startsWithinsteadof a
^-anchored regex.no-template-curly-in-stringis off for.release-it.cjsalone —${version}there is release-it's own template syntax and has to reach it uninterpolated.
CHANGELOG.mdis excluded from oxfmt:@release-it/conventional-changelogrewrites it with
*bullets on every release, and oxfmt rewrites those to-, so the release workflow's own bump PR would fail the format check it justcreated.
CI
checkscalls the shared workflow. The second job,validate, exists only tocarry the name "Lint, test and build": master's ruleset requires that exact
status check, and a called workflow reports as
<caller job> / <called job>, soit can never produce that context. Deleting that job is a one-liner once the
ruleset is pointed at the shared workflow's check — a repository settings
change, which this PR deliberately does not make.
release.ymlkeeps its shape and now installs and runs through pnpm, withformatandtypecheckadded. Publishing is untouched: release-it still shellsout to npm. CodeQL is untouched.
Verified locally
pnpm install --frozen-lockfilefrom an emptynode_modules, thenlint(0diagnostics),
format --check,typecheck,test(5 passing),build, andnpm pack --dry-run(36 files, entry point loads throughapp.plugin.js).https://claude.ai/code/session_01Fe92vvdc4R3F4BDBFoLcw1