adamantite 0.44.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -280,8 +280,8 @@ Adamantite publishes configuration that can also be consumed directly:
280
280
  | `adamantite/lint/vue` | Vue rules. |
281
281
  | `adamantite/lint/effect` | Curated [@effect/tsgo](https://github.com/Effect-TS/tsgo) rules for Effect misuse, such as effects that never run and unhandled errors. See [the effect preset](#the-effect-preset). |
282
282
  | `adamantite/lint/node` | Node.js rules. |
283
- | `adamantite/lint/jest` | Jest rules. |
284
- | `adamantite/lint/vitest` | Vitest rules. |
283
+ | `adamantite/lint/jest` | Jest rules for test files. See [the jest and vitest presets](#the-jest-and-vitest-presets). |
284
+ | `adamantite/lint/vitest` | Vitest rules for test files. See [the jest and vitest presets](#the-jest-and-vitest-presets). |
285
285
  | `adamantite/lint/strict` | Framework-neutral opinions: no deep or wide destructuring, and no type assertions outside tests. See [the strict preset](#the-strict-preset). |
286
286
  | `adamantite/lint/antislop` | Vendored [anti-slop](https://github.com/dmmulroy/anti-slop) rules that reject low-evidence, low-signal patterns. Also turns off `typescript/consistent-indexed-object-style` and `unicorn/no-immediate-mutation` from the core preset, which conflict with these rules. |
287
287
  | `adamantite/lint/shadcn` | [@shadcn/lint](https://github.com/shadcn-ui/lint) rules for Tailwind v4 design systems: no restyled components, raw colors, arbitrary values, inline styles, unknown classes, or unreadable class expressions. shadcn/ui is not required. See [the shadcn preset](#the-shadcn-preset). |
@@ -308,6 +308,51 @@ export default defineConfig({
308
308
 
309
309
  Configs generated by `adamantite init` include this automatically.
310
310
 
311
+ ### The jest and vitest presets
312
+
313
+ The `jest` and `vitest` presets apply their rules only to test files, so rules such as
314
+ `vitest/require-hook` do not report scripts and library code. Each preset exports `testFiles`, the
315
+ files that its test runner runs as tests by default. `vitest` `testFiles` match `*.test.*` and
316
+ `*.spec.*` files. `jest` `testFiles` also match files in `__tests__` folders.
317
+
318
+ Extend the preset to use these files:
319
+
320
+ ```ts
321
+ import core from "adamantite/lint"
322
+ import vitest from "adamantite/lint/vitest"
323
+ import { defineConfig } from "oxlint"
324
+
325
+ export default defineConfig({
326
+ extends: [core, vitest],
327
+ ignorePatterns: core.ignorePatterns,
328
+ })
329
+ ```
330
+
331
+ If your test runner finds tests in other files, use the preset in an override of your own
332
+ instead of `extends`. `vitest.plugins` and `vitest.rules` hold the preset's plugins and rules:
333
+
334
+ ```ts
335
+ import core from "adamantite/lint"
336
+ import vitest, { testFiles } from "adamantite/lint/vitest"
337
+ import { defineConfig } from "oxlint"
338
+
339
+ export default defineConfig({
340
+ extends: [core],
341
+ ignorePatterns: core.ignorePatterns,
342
+ overrides: [
343
+ {
344
+ files: [...testFiles, "e2e/**/*.ts"],
345
+ plugins: vitest.plugins,
346
+ rules: { ...vitest.rules, "vitest/max-expects": "off" },
347
+ },
348
+ ],
349
+ })
350
+ ```
351
+
352
+ Change a test rule in an override, not in the top-level `rules`: the preset's rules for test files
353
+ replace top-level settings. Keep `plugins: vitest.plugins` in that override too, because Oxlint
354
+ ignores the `vitest/*` rules of an override that does not enable the `vitest` plugin.
355
+
311
356
  ### The react-strict preset
312
357
 
313
358
  `adamantite/lint/react-strict` keeps state and side effects out of React feature code. Use
package/dist/index.js CHANGED
@@ -122,7 +122,7 @@ export default defineConfig({
122
122
  `)}`];return e.reference&&t.push(`Reference\n${e.reference.content.trimEnd()}`),e.notes&&e.notes.length>0&&t.push(`Notes\n${e.notes.map(e=>`• ${e}`).join(`
123
123
  `)}`),t.join(`
124
124
 
125
- `)}const Pi=t.fn(`printFindings`)(function*(e){let t=yield*Q;for(let[n,r]of e.entries())yield*t.note(Ni(r),`${n+1}. ${r.title}`)}),Fi=`0.44.0`;var Ii=c.make(`doctor`).pipe(c.withDescription(`Assess Adamantite-managed integrations in the current project`),c.withHandler(()=>t.gen(function*(){let e=f.cwd(),n=yield*ji,i=yield*Q,a=yield*Mi,o=yield*a.isInteractive;o&&(yield*i.intro(`💠 adamantite doctor`));let s=t.gen(function*(){return yield*i.outro(`⚠️ Doctor found issues.`),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)})}),c=yield*R(e);if(!c.devDependencies?.adamantite&&!c.dependencies?.adamantite){let e="`adamantite` is not installed in this project. Install it before running `adamantite doctor`.";return o?(yield*i.log.warning(e),yield*i.outro(`⚠️ Doctor found issues.`)):yield*i.message(e),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)})}let l=yield*Ci(e);if(o)for(let e of l.warnings)yield*i.log.warning(e);if(l.applicableIntegrations.length===0){o&&(yield*i.log.success(`No applicable integrations found.`),yield*i.outro(`✅ Doctor completed successfully!`));return}if(l.findings.length===0){o?(yield*i.log.success(`No issues found.`),yield*i.outro(`✅ Doctor completed successfully!`)):l.warnings.length>0&&(yield*i.message(wi(l,Fi)));return}if(!o)return yield*i.message(wi(l,Fi)),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)});let u=e=>t.gen(function*(){(yield*i.confirm({initialValue:!0,message:`Copy the Markdown prompt for a coding agent?`}))&&(yield*i.message(e),yield*a.copyToClipboard(e),yield*i.log.success(`The Markdown prompt was printed and sent to the terminal clipboard.`))});yield*Pi(l.findings);let d=yield*i.withSpinner(()=>n.detectInstalled(e),{start:`Checking for installed coding agents...`,success:e=>e.length===0?`No supported coding agent CLI was found on PATH.`:`Found ${e.map(e=>e.name).join(`, `)}.`});d.length===0&&(yield*i.log.info(`Doctor can hand findings off when one of these CLIs is installed: ${Di.map(e=>`\`${e.command}\``).join(`, `)}.`));let p=wi(l,Fi),m=yield*i.select({message:`How do you want to resolve these findings?`,options:[...d.map(e=>({label:`Hand off to ${e.name}`,value:e})),{label:`Copy the Markdown prompt for a coding agent`,value:`copy`},{label:`Do nothing`,value:`done`}]});if(m===`done`)return yield*s;if(m===`copy`)return yield*i.message(p),yield*a.copyToClipboard(p),yield*i.log.success(`The Markdown prompt was printed and sent to the terminal clipboard.`),yield*s;let h=m;if(yield*i.log.info(`Handing the terminal to ${h.name}. Exit the agent to return to Doctor.`),!(yield*n.runSession({agent:h,cwd:e,prompt:p}).pipe(t.as(!0),t.catchTag(`AgentSessionFailed`,e=>i.log.error(e.reason===`not-found`?`\`${h.command}\` was not found on PATH. Install ${h.name} or copy the prompt instead.`:`Failed to start ${h.name}: ${e.cause.message}`).pipe(t.as(!1))))))return yield*u(p),yield*s;let g=yield*Ci(e);if(g.findings.length===0)yield*i.log.success(`All findings were resolved by ${h.name}.`),yield*i.outro(`✅ Doctor completed successfully!`);else return yield*i.log.warning(`${g.findings.length} of ${l.findings.length} findings remain after the ${h.name} session.`),yield*Pi(g.findings),yield*u(wi(g,Fi)),yield*s}).pipe(t.catchTag(`OperationCancelled`,()=>t.gen(function*(){return yield*(yield*Q).cancel(`Doctor was cancelled. The findings remain.`),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)})})))));const Li=T.File(`files`,{mustExist:!0}).pipe(T.withDescription(`Specific files to fix (optional)`),T.variadic()),Ri=u.Boolean(`suggested`).pipe(u.withDefault(!1),u.withDescription(`Apply suggested fixes`)),zi=u.Boolean(`dangerous`).pipe(u.withDefault(!1),u.withDescription(`Apply dangerous fixes`)),Bi=u.Boolean(`all`).pipe(u.withDefault(!1),u.withDescription(`Apply all fixes, including suggested and dangerous fixes`)),Vi=u.Literals(`only`,[`format`,`lint`]).pipe(u.optional,u.withDescription("Run one stage: `format` (oxfmt) or `lint` (oxlint). Arguments after `--` go to that stage"));var Hi=c.make(`fix`,{all:Bi,dangerous:zi,files:Li,only:Vi,suggested:Ri}).pipe(c.withDescription(`Fix lint and formatting issues in code`),c.withHandler(({all:e,dangerous:n,files:r,only:i,suggested:a})=>t.gen(function*(){let t=yield*Ue,o=yield*j,c=d.getOrUndefined(i);if(c===`format`&&(e||n||a))return yield*new Pe({reason:"`--suggested`, `--dangerous`, and `--all` apply to the lint stage. Remove them or `--only format`."});let l=s.dedupe(r),u=c??`lint`,f=[{args:s.dedupe([`--fix`,...a||e?[`--fix-suggestions`]:[],...n||e?[`--fix-dangerously`]:[],...l]),command:W.name,name:`lint`,title:`🔧 Fixing lint issues`},{args:[`--write`,...l],command:U.name,name:`format`,title:`✨ Formatting`}];yield*o.runAll(f.filter(e=>c===void 0||e.name===c).map(e=>({args:[...e.args,...e.name===u?t:[]],command:e.command,title:e.title})))})));const Ui=' .o8 . o8o . \n "888 .o8 `"\' .o8 \n .oooo. .oooo888 .oooo. ooo. .oo. .oo. .oooo. ooo. .oo. .o888oo oooo .o888oo .ooooo. \n`P )88b d88\' `888 `P )88b `888P"Y88bP"Y88b `P )88b `888P"Y88b 888 `888 888 d88\' `88b \n .oP"888 888 888 .oP"888 888 888 888 .oP"888 888 888 888 888 888 888ooo888 \nd8( 888 888 888 d8( 888 888 888 888 d8( 888 888 888 888 . 888 888 . 888 .o \n`Y888""8o `Y8bod88P" `Y888""8o o888o o888o o888o `Y888""8o o888o o888o "888" o888o "888" `Y8bod8P\' \n \n \n ',Wi=t.gen(function*(){let e=yield*(yield*_e.Terminal).columns,t=Ui.split(`
125
+ `)}const Pi=t.fn(`printFindings`)(function*(e){let t=yield*Q;for(let[n,r]of e.entries())yield*t.note(Ni(r),`${n+1}. ${r.title}`)}),Fi=`0.45.0`;var Ii=c.make(`doctor`).pipe(c.withDescription(`Assess Adamantite-managed integrations in the current project`),c.withHandler(()=>t.gen(function*(){let e=f.cwd(),n=yield*ji,i=yield*Q,a=yield*Mi,o=yield*a.isInteractive;o&&(yield*i.intro(`💠 adamantite doctor`));let s=t.gen(function*(){return yield*i.outro(`⚠️ Doctor found issues.`),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)})}),c=yield*R(e);if(!c.devDependencies?.adamantite&&!c.dependencies?.adamantite){let e="`adamantite` is not installed in this project. Install it before running `adamantite doctor`.";return o?(yield*i.log.warning(e),yield*i.outro(`⚠️ Doctor found issues.`)):yield*i.message(e),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)})}let l=yield*Ci(e);if(o)for(let e of l.warnings)yield*i.log.warning(e);if(l.applicableIntegrations.length===0){o&&(yield*i.log.success(`No applicable integrations found.`),yield*i.outro(`✅ Doctor completed successfully!`));return}if(l.findings.length===0){o?(yield*i.log.success(`No issues found.`),yield*i.outro(`✅ Doctor completed successfully!`)):l.warnings.length>0&&(yield*i.message(wi(l,Fi)));return}if(!o)return yield*i.message(wi(l,Fi)),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)});let u=e=>t.gen(function*(){(yield*i.confirm({initialValue:!0,message:`Copy the Markdown prompt for a coding agent?`}))&&(yield*i.message(e),yield*a.copyToClipboard(e),yield*i.log.success(`The Markdown prompt was printed and sent to the terminal clipboard.`))});yield*Pi(l.findings);let d=yield*i.withSpinner(()=>n.detectInstalled(e),{start:`Checking for installed coding agents...`,success:e=>e.length===0?`No supported coding agent CLI was found on PATH.`:`Found ${e.map(e=>e.name).join(`, `)}.`});d.length===0&&(yield*i.log.info(`Doctor can hand findings off when one of these CLIs is installed: ${Di.map(e=>`\`${e.command}\``).join(`, `)}.`));let p=wi(l,Fi),m=yield*i.select({message:`How do you want to resolve these findings?`,options:[...d.map(e=>({label:`Hand off to ${e.name}`,value:e})),{label:`Copy the Markdown prompt for a coding agent`,value:`copy`},{label:`Do nothing`,value:`done`}]});if(m===`done`)return yield*s;if(m===`copy`)return yield*i.message(p),yield*a.copyToClipboard(p),yield*i.log.success(`The Markdown prompt was printed and sent to the terminal clipboard.`),yield*s;let h=m;if(yield*i.log.info(`Handing the terminal to ${h.name}. Exit the agent to return to Doctor.`),!(yield*n.runSession({agent:h,cwd:e,prompt:p}).pipe(t.as(!0),t.catchTag(`AgentSessionFailed`,e=>i.log.error(e.reason===`not-found`?`\`${h.command}\` was not found on PATH. Install ${h.name} or copy the prompt instead.`:`Failed to start ${h.name}: ${e.cause.message}`).pipe(t.as(!1))))))return yield*u(p),yield*s;let g=yield*Ci(e);if(g.findings.length===0)yield*i.log.success(`All findings were resolved by ${h.name}.`),yield*i.outro(`✅ Doctor completed successfully!`);else return yield*i.log.warning(`${g.findings.length} of ${l.findings.length} findings remain after the ${h.name} session.`),yield*Pi(g.findings),yield*u(wi(g,Fi)),yield*s}).pipe(t.catchTag(`OperationCancelled`,()=>t.gen(function*(){return yield*(yield*Q).cancel(`Doctor was cancelled. The findings remain.`),yield*new Se({command:`doctor`,exitCode:r.ExitCode(1)})})))));const Li=T.File(`files`,{mustExist:!0}).pipe(T.withDescription(`Specific files to fix (optional)`),T.variadic()),Ri=u.Boolean(`suggested`).pipe(u.withDefault(!1),u.withDescription(`Apply suggested fixes`)),zi=u.Boolean(`dangerous`).pipe(u.withDefault(!1),u.withDescription(`Apply dangerous fixes`)),Bi=u.Boolean(`all`).pipe(u.withDefault(!1),u.withDescription(`Apply all fixes, including suggested and dangerous fixes`)),Vi=u.Literals(`only`,[`format`,`lint`]).pipe(u.optional,u.withDescription("Run one stage: `format` (oxfmt) or `lint` (oxlint). Arguments after `--` go to that stage"));var Hi=c.make(`fix`,{all:Bi,dangerous:zi,files:Li,only:Vi,suggested:Ri}).pipe(c.withDescription(`Fix lint and formatting issues in code`),c.withHandler(({all:e,dangerous:n,files:r,only:i,suggested:a})=>t.gen(function*(){let t=yield*Ue,o=yield*j,c=d.getOrUndefined(i);if(c===`format`&&(e||n||a))return yield*new Pe({reason:"`--suggested`, `--dangerous`, and `--all` apply to the lint stage. Remove them or `--only format`."});let l=s.dedupe(r),u=c??`lint`,f=[{args:s.dedupe([`--fix`,...a||e?[`--fix-suggestions`]:[],...n||e?[`--fix-dangerously`]:[],...l]),command:W.name,name:`lint`,title:`🔧 Fixing lint issues`},{args:[`--write`,...l],command:U.name,name:`format`,title:`✨ Formatting`}];yield*o.runAll(f.filter(e=>c===void 0||e.name===c).map(e=>({args:[...e.args,...e.name===u?t:[]],command:e.command,title:e.title})))})));const Ui=' .o8 . o8o . \n "888 .o8 `"\' .o8 \n .oooo. .oooo888 .oooo. ooo. .oo. .oo. .oooo. ooo. .oo. .o888oo oooo .o888oo .ooooo. \n`P )88b d88\' `888 `P )88b `888P"Y88bP"Y88b `P )88b `888P"Y88b 888 `888 888 d88\' `88b \n .oP"888 888 888 .oP"888 888 888 888 .oP"888 888 888 888 888 888 888ooo888 \nd8( 888 888 888 d8( 888 888 888 888 d8( 888 888 888 888 . 888 888 . 888 .o \n`Y888""8o `Y8bod88P" `Y888""8o o888o o888o o888o `Y888""8o o888o o888o "888" o888o "888" `Y8bod8P\' \n \n \n ',Wi=t.gen(function*(){let e=yield*(yield*_e.Terminal).columns,t=Ui.split(`
126
126
  `).reduce((e,t)=>Math.max(e,t.trim().length),0);!e||e<t||(yield*h.info(Ui))}),Gi=[`check`,`fix`,`analyze`],Ki=[`vscode`,`zed`],qi=t.fn(`validateInitOptions`)(function*(e,t){let n={agents:e.agents,editors:s.dedupe(e.editors),githubActions:e.githubActions,installExtensions:e.installExtensions,overwriteScripts:e.overwriteScripts,presets:s.dedupe(e.presets),scripts:s.dedupe(e.scripts),typescript:e.typescript};if(n.scripts.length===0)return yield*new A({reason:"Select at least one script with `--script <name>`."});let r=n.scripts.includes(`check`)||n.scripts.includes(`fix`);return n.presets.length>0&&!r?yield*new A({reason:"`--preset` requires the `check` or `fix` script."}):n.presets.includes(`react-doctor`)&&!n.presets.includes(`react`)?yield*new A({reason:"`--preset react-doctor` requires `--preset react`."}):n.typescript&&!r?yield*new A({reason:"`--typescript` requires the `check` or `fix` script."}):n.installExtensions&&n.editors.length===0?yield*new A({reason:"`--install-extensions` requires at least one `--editor`."}):n.githubActions&&!zn(n.scripts)?yield*new A({reason:"`--github-actions` requires a CI-compatible script."}):n.githubActions&&t.nonInteractive&&!lt(t.packageManager)?yield*new A({reason:`\`--github-actions\` does not support the detected package manager \`${t.packageManager}\`. Use bun, deno, npm, pnpm, or yarn.`}):n}),Ji=u.Boolean(`non-interactive`).pipe(u.withDefault(!1),u.withDescription(`Configure without prompts; requires at least one --script`)),Yi=u.Literals(`script`,Gi).pipe(u.atMost(Gi.length),u.withDescription(`Package script to configure; repeatable and required in non-interactive mode`)),Xi=u.Literals(`preset`,Rt).pipe(u.atMost(Rt.length),u.withDescription(`Oxlint preset to configure; repeatable and requires --script check or fix`)),Zi=u.Literals(`editor`,Ki).pipe(u.atMost(Ki.length),u.withDescription(`Editor to configure; may be specified multiple times`)),Qi=u.Boolean(`typescript`).pipe(u.withDefault(!1),u.withDescription(`Configure the TypeScript preset; requires --script check or fix`)),$i=u.Boolean(`install-extensions`).pipe(u.withDefault(!1),u.withDescription(`Install recommended extensions; requires at least one --editor`)),ea=u.Boolean(`github-actions`).pipe(u.withDefault(!1),u.withDescription(`Configure CI; requires a compatible script and bun, deno, npm, pnpm, or yarn`)),ta={agents:u.Boolean(`agents`).pipe(u.withDefault(!1),u.withDescription(`Add Adamantite guidance to AGENTS.md`)),editors:Zi,githubActions:ea,installExtensions:$i,nonInteractive:Ji,overwriteScripts:u.Boolean(`overwrite-scripts`).pipe(u.withDefault(!1),u.withDescription(`Replace existing package scripts that conflict with Adamantite's managed commands`)),presets:Xi,scripts:Yi,typescript:Qi},na=e=>`\`${e.script}\` is currently \`${e.command}\`; Adamantite would replace it with \`${z[e.script]}\`.`,ra=t.fn(`collectInteractiveInitOptions`)(function*(e,t){let n=yield*Q,r=yield*n.multiselect({message:"Which scripts do you want to add to your `package.json`?",options:[{hint:`recommended`,label:`check - check formatting, lint issues, and type errors`,value:`check`},{hint:`recommended`,label:`fix - apply safe lint fixes and format code`,value:`fix`},{label:t?`analyze - check monorepo consistency using Sherif and find unused dependencies, exports, and files using knip`:`analyze - find unused dependencies, exports, and files using knip`,value:`analyze`}]}),i=dt(yield*R(e),r),a=!1;if(i.length>0){for(let e of i)yield*n.log.warning(na(e));a=yield*n.confirm({initialValue:!1,message:i.length===1?`Overwrite this existing script with Adamantite's command?`:`Overwrite these existing scripts with Adamantite's commands?`})}let o=r.includes(`check`)||r.includes(`fix`),s=[];o&&(s=yield*n.multiselect({message:`Which presets do you want to install? (core is always included)`,options:[{hint:`React correctness, JSX accessibility, and render performance rules`,label:`react`,value:`react`},{hint:`Strict React feature code: no state, effect, or memoization hooks outside hook modules`,label:`react-strict`,value:`react-strict`},{hint:`React state and effect misuse, such as effects used as event handlers and fetches in effects; requires react`,label:`react-doctor`,value:`react-doctor`},{hint:`TanStack Query: queries defined once with queryOptions() so route loaders can preload them, no query data copied into state`,label:`tanstack`,value:`tanstack`},{hint:`Next.js pitfalls around scripts, fonts, images, and document/head usage`,label:`next.js`,value:`nextjs`},{hint:`Vue 3 correctness rules and deprecated API detection`,label:`vue`,value:`vue`},{hint:`Effect misuse, such as effects that never run and unhandled errors; installs @effect/tsgo and patches Oxlint and TypeScript`,label:`effect`,value:`effect`},{hint:`Jest test hygiene: focused/disabled tests, matcher and snapshot discipline`,label:`jest`,value:`jest`},{hint:`Vitest test hygiene: focused/disabled tests, matcher and snapshot discipline`,label:`vitest`,value:`vitest`},{hint:`Node.js callback and CommonJS pitfalls`,label:`node`,value:`node`},{hint:`Framework-neutral strictness: no deep or wide destructuring, no type assertions outside tests`,label:`strict`,value:`strict`},{hint:`Rejects low-signal escape hatches: unjustified assertions, unknown leaks, module mocks`,label:`antislop`,value:`antislop`},{hint:`Tailwind v4 design system: no restyled components, raw colors, arbitrary values, or unknown classes`,label:`shadcn`,value:`shadcn`}],required:!1}),s.includes(`react-doctor`)&&!s.includes(`react`)&&(s=[`react`,...s],yield*n.log.info("Added the `react` preset, which `react-doctor` requires.")));let c=o?yield*n.confirm({initialValue:!0,message:t?"Adamantite provides a TypeScript preset to enforce strict type-safety. In a monorepo, each package's `tsconfig.json` must extend it. Would you like instructions on how to set it up?":`Adamantite provides a TypeScript preset to enforce strict type-safety. Would you like to use it?`}):!1,l=yield*n.multiselect({message:`Which editors do you want to configure? (optional)`,options:[{label:`VSCode / Cursor / Windsurf`,value:`vscode`},{label:`Zed`,value:`zed`}],required:!1}),u=l.length>0?yield*n.confirm({initialValue:!0,message:`Do you want to install the recommended editor extensions?`}):!1,d=zn(r)?yield*n.confirm({message:`Do you want to add a GitHub Actions workflow to run checks in CI?`}):!1;return{agents:yield*n.confirm({initialValue:!0,message:`Add Adamantite guidance to AGENTS.md so coding agents know how to run project checks?`}),editors:l,githubActions:d,installExtensions:u,overwriteScripts:a,presets:s,scripts:r,typescript:c}}),ia=[{path:`.vscode/settings.json`,type:`config`}],aa={"[css]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[graphql]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[javascript]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[javascriptreact]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[json]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[jsonc]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[typescript]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"[typescriptreact]":{"editor.defaultFormatter":`oxc.oxc-vscode`},"editor.codeActionsOnSave":{"source.fixAll.oxc":`explicit`},"editor.defaultFormatter":`oxc.oxc-vscode`,"editor.formatOnPaste":!0,"editor.formatOnSave":!0,"oxc.typeAware":!0};var $=M({config:ia[0].path,create:e=>t.gen(function*(){let t=yield*w.Path,n=t.join(e,ia[0].path);yield*We(t.dirname(n)),yield*Ke(n,aa)}),detect:e=>t.gen(function*(){let t=yield*C.FileSystem,n=yield*w.Path;return yield*t.exists(n.join(e,ia[0].path))}),extension:(e=[])=>t.gen(function*(){let n=[];(e.includes(`check`)||e.includes(`fix`))&&n.push(`oxc.oxc-vscode`),e.includes(`analyze`)&&n.push(`webpro.vscode-knip`);for(let e of n)yield*(yield*j).run({args:[`--install-extension`,e],command:`code`}).pipe(t.mapError(t=>t._tag===`CliNotFound`&&t.command===`code`?new ze({cause:t}):new Ee({cause:t._tag===`CommandFailed`?t.exitCode:t,extension:e})))}),files:ia,kind:`editor`,name:`vscode`,update:e=>t.gen(function*(){let t=(yield*w.Path).join(e,ia[0].path),n=yield*N(t),r=yield*I(n,t);if(!x.isObject(r))return yield*new Me({path:t});let i=yield*Je(aa,r);yield*F(t,it(n,i))})});const oa=`<!-- ADAMANTITE:START -->`,sa=`<!-- ADAMANTITE:END -->`;function ca(e,t,n){let r=me(e,t),i=z[t];switch(t){case`analyze`:return`- Run \`${r}\` after changing dependencies, imports, or exports.${n?` It also checks monorepo package consistency.`:``} Direct command: \`${i}\`.`;case`check`:return`- Run \`${r}\` to catch formatting, lint, and type issues. Direct command: \`${i}\`.`;case`fix`:return`- Run \`${r}\` to apply safe lint fixes and format code. Direct command: \`${i}\`.`}}function la({isMonorepo:e,packageManager:t,scripts:n}){let r=[`check`,`fix`,`analyze`].filter(e=>n.includes(e)).map(n=>ca(t,n,e)),i=[`## Adamantite`,``,`This project uses Adamantite for its managed formatting, linting, type checking, and dependency-analysis setup.`,``,...r.length>0?[`- Prefer the package scripts Adamantite added for this workspace.`,...r]:[],...n.includes(`check`)||n.includes(`fix`)?["- Write project-specific lint rules in `.adamantite/rules/`, one rule for each file. Run `adamantite rule add <name>` to start a rule with its authoring guidance."]:[],"- Run `adamantite doctor` and follow its findings to repair managed setup."].join(`
127
127
  `);return[oa,``,i,``,sa,``].join(`
128
128
  `)}const ua=(e,n)=>t.gen(function*(){let t=(yield*w.Path).join(e,`AGENTS.md`),r=b(yield*P(t),d.getOrElse(()=>``)),i=b(r,S.indexOf(oa)),a=b(r,S.indexOf(sa)),o=b(d.all({end:a,start:i}),d.filter(({end:e,start:t})=>t<e)),s=la(n);if(d.isSome(o)){let{end:e,start:n}=o.value,i=e+23,a=`${r.slice(0,n)}${s.trimEnd()}${r.slice(i)}`;return yield*F(t,a.endsWith(`
@@ -132,4 +132,4 @@ export default defineConfig({
132
132
  `)?`
133
133
  `:`
134
134
 
135
- `;return yield*F(t,`${r}${c}${s}`),`updated`}),da=(e,n)=>t.gen(function*(){yield*(yield*Q).withSpinner(()=>Hn(e,n),{failure:`Failed to install dependencies.`,start:`Installing dependencies...`,success:`Dependencies installed.`})});function fa(e,n,r){return t.gen(function*(){let i=yield*Q,a=yield*i.withSpinner(a=>t.gen(function*(){let t=yield*n.detect(e);for(let e of t.warnings)yield*i.log.warning(e);return t.active===null?(yield*a.message(`\`${n.config}\` not found, creating...`),yield*r,{created:!0,legacyConfig:null}):t.active.format===`ts`?(yield*a.message(`Found \`${n.config}\`, keeping existing config.`),{created:!1,legacyConfig:null}):(yield*a.message(`Found \`${t.active.file}\`, keeping existing config.`),{created:!1,legacyConfig:t.active.file})}),{failure:`Failed to set up ${n.name} config.`,start:`Setting up ${n.name} config...`,success:e=>e.created?`${n.name} config created successfully.`:`${n.name} config is ready.`});a.legacyConfig&&(yield*i.log.info(`Legacy \`${a.legacyConfig}\` was preserved during \`adamantite init\`. Run \`adamantite doctor\` and follow its findings to migrate it to the latest ${n.name} config.`))})}const pa=(e,n,r)=>t.gen(function*(){let i=yield*Q,a=yield*R(e),o=r.overwriteScripts?[]:dt(a,n),s=new Set(o.map(e=>e.script)),c=n.filter(e=>!s.has(e));yield*i.withSpinner(()=>t.gen(function*(){if(c.length!==0){a.scripts??={};for(let e of c)a.scripts[e]=z[e];yield*ct(e,a)}}),{failure:"Failed to add scripts to `package.json`.",start:"Adding scripts to your `package.json`...",success:o.length===0?"Scripts added to your `package.json`":c.length===0?"Kept your existing `package.json` scripts.":"Scripts added to your `package.json`; conflicting scripts were kept."});let l=r.nonInteractive?"Use `--overwrite-scripts` to replace it.":"Re-run `adamantite init` and confirm overwriting to replace it.";for(let e of o)yield*i.log.warning(`Kept existing \`${e.script}\` script (\`${e.command}\`) instead of \`${z[e.script]}\`. ${l}`);return o.length>0&&(yield*i.log.info("Adamantite commands forward extra arguments after `--`, so custom flags can be kept, e.g. `adamantite analyze -- --directory packages/app`.")),c}),ma=(e,n)=>t.gen(function*(){let r=yield*Q;n?yield*b(vi,t.forEach(e=>r.log.info(e))):yield*r.withSpinner(n=>t.gen(function*(){return(yield*Z.detect(e))?(yield*n.message(`\`${Z.config}\` found, updating...`),yield*Z.update(e),!1):(yield*n.message(`\`${Z.config}\` not found, creating...`),yield*Z.create(e),!0)}),{failure:`Failed to set up TypeScript config.`,start:`Setting up TypeScript config...`,success:e=>e?`\`${Z.config}\` created successfully`:`\`${Z.config}\` updated successfully`})}),ha=e=>t.gen(function*(){let t=yield*Q,n=yield*Y.update(e);n.prepare===`merged`&&(yield*t.log.info("Added `adamantite prepare` to the start of your existing `prepare` script.")),n.tsconfig===`monorepo`&&(yield*t.log.info("In a monorepo, add the Effect language service entry to each package's `tsconfig.json` or to a shared base config. Run `adamantite doctor` for the reference entry.")),n.tsconfig===`missing`&&(yield*t.log.warning("No `tsconfig.json` found, so the Effect language service is not configured. Run `adamantite doctor` for the reference entry."))}),ga=e=>t.gen(function*(){let n=yield*Q;(yield*Y.detect(e))&&(yield*n.withSpinner(()=>Y.patch(e,{quiet:!0}),{failure:`Failed to patch Oxlint and TypeScript.`,start:`Patching Oxlint and TypeScript with @effect/tsgo...`,success:`Oxlint and TypeScript patched with @effect/tsgo.`}).pipe(t.catchTag([`CommandFailed`,`CliNotFound`,`PlatformError`],()=>n.log.warning("Run `adamantite prepare` to see why the patch failed. Oxlint cannot load the `effect` preset until it succeeds."))))}),_a=(e,n)=>t.gen(function*(){let r=yield*Q;n.includes(`vscode`)&&(yield*r.withSpinner(n=>t.gen(function*(){let t=yield*$.detect(e);return yield*n.message(`\`${$.config}\` ${t?`found, updating`:`not found, creating`}...`),yield*t?$.update(e):$.create(e),!t}),{failure:`Failed to set up VS Code config.`,start:`Checking for \`${$.config}\`...`,success:e=>`\`${$.config}\` ${e?`created`:`updated`} with Adamantite preset.`})),n.includes(`zed`)&&(yield*r.withSpinner(n=>t.gen(function*(){let t=yield*K.detect(e);return yield*n.message(`\`${K.config}\` ${t?`found, updating`:`not found, creating`}...`),yield*t?K.update(e):K.create(e),!t}),{failure:`Failed to set up Zed config.`,start:`Checking for \`${K.config}\`...`,success:e=>`\`${K.config}\` ${e?`created`:`updated`} with Adamantite preset.`}))}),va=(e,n)=>t.gen(function*(){let r=yield*Q,i=e.includes(`zed`),a=e.includes(`vscode`);yield*r.withSpinner(e=>t.gen(function*(){return a&&(yield*e.message(`Installing VS Code extension...`),yield*$.extension(n)),!0}),{start:`Installing editor extensions...`,success:i&&!a?`Zed extensions require manual install.`:`Editor extensions installed successfully.`}).pipe(t.catchTags({FailedToInstallExtension:e=>t.gen(function*(){return yield*r.log.warning(`⚠️ ${e.message}`),yield*r.log.warning(`Please install it manually after setup completes.`),!1}),VscodeCliNotFound:()=>t.gen(function*(){return yield*r.log.error(`VSCode CLI ('code' command) not found.`),yield*r.log.info(`To install it:`),yield*r.log.info(` 1. Open VS Code`),yield*r.log.info(` 2. Press Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows/Linux)`),yield*r.log.info(` 3. Run 'Shell Command: Install "code" command in PATH'`),!1})})),i&&(yield*r.log.info("Install the Zed `oxc` extension: zed://extension/oxc"))}),ya=(e,n,r)=>t.gen(function*(){let i=yield*Q,a=pr.files[0].path;yield*i.withSpinner(i=>t.gen(function*(){let t=yield*pr.detect(e);return yield*i.message(t?`\`${a}\` found, updating...`:`Creating \`${a}\`...`),yield*t?pr.update(e,{packageManager:n,scripts:r}):pr.create(e,{packageManager:n,scripts:r}),!t}),{failure:`Failed to set up GitHub Actions workflow.`,start:`Setting up GitHub Actions workflow...`,success:e=>`GitHub Actions workflow ${e?`created`:`updated`} successfully.`})}).pipe(t.catchTag([`FailedToCreateDirectory`,`FailedToParseFile`,`FailedToReadFile`,`FailedToWriteFile`,`PlatformError`],e=>t.gen(function*(){let t=yield*Q;yield*t.log.warning(`Could not set up the GitHub Actions workflow. ${e.message}`),yield*t.log.warning("Fix the reported problem and run `adamantite init` again, or create the workflow manually.")}))),ba=(e,n,r,i)=>t.gen(function*(){let a=yield*Q,o=yield*a.withSpinner(()=>ua(e,{isMonorepo:i,packageManager:n,scripts:r}),{failure:`Failed to update AGENTS.md.`,start:`Updating AGENTS.md...`,success:`AGENTS.md check complete.`}).pipe(t.catchTag([`FailedToReadFile`,`FailedToWriteFile`],e=>t.gen(function*(){return yield*a.log.warning(`Could not update AGENTS.md. ${e.message} Adamantite will continue initialization.`),`failed`})));o!==`failed`&&o===`malformed`&&(yield*a.log.warning(`Could not update AGENTS.md because Adamantite markers are incomplete. Remove the stale ADAMANTITE marker and run adamantite init again.`))});var xa=c.make(`init`,ta).pipe(c.withDescription(`Initialize Adamantite in the current directory. Setup flags require --non-interactive; omitted boolean setup flags are disabled`),c.withExamples([{command:`adamantite init --non-interactive --script check`,description:`Configure linting without prompts`},{command:`adamantite init --non-interactive --script check --preset react --editor vscode --typescript --install-extensions --github-actions --agents`,description:`Configure a React project with VS Code, TypeScript, CI, and agent guidance`},{command:`adamantite init --non-interactive --script analyze`,description:`Configure analysis; in a detected monorepo it includes Sherif`}]),c.withHandler(e=>t.gen(function*(){let t=f.cwd(),n=yield*Q;yield*Wi,yield*n.intro(`💠 adamantite init`);let r=yield*(yield*Bn).detectPackageManager(t);if(!r)return yield*new Ie({});if(r.warnings?.length)for(let e of r.warnings)yield*n.log.warning(e);yield*n.log.info(`Detected package manager: ${r.name}`);let i=yield*B(t);if(i&&(yield*n.log.info(`We've detected a monorepo setup in your project.`)),!e.nonInteractive&&s.some([e.scripts.length>0,e.presets.length>0,e.editors.length>0,e.typescript,e.installExtensions,e.githubActions,e.agents,e.overwriteScripts],x.isTruthy))return yield*new A({reason:"Setup flags require `--non-interactive`."});let a=e.nonInteractive?e:yield*ra(t,i),o=yield*qi(a,{nonInteractive:e.nonInteractive,packageManager:r.name}),c=o.scripts,l=o.presets,u=o.editors,d=o.typescript,p=o.installExtensions,m=o.githubActions,h=o.agents,g=c.includes(`check`)||c.includes(`fix`),_=c.includes(`analyze`),v=_&&i,y=yield*R(t),b=x.isNotUndefined(y.devDependencies?.adamantite??y.dependencies?.adamantite)?[]:[`adamantite`];g&&b.push(`${W.name}@${W.version}`,`${hi.name}@${hi.version}`,`${U.name}@${U.version}`,...mi.filter(e=>l.includes(e.preset)).map(e=>`${e.name}@${e.version}`)),v&&b.push(`${H.name}@${H.version}`),_&&b.push(`${V.name}@${V.version}`),yield*da(t,b),g&&(yield*fa(t,U,U.create(t)),yield*fa(t,W,W.create(t,l))),_&&(yield*fa(t,V,V.create(t)));let S=yield*pa(t,c,{nonInteractive:e.nonInteractive,overwriteScripts:o.overwriteScripts});if(h&&(yield*ba(t,r.name,S,i)),d&&(yield*ma(t,i)),l.includes(`effect`)&&(yield*ha(t)),g&&(yield*ga(t)),yield*_a(t,u),p&&(yield*va(u,c)),m){let e=r.name;lt(e)?yield*ya(t,e,c):yield*n.log.warning(`Skipping GitHub Actions setup: \`${e}\` is not a supported package manager for CI workflow generation.`)}yield*n.log.success(`Your project is now configured`),yield*n.outro(`💠 Adamantite initialized successfully!`)}).pipe(t.catchTags({OperationCancelled:()=>t.gen(function*(){yield*(yield*Q).cancel(`You've cancelled the initialization process.`)})})))),Sa=c.make(`prepare`).pipe(c.withDescription("Patch Oxlint and TypeScript for the effect preset. The `prepare` script runs it after each install"),c.withHandler(()=>t.gen(function*(){let e=f.cwd();(yield*Y.detect(e))&&(yield*Y.patch(e,{quiet:!1}))})));const Ca=T.File(`file`,{mustExist:!0}).pipe(T.withDescription(`The JavaScript or TypeScript file to parse`)),wa=c.make(`ast`,{file:Ca}).pipe(c.withDescription(`Print the ESTree-shaped AST that Oxlint rules visit, to find node names for a custom rule`),c.withHandler(({file:e})=>t.gen(function*(){let t=yield*Q,n=yield*N(e),r=de(e,n,{preserveParens:!1});if(r.errors.length>0)return yield*new ke({errors:r.errors.map(e=>e.message),path:e});yield*t.message(JSON.stringify(r.program,null,2))}))),Ta=T.String(`name`).pipe(T.withDescription(`The rule name in kebab-case, such as no-process-env`)),Ea=u.String(`dir`).pipe(u.optional,u.withDescription(`The rules folder, relative to the current directory. Default: ${Fr}. Pass the folder that \`custom({ dir })\` loads, such as a rules folder in a monorepo tooling package`)),Da=c.make(`add`,{dir:Ea,name:Ta}).pipe(c.withDescription(`Write a custom rule stub, and the authoring guidance when the rules folder has none`),c.withHandler(({dir:e,name:n})=>t.gen(function*(){let t=yield*Q,r=yield*w.Path,i=f.cwd(),a=yield*Rr(i,n,d.getOrUndefined(e));yield*t.log.success(`Wrote ${r.relative(i,a.rulePath)}.`),a.agentsPath!==void 0&&(yield*t.log.success(`Wrote ${r.relative(i,a.agentsPath)}.`)),yield*t.log.info("`custom()` from `adamantite/lint/custom` loads the rule. Run `adamantite doctor` to check the wiring.")})));var Oa=c.make(`rule`).pipe(c.withDescription(`Write and inspect custom Oxlint rules`),c.withSubcommands([Da,wa]));const ka=[W,hi,U,H,V];function Aa(e,t){return s.filterMap(ka,n=>{if(t.has(n.name))return v.failVoid;let r=e.devDependencies?.[n.name]??e.dependencies?.[n.name];return r&&st(r)!==n.version?v.succeed({currentVersion:r,name:n.name,targetVersion:n.version}):v.failVoid})}var ja=c.make(`update`).pipe(c.withDescription(`Update Adamantite-managed dependencies and report setup findings`),c.withHandler(()=>t.gen(function*(){let e=f.cwd(),t=yield*Q;yield*t.intro(`💠 adamantite update`);let n=yield*R(e),r=yield*Ci(e,n),i=r.packageActions,a=new Set(i.map(e=>e.package)),o=[...i.map(e=>({currentVersion:e.type===`install_package`?`not installed`:e.currentVersion,name:e.package,targetVersion:e.targetVersion})),...Aa(n,a)];if(o.length>0){yield*t.log.info(`The following dependencies will be updated:`);for(let e of o)yield*t.log.info(` ${e.name}: ${e.currentVersion} → ${e.targetVersion}`);yield*t.withSpinner(()=>Hn(e,o.map(e=>`${e.name}@${e.targetVersion}`)),{failure:`Failed to update dependencies`,start:`Updating dependencies...`,success:`Dependencies updated successfully`}),yield*t.log.success(`Dependencies updated successfully.`),(yield*Y.detect(e))&&(yield*t.withSpinner(()=>Y.patch(e,{quiet:!0}),{failure:"Failed to patch Oxlint and TypeScript. Run `adamantite prepare` to see why.",start:`Patching Oxlint and TypeScript with @effect/tsgo...`,success:`Oxlint and TypeScript patched with @effect/tsgo.`}))}let s=o.length===0?r:yield*Ci(e);for(let e of s.warnings)yield*t.log.warning(e);let c=s.findings;return c.length>0&&(yield*Pi(c),yield*t.log.info("Run `adamantite doctor` to get the combined Markdown repair prompt.")),o.length===0&&c.length===0?(yield*t.log.success(`No changes needed.`),`no-changes`):`success`}).pipe(t.tapError(()=>t.gen(function*(){yield*(yield*Q).outro(`❌ Update failed`)})),t.tap(e=>t.gen(function*(){yield*(yield*Q).outro(e===`no-changes`?`✅ Adamantite is already up to date.`:`✅ Update completed successfully!`)})))));const Ma=[Tn,In,Hi],Na=[Tn,In,Ii,Hi,xa,Sa,Oa,ja],Pa=new Set(Na.map(e=>e.name)),Fa=new Set(Ma.map(e=>e.name)),Ia=c.make(`adamantite`).pipe(c.withDescription(`Opinionated preset package for modern TypeScript applications`),c.withSubcommands(Na)),La=e=>l.mergeAll(o.layer,Qn.layer.pipe(l.provide(o.layer)),Q.layer,ji.layer.pipe(l.provideMerge(e),l.provide(o.layer)),Bn.layer,Mi.layer),Ra=t.fn(`runCli`)(function*(e,n){let[r,i]=s.splitWhere(e,e=>e===`--`),a=s.drop(i,1),o=r.find(e=>Pa.has(e));if(a.length>0&&o!==void 0&&!Fa.has(o))return yield*new Re({command:o});yield*c.runWith(Ia,{version:n})(r).pipe(t.provideService(Ue,a))}),za=t.gen(function*(){let e=yield*(yield*a.Stdio).args;yield*Ra(e,`0.44.0`)}).pipe(t.as(0),t.catchTag(`CommandFailed`,e=>t.succeed(e.exitCode)),t.catch(e=>t.service(Q).pipe(t.flatMap(t=>t.log.error(e.message)),t.as(r.ExitCode(1)))),t.provide(La(j.layer)));e.runMain(za,{teardown:(e,t)=>{n.isSuccess(e)?t(Number(e.value)):i.defaultTeardown(e,t)}});export{};
135
+ `;return yield*F(t,`${r}${c}${s}`),`updated`}),da=(e,n)=>t.gen(function*(){yield*(yield*Q).withSpinner(()=>Hn(e,n),{failure:`Failed to install dependencies.`,start:`Installing dependencies...`,success:`Dependencies installed.`})});function fa(e,n,r){return t.gen(function*(){let i=yield*Q,a=yield*i.withSpinner(a=>t.gen(function*(){let t=yield*n.detect(e);for(let e of t.warnings)yield*i.log.warning(e);return t.active===null?(yield*a.message(`\`${n.config}\` not found, creating...`),yield*r,{created:!0,legacyConfig:null}):t.active.format===`ts`?(yield*a.message(`Found \`${n.config}\`, keeping existing config.`),{created:!1,legacyConfig:null}):(yield*a.message(`Found \`${t.active.file}\`, keeping existing config.`),{created:!1,legacyConfig:t.active.file})}),{failure:`Failed to set up ${n.name} config.`,start:`Setting up ${n.name} config...`,success:e=>e.created?`${n.name} config created successfully.`:`${n.name} config is ready.`});a.legacyConfig&&(yield*i.log.info(`Legacy \`${a.legacyConfig}\` was preserved during \`adamantite init\`. Run \`adamantite doctor\` and follow its findings to migrate it to the latest ${n.name} config.`))})}const pa=(e,n,r)=>t.gen(function*(){let i=yield*Q,a=yield*R(e),o=r.overwriteScripts?[]:dt(a,n),s=new Set(o.map(e=>e.script)),c=n.filter(e=>!s.has(e));yield*i.withSpinner(()=>t.gen(function*(){if(c.length!==0){a.scripts??={};for(let e of c)a.scripts[e]=z[e];yield*ct(e,a)}}),{failure:"Failed to add scripts to `package.json`.",start:"Adding scripts to your `package.json`...",success:o.length===0?"Scripts added to your `package.json`":c.length===0?"Kept your existing `package.json` scripts.":"Scripts added to your `package.json`; conflicting scripts were kept."});let l=r.nonInteractive?"Use `--overwrite-scripts` to replace it.":"Re-run `adamantite init` and confirm overwriting to replace it.";for(let e of o)yield*i.log.warning(`Kept existing \`${e.script}\` script (\`${e.command}\`) instead of \`${z[e.script]}\`. ${l}`);return o.length>0&&(yield*i.log.info("Adamantite commands forward extra arguments after `--`, so custom flags can be kept, e.g. `adamantite analyze -- --directory packages/app`.")),c}),ma=(e,n)=>t.gen(function*(){let r=yield*Q;n?yield*b(vi,t.forEach(e=>r.log.info(e))):yield*r.withSpinner(n=>t.gen(function*(){return(yield*Z.detect(e))?(yield*n.message(`\`${Z.config}\` found, updating...`),yield*Z.update(e),!1):(yield*n.message(`\`${Z.config}\` not found, creating...`),yield*Z.create(e),!0)}),{failure:`Failed to set up TypeScript config.`,start:`Setting up TypeScript config...`,success:e=>e?`\`${Z.config}\` created successfully`:`\`${Z.config}\` updated successfully`})}),ha=e=>t.gen(function*(){let t=yield*Q,n=yield*Y.update(e);n.prepare===`merged`&&(yield*t.log.info("Added `adamantite prepare` to the start of your existing `prepare` script.")),n.tsconfig===`monorepo`&&(yield*t.log.info("In a monorepo, add the Effect language service entry to each package's `tsconfig.json` or to a shared base config. Run `adamantite doctor` for the reference entry.")),n.tsconfig===`missing`&&(yield*t.log.warning("No `tsconfig.json` found, so the Effect language service is not configured. Run `adamantite doctor` for the reference entry."))}),ga=e=>t.gen(function*(){let n=yield*Q;(yield*Y.detect(e))&&(yield*n.withSpinner(()=>Y.patch(e,{quiet:!0}),{failure:`Failed to patch Oxlint and TypeScript.`,start:`Patching Oxlint and TypeScript with @effect/tsgo...`,success:`Oxlint and TypeScript patched with @effect/tsgo.`}).pipe(t.catchTag([`CommandFailed`,`CliNotFound`,`PlatformError`],()=>n.log.warning("Run `adamantite prepare` to see why the patch failed. Oxlint cannot load the `effect` preset until it succeeds."))))}),_a=(e,n)=>t.gen(function*(){let r=yield*Q;n.includes(`vscode`)&&(yield*r.withSpinner(n=>t.gen(function*(){let t=yield*$.detect(e);return yield*n.message(`\`${$.config}\` ${t?`found, updating`:`not found, creating`}...`),yield*t?$.update(e):$.create(e),!t}),{failure:`Failed to set up VS Code config.`,start:`Checking for \`${$.config}\`...`,success:e=>`\`${$.config}\` ${e?`created`:`updated`} with Adamantite preset.`})),n.includes(`zed`)&&(yield*r.withSpinner(n=>t.gen(function*(){let t=yield*K.detect(e);return yield*n.message(`\`${K.config}\` ${t?`found, updating`:`not found, creating`}...`),yield*t?K.update(e):K.create(e),!t}),{failure:`Failed to set up Zed config.`,start:`Checking for \`${K.config}\`...`,success:e=>`\`${K.config}\` ${e?`created`:`updated`} with Adamantite preset.`}))}),va=(e,n)=>t.gen(function*(){let r=yield*Q,i=e.includes(`zed`),a=e.includes(`vscode`);yield*r.withSpinner(e=>t.gen(function*(){return a&&(yield*e.message(`Installing VS Code extension...`),yield*$.extension(n)),!0}),{start:`Installing editor extensions...`,success:i&&!a?`Zed extensions require manual install.`:`Editor extensions installed successfully.`}).pipe(t.catchTags({FailedToInstallExtension:e=>t.gen(function*(){return yield*r.log.warning(`⚠️ ${e.message}`),yield*r.log.warning(`Please install it manually after setup completes.`),!1}),VscodeCliNotFound:()=>t.gen(function*(){return yield*r.log.error(`VSCode CLI ('code' command) not found.`),yield*r.log.info(`To install it:`),yield*r.log.info(` 1. Open VS Code`),yield*r.log.info(` 2. Press Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows/Linux)`),yield*r.log.info(` 3. Run 'Shell Command: Install "code" command in PATH'`),!1})})),i&&(yield*r.log.info("Install the Zed `oxc` extension: zed://extension/oxc"))}),ya=(e,n,r)=>t.gen(function*(){let i=yield*Q,a=pr.files[0].path;yield*i.withSpinner(i=>t.gen(function*(){let t=yield*pr.detect(e);return yield*i.message(t?`\`${a}\` found, updating...`:`Creating \`${a}\`...`),yield*t?pr.update(e,{packageManager:n,scripts:r}):pr.create(e,{packageManager:n,scripts:r}),!t}),{failure:`Failed to set up GitHub Actions workflow.`,start:`Setting up GitHub Actions workflow...`,success:e=>`GitHub Actions workflow ${e?`created`:`updated`} successfully.`})}).pipe(t.catchTag([`FailedToCreateDirectory`,`FailedToParseFile`,`FailedToReadFile`,`FailedToWriteFile`,`PlatformError`],e=>t.gen(function*(){let t=yield*Q;yield*t.log.warning(`Could not set up the GitHub Actions workflow. ${e.message}`),yield*t.log.warning("Fix the reported problem and run `adamantite init` again, or create the workflow manually.")}))),ba=(e,n,r,i)=>t.gen(function*(){let a=yield*Q,o=yield*a.withSpinner(()=>ua(e,{isMonorepo:i,packageManager:n,scripts:r}),{failure:`Failed to update AGENTS.md.`,start:`Updating AGENTS.md...`,success:`AGENTS.md check complete.`}).pipe(t.catchTag([`FailedToReadFile`,`FailedToWriteFile`],e=>t.gen(function*(){return yield*a.log.warning(`Could not update AGENTS.md. ${e.message} Adamantite will continue initialization.`),`failed`})));o!==`failed`&&o===`malformed`&&(yield*a.log.warning(`Could not update AGENTS.md because Adamantite markers are incomplete. Remove the stale ADAMANTITE marker and run adamantite init again.`))});var xa=c.make(`init`,ta).pipe(c.withDescription(`Initialize Adamantite in the current directory. Setup flags require --non-interactive; omitted boolean setup flags are disabled`),c.withExamples([{command:`adamantite init --non-interactive --script check`,description:`Configure linting without prompts`},{command:`adamantite init --non-interactive --script check --preset react --editor vscode --typescript --install-extensions --github-actions --agents`,description:`Configure a React project with VS Code, TypeScript, CI, and agent guidance`},{command:`adamantite init --non-interactive --script analyze`,description:`Configure analysis; in a detected monorepo it includes Sherif`}]),c.withHandler(e=>t.gen(function*(){let t=f.cwd(),n=yield*Q;yield*Wi,yield*n.intro(`💠 adamantite init`);let r=yield*(yield*Bn).detectPackageManager(t);if(!r)return yield*new Ie({});if(r.warnings?.length)for(let e of r.warnings)yield*n.log.warning(e);yield*n.log.info(`Detected package manager: ${r.name}`);let i=yield*B(t);if(i&&(yield*n.log.info(`We've detected a monorepo setup in your project.`)),!e.nonInteractive&&s.some([e.scripts.length>0,e.presets.length>0,e.editors.length>0,e.typescript,e.installExtensions,e.githubActions,e.agents,e.overwriteScripts],x.isTruthy))return yield*new A({reason:"Setup flags require `--non-interactive`."});let a=e.nonInteractive?e:yield*ra(t,i),o=yield*qi(a,{nonInteractive:e.nonInteractive,packageManager:r.name}),c=o.scripts,l=o.presets,u=o.editors,d=o.typescript,p=o.installExtensions,m=o.githubActions,h=o.agents,g=c.includes(`check`)||c.includes(`fix`),_=c.includes(`analyze`),v=_&&i,y=yield*R(t),b=x.isNotUndefined(y.devDependencies?.adamantite??y.dependencies?.adamantite)?[]:[`adamantite`];g&&b.push(`${W.name}@${W.version}`,`${hi.name}@${hi.version}`,`${U.name}@${U.version}`,...mi.filter(e=>l.includes(e.preset)).map(e=>`${e.name}@${e.version}`)),v&&b.push(`${H.name}@${H.version}`),_&&b.push(`${V.name}@${V.version}`),yield*da(t,b),g&&(yield*fa(t,U,U.create(t)),yield*fa(t,W,W.create(t,l))),_&&(yield*fa(t,V,V.create(t)));let S=yield*pa(t,c,{nonInteractive:e.nonInteractive,overwriteScripts:o.overwriteScripts});if(h&&(yield*ba(t,r.name,S,i)),d&&(yield*ma(t,i)),l.includes(`effect`)&&(yield*ha(t)),g&&(yield*ga(t)),yield*_a(t,u),p&&(yield*va(u,c)),m){let e=r.name;lt(e)?yield*ya(t,e,c):yield*n.log.warning(`Skipping GitHub Actions setup: \`${e}\` is not a supported package manager for CI workflow generation.`)}yield*n.log.success(`Your project is now configured`),yield*n.outro(`💠 Adamantite initialized successfully!`)}).pipe(t.catchTags({OperationCancelled:()=>t.gen(function*(){yield*(yield*Q).cancel(`You've cancelled the initialization process.`)})})))),Sa=c.make(`prepare`).pipe(c.withDescription("Patch Oxlint and TypeScript for the effect preset. The `prepare` script runs it after each install"),c.withHandler(()=>t.gen(function*(){let e=f.cwd();(yield*Y.detect(e))&&(yield*Y.patch(e,{quiet:!1}))})));const Ca=T.File(`file`,{mustExist:!0}).pipe(T.withDescription(`The JavaScript or TypeScript file to parse`)),wa=c.make(`ast`,{file:Ca}).pipe(c.withDescription(`Print the ESTree-shaped AST that Oxlint rules visit, to find node names for a custom rule`),c.withHandler(({file:e})=>t.gen(function*(){let t=yield*Q,n=yield*N(e),r=de(e,n,{preserveParens:!1});if(r.errors.length>0)return yield*new ke({errors:r.errors.map(e=>e.message),path:e});yield*t.message(JSON.stringify(r.program,null,2))}))),Ta=T.String(`name`).pipe(T.withDescription(`The rule name in kebab-case, such as no-process-env`)),Ea=u.String(`dir`).pipe(u.optional,u.withDescription(`The rules folder, relative to the current directory. Default: ${Fr}. Pass the folder that \`custom({ dir })\` loads, such as a rules folder in a monorepo tooling package`)),Da=c.make(`add`,{dir:Ea,name:Ta}).pipe(c.withDescription(`Write a custom rule stub, and the authoring guidance when the rules folder has none`),c.withHandler(({dir:e,name:n})=>t.gen(function*(){let t=yield*Q,r=yield*w.Path,i=f.cwd(),a=yield*Rr(i,n,d.getOrUndefined(e));yield*t.log.success(`Wrote ${r.relative(i,a.rulePath)}.`),a.agentsPath!==void 0&&(yield*t.log.success(`Wrote ${r.relative(i,a.agentsPath)}.`)),yield*t.log.info("`custom()` from `adamantite/lint/custom` loads the rule. Run `adamantite doctor` to check the wiring.")})));var Oa=c.make(`rule`).pipe(c.withDescription(`Write and inspect custom Oxlint rules`),c.withSubcommands([Da,wa]));const ka=[W,hi,U,H,V];function Aa(e,t){return s.filterMap(ka,n=>{if(t.has(n.name))return v.failVoid;let r=e.devDependencies?.[n.name]??e.dependencies?.[n.name];return r&&st(r)!==n.version?v.succeed({currentVersion:r,name:n.name,targetVersion:n.version}):v.failVoid})}var ja=c.make(`update`).pipe(c.withDescription(`Update Adamantite-managed dependencies and report setup findings`),c.withHandler(()=>t.gen(function*(){let e=f.cwd(),t=yield*Q;yield*t.intro(`💠 adamantite update`);let n=yield*R(e),r=yield*Ci(e,n),i=r.packageActions,a=new Set(i.map(e=>e.package)),o=[...i.map(e=>({currentVersion:e.type===`install_package`?`not installed`:e.currentVersion,name:e.package,targetVersion:e.targetVersion})),...Aa(n,a)];if(o.length>0){yield*t.log.info(`The following dependencies will be updated:`);for(let e of o)yield*t.log.info(` ${e.name}: ${e.currentVersion} → ${e.targetVersion}`);yield*t.withSpinner(()=>Hn(e,o.map(e=>`${e.name}@${e.targetVersion}`)),{failure:`Failed to update dependencies`,start:`Updating dependencies...`,success:`Dependencies updated successfully`}),yield*t.log.success(`Dependencies updated successfully.`),(yield*Y.detect(e))&&(yield*t.withSpinner(()=>Y.patch(e,{quiet:!0}),{failure:"Failed to patch Oxlint and TypeScript. Run `adamantite prepare` to see why.",start:`Patching Oxlint and TypeScript with @effect/tsgo...`,success:`Oxlint and TypeScript patched with @effect/tsgo.`}))}let s=o.length===0?r:yield*Ci(e);for(let e of s.warnings)yield*t.log.warning(e);let c=s.findings;return c.length>0&&(yield*Pi(c),yield*t.log.info("Run `adamantite doctor` to get the combined Markdown repair prompt.")),o.length===0&&c.length===0?(yield*t.log.success(`No changes needed.`),`no-changes`):`success`}).pipe(t.tapError(()=>t.gen(function*(){yield*(yield*Q).outro(`❌ Update failed`)})),t.tap(e=>t.gen(function*(){yield*(yield*Q).outro(e===`no-changes`?`✅ Adamantite is already up to date.`:`✅ Update completed successfully!`)})))));const Ma=[Tn,In,Hi],Na=[Tn,In,Ii,Hi,xa,Sa,Oa,ja],Pa=new Set(Na.map(e=>e.name)),Fa=new Set(Ma.map(e=>e.name)),Ia=c.make(`adamantite`).pipe(c.withDescription(`Opinionated preset package for modern TypeScript applications`),c.withSubcommands(Na)),La=e=>l.mergeAll(o.layer,Qn.layer.pipe(l.provide(o.layer)),Q.layer,ji.layer.pipe(l.provideMerge(e),l.provide(o.layer)),Bn.layer,Mi.layer),Ra=t.fn(`runCli`)(function*(e,n){let[r,i]=s.splitWhere(e,e=>e===`--`),a=s.drop(i,1),o=r.find(e=>Pa.has(e));if(a.length>0&&o!==void 0&&!Fa.has(o))return yield*new Re({command:o});yield*c.runWith(Ia,{version:n})(r).pipe(t.provideService(Ue,a))}),za=t.gen(function*(){let e=yield*(yield*a.Stdio).args;yield*Ra(e,`0.45.0`)}).pipe(t.as(0),t.catchTag(`CommandFailed`,e=>t.succeed(e.exitCode)),t.catch(e=>t.service(Q).pipe(t.flatMap(t=>t.log.error(e.message)),t.as(r.ExitCode(1)))),t.provide(La(j.layer)));e.runMain(za,{teardown:(e,t)=>{n.isSuccess(e)?t(Number(e.value)):i.defaultTeardown(e,t)}});export{};
@@ -1,5 +1,18 @@
1
1
  import { OxlintConfig } from "oxlint";
2
2
  //#region presets/lint/jest.d.ts
3
+ /**
4
+ * The files that Jest runs as tests by default. The preset applies its rules only to these files.
5
+ * To change the files or a rule, use the preset in an override of your own instead of `extends`:
6
+ *
7
+ * ```ts
8
+ * import jest, { testFiles } from "adamantite/lint/jest"
9
+ *
10
+ * export default defineConfig({
11
+ * overrides: [{ files: testFiles, plugins: jest.plugins, rules: { ...jest.rules } }],
12
+ * })
13
+ * ```
14
+ */
15
+ export declare const testFiles: string[];
3
16
  declare const config: OxlintConfig;
4
17
  //#endregion
5
18
  export { config as default };
@@ -1,71 +1,91 @@
1
1
  //#region presets/lint/jest.ts
2
- const config = {
3
- plugins: ["jest"],
4
- rules: {
5
- "jest/consistent-test-it": "error",
6
- "jest/expect-expect": "error",
7
- "jest/max-expects": "error",
8
- "jest/max-nested-describe": "error",
9
- "jest/no-alias-methods": "error",
10
- "jest/no-commented-out-tests": "error",
11
- "jest/no-conditional-expect": "error",
12
- "jest/no-conditional-in-test": "error",
13
- "jest/no-confusing-set-timeout": "error",
14
- "jest/no-deprecated-functions": "error",
15
- "jest/no-disabled-tests": "error",
16
- "jest/no-done-callback": "error",
17
- "jest/no-duplicate-hooks": "error",
18
- "jest/no-export": "error",
19
- "jest/no-focused-tests": "error",
20
- "jest/no-hooks": "off",
21
- "jest/no-identical-title": "error",
22
- "jest/no-interpolation-in-snapshots": "error",
23
- "jest/no-jasmine-globals": "error",
24
- "jest/no-large-snapshots": "error",
25
- "jest/no-mocks-import": "error",
26
- "jest/no-restricted-jest-methods": "error",
27
- "jest/no-restricted-matchers": "error",
28
- "jest/no-standalone-expect": "error",
29
- "jest/no-test-prefixes": "error",
30
- "jest/no-test-return-statement": "error",
31
- "jest/no-unneeded-async-expect-function": "error",
32
- "jest/no-untyped-mock-factory": "error",
33
- "jest/padding-around-after-all-blocks": "error",
34
- "jest/padding-around-test-blocks": "error",
35
- "jest/prefer-called-with": "error",
36
- "jest/prefer-comparison-matcher": "error",
37
- "jest/prefer-each": "error",
38
- "jest/prefer-ending-with-an-expect": "error",
39
- "jest/prefer-equality-matcher": "error",
40
- "jest/prefer-expect-assertions": ["error", {
41
- onlyFunctionsWithAsyncKeyword: true,
42
- onlyFunctionsWithExpectInCallback: true,
43
- onlyFunctionsWithExpectInLoop: true
44
- }],
45
- "jest/prefer-expect-resolves": "error",
46
- "jest/prefer-hooks-in-order": "error",
47
- "jest/prefer-hooks-on-top": "error",
48
- "jest/prefer-jest-mocked": "error",
49
- "jest/prefer-lowercase-title": "error",
50
- "jest/prefer-mock-promise-shorthand": "error",
51
- "jest/prefer-mock-return-shorthand": "error",
52
- "jest/prefer-snapshot-hint": "error",
53
- "jest/prefer-spy-on": "error",
54
- "jest/prefer-strict-equal": "error",
55
- "jest/prefer-to-be": "error",
56
- "jest/prefer-to-contain": "error",
57
- "jest/prefer-to-have-been-called": "error",
58
- "jest/prefer-to-have-been-called-times": "error",
59
- "jest/prefer-to-have-length": "error",
60
- "jest/prefer-todo": "error",
61
- "jest/require-hook": "error",
62
- "jest/require-to-throw-message": "error",
63
- "jest/require-top-level-describe": "error",
64
- "jest/valid-describe-callback": "error",
65
- "jest/valid-expect": "error",
66
- "jest/valid-expect-in-promise": "error",
67
- "jest/valid-title": "error"
68
- }
2
+ /**
3
+ * The files that Jest runs as tests by default. The preset applies its rules only to these files.
4
+ * To change the files or a rule, use the preset in an override of your own instead of `extends`:
5
+ *
6
+ * ```ts
7
+ * import jest, { testFiles } from "adamantite/lint/jest"
8
+ *
9
+ * export default defineConfig({
10
+ * overrides: [{ files: testFiles, plugins: jest.plugins, rules: { ...jest.rules } }],
11
+ * })
12
+ * ```
13
+ */
14
+ const testFiles = ["**/*.{test,spec}.{js,jsx,mjs,cjs,ts,tsx,mts,cts}", "**/__tests__/**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"];
15
+ const plugins = ["jest"];
16
+ const rules = {
17
+ "jest/consistent-test-it": "error",
18
+ "jest/expect-expect": "error",
19
+ "jest/max-expects": "error",
20
+ "jest/max-nested-describe": "error",
21
+ "jest/no-alias-methods": "error",
22
+ "jest/no-commented-out-tests": "error",
23
+ "jest/no-conditional-expect": "error",
24
+ "jest/no-conditional-in-test": "error",
25
+ "jest/no-confusing-set-timeout": "error",
26
+ "jest/no-deprecated-functions": "error",
27
+ "jest/no-disabled-tests": "error",
28
+ "jest/no-done-callback": "error",
29
+ "jest/no-duplicate-hooks": "error",
30
+ "jest/no-export": "error",
31
+ "jest/no-focused-tests": "error",
32
+ "jest/no-hooks": "off",
33
+ "jest/no-identical-title": "error",
34
+ "jest/no-interpolation-in-snapshots": "error",
35
+ "jest/no-jasmine-globals": "error",
36
+ "jest/no-large-snapshots": "error",
37
+ "jest/no-mocks-import": "error",
38
+ "jest/no-restricted-jest-methods": "error",
39
+ "jest/no-restricted-matchers": "error",
40
+ "jest/no-standalone-expect": "error",
41
+ "jest/no-test-prefixes": "error",
42
+ "jest/no-test-return-statement": "error",
43
+ "jest/no-unneeded-async-expect-function": "error",
44
+ "jest/no-untyped-mock-factory": "error",
45
+ "jest/padding-around-after-all-blocks": "error",
46
+ "jest/padding-around-test-blocks": "error",
47
+ "jest/prefer-called-with": "error",
48
+ "jest/prefer-comparison-matcher": "error",
49
+ "jest/prefer-each": "error",
50
+ "jest/prefer-ending-with-an-expect": "error",
51
+ "jest/prefer-equality-matcher": "error",
52
+ "jest/prefer-expect-assertions": ["error", {
53
+ onlyFunctionsWithAsyncKeyword: true,
54
+ onlyFunctionsWithExpectInCallback: true,
55
+ onlyFunctionsWithExpectInLoop: true
56
+ }],
57
+ "jest/prefer-expect-resolves": "error",
58
+ "jest/prefer-hooks-in-order": "error",
59
+ "jest/prefer-hooks-on-top": "error",
60
+ "jest/prefer-jest-mocked": "error",
61
+ "jest/prefer-lowercase-title": "error",
62
+ "jest/prefer-mock-promise-shorthand": "error",
63
+ "jest/prefer-mock-return-shorthand": "error",
64
+ "jest/prefer-snapshot-hint": "error",
65
+ "jest/prefer-spy-on": "error",
66
+ "jest/prefer-strict-equal": "error",
67
+ "jest/prefer-to-be": "error",
68
+ "jest/prefer-to-contain": "error",
69
+ "jest/prefer-to-have-been-called": "error",
70
+ "jest/prefer-to-have-been-called-times": "error",
71
+ "jest/prefer-to-have-length": "error",
72
+ "jest/prefer-todo": "error",
73
+ "jest/require-hook": "error",
74
+ "jest/require-to-throw-message": "error",
75
+ "jest/require-top-level-describe": "error",
76
+ "jest/valid-describe-callback": "error",
77
+ "jest/valid-expect": "error",
78
+ "jest/valid-expect-in-promise": "error",
79
+ "jest/valid-title": "error"
69
80
  };
81
+ const config = { overrides: [{
82
+ files: testFiles,
83
+ plugins,
84
+ rules
85
+ }] };
86
+ Object.defineProperties(config, {
87
+ plugins: { value: plugins },
88
+ rules: { value: rules }
89
+ });
70
90
  //#endregion
71
- export { config as default };
91
+ export { config as default, testFiles };
@@ -1,5 +1,18 @@
1
1
  import { OxlintConfig } from "oxlint";
2
2
  //#region presets/lint/vitest.d.ts
3
+ /**
4
+ * The files that Vitest runs as tests by default. The preset applies its rules only to these files.
5
+ * To change the files or a rule, use the preset in an override of your own instead of `extends`:
6
+ *
7
+ * ```ts
8
+ * import vitest, { testFiles } from "adamantite/lint/vitest"
9
+ *
10
+ * export default defineConfig({
11
+ * overrides: [{ files: testFiles, plugins: vitest.plugins, rules: { ...vitest.rules } }],
12
+ * })
13
+ * ```
14
+ */
15
+ export declare const testFiles: string[];
3
16
  declare const config: OxlintConfig;
4
17
  //#endregion
5
18
  export { config as default };
@@ -1,94 +1,114 @@
1
1
  //#region presets/lint/vitest.ts
2
- const config = {
3
- plugins: ["vitest"],
4
- rules: {
5
- "vitest/consistent-each-for": "error",
6
- "vitest/consistent-test-it": "error",
7
- "vitest/hoisted-apis-on-top": "error",
8
- "vitest/max-expects": "error",
9
- "vitest/max-nested-describe": "error",
10
- "vitest/no-alias-methods": "error",
11
- "vitest/no-commented-out-tests": "error",
12
- "vitest/no-conditional-expect": "error",
13
- "vitest/no-conditional-in-test": "error",
14
- "vitest/no-conditional-tests": "error",
15
- "vitest/no-disabled-tests": "error",
16
- "vitest/no-duplicate-hooks": "error",
17
- "vitest/no-focused-tests": "error",
18
- "vitest/no-hooks": "off",
19
- "vitest/no-identical-title": "error",
20
- "vitest/no-import-node-test": "error",
21
- "vitest/no-importing-vitest-globals": "off",
22
- "vitest/no-interpolation-in-snapshots": "error",
23
- "vitest/no-large-snapshots": "error",
24
- "vitest/no-mocks-import": "error",
25
- "vitest/no-restricted-matchers": "error",
26
- "vitest/no-standalone-expect": ["error", { additionalTestBlockFunctions: [
27
- "it.effect",
28
- "it.effect.each",
29
- "it.effect.fails",
30
- "it.effect.only",
31
- "it.effect.prop",
32
- "it.effect.runIf",
33
- "it.effect.skip",
34
- "it.effect.skipIf",
35
- "it.live",
36
- "it.live.each",
37
- "it.live.fails",
38
- "it.live.only",
39
- "it.live.prop",
40
- "it.live.runIf",
41
- "it.live.skip",
42
- "it.live.skipIf",
43
- "it.prop"
44
- ] }],
45
- "vitest/no-test-prefixes": "error",
46
- "vitest/no-test-return-statement": "error",
47
- "vitest/no-unneeded-async-expect-function": "error",
48
- "vitest/padding-around-test-blocks": "error",
49
- "vitest/prefer-called-exactly-once-with": "error",
50
- "vitest/prefer-called-once": "error",
51
- "vitest/prefer-called-times": "off",
52
- "vitest/prefer-called-with": "error",
53
- "vitest/prefer-comparison-matcher": "error",
54
- "vitest/prefer-describe-function-title": "off",
55
- "vitest/prefer-each": "error",
56
- "vitest/prefer-equality-matcher": "error",
57
- "vitest/prefer-expect-assertions": ["error", {
58
- onlyFunctionsWithAsyncKeyword: true,
59
- onlyFunctionsWithExpectInCallback: true,
60
- onlyFunctionsWithExpectInLoop: true
61
- }],
62
- "vitest/prefer-expect-resolves": "error",
63
- "vitest/prefer-expect-type-of": "error",
64
- "vitest/prefer-hooks-in-order": "error",
65
- "vitest/prefer-hooks-on-top": "error",
66
- "vitest/prefer-import-in-mock": "error",
67
- "vitest/prefer-importing-vitest-globals": "error",
68
- "vitest/prefer-lowercase-title": "error",
69
- "vitest/prefer-mock-promise-shorthand": "error",
70
- "vitest/prefer-mock-return-shorthand": "error",
71
- "vitest/prefer-snapshot-hint": "error",
72
- "vitest/prefer-spy-on": "error",
73
- "vitest/prefer-strict-boolean-matchers": "error",
74
- "vitest/prefer-strict-equal": "error",
75
- "vitest/prefer-to-be": "error",
76
- "vitest/prefer-to-be-object": "error",
77
- "vitest/prefer-to-contain": "error",
78
- "vitest/prefer-to-have-been-called-times": "error",
79
- "vitest/prefer-to-have-length": "error",
80
- "vitest/prefer-todo": "error",
81
- "vitest/require-awaited-expect-poll": "error",
82
- "vitest/require-hook": "error",
83
- "vitest/require-local-test-context-for-concurrent-snapshots": "error",
84
- "vitest/require-mock-type-parameters": "error",
85
- "vitest/require-to-throw-message": "error",
86
- "vitest/require-top-level-describe": "error",
87
- "vitest/valid-describe-callback": "error",
88
- "vitest/valid-expect": "error",
89
- "vitest/valid-expect-in-promise": "error",
90
- "vitest/valid-title": "error"
91
- }
2
+ /**
3
+ * The files that Vitest runs as tests by default. The preset applies its rules only to these files.
4
+ * To change the files or a rule, use the preset in an override of your own instead of `extends`:
5
+ *
6
+ * ```ts
7
+ * import vitest, { testFiles } from "adamantite/lint/vitest"
8
+ *
9
+ * export default defineConfig({
10
+ * overrides: [{ files: testFiles, plugins: vitest.plugins, rules: { ...vitest.rules } }],
11
+ * })
12
+ * ```
13
+ */
14
+ const testFiles = ["**/*.{test,spec}.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"];
15
+ const plugins = ["vitest"];
16
+ const rules = {
17
+ "vitest/consistent-each-for": "error",
18
+ "vitest/consistent-test-it": "error",
19
+ "vitest/hoisted-apis-on-top": "error",
20
+ "vitest/max-expects": "error",
21
+ "vitest/max-nested-describe": "error",
22
+ "vitest/no-alias-methods": "error",
23
+ "vitest/no-commented-out-tests": "error",
24
+ "vitest/no-conditional-expect": "error",
25
+ "vitest/no-conditional-in-test": "error",
26
+ "vitest/no-conditional-tests": "error",
27
+ "vitest/no-disabled-tests": "error",
28
+ "vitest/no-duplicate-hooks": "error",
29
+ "vitest/no-focused-tests": "error",
30
+ "vitest/no-hooks": "off",
31
+ "vitest/no-identical-title": "error",
32
+ "vitest/no-import-node-test": "error",
33
+ "vitest/no-importing-vitest-globals": "off",
34
+ "vitest/no-interpolation-in-snapshots": "error",
35
+ "vitest/no-large-snapshots": "error",
36
+ "vitest/no-mocks-import": "error",
37
+ "vitest/no-restricted-matchers": "error",
38
+ "vitest/no-standalone-expect": ["error", { additionalTestBlockFunctions: [
39
+ "it.effect",
40
+ "it.effect.each",
41
+ "it.effect.fails",
42
+ "it.effect.only",
43
+ "it.effect.prop",
44
+ "it.effect.runIf",
45
+ "it.effect.skip",
46
+ "it.effect.skipIf",
47
+ "it.live",
48
+ "it.live.each",
49
+ "it.live.fails",
50
+ "it.live.only",
51
+ "it.live.prop",
52
+ "it.live.runIf",
53
+ "it.live.skip",
54
+ "it.live.skipIf",
55
+ "it.prop"
56
+ ] }],
57
+ "vitest/no-test-prefixes": "error",
58
+ "vitest/no-test-return-statement": "error",
59
+ "vitest/no-unneeded-async-expect-function": "error",
60
+ "vitest/padding-around-test-blocks": "error",
61
+ "vitest/prefer-called-exactly-once-with": "error",
62
+ "vitest/prefer-called-once": "error",
63
+ "vitest/prefer-called-times": "off",
64
+ "vitest/prefer-called-with": "error",
65
+ "vitest/prefer-comparison-matcher": "error",
66
+ "vitest/prefer-describe-function-title": "off",
67
+ "vitest/prefer-each": "error",
68
+ "vitest/prefer-equality-matcher": "error",
69
+ "vitest/prefer-expect-assertions": ["error", {
70
+ onlyFunctionsWithAsyncKeyword: true,
71
+ onlyFunctionsWithExpectInCallback: true,
72
+ onlyFunctionsWithExpectInLoop: true
73
+ }],
74
+ "vitest/prefer-expect-resolves": "error",
75
+ "vitest/prefer-expect-type-of": "error",
76
+ "vitest/prefer-hooks-in-order": "error",
77
+ "vitest/prefer-hooks-on-top": "error",
78
+ "vitest/prefer-import-in-mock": "error",
79
+ "vitest/prefer-importing-vitest-globals": "error",
80
+ "vitest/prefer-lowercase-title": "error",
81
+ "vitest/prefer-mock-promise-shorthand": "error",
82
+ "vitest/prefer-mock-return-shorthand": "error",
83
+ "vitest/prefer-snapshot-hint": "error",
84
+ "vitest/prefer-spy-on": "error",
85
+ "vitest/prefer-strict-boolean-matchers": "error",
86
+ "vitest/prefer-strict-equal": "error",
87
+ "vitest/prefer-to-be": "error",
88
+ "vitest/prefer-to-be-object": "error",
89
+ "vitest/prefer-to-contain": "error",
90
+ "vitest/prefer-to-have-been-called-times": "error",
91
+ "vitest/prefer-to-have-length": "error",
92
+ "vitest/prefer-todo": "error",
93
+ "vitest/require-awaited-expect-poll": "error",
94
+ "vitest/require-hook": "error",
95
+ "vitest/require-local-test-context-for-concurrent-snapshots": "error",
96
+ "vitest/require-mock-type-parameters": "error",
97
+ "vitest/require-to-throw-message": "error",
98
+ "vitest/require-top-level-describe": "error",
99
+ "vitest/valid-describe-callback": "error",
100
+ "vitest/valid-expect": "error",
101
+ "vitest/valid-expect-in-promise": "error",
102
+ "vitest/valid-title": "error"
92
103
  };
104
+ const config = { overrides: [{
105
+ files: testFiles,
106
+ plugins,
107
+ rules
108
+ }] };
109
+ Object.defineProperties(config, {
110
+ plugins: { value: plugins },
111
+ rules: { value: rules }
112
+ });
93
113
  //#endregion
94
- export { config as default };
114
+ export { config as default, testFiles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adamantite",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "description": "An strict and opinionated set of presets for modern TypeScript applications",
5
5
  "keywords": [
6
6
  "adamantite",
@@ -58,6 +58,13 @@ preload it in the route loader with `ensureQueryData()`, and pass the same optio
58
58
  value from the query result and keep only the user's edits in state. Do not wrap options in a
59
59
  spread object, or move the hook out of the route file, only to silence `query-from-loader`.
60
60
 
61
+ The `jest` and `vitest` presets apply their rules only to the files that the test runner runs as
62
+ tests by default, which each preset exports as `testFiles`. When the project keeps tests in other
63
+ files, replace the preset in `extends` with an override of the project's own:
64
+ `{ files: [...], plugins: vitest.plugins, rules: { ...vitest.rules } }`. Change a test rule in such
65
+ an override, not in the top-level `rules`, which do not reach test files. Keep `plugins` in the
66
+ override, because Oxlint ignores its `vitest/*` rules without it.
67
+
61
68
  The `react-doctor` preset requires the `react` preset and the `oxlint-plugin-react-doctor`
62
69
  package, which `init` installs at a pinned version and doctor and `update` keep in sync. It
63
70
  enables curated React Doctor rules for React state and effect misuse, such as