diff --git a/.github/workflows/branch-check.yml b/.github/workflows/branch-check.yml index bd4ec90..34e82c3 100644 --- a/.github/workflows/branch-check.yml +++ b/.github/workflows/branch-check.yml @@ -50,7 +50,7 @@ jobs: checkup: name: 👮 Checkup needs: dedupe - uses: GSTJ/magic/.github/workflows/ci.yml@main + uses: GSTJ/magic/.github/workflows/ci.yml@v1 # Kept out of the shared checkup job: only this one needs a database, and # folding it in would spin up Postgres for typecheck/lint/format too. No diff --git a/apps/nextjs/oxlint.config.mts b/apps/nextjs/oxlint.config.mts index 25a4bbf..ae116d8 100644 --- a/apps/nextjs/oxlint.config.mts +++ b/apps/nextjs/oxlint.config.mts @@ -1,9 +1,27 @@ +import { extendConfig } from "magic-oxlint-config"; +import next from "magic-oxlint-config/next"; + /** * A nested config replaces the root one for everything under `apps/nextjs`, so - * this file has to be complete on its own. Re-exporting the preset — rather - * than `defineConfig({ extends: [next] })` — is what makes it complete: - * `extends` still drops the extended config's `ignorePatterns` on oxlint - * 1.75.0, so the extends form needs them copied back by hand. The re-export - * loads the preset as *the* config and every field applies. + * this file has to be complete on its own. `extendConfig` flattens the preset + * and the additions below into one config, which is what makes it complete: + * oxlint's own `extends` still drops the extended config's `ignorePatterns` on + * 1.75.0 and needs them re-listed by hand. + * + * This is the only config in the repo that loads the `magic` jsPlugin. + * `magic/no-manual-classname` is a Tailwind rule and Tailwind stops at this + * app — the mobile app styles with styled-components and `packages/*` has no + * JSX at all, so loading the plugin there would cost startup time to lint + * nothing. */ -export { default } from "magic-oxlint-config/next"; +export default extendConfig(next, { + jsPlugins: [{ name: "magic", specifier: "magic-oxlint-plugin" }], + + rules: { + // Class strings go through `cn` (src/lib/utils.ts, `twMerge(clsx(...))`) + // or a `cva` variant table. A hand-built one loses the conflict + // resolution: `p-2 ${active ? "p-4" : ""}` emits both paddings and leaves + // the winner to stylesheet order, where `cn` keeps the last one. + "magic/no-manual-classname": "error", + }, +}); diff --git a/package.json b/package.json index f7d16b6..a69221a 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "magic-codemods": "^1.1.0", "magic-oxfmt-config": "^1.2.0", "magic-oxlint-config": "^1.2.0", + "magic-oxlint-plugin": "^1.1.0", "magic-tsconfig": "^1.2.0", "oxfmt": "0.60.0", "oxlint": "1.75.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09b3883..5e97a69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,6 +55,9 @@ importers: magic-oxlint-config: specifier: ^1.2.0 version: 1.2.0(eslint@9.39.5(jiti@1.21.0)(supports-color@8.1.1))(oxlint@1.75.0) + magic-oxlint-plugin: + specifier: ^1.1.0 + version: 1.1.0(oxlint@1.75.0) magic-tsconfig: specifier: ^1.2.0 version: 1.2.0 @@ -5739,6 +5742,15 @@ packages: oxlint: optional: true + magic-oxlint-plugin@1.1.0: + resolution: {integrity: sha512-SdxWuNO9B0g5vMivlk9gP0B+PIKmBoImn8QWn9HQy0tIiyELzSmdrBkGtB2OB7IY23KK6b3gx1WDy8gxy763EA==} + engines: {node: '>=22'} + peerDependencies: + oxlint: '>=1.75.0' + peerDependenciesMeta: + oxlint: + optional: true + magic-tsconfig@1.2.0: resolution: {integrity: sha512-odQYUHWzEvgB85RsdRvKwYQPm4/zMMgXABhPPHyNpkEd4T4OqPp8SI4LfYQAn5LXLIktLHzqSJktH5g0Y5EeIQ==} @@ -13732,6 +13744,10 @@ snapshots: transitivePeerDependencies: - eslint + magic-oxlint-plugin@1.1.0(oxlint@1.75.0): + optionalDependencies: + oxlint: 1.75.0 + magic-tsconfig@1.2.0: {} make-dir@4.0.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 51b27be..356c632 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -64,6 +64,7 @@ minimumReleaseAgeExclude: - magic-oxfmt-config@1.2.0 - magic-oxlint-config@1.2.0 - magic-tsconfig@1.2.0 + - magic-oxlint-plugin@1.1.0 # pnpm 10+ does not run dependency install scripts unless they are listed here, # and pnpm 11 makes an unanswered list a hard install failure. Every entry ran