Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@max-health-inc/config

Shared configuration presets for Max Health repositories.
Aligned with latest Vite template (ES2023, erasableSyntaxOnly, flat ESLint config with recommended presets).

Install

Published to GitHub Packages. Add the registry mapping once per consuming repo (.npmrc):

@max-health-inc:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
npm install --save-dev @max-health-inc/config

NODE_AUTH_TOKEN needs read:packages. In CI it is the GH_PACKAGES_TOKEN secret; locally, gh auth token works.

What each preset needs you to install

Only the four packages both ESLint presets import are declared as peerDependencies, so npm installs those and nothing else. The rest are listed here instead of being declared, because peerDependencies apply to the whole package while these are needed by ONE subpath — npm cannot scope a peer to ./vite or ./eslint/react, so declaring them made every consumer install them. A Cloudflare Worker using only eslint/node was pulling in Vite, the React SWC plugin, both React ESLint plugins and Tailwind's Vite plugin (~77 dev packages, @babel tree included) for nothing.

Preset Install alongside it
eslint/node, eslint/react, eslint/rules eslint, @eslint/js, globals, typescript-eslintdeclared as peers, installed for you
eslint/react (additionally) eslint-plugin-react-hooks (>=5), eslint-plugin-react-refresh (>=0.4)
vite vite (>=6), @vitejs/plugin-react-swc (>=4)
vitest vitest (>=2)
tsconfig/* nothing (tsconfig/worker.json wants @cloudflare/workers-types)

A React app on Vite already has every one of these in its own devDependencies, so this is a no-op there. If one is missing the preset fails at import with a plain module-not-found naming the package.

@tailwindcss/vite is not in the table because nothing here imports it: pass it in yourself via createViteConfig({ plugins: [tailwindcss()] }) as shown below. It was declared as a peer purely by mistake.

Usage

TypeScript

tsconfig.app.json:

{
  "extends": "@max-health-inc/config/tsconfig/app.json",
  "compilerOptions": {
    "paths": { "@/*": ["./src/*"] }
  },
  "include": ["src"]
}

Note: types: ["vite/client"] is included — no vite-env.d.ts file needed!

tsconfig.node.json:

{
  "extends": "@max-health-inc/config/tsconfig/node.json",
  "include": ["vite.config.ts"]
}

tsconfig.json (Cloudflare Worker):

{
  "extends": "@max-health-inc/config/tsconfig/worker.json",
  "include": ["src/**/*.ts", "tests/**/*.ts"]
}

Uses @cloudflare/workers-types (no DOM libs). Add @cloudflare/workers-types to the worker's devDependencies.

tsconfig.json (root):

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

ESLint

eslint.config.js (React):

import { createReactConfig } from '@max-health-inc/config/eslint/react'
import { fileURLToPath } from 'url'
import path from 'path'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

export default createReactConfig({ tsconfigRootDir: __dirname })

eslint.config.js (Node.js):

import { createNodeConfig } from '@max-health-inc/config/eslint/node'
import { fileURLToPath } from 'url'
import path from 'path'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

export default createNodeConfig({ tsconfigRootDir: __dirname })

With security rules (backend/workers):

export default createNodeConfig({
  tsconfigRootDir: __dirname,
  security: true,
})

Without type-checked rules (faster linting):

export default createNodeConfig({
  tsconfigRootDir: __dirname,
  typeChecked: false,
})

Vite

vite.config.ts:

import { createViteConfig } from '@max-health-inc/config/vite'
import tailwindcss from '@tailwindcss/vite'

export default createViteConfig({
  port: 5175,
  plugins: [tailwindcss()],
})

Vitest

vitest.config.ts — a leaf package needs only its test globs:

import { createVitestConfig } from '@max-health-inc/config/vitest'

export default createVitestConfig()

A repo root that publishes coverage and CI reports opts into both:

import { createVitestConfig } from '@max-health-inc/config/vitest'

export default createVitestConfig({
  include: ['src/test/**/*.test.{ts,js}'],
  exclude: ['**/pipelineParity.test.ts'],
  setupFiles: ['src/test/setup.ts'],
  fileParallelism: false,       // sequential — avoids cache/file races on Windows
  coverage: true,               // v8 → test/coverage
  reports: true,                // html + junit + default → test/
})

coverage and reports are opt-in: a leaf package running vitest run should not have to name an html/junit destination, and repos that do publish them get one layout instead of re-deriving it. Pass an object to either for overrides, and test as a last-resort escape hatch for keys the preset does not model.

What's included

Config Key settings
tsconfig/app.json ES2023, bundler resolution, strict, react-jsx, verbatimModuleSyntax, erasableSyntaxOnly, noUncheckedIndexedAccess, types: ["vite/client"]
tsconfig/node.json ES2023, bundler resolution, strict, erasableSyntaxOnly, types: ["node"]
tsconfig/worker.json ES2023, bundler resolution, strict, erasableSyntaxOnly, noUncheckedIndexedAccess, types: ["@cloudflare/workers-types"] (no DOM)
eslint/react typescript-eslint recommended + reactHooks + reactRefresh + type-checked rules + consistent-type-imports
eslint/node typescript-eslint recommended + type-checked rules + consistent-type-imports
vite react-swc, @ alias, VITE_PROXY_BASE/VITE_BASE env support
vitest src/**/*.test.ts discovery, build-artifact excludes, opt-in v8 coverage and html/junit reports
doccheck CLI: badge/link rot, doc examples that must compile, API-docs coverage

doccheck

Docs rot without ever failing a build. doccheck makes three kinds of rot fail one. It ships as a bin, has no runtime dependencies, and shells out to the repo's own tsc, so it drags nothing into a consumer that does not run it.

npx doccheck badges     # badges resolve, version badge matches package.json
npx doccheck examples   # fenced ts/tsx examples compile against the real packages
npx doccheck coverage   # how much of the published API the docs name
npx doccheck all

Configure with a doccheck key in the repo's package.json:

{
  "doccheck": {
    "workspace": "frontend/ui",
    "skipSegments": ["lib"],
    "skipPrefixes": ["backend/public"],
    "minCoverage": 8
  }
}
Option Default Description
workspace "." Directory the examples are compiled from
skipSegments see below Extra path segments to ignore
skipPrefixes [] Extra repo-relative prefixes to ignore
minCoverage null Coverage floor; use as a ratchet

node_modules, dist, build, coverage, .git, .doccheck, .next, out and vendor are always skipped.

workspace is the setting that matters. The examples have to be compiled from a directory their own imports resolve from. Compiling somewhere else and bridging with tsconfig paths looks equivalent and is not: it breaks React's own types, so React.ComponentProps<"button"> quietly loses className and children, and every tsx example is checked against a degraded surface while still reporting a pass. Point workspace at the app that has the UI packages installed.

What each command checks

badges — a hardcoded version badge that disagrees with package.json (prefer a dynamic img.shields.io/github/v/release endpoint, which cannot drift), an Actions badge naming a workflow that is not on disk or pointing at another repository, a badge with no alt text, an anchor matching no heading, and an in-repo link resolving to nothing. Links follow VitePress conventions, so extensionless targets and root-absolute site routes are not false positives. A page can declare <!-- linkcheck: external /prefix/ --> for paths injected at deploy time; only the prefixes it names are excused.

examples — every fenced ts/tsx block is compiled. Blocks are fragments, so three kinds of noise resolve from the compiler's own diagnostics rather than by hand: a bare return means the block is a function body and it is re-emitted wrapped; a name the example never defines is declared as both a value and a type; an illustrative import path is dropped and its bindings declared, while imports that should resolve are checked for real. A block that is a shape sketch rather than code opts out with <!-- doccheck: skip — why -->.

coverage — walks each publishable workspace package's entry points, follows export * and export { x } from, and matches the resulting symbols against the prose. Deliberately coarse: naming a symbol is not explaining it. It catches a package growing an export that nobody writes a word about.

Both badges and examples carry a canary: a fixture that must produce its expected result before the real run is trusted. A checker whose parser has silently stopped matching otherwise reports a clean pass over zero findings, which is the one failure mode a checker must never have.

Vitest Options

Option Default Description
include ['src/**/*.test.ts'] Test globs
exclude [] Merged over node_modules/**, dist/**, out/**, **/coverage/**
setupFiles Files run before each test file
fileParallelism vitest default false runs test files sequentially
testTimeout / hookTimeout vitest default Timeouts in ms
environment node node | jsdom | happy-dom | edge-runtime
coverage off true for v8 → test/coverage, or an object
reports off true for html+junit+default → test/, or an object
viteConfig {} Extra Vite-level config (plugins, resolve, …)
test {} Escape hatch merged last over test

ESLint Options

Option Default Description
tsconfigRootDir Root dir for tsconfig resolution
typeChecked true Enable no-floating-promises, await-thenable, no-misused-promises
security false Enable no-eval, no-implied-eval, no-new-func
ignores [] Additional ignore patterns
extraRules {} Additional rules to merge

Rules included

Base (always active)

  • @typescript-eslint/no-unused-vars (error, ignores _ prefix)
  • @typescript-eslint/no-explicit-any (error)
  • @typescript-eslint/consistent-type-imports (error, inline-type-imports)
  • @typescript-eslint/no-unused-expressions (error)
  • eqeqeq (error, allows == null)
  • no-var (error)
  • prefer-const (error)
  • no-console (warn, allows warn/error/info)
  • no-debugger (error)
  • no-duplicate-imports (error)

Type-checked (opt-in, default: on)

  • @typescript-eslint/no-floating-promises (error)
  • @typescript-eslint/await-thenable (error)
  • @typescript-eslint/no-misused-promises (error)

Security (opt-in, default: off)

  • no-eval (error)
  • no-implied-eval (error)
  • no-new-func (error)

Migration to v3 (package renamed)

v3 renames the package from @max-health/config to @max-health-inc/config and publishes it to GitHub Packages instead of being installed from a git URL. No config values changed; v3.0.0 is byte-identical to v2.4.0 apart from its name.

The rename is not cosmetic: GitHub Packages resolves an npm scope to the owner that hosts it, so a package named @max-health/* can never be published from the Max-Health-Inc org — the registry answers 404 … does not exist under owner "max-health". Matching the scope to the owner is the only way to serve it from GitHub Packages, the same as @max-health-inc/shared-ui and the @max-network/* packages.

In each consuming repo:

- "@max-health/config": "github:max-health-inc/config"
+ "@max-health-inc/config": "^3.0.0"

then update every specifier that names it — extends in each tsconfig, plus the eslint / vite / vitest imports:

- "extends": "@max-health/config/tsconfig/app.json"
+ "extends": "@max-health-inc/config/tsconfig/app.json"

and make sure the repo's .npmrc maps the scope (see Install). A stale @max-health/config specifier fails at resolution, so nothing silently keeps the old copy.

Migration from v1

v2 enables typeChecked by default. If your project doesn't have a tsconfig with project references set up, pass typeChecked: false.

Customization

All factories accept options for overrides. The tsconfig files can be extended with additional compilerOptions. See each file for available options.

About

Shared configs (tsconfig, eslint, vite) for Max Health repos — install via github:max-health-inc/config

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages