Skip to content

refactor: update tsconfig to use TypeScript project references#16505

Merged
ematipico merged 4 commits intowithastro:mainfrom
ocavue-forks:ocavue-test-cleanup8
May 6, 2026
Merged

refactor: update tsconfig to use TypeScript project references#16505
ematipico merged 4 commits intowithastro:mainfrom
ocavue-forks:ocavue-test-cleanup8

Conversation

@ocavue
Copy link
Copy Markdown
Contributor

@ocavue ocavue commented Apr 28, 2026

WHAT is TypeScript project reference?

TypeScript project reference is a TypeScript feature that lets you split a TypeScript codebase into smaller, independently buildable projects. The mental model is similar to a PNPM monorepo: each project declares its dependencies on other projects, and the compiler builds them in the correct order. PNPM monorepo use package.json and dependencies/devDependencies fields to declare dependency graph, which TypeScript uses tsconfig.json and references field to declare the graph.

To use TypeScript project references, the following changes are needed:

  • Use tsc --build or tsc -b instead of tsc or tsc --project
  • Add composite: true to tsconfig
  • Add noEmit: false since you need to emit and cache declaration files
  • Use the references field in tsconfig to declare the dependency graph

WHY is it better?

  1. Faster builds. With project references, TypeScript only rebuilds the projects that have actually changed (and their dependents), instead of re-typechecking the entire codebase from scratch on every run. This significantly reduces build times in a large monorepo like this one.

    $ pnpm -w typecheck --clean # clean all emitted files and cache before typechecking
    > tsc -b --clean
    
    $ time pnpm -w typecheck # build the whole repo from scratch
    > tsc -b
    15.044 total
    
    $ time pnpm -w typecheck # build again. Nothing changed so it should be very fast
    > tsc -b
    0.510 total
    
    $ vim packages/integrations/cloudflare/src/index.ts # make a small change 
    $ time pnpm -w typecheck # build again. Only cloudflare package and its dependents should be rebuilt.
    > tsc -b
    1.886 total
  2. Better editor support. "Go to Definition" can jump straight to the original .ts source across project boundaries, even before pnpm build, and across different pnpm packages. See the demo below: it's a fresh clone of the repo with no dist/, and I can still jump from cloudflare/test/sessions.test.ts to cloudflare/src/index.ts (a source file in the same pnpm package) and on to packages/astro/src/types/public/config.ts (a source file in a different pnpm package).

    astro-jump-to-def.mp4

HOW are tsconfigs organized in this repo?

Shared configs live under configs/ at the repo root:

  • configs/tsconfig.base.json: The base tsconfig that every other tsconfig extends from.
  • configs/tsconfig.build.json: The tsconfig used to build packages. It includes src/ and writes declaration files to dist/.
  • configs/tsconfig.test.json: The tsconfig used to typecheck tests. It includes test/ and excludes test/fixtures/.
  • configs/tsconfig.language-tools.json: The tsconfig used to build packages under packages/language-tools/. The main difference from tsconfig.build.json is that it targets commonjs.

A classic package at packages/xx/ then has three tsconfig files:

// packages/xx/tsconfig.build.json
{
  "extends": "../../configs/tsconfig.build.json",
  // Use `references` to declare workspace dependencies. Every referenced package
  // is built before this one. This list should mirror the workspace dependencies
  // declared in package.json. We maintain it manually for now since the
  // dependency graph rarely changes.
  "references": [{ "path": "../dep1/tsconfig.json" }, { "path": "../dep2/tsconfig.json" }]
}
// packages/xx/tsconfig.test.json
{
  "extends": "../../configs/tsconfig.test.json",
  // Reference the package's own tsconfig.build.json so dist/ is built before
  // tests are typechecked.
  "references": [{ "path": "./tsconfig.build.json" }]
}
// packages/xx/tsconfig.json
{
  "extends": "../../configs/tsconfig.base.json",
  // `"files": []` makes this a pure solution file: it doesn't include any source
  // files itself. Its only purpose is to act as an entry point that points to
  // tsconfig.build.json and tsconfig.test.json.
  "files": [],
  "references": [{ "path": "./tsconfig.build.json" }, { "path": "./tsconfig.test.json" }]
}

The repo-root tsconfig.json is the top-level solution file that references every package:

// tsconfig.json
{
  "extends": "./configs/tsconfig.base.json",
  // A few loose files that don't belong to any package (e.g. config files) can
  // be listed here directly.
  "files": ["./prettier.config.mjs"],
  "references": [
    { "path": "./packages/pkg1/tsconfig.json" },
    { "path": "./packages/pkg2/tsconfig.json" },
    // ...
  ]
}

Testing

This PR touches a large number of tsconfig files, which are part of the build pipeline, so it's important to verify that the build artifacts are unchanged after the refactor.

I wrote a script that compares the packed output of this branch against the target branch. The two things it checks are:

  1. The dist/ directory contains the exact same .js and .d.ts files in both branches.
  2. The .tsbuildinfo cache files do not leak into the packed output.

The script and its output are available here: https://gist.github.com/ocavue/065faf832d2c45137c93dc2d0f1c72e5

Docs

Not included in this PR yet, but the plan is to add a section to CONTRIBUTING.md based on a refined version of this PR description.

@github-actions github-actions Bot added feat: markdown Related to Markdown (scope) pkg: svelte Related to Svelte (scope) pkg: vue Related to Vue (scope) pkg: react Related to React (scope) pkg: preact Related to Preact (scope) pkg: solid Related to Solid (scope) pkg: integration Related to any renderer integration (scope) pkg: create-astro Related to the `create-astro` package (scope) pkg: astro Related to the core `astro` package (scope) labels Apr 28, 2026
@ocavue ocavue force-pushed the ocavue-test-cleanup8 branch from f335cb5 to b5c76bf Compare April 28, 2026 13:44
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 28, 2026

⚠️ No Changeset found

Latest commit: bcab968

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot added the 🚨 action Modifies GitHub Actions label Apr 30, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 30, 2026

Merging this PR will not alter performance

✅ 18 untouched benchmarks


Comparing ocavue-forks:ocavue-test-cleanup8 (bcab968) with main (12a03f2)

Open in CodSpeed

@ocavue ocavue force-pushed the ocavue-test-cleanup8 branch from d48b758 to fceae66 Compare April 30, 2026 14:36
@ocavue ocavue changed the title wip: typescript project reference refactor refactor: restructure tsconfigs to use TypeScript project references May 2, 2026
Comment thread scripts/cmd/build.js
async function clean(outdir, cleanDts) {
const files = await glob('**', {
cwd: outdir,
dot: true,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers:

By default, glob doesn't include files or directories that start with ., but we want to clean the ._cache/ under dist/.

Comment thread tsconfig.eslint.json
"allowJs": true
},
"extends": "./tsconfig.base.json"
"extends": "./configs/tsconfig.base.json"
Copy link
Copy Markdown
Contributor Author

@ocavue ocavue May 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers:

We no longer need a separate tsconfig.eslint.json for ESLint. It can use the root tsconfig.json to scan the entire monorepo, with the added benefit of the TypeScript cache from pnpm build or pnpm typecheck.

However, I’d prefer to address this in a separate PR to keep this PR a bit smaller and easier to review.

Comment thread package.json
"test:e2e:match": "cd packages/astro && pnpm playwright install firefox && pnpm run test:e2e:match",
"test:e2e:hosts": "turbo run test:hosted",
"typecheck:tests": "pnpm -r run --sequential typecheck:tests",
"typecheck": "tsc -b",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers:

We don’t need a standalone typecheck:tests script in each package. The root typecheck already covers both src/ and test/ across all packages.

@ocavue ocavue changed the title refactor: restructure tsconfigs to use TypeScript project references refactor: update tsconfigs to use TypeScript project references May 2, 2026
@ocavue ocavue changed the title refactor: update tsconfigs to use TypeScript project references refactor: update tsconfig to use TypeScript project references May 2, 2026
@ocavue ocavue force-pushed the ocavue-test-cleanup8 branch from 0d10ea4 to 1f781dc Compare May 2, 2026 10:15
@ocavue ocavue marked this pull request as ready for review May 2, 2026 10:40
@ocavue ocavue force-pushed the ocavue-test-cleanup8 branch from 1f781dc to 824ce06 Compare May 5, 2026 12:48
@ematipico ematipico added pr preview Apply this label to a PR to generate a preview release labels May 5, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 5, 2026

npm i https://pkg.pr.new/astro@16505

commit: 824ce06

Comment thread configs/tsconfig.base.json Outdated
@Princesseuh
Copy link
Copy Markdown
Member

I like this, awesome work

Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tested it locally. It's freaking fast on my machine. Amazing work!

@ematipico ematipico merged commit 5a8cd09 into withastro:main May 6, 2026
28 of 30 checks passed
@ocavue
Copy link
Copy Markdown
Contributor Author

ocavue commented May 6, 2026

@ematipico Thanks. I will open a new PR to update CONTRIBUTING.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚨 action Modifies GitHub Actions feat: markdown Related to Markdown (scope) pkg: astro Related to the core `astro` package (scope) pkg: create-astro Related to the `create-astro` package (scope) pkg: integration Related to any renderer integration (scope) pkg: preact Related to Preact (scope) pkg: react Related to React (scope) pkg: solid Related to Solid (scope) pkg: svelte Related to Svelte (scope) pkg: vue Related to Vue (scope) pr preview Apply this label to a PR to generate a preview release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants