Commit Graph
232 Commits
Author SHA1 Message Date
tmu c3f913fae9 🔧 Fix small issues with the project setup 2026-09-03 21:01:28 +00:00
tmu 0598a9eb4a 🔧 Expand oxfmt to format project config and docs files 2026-09-03 20:58:33 +00:00
tmu 7349d0dec2 🔥 Remove sort-package-json in favor of oxfmt's built-in sortPackageJson 2026-09-03 20:58:17 +00:00
tmu e45592ae00 🔧 Unify lefthook and package.json scripts via LEFTHOOK_FILES env var
lefthook and package.json had parallel command definitions for the
same tools (oxlint, oxfmt, cspell). Consolidate by making lefthook
call the npm scripts, with staged files passed via the
LEFTHOOK_FILES env var. The scripts use ${LEFTHOOK_FILES:-<default>}
so they default to the full project when invoked manually and to
the staged-files list when invoked from lefthook.

Changes:
- package.json#check:oxlint: `oxlint ${LEFTHOOK_FILES:-src}`
  (lints src/ manually; staged files from lefthook)
- package.json#check:oxfmt: `oxfmt --check ${LEFTHOOK_FILES:-src}`
- package.json#check:cspell: `cspell lint ${LEFTHOOK_FILES:-.}`
  (walks CWD manually; staged files from lefthook)
- package.json#check:tsc, check📦 unchanged (no file args)
- lefthook.yml: file-filtered hooks (oxlint, oxfmt, cspell) now use
  `sh -c 'LEFTHOOK_FILES="$0" npm run check:*' {staged_files}` to
  inject the staged-files list into the env. sort-package-json and
  typecheck call npm scripts directly (no file args).
- project-specs.md: document the unification pattern

Why sh -c + env var instead of the simpler 'npm run ... -- {staged_files}':
  'oxlint src file.ts' lints the whole src/ tree *plus* file.ts
  (oxlint doesn't dedupe paths). Setting LEFTHOOK_FILES as an env
  var (which lefthook's 'env:' config does not template) requires
  the sh -c wrapper, but it gives the right semantics: when the
  var is set, only the explicit files are checked; when unset,
  the default (src/ or .) is used.

Verified:
- 'npm run check:oxlint' (no env) lints all of src/
- 'LEFTHOOK_FILES=src/match.ts npm run check:oxlint' lints only that file
- 'npx lefthook run pre-commit' with a staged TS file: cspell output
  shows '1/1 src/match.ts' (only staged file, not whole project)
- All 5 hooks pass on a real staged change
- 'lefthook validate' reports 'All good'
- 'npm run check' exits 0
2026-09-03 18:44:19 +00:00
tmu 45b6c141e8 🔧 Migrate lefthook config to v2 schema
The previous config was a hybrid of v1 (jobs: array) and v1
(top-level commands: block) that no longer validates under
lefthook 2.x. lefthook 2.0.0's schema has top-level 'commands:'
set to 'false'; the v1 jobs: array form still works, but the
orphan named-commands block at the top was silently invalid
(caught by 'lefthook validate').

Migrate to the v2-native 'commands:' (named) form under the hook:

  pre-commit:
    parallel: true
    commands:
      oxlint:
        glob: ...
        run: npx oxlint {staged_files}
      ...

Changes:
- Add 'min_version: 2.0.0' to declare the v2 schema explicitly
- Replace pre-commit.jobs: array with pre-commit.commands: (named)
- Inline the glob on each command (was a top-level property on each
  job in v1)
- Remove the orphan top-level 'commands:' block — it was never
  referenced by any hook, and v2 forbids it
- Update project-specs.md: drop the dangling reference to the
  lefthook 'commands:' block (it was the dead top-level one);
  describe the v2 commands: form

Verified:
- 'lefthook validate' reports 'All good'
- 'lefthook run pre-commit' executes all 5 hooks (oxlint, oxfmt,
  sort-package-json, typecheck, cspell); globs correctly skip hooks
  on non-matching files (e.g. lefthook.yml alone) and run them on
  TS/JS changes
- 'npm run check' exits 0
- 'lefthook dump' shows the normalized v2 config
2026-09-03 18:12:01 +00:00
tmu 9bc87aab87 ⬆️ Upgrade devDependencies to latest major versions
- @types/node 22.20.1 -> 26.4.1
- c8 10.1.3 -> 12.0.0
- check-outdated 2.16.1 -> 3.0.0
- cspell 8.19.4 -> 10.2.1
- lefthook 1.13.6 -> 2.1.12
- sort-package-json 3.7.1 -> 4.0.0
- type-fest 4.41.0 -> 5.9.0

Verified all configs remain compatible without changes:
- cspell 10 reads the existing JSON config (v0.2 format unchanged)
- lefthook 2 accepts the v1-style config (pre-commit.jobs[], commands:)
- c8 12 still wraps 'node --test --strip-types' and produces the same
  text/lcov/html reports
- check-outdated 3 keeps the --ignore-packages and --ignore-pre-releases
  flags we use

Full pipeline passes:
- npm run check exits 0 (oxlint, oxfmt, tsc, cspell, sort-package-json,
  check-outdated all green)
- npm test: 6/6 pass
- npm run test:ci: coverage report generated successfully
- lefthook run pre-commit: hooks execute correctly
2026-09-03 14:08:28 +00:00
tmu 0c3e51c768 ✅ Ignore coverage directory in .gitignore 2026-09-03 13:54:34 +00:00
tmu ab6eb0f34c 📚 Sync project-specs.md and package.json with current state
project-specs.md:
- Replace Prettier/ESLint/Vite/Vitest references with oxfmt/oxlint/oxc
  and TypeScript 7 / node --test
- Drop @tsconfig/strictest note; rules are inlined in tsconfig.json,
  enumerated explicitly
- Document oxlint rule disables (no-undefined, sort-keys, id-length,
  no-named-export) and test-file overrides (no-unused-expressions,
  no-empty-file, no-nodejs-modules, no-magic-numbers)
- Document Node 26 / .node-version / engines.node / CI follow
- Document allowImportingTsExtensions + rewriteRelativeImportExtensions
  for the node --strip-types quirk
- Replace Vite/vitest coverage example with c8 + node --test
- Sync scripts section: drop check:eslint, check:prettier and their
  fix:* counterparts; add check:oxlint/check:oxfmt/fix:oxlint/fix:oxfmt
- Add current source layout (index.ts, match.ts, pattern.ts, index.test.ts)
- Update VSCode integration section with actual settings.json and
  extensions.json contents
- Document the actual commit history with gitmoji prefixes
- Fix 'initilizing' typo
- Drop 'changesets is a devDep' claims (changesets is not installed)

package.json:
- Move @oxfmt/binding-* and @oxlint/binding-* from devDependencies to
  optionalDependencies so the right binding is selected per platform
  (CI on Ubuntu gnu gets the gnu binding automatically, not the
  local musl one)
- Extend check:outdated to ignore the platform-specific bindings (they
  show as 'not installed' on the current platform, which is correct
- Add cspell words: gitmoji, dbaeumer, msvc (the latter for the Windows
  binding variant). Drop the unused 'nocheck' word

README.md:
- Drop the 'changesets is available as a devDep' line; changesets
  isn't installed
2026-09-03 13:42:37 +00:00
tmu cb5b302238 🔧 Remove Prettier from editor formatter config
Prettier is no longer a project dependency and oxc.oxc-vscode handles
formatting for all file types the project touches. Switch [json],
[jsonc], [markdown], [mdx], and [yaml] formatters from
esbenp.prettier-vscode to oxc.oxc-vscode (oxfmt under the hood,
Prettier-compatible for JS/TS and native for JSON/MD/YAML).

Drop 'esbenp' from cspell dictionary (no longer referenced).
2026-09-03 13:09:53 +00:00
tmu 9a242225ee 🐛 Use .ts extensions in imports for node --strip-types
Node's --strip-types does not rewrite import specifiers the way bundlers
and Deno do, so 'import ... from "./match.js"' literally looks for
match.js (not match.ts) and fails with ERR_MODULE_NOT_FOUND at test
time. Fix by switching source imports to .ts extensions and letting
TypeScript's rewriteRelativeImportExtensions do the rewrite at build
emit time.

Changes:
- tsconfig.json: enable allowImportingTsExtensions (works because the
  root config has noEmit: true)
- tsconfig.build.json: enable rewriteRelativeImportExtensions so the
  emitted dist/*.js files keep './match.js' style imports (correct for
  consumers), not './match.ts'
- src/index.ts: import './match.ts' and './pattern.ts'
- src/match.ts: import './pattern.ts'
- src/index.test.ts: import './index.ts'

Verified: npm run test runs tsc --noEmit (passes) and node --test
--strip-types src/ — all 6 tests pass. tsc -p tsconfig.build.json
emits dist/*.js with './match.js' imports as before; consumers see no
change.
2026-09-03 13:05:20 +00:00
tmu 77dc40da07 🔧 Declare Node 26 as the supported runtime
- Add .node-version (single line: '26') so fnm/nvm/volta/mise and the
  VS Code Node version manager extension auto-switch when entering the
  project directory
- engines.node in package.json: bump from '>=22.6' to '>=26' (npm-side
  install-time declaration)
- Drop --experimental-strip-types flag from node --test invocations:
  Node 26 has --strip-types unflagged
- .github/workflows/ci.yml: switch from hard-coded node-version: 24 to
  node-version-file: .node-version in both build and publish jobs so CI
  follows the .node-version file (single source of truth)
- .npmignore: add .node-version (developer-tool file, not for publish)
- .vscode/settings.json: pin js/ts.tsdk.path to node_modules/typescript/lib
  so the editor uses the project's TS 7, not a different global install
- .vscode/extensions.json: swap ms-vscode.vscode-typescript-next for
  typescriptteam.native-preview (the official TS team extension that
  ships nightly TS support, including TS 7)
- cspell: add 'esbenp' and 'typescriptteam' to dictionary
- package-lock.json: regenerated for engines.node >=26

tsconfig.json target/lib are unchanged: es2024 is the highest stable ES
target TS 7.0.2 ships, and Node 26 supports all ES2024 features natively.
2026-09-03 12:45:32 +00:00
tmu e5df17310e ✅ Add expect-type for type-level tests
- Install expect-type@1.4.0 (devDependency)
- New src/index.test.ts exercises the public API with both runtime
  assertions (node:assert/strict inside node:test) and type-level
  assertions (expectTypeOf().toEqualTypeOf, toMatchTypeOf, toHaveProperty)
- Tests are excluded from tsc -p tsconfig.build.json (no .d.ts pollution
  in dist/)
- Type-level checks happen during tsc --noEmit (run by 'npm run check' and
  the test script); expectTypeOf assertions have no runtime side effect,
  so they pair naturally with node --test in the same test() blocks
- cspell: add EDITMSG to dictionary (used in commit-message-template path)
- oxlint test-file overrides: silence no-nodejs-modules (we use node:
  builtins) and no-magic-numbers (literals in tests are fine)

Coverage:
- match() returns a builder with with/exhaustive/otherwise
- P.literal narrows to its literal type, P.type to its typeof target
- exhaustive() returns the union of handler return types
- otherwise() falls back when no case matches
- exhaustive() throws when no case matches
2026-09-03 10:39:15 +00:00
tmu 08384bbc82 ✨ Add initial pattern-matching API
Introduce the core builder and pattern constructors:

- match(value): chain .with(pattern, handler) and terminate with
  .exhaustive() (throws on no match) or .otherwise(handler).
- P.literal, P.type, P.when, P.any, P.shape for pattern construction.
- Each pattern is a Matcher<T> whose matches acts as a type guard, so
  handler parameters are narrowed to the matched type.

The MatchBuilder accumulates handlers and returns a new builder per
.with() call (immutable chaining); finalization runs the cases in order
and returns the first match's handler result.

TypeScript configuration relaxed in .oxlintrc.json:
- Disable eslint/no-undefined (we use undefined as the no-match sentinel)
- Disable eslint/sort-keys (handler order matters; alphabetical would
  be wrong)
- Disable eslint/id-length (T/R/U/V generics are standard TS convention)
- Disable import/no-named-export (false positive for library entry points)

Verified:
- tsc --noEmit passes with strict + noUncheckedIndexedAccess
- tsc -p tsconfig.build.json emits dist/*.js + .d.ts + sourcemaps
- node -e "import('./dist/index.js')" loads and exposes { match, P }
2026-09-03 10:35:34 +00:00
tmu 6c67e91e68 🔥 Remove Vite scaffold leftovers
- Delete public/vite.svg, src/typescript.svg, src/style.css
- Empty public/ directory removed
- .npmignore: drop 'public/' entry (directory no longer exists), add
  .oxlintrc.json and .oxfmtrc.json to ignore list
- cspell: add .oxfmtrc, .oxlintrc, .sortpackagerc to dictionary so
  config-file name references aren't flagged
- lefthook: drop 'outdated' from pre-commit (kept in commands block for
  explicit CI invocation); it shouldn't block commits for being behind on
  upstream patch releases
2026-09-03 10:28:08 +00:00
tmu 078a8c30fd 🔧 Replace ESLint and Prettier with oxlint and oxfmt
- Drop .eslintrc.cjs and .prettierrc, add .oxlintrc.json and .oxfmtrc.json
- oxlint covers correctness/suspicious/perf/style/restriction categories
  plus import and typescript plugins; oxfmt reads the same .prettierrc-style
  options we had before
- Update lefthook.yml pre-commit jobs to run oxlint/oxfmt instead of
  eslint/prettier
- Update .vscode/extensions.json (oxc replaces eslint/prettier/vitest) and
  settings.json (oxc becomes the default formatter for TS/JS)
- Add 'oxlint', 'oxfmt', 'oxc', 'nodenext' to cspell dictionary
- Rewrite README 'Development' section to describe the new toolchain
2026-09-03 10:25:47 +00:00
tmu 3530262261 🔧 Replace Vite/Vitest with TypeScript 7 and node --test
- Use tsc 7 for build (vite no longer needed for a Node library)
- Use node --test with --experimental-strip-types for tests (vitest dropped)
- Use c8 for coverage instead of vitest's built-in coverage
- Add tsconfig.build.json separating typecheck from emit config
- Drop DOM lib, add strict noUncheckedIndexedAccess/noImplicitOverride
- Pin engines.node to >=22.6 (required for --experimental-strip-types)
- Update CI to Node 24 LTS (unflagged strip-types)
- Add @types/node for node:test / node:assert types
2026-09-03 09:54:21 +00:00
tmu cac1349c95 🎉 Initial commit 2026-02-02 13:38:42 +01:00
Thomas Müller 15c8ef7616 🔧 Track .vscode/settings.json for workspace settings 2025-04-29 13:45:02 +02:00
Thomas Müller 0c08a2d395 ✨ Scaffolded src/index.ts entry point for library code 2025-04-29 13:43:53 +02:00
Thomas Müller 1dc94c0dd1 👷 Added GitHub Actions workflow for CI/CD 2025-04-29 13:43:47 +02:00
Thomas Müller 25353662ec 🧪 Added Vitest configuration with coverage 2025-04-29 13:43:40 +02:00
Thomas Müller f6bbcd8b2e 📝 Scaffolded README.md structure 2025-04-29 13:43:20 +02:00
Thomas Müller 45c0bb6423 🔧 Added .npmignore to exclude non-dist files from npm package 2025-04-29 13:43:15 +02:00
Thomas Müller 142989aa2b 📝 Added commit message template 2025-04-29 13:43:11 +02:00
Thomas Müller 4f65d22efe 📄 Added MIT LICENSE 2025-04-29 13:43:07 +02:00
Thomas Müller 02a33c7b44 🔧 Added sort-package-json configuration 2025-04-29 13:42:39 +02:00
Thomas Müller f31a9b1175 🔧 Configured Lefthook for pre-commit checks 2025-04-29 13:42:25 +02:00
Thomas Müller 1d2e78dde7 🔧 Added cspell configuration for spelling checks 2025-04-29 13:42:20 +02:00
Thomas Müller 80a43072e6 🔧 Added .editorconfig for consistent editor settings 2025-04-29 13:42:16 +02:00
Thomas Müller dbc576edfe 🔧 Configured ESLint (strict, type-checked, Prettier integration, import sorting) 2025-04-29 13:41:49 +02:00
Thomas Müller c9b2d778be ✨ Configured Vite (ESM-only, vanilla-ts template, removed default app files) 2025-04-29 13:20:05 +02:00
Thomas Müller 20308c5a6d 🎉 Initial commit with empty README 2025-04-29 13:13:38 +02:00