The pre-commit hook is file-scoped (LEFTHOOK_FILES), so it can't naturally run the test suite. Pre-push is the right tier for it: - Runs after all commits are made but before the push leaves the machine, catching regressions that span multiple commits - ~3.5s including the tsc step (negligible vs the typical push round-trip to CI) - Offline and deterministic, same philosophy as pre-commit lefthook.yml: new pre-push section, sequential (parallel: false since there's only one command, but the explicit value documents the intent that this hook runs commands in order rather than racing). project-specs.md: updated the CHECK TIERS table to add the pre-push column with in it. Updated the rule-of-thumb list to include the pre-push tier. Updated the 'why' notes to explain why lives in pre-push rather than pre-commit (LEFTHOOK_FILES doesn't apply to the test runner).
28 KiB
Project Specifications for TypeScript NPM Module
- name of package: tiny-pattern-ts
0. References
typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starter-tiny/
1. Development Environment
-
TypeScript: Use strictest practical rules. Inline in
tsconfig.json(not via@tsconfig/strictest). The rule set is:strict,noImplicitAny,noImplicitThis,alwaysStrict,strictNullChecks,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitReturns,noFallthroughCasesInSwitch,noUncheckedIndexedAccess,noImplicitOverride,noUnusedLocals,noUnusedParameters,forceConsistentCasingInFileNames,isolatedModules,verbatimModuleSyntax. -
EditorConfig: Use
.editorconfigfrom typescript-lib-starter-tiny. -
oxfmt: Rust-based formatter, Prettier-compatible. Replaces Prettier. Config in
.oxfmtrc.json(same shape as.prettierrc). -
oxlint: Rust-based linter. Replaces ESLint. Config in
.oxlintrc.jsonwithtypescript,unicorn,oxc,importplugins. Categories enabled as errors:correctness,suspicious,restriction. As warnings:perf,style.nurseryis off. Type-aware rules are enabled viaoptions.typeAware: truein.oxlintrc.json(no CLI flag needed); the linter then usesoxlint-tsgolintfor rules that require type information. -
oxlint-tsgolint: TypeScript-Go-backed type-aware linter for oxlint. Activated by
options.typeAware: truein.oxlintrc.json. Native bindings installed asoptionalDependenciesper platform (same pattern as oxlint). -
oxlint rules disabled by design (in
.oxlintrc.json):eslint/no-undefined— we useundefinedas the no-match sentinel.eslint/sort-keys— handler/case order is semantic, not alphabetical.eslint/id-length—T,R,U,Vare standard TS generics.import/no-named-export— false positive on library entry re-exports.- Stylistic rules superseded by oxfmt (oxfmt is the canonical
formatter; these rules either conflict with its output or
duplicate features oxfmt already provides):
eslint/one-var— oxfmt uses comma-joinedconstdeclarations; oxlint wanted to split them.import/group-exports— oxfmt keeps separateexportstatements as-is.import/exports-last— statement ordering is up to oxfmt.eslint/sort-imports— replaced by oxfmt's built-insortImports(enabled in.oxfmtrc.json).import/consistent-type-specifier-style— inlineimport { type X, Y }is intentional for grouping.unicorn/prefer-export-from— conflicts with how the barrelsrc/index.tsre-exports throughsrc/match.ts.typescript/method-signature-style— method signatures ininterfaceare conventional TS ergonomics. For*.test.tsfiles additionally:no-unused-expressions(forexpectTypeOf(...)calls),no-empty-file(we have a single import per test file in some cases),import/no-nodejs-modules(we usenode:test/node:assert/node:fsintentionally),eslint/no-magic-numbers(literals in tests are fine).
-
Import sorting: built into oxfmt (no separate plugin).
-
cspell: Basic spelling configuration. Words dictionary in
cspell.jsoncovers tooling names (oxlint,oxfmt,oxc,nodenext,oxfmtrc,oxlintrc,EDITMSG,typescriptteam,gitmoji,dbaeumer,msvc) and a few library names (tslib,typefest,lefthook). -
Lefthook: Pre-commit checks (run in parallel, lefthook v2 schema). Single source of truth for the underlying commands is in
package.json#scripts; the lefthook config only describes what to run on which files. Hooks that should run on staged files (oxlint,oxfmt,cspell) set theLEFTHOOK_FILESenv var to the staged-files list via ash -cwrapper, and the npm script uses${LEFTHOOK_FILES:-<default>}to default to the full project when invoked manually:pre-commit.commands.oxlint(glob*.{ts,tsx,js,jsx,mjs,cjs}):sh -c 'LEFTHOOK_FILES="$0" npm run check:oxlint' {staged_files}pre-commit.commands.oxfmt(glob*.{ts,tsx,js,jsx,mjs,cjs}):sh -c 'LEFTHOOK_FILES="$0" npm run check:oxfmt' {staged_files}pre-commit.commands.cspell:sh -c 'LEFTHOOK_FILES="$0" npm run check:cspell' {staged_files}pre-commit.commands.typecheck:npm run check:tsc(no file args needed)outdatedis intentionally NOT a pre-commit check (it can flag upstream patch releases that aren't actionable locally); it runs in CI and via the explicitlefthook run outdatedcommand.
-
Additional Dev Dependencies:
- lefthook
- check-outdated
- c8 (coverage for
node --test) - expect-type (type-level assertions in tests)
- oxfmt, oxlint (with their native bindings as
optionalDependenciesso the correct binding is selected per platform automatically) - typescript (TS 7)
- @types/node
- tslib (available for future runtime helper imports)
- type-fest (utility types)
2. Build & Test
- Build Tool: TypeScript 7 (
tsc) — no bundler, no Vite. The build is a plaintsc -p tsconfig.build.jsoninvocation that emits ESM JavaScript and.d.tsdeclarations todist/. ESM only, no CJS. - Testing: Node's built-in
node --testwith--strip-types(Node 22.6+, unflagged on Node 24/26). Test files are co-located with source as*.test.ts. No Vitest. - TypeScript Build Output:
distdirectory (set intsconfig.build.json#outDir;tsconfig.jsonkeepsoutDirfor editor tooling but excludes tests from emission viatsconfig.build.json#exclude). - Import extensions: Source uses
.tsextensions in imports (e.g.from "./match.ts") sonode --strip-typesresolves them at test time. TypeScript'srewriteRelativeImportExtensions(intsconfig.build.json) rewrites these to.jsin the emitteddist/*output, so consumers see conventional ESM imports. - TypeScript Compiler Options Notes:
allowImportingTsExtensions: trueis set in the roottsconfig.json(works because the root config hasnoEmit: true).rewriteRelativeImportExtensions: trueis set intsconfig.build.jsonto rewrite.tsto.json emit.module: nodenext,moduleResolution: nodenextfor ESM-first Node packages.verbatimModuleSyntax: trueenforces explicitimport type.target: es2024,lib: ["es2024"](Node 26 supports all ES2024 features natively).
- Additional Dev Dependencies (for types and tests):
tslib— available for runtime helper imports; not currently used by source (kept for future use per the original spec).type-fest— utility types; not currently used by source (kept for future use per the original spec).@types/node— types fornode:test,node:assert,node:fs.
- Node Version:
>=26(engines field). Pinned via.node-versionfor fnm/nvm/volta/mise auto-switching and CI (actions/setup-node@v4withnode-version-file: .node-version).
3. Project Structure & Files
- .gitignore: Ignore
dist,node_modules,coverage, and other common files. - .node-version: Single line containing the Node major version
(currently
26). Used by version managers and CI. - .npmignore: Ignore
node_modules/,coverage/,*.log,*.tsbuildinfo,src/,.vscode/,.editorconfig,.oxfmtrc.json,.oxlintrc.json,.node-version,cspell.json,lefthook.yml,commit-message-template. (Note:dist/is included viapackage.json#files, not by absence from.npmignore.) - .oxfmtrc.json: oxfmt configuration (Prettier-shaped).
- .oxlintrc.json: oxlint configuration.
- .oxlintrc.json + .oxfmtrc.json replace the old
.eslintrc.cjsand.prettierrc. - LICENSE: MIT.
- README.md: Scaffolded.
- commit-message-template: From typescript-lib-starter-tiny.
- Target Environments: Node 26 LTS only (no browser target; this is a pure Node library, no DOM, no DOM lib in tsconfig).
- No React, No CJS, ESM only.
4. Automation & Quality
- Version Automation: Use standard
npm versionfor versioning. - Unused Dependency Check: Use
check-outdated(devDep, runs in CI and via the explicitlefthook run outdatedcommand). - No commitlint, no conventional commits. Commits use gitmoji
prefixes (e.g.
:sparkles:,:wrench:,:bug:,:fire:,:white_check_mark:,:tada:) for at-a-glance categorization.
5. Scripts
PREFIX CONVENTION
Script names use a prefix that signals when the script is intended
to run. A <prefix>:<name> script is implicitly aggregated by a
<prefix> script (if one exists) and run by the corresponding
lefthook hook or CI step. Picking the right prefix documents the
script's intended lifecycle:
check:*— read-only verification. Aggregated bynpm run check(which runs allcheck:*scripts in order). Called from the lefthook pre-commit hook on staged files, and from the CI build job on the full project. Read-only; never modifies files.fix:*— mutating counterpart of acheck:*script. There is nonpm run fixaggregator by design: fixes should be intentional, not batched. Run individually.test:*— test scripts.npm run testis the canonical entry point (check:tsc+ unit tests);test:unitskips the typecheck for fast local iteration;test:ciadds c8 coverage and is the CI variant.publish:*— runs only at publish time, in the CIpublishjob (immediately beforenpm publish). There is no localnpm run publishscript — publishing is CI-only by policy (see §7). Thepublish:prefix still documents intent: this script validates the publishable artifact (e.g.,dist/) rather than the source.
A new script should pick the prefix that matches its lifecycle, not invent a new one. If no existing prefix fits, that is a signal the script does not belong in the standard pipeline.
SETUP
use:git-commit-message: Set up commit message template (if needed).
TEST
test: Runcheck:tscthennode --test --strip-types "src/**/*.test.ts". The glob is required because Node 26 does not auto-discover test files in a bare directory argument (node --test src/is interpreted as a module path on Node 26+).test:unit: Run unit tests withnode --test --strip-types "src/**/*.test.ts"(no preceding typecheck).test:ci: Run tests in CI mode with c8 coverage (text + lcov + html reporters), uploadingcoverage/as an artifact.
BUILD
build: Build the project usingtsc -p tsconfig.build.json(emitsdist/*.js+dist/*.d.ts+ sourcemaps, with.tsimports rewritten to.js).
CLEAN
clean: Removedist/vianode -e "fs.rmSync('dist', {recursive:true, force:true})". (Replacesclean:buildfrom the original spec — same effect, norimrafdep needed.)
CHECK
check: Run all checks in order —check:oxlint,check:oxfmt,check:tsc,check:cspell,check:knip,check:outdated.check:oxlint:oxlint ${LEFTHOOK_FILES:-src}— lintssrc/by default; when invoked from the lefthook pre-commit hook withLEFTHOOK_FILESset to the staged-files list, lints only those files. This is the single source of truth for the oxlint command and is shared between the manualnpm run checkand the pre-commit hook. Type-aware rules are activated declaratively viaoptions.typeAware: truein.oxlintrc.json(not via a CLI flag), so the script command stays clean. oxlint only understands JS/TS- family languages, so config files (JSON/YAML/Markdown) are intentionally outside its scope; they are checked only by oxfmt. Source-leveloxlint-disabledirectives are used to silence type-aware false positives in the generic type machinery: 4× fortypescript/no-unsafe-type-assertionand 1× fortypescript/no-unnecessary-type-parametersinsrc/pattern.ts/src/match.ts(legitimate type machinery that needs restructuring, documented in the source); 1× file-level fortypescript/no-floating-promisesinsrc/index.test.ts(expectTypeOf(...)is a sync type-assertion library that oxlint-tsgolint misidentifies).check:oxfmt:oxfmt --check ${LEFTHOOK_FILES:-.}— formats the whole project (.) by default, including JS/TS, JSON/JSONC, YAML, Markdown, MDX and other supported file types. From lefthook pre-commit, theLEFTHOOK_FILESenv var scopes to the staged files matching*.{ts,tsx,js,jsx,mjs,cjs,json,jsonc,yaml,yml,md,mdx}. Built-insortPackageJson: truekeepspackage.jsonkeys alphabetized (replaces the formersort-package-jsontool). Built-in import sorting (enabled viasortImports: true) replaces any external import-sort plugin.check:tsc:tsc(noEmit is set intsconfig.json).check:cspell:cspell lint ${LEFTHOOK_FILES:-.}— walks the project root by default; from lefthook, only the staged files.check:outdated:check-outdated --ignore-pre-releases --ignore-packages @oxfmt/binding-*,@oxlint/binding-*. The oxc native bindings are declared asoptionalDependenciesso the correct one is selected per platform automatically; the*-platform bindings show as "not installed" on the current platform and are explicitly ignored here.check:knip:knip --include dependencies,exports,files— finds unused dependencies, value exports, and source files. The scoped--includelist intentionally omits thetypescategory, which produces systematic false positives for libraries whose exported types (e.g.Matcher,Pattern) are part of the public API and not consumed internally. The targeted scope keeps the signal high (real unused-dep detection) without config-file boilerplate.
FIX
fix:oxlint:oxlint --fix src.fix:oxfmt:oxfmt ${LEFTHOOK_FILES:-.}— same scoping ascheck:oxfmt(whole project by default, staged files from lefthook). Writes changes in place. (There is nofixaggregator in the scripts; run thefix:*scripts individually.)
PUBLISH
publish:publint:publint— runs the pack-and-lint flow against the current project (usesnpm packto validate the actual publishable artifact againstpackage.json'sfiles,exports,main, etc.). Requires a freshbuildto have populateddist/. Called by the CIpublishjob immediately beforenpm publish(see §6). Not part ofnpm run checkand not run on commit: it validates publishing correctness, not source correctness.publish:attw:attw . --pack --profile esm-only— validates the emitted.d.tsdeclarations against multiple TypeScript module-resolution scenarios. Uses--profile esm-onlybecause this package is intentionally ESM-only (no CommonJS shim); CJS resolution scenarios are explicitly out of scope by design, not a bug. Called by the CIpublishjob alongsidepublish:publintandnpm publish(see §6). Not part ofnpm run checkand not run on commit: same rationale aspublish:publint.
HOOKS
- Lefthook runs the relevant
check:*scripts on staged files in parallel for pre-commit, andnpm teston pre-push. Seelefthook.yml.
CHECK TIERS — what runs where and why
The check:* scripts are split across three execution contexts
based on speed, scope (staged files vs whole project), and
side effects (network, registry queries). The split is a
deliberate design choice; the rule of thumb is:
- pre-commit hook: fast, file-scoped, offline, deterministic. Catches what you just changed in under a second or two.
- pre-push hook: runs the unit test suite (
npm test, including a fulltscover the project). ~500ms. Catches runtime/logic bugs across the whole codebase before the push leaves your machine. Thetscstep is technically redundant with pre-commit but validates against unstaged changes too. npm run check: full project audit. Slower, may query the network, runs everything that's not in pre-commit.- CI build job: authoritative. Runs
npm run checkandnpm run test:cion every push and PR. The ground truth — if CI passes, the codebase is clean.
| Script | pre-commit | pre-push | npm run check |
CI build | CI publish |
|---|---|---|---|---|---|
check:tsc |
✅ | ✅ | ✅ | ✅ | — |
check:oxlint |
✅ | — | ✅ | ✅ | — |
check:oxfmt |
✅ | — | ✅ | ✅ | — |
check:cspell |
✅ | — | ✅ | ✅ | — |
test |
— | ✅ | — | ✅ | — |
check:knip |
— | — | ✅ | ✅ | — |
check:outdated |
— | — | ✅ | ✅ | — |
publish:publint |
— | — | — | — | ✅ |
publish:attw |
— | — | — | — | ✅ |
Why these splits?
-
check:tsc,check:oxlint,check:oxfmt,check:cspellare in pre-commit because they are fast (~0.2–0.5s each), fully offline, and naturally scope to staged files via theLEFTHOOK_FILESenv var convention. They give instant feedback on what you typed. -
test(and thetscit includes) is in pre-push because it runs the whole test suite across the whole project (~500ms for the current 6 tests). The pre-commitLEFTHOOK_FILESconvention doesn't apply to the test runner, so pre-commit isn't the right home. Pre-push is the natural place: it runs after all commits are made but before the push leaves the machine, catching regressions that span multiple commits. -
check:knipis NOT in pre-commit or pre-push because it scans the whole project (not staged files) and takes ~4s. Adding it would noticeably slow both hooks. It's still fast enough to run locally before pushing, and CI catches it regardless. -
check:outdatedis NOT in pre-commit because (a) it queries the npm registry (~6.5s) which means network dependency in a hook, (b) the result is advisory, not a pass/fail correctness check, and (c) the same network call in CI or locally on demand gives the same answer. It belongs in scheduled or on-demand runs, not in a blocking hook. -
publish:publintandpublish:attware NOT incheck— they belong to thepublish:prefix (see above) because they validate the publishable artifact (dist/) and require a fresh build. Running them on every commit would be wasteful (and slow,npm packis involved). They run in the CIpublishjob immediately beforenpm publish.
What to do before pushing
Run npm run check locally. If you only trust pre-commit + CI,
know that anything check:knip or check:outdated would catch
will be caught by CI on the PR before merge (assuming branch
protection is configured to require CI to pass).
6. Repository & CI/CD
- Repository: Hosted on GitHub.
- Build Pipeline: GitHub Actions (
.github/workflows/ci.yml).buildjob on push and pull_request tomainand onworkflow_dispatch. Steps:actions/checkout@v4,actions/setup-node@v4(withnode-version-file: .node-version,cache: npm),npm ci,npm run build,npm run check,npm run test:ci, then uploadcoverage/as an artifact. Runs the fullcheckchain (all 6check:*scripts) and the test suite with coverage. This is the authoritative gate — if it passes, the codebase is clean. See §5 ("CHECK TIERS") for which checks run here vs. locally vs. pre-commit.publishjob: only onrefs/tags/*, depends onbuild. Steps:actions/checkout@v4,actions/setup-node@v4(withnode-version-file: .node-versionandregistry-url),npm ci,npm run build,npm run publish:publint(see §5 for why this ispublish:and notcheck:),npm run publish:attw, andnpm publish --access publicwithNODE_AUTH_TOKENfrom secrets. Runs the publish-tier checks that validate the publishable artifact before publishing to npm.
7. Versioning & Publishing
- Version Update: Use
npm versionto bump version after merging to main and before publishing. - Publishing to npm: Only publish from CI on tagged commits.
- Recommended Workflow:
- Develop and merge PRs to main
- Run all checks via CI
- Bump version with
npm version <patch|minor|major> - Push tag to GitHub
- CI builds and publishes to npm on tag
8. NPM Keywords
- pattern-matching
- pattern
- match
- algebraic-data-types
- adt
- typescript
The library is for pattern matching (not regex), similar to F#'s pattern matching, for TypeScript/ESM environments.
9. Code Coverage
- Tool: c8 (V8-native coverage, no instrumentation step).
- Configuration: c8 has no project config; the report shape is
pinned in the
test:ciscript:
"test:ci": "c8 --reporter=text --reporter=lcov --reporter=html node --test --strip-types src/"
- Reporters: text summary, HTML, and lcov (matching the original spec).
- CI:
coverage/is uploaded as a workflow artifact viaactions/upload-artifact@v4(see.github/workflows/ci.yml). - Optional: Coverage thresholds can be added in c8 config when the library surface stabilizes.
10. Source Structure & Tree Shaking
- Source Directory: All source code resides in
src/and is exported viasrc/index.ts. - Configuration:
sideEffects: falseinpackage.json(set).- ESM-only exports;
package.json#exportsfield maps.to{"types": "./dist/index.d.ts", "import": "./dist/index.js"}. - Avoid top-level side effects in modules.
- Explicit re-exports in
index.tsfor best results.
- Source structure (current):
src/index.ts— public barrel.src/match.ts—match(value).with(...).exhaustive() / .otherwise(...)builder.src/pattern.ts—P.literal,P.type,P.when,P.any,P.shapeconstructors and theMatcher<T>interface.src/index.test.ts— runtime + type-level tests usingnode --test+expect-type.
- The human will implement the source code.
11. Included Templates from typescript-lib-starter-tiny
.editorconfig
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true
quote_type = double
[*.md]
max_line_length = 0
trim_trailing_whitespace = false
[COMMIT_EDITMSG]
max_line_length = 0
commit-message-template
# If applied, this commit will... (Max 50 char)
# Explain why this change is being made (Max 72 Char) [WHAT and WHY vs HOW]
# Provide links or keys to any relevant tickets, articles or other resources
Resolves #...
# --- COMMIT END ---
# Remember to
# Use the imperative mood in the subject line
# Capitalize the subject line
# Do not end the subject line with a period
# Separate subject from body with a blank line
# Use the body to explain what and why vs. how
# Can use multiple lines with "-" for bullet points in body
README.md Structure (scaffolded)
- Project title and description
- Development
- Build:
npm run build - Test:
npm run test,npm run test:ci - Checks:
npm run check,npm run fix:oxfmt,npm run fix:oxlint
- Build:
- Tooling section listing TypeScript 7, node --test, c8, oxlint, oxfmt, cspell, lefthook
- Requirements: Node.js >= 26
- VSCode integration
- Debugging
- Running tests
- oxc.oxc-vscode provides oxlint and oxfmt in-editor
- Workflows
- Version updates via
npm version - Publishing via GitHub Actions on tagged commits
- Version updates via
- Contribution guidelines
- Commit signing (GPG)
- How to set up commit message template (
npm run use:git-commit-message) - Reference to commit-message-template
- Type-level tests use
expect-type'sexpectTypeOf(...)insidenode --testcases
12. Project Initialization & Commit Strategy
- Start by initializing git with a
mainbranch. - Initial commit: empty README + LICENSE.
- Create a feature branch:
feature/setup. - For each technology or tool added (and its configuration), create a
separate commit:
- Prepend each commit message with a matching gitmoji (e.g.
:sparkles:for new features,:wrench:for config,:bug:for fixes,:fire:for removals,:white_check_mark:for tests,:tada:for initial commit). - Example commit messages used in this project:
:tada: Initial commit with empty README:wrench: Track .vscode/settings.json for workspace settings:construction_worker: Added GitHub Actions workflow for CI/CD:test_tube: Added Vitest configuration with coverage(later removed):sparkles: Scaffolded src/index.ts entry point for library code:wrench: Replace Vite/Vitest with TypeScript 7 and node --test:wrench: Replace ESLint and Prettier with oxlint and oxfmt:fire: Remove Vite scaffold leftovers:sparkles: Add initial pattern-matching API:white_check_mark: Add expect-type for type-level tests:wrench: Declare Node 26 as the supported runtime:bug: Use .ts extensions in imports for node --strip-types:wrench: Remove Prettier from editor formatter config
- Prepend each commit message with a matching gitmoji (e.g.
- Each commit should include only the relevant files and configuration for that technology/tool. This approach ensures a clean, understandable project history and makes it easy to review or revert specific setup steps.
13. Changelog Automation
- Not currently configured. The intended workflow, when adopted, is a
changesets-driven release process: feature PRs include a changeset,
which gets consumed by a release workflow, producing a
CHANGELOG.mdand a version bump on merge to main. - The changelog should be updated as part of the release process.
14. Publishing Public
- npm publishing is configured to be public by default.
publishConfig: { "access": "public" }is set inpackage.json.- The CI/CD pipeline publishes with
--access publicon tagged commits.
15. VSCode Integration
.vscode/settings.jsonusesoxc.oxc-vscodeas the default formatter for[typescript],[javascript],[json],[jsonc],[markdown],[mdx], and[yaml](oxfmt under the hood).
{
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"[javascript]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"[markdown]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"[mdx]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"[yaml]": {
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
},
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true
}
.vscode/extensions.jsonrecommends:oxc.oxc-vscode(oxlint + oxfmt, replaces eslint/prettier/vitest)streetsidesoftware.code-spell-checker(cspell)typescriptteam.native-preview(TypeScript 7 nightly support; replaces the olderms-vscode.vscode-typescript-next)
{
"recommendations": [
"oxc.oxc-vscode",
"streetsidesoftware.code-spell-checker",
"typescriptteam.native-preview"
]
}
.vscode/tasks.jsonis not currently provided; common tasks (build, test, lint, typecheck, format, spell, check:outdated) are run via the npm scripts inpackage.jsonfrom the integrated terminal.- VSCode uses TypeScript 7 via the
typescriptteam.native-previewextension, withoxc.oxc-vscodefor formatting and linting.