npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aleph-alpha/tsconfig-frontend

v0.7.3

Published

Shared strict TypeScript presets (base, vue-app, lib, node) for Aleph Alpha frontend projects

Readme

@aleph-alpha/tsconfig-frontend

Shared, strict-by-default TypeScript configuration for Aleph Alpha frontend projects.

It ships a small set of composable presets. The idea: everything that is genuinely shareable (language target, module resolution, the strict family, common quality flags) lives here in one reviewed place; everything project-specific (paths, include/exclude, emit layout, environment types) stays in the consuming project. Every compilerOption a preset sets can be overridden locally — presets are a baseline, not a straitjacket.

pnpm add -D @aleph-alpha/tsconfig-frontend

Why four presets instead of one?

base is the single extensible core. The other three exist because TypeScript compilation has distinct, mutually-exclusive contexts that one config cannot serve at once — and which every frontend project already separates (a tsconfig.app.json for browser code + a tsconfig.node.json for tooling is the de-facto standard):

  • Browser/Vue code needs DOM libs + JSX; Node tooling must not have them.
  • Node tooling needs types: ["node"]; browser code must not pollute its global scope with them.
  • Packages that emit .d.ts need declaration/composite; apps that don't emit would error on them.

Collapsing to one config wouldn't remove that configuration — it would just copy-paste it into every project (and invite mistakes like a too-low lib). The presets move it into one place.

Presets at a glance

| Preset | Extends | Use it for | Emits? | | --- | --- | --- | --- | | /base | — | Framework-agnostic TS: shared libs, utils, pure logic | no | | /vue-app | — (mirrors @vue/tsconfig) | Vue SFCs & browser code (apps and Vue component libs) | no (Vite/dts plugin emits) | | /lib | ./base | Buildable libraries that emit .d.ts via tsc | yes | | /node | — (mirrors @tsconfig/node24) | Node tooling: vite.config, vitest.config, eslint.config, scripts | no |

The bare @aleph-alpha/tsconfig-frontend entry still resolves (it maps to the legacy flat config) for backwards compatibility. New code should extend one of the four presets above.


/base

What — the framework-agnostic strict baseline. No DOM, no Node, no JSX assumptions.

Sets (the shareable core all other presets build on conceptually):

{
  "target": "es2022",
  "module": "esnext",
  "moduleResolution": "bundler",
  "lib": ["es2022"],

  // strict family
  "strict": true,                     // implies noImplicitAny, strictNullChecks, ...
  "noUncheckedIndexedAccess": true,   // arr[i] / obj[key] is T | undefined
  "verbatimModuleSyntax": true,       // type-only imports must use `import type`
  "useDefineForClassFields": true,

  // safe bug-catchers (no emit impact, catch real bugs)
  "noImplicitOverride": true,         // overrides must be marked `override`
  "noFallthroughCasesInSwitch": true,
  "noImplicitReturns": true,

  // ergonomics
  "isolatedModules": true,
  "esModuleInterop": true,
  "forceConsistentCasingInFileNames": true,
  "importHelpers": true,
  "resolveJsonModule": true,
  "skipLibCheck": true,
  "sourceMap": true
}

Why — this is the strictness contract for the whole org. noUncheckedIndexedAccess and the bug-catchers catch real defects; verbatimModuleSyntax keeps imports unambiguous for bundlers.

When to use — pure TypeScript with no browser or Node assumptions (e.g. a utils package).

How to use

{
  "extends": "@aleph-alpha/tsconfig-frontend/base",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src/**/*.ts"]
}

/vue-app

What — config for Vue Single-File Components and browser code. Mirrors the Vue team's official @vue/tsconfig/tsconfig.dom.json, inlined verbatim rather than extended (see Why the presets are self-contained).

Sets (on top of the inlined @vue/tsconfig baseline, which already provides strict, verbatimModuleSyntax, useDefineForClassFields, jsx: "preserve", jsxImportSource: "vue", noEmit):

{
  "lib": ["ES2022", "DOM", "DOM.Iterable"],
  "types": ["vite/client"],
  "noImplicitOverride": true,
  "noFallthroughCasesInSwitch": true,
  "noImplicitReturns": true
}

Why.vue files need JSX + DOM libs + Vue's jsxImportSource. Mirroring @vue/tsconfig (rather than re-deriving it) gives us the Vue team's battle-tested defaults; re-merge the upstream file by hand when bumping it.

When to use — any app or component library containing .vue files. The actual JS/.d.ts output is produced by Vite / vite-plugin-dts, not tsc — so noEmit: true (from the inlined baseline) is correct here.

How to use

{
  "extends": "@aleph-alpha/tsconfig-frontend/vue-app",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

/lib

What — for buildable library packages that emit type declarations through tsc.

Sets (extends ./base):

{
  "declaration": true,
  "composite": true,
  "lib": ["es2022", "dom", "dom.iterable"]
}

Why — emitting libraries need declaration (to ship .d.ts) and composite (project references / incremental builds). These would be wrong for apps, so they live in their own preset.

When — a non-Vue package whose .d.ts is produced by tsc (not by Vite). Typically paired with a local outDir/rootDir.

How

{
  "extends": "@aleph-alpha/tsconfig-frontend/lib",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src/**/*.ts"]
}

Note: composite is incompatible with outFile. If a package bundles its declarations into a single file via outFile, extend /base instead and add declaration + emitDeclarationOnly locally (see the outFile gotcha below).


/node

What — config for Node-executed tooling files, not shipped browser code.

Sets (on top of the inlined @tsconfig/node24 baseline):

{
  "noEmit": true,
  "module": "esnext",
  "moduleResolution": "bundler",
  "types": ["node"],
  "noImplicitOverride": true,
  "noFallthroughCasesInSwitch": true,
  "noImplicitReturns": true
}

Whyvite.config.ts, vitest.config.ts, eslint.config.ts, build scripts, etc. run in Node and need Node globals/types, not the DOM. Keeping them in a separate config means browser code never accidentally pulls in Node types and vice-versa.

When to use — the tsconfig.node.json that type-checks your config/tooling files.

How to use

{
  "extends": "@aleph-alpha/tsconfig-frontend/node",
  "include": ["vite.config.ts", "vitest.config.ts", "eslint.config.ts"]
}

What the package owns vs. what the project owns

In the presets (shareable): target, module/moduleResolution, baseline lib, the strict family, the bug-catchers, and the emit shape for /lib. Change these only by changing the package (so every consumer moves together).

In the project (always local & overridable): paths + baseUrl, include/exclude, rootDir/outDir/tsBuildInfoFile, environment types, references, experimentalDecorators/emitDecoratorMetadata, and any per-project strict opt-out/opt-in.

Why the presets are self-contained

No preset here extends a config from another package/vue-app and /node inline their upstream baselines (@vue/tsconfig, @tsconfig/node24) instead of extending them.

TypeScript resolves a package-style extends the way Node resolves an import, walking up node_modules and following symlinks. Other tools that read tsconfig.json do not. Playwright, for one, looks only in <the extending file>/node_modules/… — so with pnpm, where a package's own dependencies live in the store rather than inside the package folder, it cannot follow node.json → @tsconfig/node24 and fails to load the config entirely:

Failed to load tsconfig file at …/apps/<app>-e2e/tsconfig.json:
Failed to resolve "extends" path "@tsconfig/node24/tsconfig.json"
referenced from …/node_modules/@aleph-alpha/tsconfig-frontend/node.json

Keeping every preset self-contained removes that whole class of failure — for Playwright and for any other tool with a naive resolver. The cost is that upstream bumps must be re-merged by hand; the provenance (package + exact version) is recorded in a "//" key at the top of each affected preset.

Relative extends within this package (/lib → ./base) is fine — every tool handles that.

@vue/tsconfig and @tsconfig/node24 remain in this package's dependencies even though no preset extends them anymore. That is deliberate: dropping them would break any consumer that extends either package directly and relies on it arriving transitively. They are kept for backwards compatibility and slated for removal in a future major — if you extend either one directly, declare it in your own package.json.

Common overrides & gotchas

These come up in real migrations — all are legitimate local overrides:

  • Need a newer JS built-in (Array.prototype.toSorted = ES2023, etc.)? @vue/tsconfig and our presets target a conservative lib. Bump it locally: "lib": ["ES2023", "DOM", "DOM.Iterable"]. This only affects type-checking — Vite controls the real browser target.
  • Code uses window/DOM but extends /base? Add DOM to lib locally: "lib": ["ES2022", "DOM"]. (Or use /vue-app, which includes DOM.)
  • outFile for a single bundled .d.ts? outFile is incompatible with both composite and verbatimModuleSyntax. Extend /base, set declaration + emitDeclarationOnly + outFile, and override "verbatimModuleSyntax": false locally.
  • Node tooling that isn't a config file (e.g. a Node-targeted library)? Extend /base and add "types": ["node"] rather than /node (which forces noEmit).
  • Migrating a codebase that predates noImplicitAny? If turning it on surfaces a large backlog, you may temporarily set "noImplicitAny": false locally and burn the errors down later — but keep it on in base so new/clean code stays strict.

Convention: document the reason for any strict opt-out in the PR description, not as a comment in the tsconfig — so it doesn't rot when the override is later removed.