# 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 `.editorconfig` from 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.json` with `typescript`, `unicorn`, `oxc`, `import` plugins. Categories enabled as errors: `correctness`, `suspicious`, `restriction`. As warnings: `perf`, `style`. `nursery` is off. Type-aware rules are enabled via `options.typeAware: true` in `.oxlintrc.json` (no CLI flag needed); the linter then uses `oxlint-tsgolint` for rules that require type information. - **oxlint-tsgolint**: TypeScript-Go-backed type-aware linter for oxlint. Activated by `options.typeAware: true` in `.oxlintrc.json`. Native bindings installed as `optionalDependencies` per platform (same pattern as oxlint). - **oxlint rules disabled by design** (in `.oxlintrc.json`): - `eslint/no-undefined` — we use `undefined` as the no-match sentinel. - `eslint/sort-keys` — handler/case order is semantic, not alphabetical. - `eslint/id-length` — `T`, `R`, `U`, `V` are 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-joined `const` declarations; oxlint wanted to split them. - `import/group-exports` — oxfmt keeps separate `export` statements as-is. - `import/exports-last` — statement ordering is up to oxfmt. - `eslint/sort-imports` — replaced by oxfmt's built-in `sortImports` (enabled in `.oxfmtrc.json`). - `import/consistent-type-specifier-style` — inline `import { type X, Y }` is intentional for grouping. - `unicorn/prefer-export-from` — conflicts with how the barrel `src/index.ts` re-exports through `src/match.ts`. - `typescript/method-signature-style` — method signatures in `interface` are conventional TS ergonomics. For `*.test.ts` files additionally: `no-unused-expressions` (for `expectTypeOf(...)` calls), `no-empty-file` (we have a single import per test file in some cases), `import/no-nodejs-modules` (we use `node:test`/`node:assert`/`node:fs` intentionally), `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.json` covers 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 the `LEFTHOOK_FILES` env var to the staged-files list via a `sh -c` wrapper, and the npm script uses `${LEFTHOOK_FILES:-}` 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) `outdated` is intentionally NOT a pre-commit check (it can flag upstream patch releases that aren't actionable locally); it runs in CI and via the explicit `lefthook run outdated` command. - **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 `optionalDependencies` so 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 plain `tsc -p tsconfig.build.json` invocation that emits ESM JavaScript and `.d.ts` declarations to `dist/`. ESM only, no CJS. - **Testing**: Node's built-in `node --test` with `--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**: `dist` directory (set in `tsconfig.build.json#outDir`; `tsconfig.json` keeps `outDir` for editor tooling but excludes tests from emission via `tsconfig.build.json#exclude`). - **Import extensions**: Source uses `.ts` extensions in imports (e.g. `from "./match.ts"`) so `node --strip-types` resolves them at test time. TypeScript's `rewriteRelativeImportExtensions` (in `tsconfig.build.json`) rewrites these to `.js` in the emitted `dist/*` output, so consumers see conventional ESM imports. - **TypeScript Compiler Options Notes**: - `allowImportingTsExtensions: true` is set in the root `tsconfig.json` (works because the root config has `noEmit: true`). - `rewriteRelativeImportExtensions: true` is set in `tsconfig.build.json` to rewrite `.ts` to `.js` on emit. - `module: nodenext`, `moduleResolution: nodenext` for ESM-first Node packages. - `verbatimModuleSyntax: true` enforces explicit `import 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 for `node:test`, `node:assert`, `node:fs`. - **Node Version**: `>=26` (engines field). Pinned via `.node-version` for fnm/nvm/volta/mise auto-switching and CI (`actions/setup-node@v4` with `node-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 via `package.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.cjs` and `.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 version` for versioning. - **Unused Dependency Check**: Use `check-outdated` (devDep, runs in CI and via the explicit `lefthook run outdated` command). - **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 `:` script is implicitly aggregated by a `` 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 by `npm run check` (which runs all `check:*` 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 a `check:*` script. There is **no** `npm run fix` aggregator by design: fixes should be intentional, not batched. Run individually. - `test:*` — test scripts. `npm run test` is the canonical entry point (`check:tsc` + unit tests); `test:unit` skips the typecheck for fast local iteration; `test:ci` adds c8 coverage and is the CI variant. - `publish:*` — runs only at publish time, in the CI `publish` job (immediately before `npm publish`). There is **no** local `npm run publish` script — publishing is CI-only by policy (see §7). The `publish:` 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`: Run `check:tsc` then `node --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 with `node --test --strip-types "src/**/*.test.ts"` (no preceding typecheck). - `test:ci`: Run tests in CI mode with c8 coverage (text + lcov + html reporters), uploading `coverage/` as an artifact. ### BUILD - `build`: Build the project using `tsc -p tsconfig.build.json` (emits `dist/*.js` + `dist/*.d.ts` + sourcemaps, with `.ts` imports rewritten to `.js`). ### CLEAN - `clean`: Remove `dist/` via `node -e "fs.rmSync('dist', {recursive:true, force:true})"`. (Replaces `clean:build` from the original spec — same effect, no `rimraf` dep 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}` — lints `src/` by default; when invoked from the lefthook pre-commit hook with `LEFTHOOK_FILES` set 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 manual `npm run check` and the pre-commit hook. Type-aware rules are activated declaratively via `options.typeAware: true` in `.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-level `oxlint-disable` directives are used to silence type-aware false positives in the generic type machinery: 4× for `typescript/no-unsafe-type-assertion` and 1× for `typescript/no-unnecessary-type-parameters` in `src/pattern.ts` / `src/match.ts` (legitimate type machinery that needs restructuring, documented in the source); 1× file-level for `typescript/no-floating-promises` in `src/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, the `LEFTHOOK_FILES` env var scopes to the staged files matching `*.{ts,tsx,js,jsx,mjs,cjs,json,jsonc,yaml,yml,md,mdx}`. Built-in `sortPackageJson: true` keeps `package.json` keys alphabetized (replaces the former `sort-package-json` tool). Built-in import sorting (enabled via `sortImports: true`) replaces any external import-sort plugin. - `check:tsc`: `tsc` (noEmit is set in `tsconfig.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 as `optionalDependencies` so 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 `--include` list intentionally omits the `types` category, 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 as `check:oxfmt` (whole project by default, staged files from lefthook). Writes changes in place. (There is no `fix` aggregator in the scripts; run the `fix:*` scripts individually.) ### PUBLISH - `publish:publint`: `publint` — runs the pack-and-lint flow against the current project (uses `npm pack` to validate the actual publishable artifact against `package.json`'s `files`, `exports`, `main`, etc.). Requires a fresh `build` to have populated `dist/`. Called by the CI `publish` job immediately before `npm publish` (see §6). Not part of `npm run check` and not run on commit: it validates publishing correctness, not source correctness. - `publish:attw`: `attw . --pack --profile esm-only` — validates the emitted `.d.ts` declarations against multiple TypeScript module-resolution scenarios. Uses `--profile esm-only` because 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 CI `publish` job alongside `publish:publint` and `npm publish` (see §6). Not part of `npm run check` and not run on commit: same rationale as `publish:publint`. ### HOOKS - Lefthook runs the relevant `check:*` scripts on staged files in parallel for pre-commit. See `lefthook.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. - **`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 check` and `npm run test:ci` on every push and PR. The ground truth — if CI passes, the codebase is clean. | Script | pre-commit | `npm run check` | CI build | CI publish | | ----------------- | :--------: | :-------------: | :------: | :--------: | | `check:tsc` | ✅ | ✅ | ✅ | — | | `check:oxlint` | ✅ | ✅ | ✅ | — | | `check:oxfmt` | ✅ | ✅ | ✅ | — | | `check:cspell` | ✅ | ✅ | ✅ | — | | `check:knip` | — | ✅ | ✅ | — | | `check:outdated` | — | ✅ | ✅ | — | | `publish:publint` | — | — | — | ✅ | | `publish:attw` | — | — | — | ✅ | #### Why these splits? - **`check:tsc`, `check:oxlint`, `check:oxfmt`, `check:cspell`** are in pre-commit because they are fast (~0.2–0.5s each), fully offline, and naturally scope to staged files via the `LEFTHOOK_FILES` env var convention. They give instant feedback on what you typed. - **`check:knip` is NOT in pre-commit** because it scans the whole project (not staged files) and takes ~4s. Adding it would roughly triple pre-commit time. It's still fast enough to run locally before pushing, and CI catches it regardless. - **`check:outdated` is 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:publint` and `publish:attw` are NOT in `check`** — they belong to the `publish:` 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 pack` is involved). They run in the CI `publish` job immediately before `npm 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`). - `build` job on push and pull_request to `main` and on `workflow_dispatch`. Steps: `actions/checkout@v4`, `actions/setup-node@v4` (with `node-version-file: .node-version`, `cache: npm`), `npm ci`, `npm run build`, `npm run check`, `npm run test:ci`, then upload `coverage/` as an artifact. Runs the **full `check` chain** (all 6 `check:*` 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. - `publish` job: only on `refs/tags/*`, depends on `build`. Steps: `actions/checkout@v4`, `actions/setup-node@v4` (with `node-version-file: .node-version` and `registry-url`), `npm ci`, `npm run build`, `npm run publish:publint` (see §5 for why this is `publish:` and not `check:`), `npm run publish:attw`, and `npm publish --access public` with `NODE_AUTH_TOKEN` from secrets. Runs the **publish-tier checks** that validate the publishable artifact before publishing to npm. ## 7. Versioning & Publishing - **Version Update**: Use `npm version` to bump version after merging to main and before publishing. - **Publishing to npm**: Only publish from CI on tagged commits. - **Recommended Workflow**: 1. Develop and merge PRs to main 2. Run all checks via CI 3. Bump version with `npm version ` 4. Push tag to GitHub 5. 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:ci` script: ```jsonc "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 via `actions/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 via `src/index.ts`. - **Configuration**: - `sideEffects: false` in `package.json` (set). - ESM-only exports; `package.json#exports` field maps `.` to `{"types": "./dist/index.d.ts", "import": "./dist/index.js"}`. - Avoid top-level side effects in modules. - Explicit re-exports in `index.ts` for 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.shape` constructors and the `Matcher` interface. - `src/index.test.ts` — runtime + type-level tests using `node --test` + `expect-type`. - The human will implement the source code. ## 11. Included Templates from typescript-lib-starter-tiny ### .editorconfig ```plaintext # 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 ```plaintext # 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` - 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 - 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`'s `expectTypeOf(...)` inside `node --test` cases ## 12. Project Initialization & Commit Strategy - Start by initializing git with a `main` branch. - 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` - 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.md` and 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 in `package.json`. - The CI/CD pipeline publishes with `--access public` on tagged commits. ## 15. VSCode Integration - `.vscode/settings.json` uses `oxc.oxc-vscode` as the default formatter for `[typescript]`, `[javascript]`, `[json]`, `[jsonc]`, `[markdown]`, `[mdx]`, and `[yaml]` (oxfmt under the hood). ```json { "[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.json` recommends: - `oxc.oxc-vscode` (oxlint + oxfmt, replaces eslint/prettier/vitest) - `streetsidesoftware.code-spell-checker` (cspell) - `typescriptteam.native-preview` (TypeScript 7 nightly support; replaces the older `ms-vscode.vscode-typescript-next`) ```json { "recommendations": [ "oxc.oxc-vscode", "streetsidesoftware.code-spell-checker", "typescriptteam.native-preview" ] } ``` - `.vscode/tasks.json` is not currently provided; common tasks (build, test, lint, typecheck, format, spell, check:outdated) are run via the npm scripts in `package.json` from the integrated terminal. - VSCode uses TypeScript 7 via the `typescriptteam.native-preview` extension, with `oxc.oxc-vscode` for formatting and linting.