Skip to content

feat: respect tsconfig excludes when using swc#3276

Open
enduity wants to merge 6 commits intonestjs:masterfrom
enduity:respect-tsconfig-excludes-for-swc
Open

feat: respect tsconfig excludes when using swc#3276
enduity wants to merge 6 commits intonestjs:masterfrom
enduity:respect-tsconfig-excludes-for-swc

Conversation

@enduity
Copy link
Copy Markdown

@enduity enduity commented Feb 19, 2026

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
    • I am unsure where/how to add these. I would be happy to do so after receiving additional guidance.

PR Type

What kind of change does this PR introduce?

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior?

When switching a fresh NestJS starter over to use SWC (using the official guide), the exclude field in tsconfig.build.json is no longer considered for build output. This means that a file like src/app.controller.spec.ts (included in the starter) ends up in the build as dist/app.controller.spec.js, even though tsconfig.build.json contains an exclusion glob pattern of "**/*spec.ts".

It is expected that switching the NestJS builder from tsc to swc does not cause test files to be included in the nest build output.

Issue Number: #2976

What is the new behavior?

The nest build command uses the parsed TypeScript config's exclude value to generate a default value for the ignore @swc/cli option.

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Unfortunately, it is possible that someone has come to rely on the unexpected behaviour of tsconfig.build.json excludes not working for SWC compilation. However, I do think it's extremely unlikely. I welcome opposing thoughts.

Other information

Full disclosure that I did use an AI agent to generate the tests specifically. I can drop the relevant commits (25f9438, f1107f1) if necessary, but then this feature will not have any tests. I believe we should at leave in a test of the retrieval of exclude from a tsconfig.json, because the TypeScript API could change in the future – the ParsedCommandLine['raw'] value we use is currently typed as any by TypeScript.

Also, there is already a similar PR, but it uses potentially more fragile mechanisms to retrieve the exclude value from a tsconfig.json:

@glensc
Copy link
Copy Markdown

glensc commented Feb 19, 2026

@enduity these commits are not pushed: 25f9438, f1107f1

@enduity enduity force-pushed the respect-tsconfig-excludes-for-swc branch from 8709bf7 to f1107f1 Compare February 20, 2026 07:04
@enduity
Copy link
Copy Markdown
Author

enduity commented Feb 20, 2026

@enduity these commits are not pushed: 25f9438, f1107f1

fixed, thanks

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for respecting the exclude field in tsconfig.json (or tsconfig.build.json) when using the SWC builder for nest build. Previously, files matching the exclude globs (e.g., **/*.spec.ts) would still appear in the build output when using SWC, while they were correctly excluded when using the default tsc builder.

Changes:

  • TsConfigProviderOutput type added to tsconfig-provider.ts, which extends the parsed TypeScript options with an exclude: string[] field extracted from the raw config. The tsOptions type in swc-compiler.ts and build.action.ts is updated to use this new type.
  • swcDefaultsFactory in swc-defaults.ts now populates cliOptions.ignore from tsOptions.exclude, passing the tsconfig excludes to the SWC CLI's --ignore option.
  • New test files added for both TsConfigProvider and swcDefaultsFactory.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
lib/compiler/helpers/tsconfig-provider.ts Adds TsConfigProviderOutput type and parseExclude helper; getByConfigFilename now extracts and validates exclude from raw tsconfig
lib/compiler/defaults/swc-defaults.ts Uses tsOptions.exclude to set SWC CLI's ignore option
lib/compiler/swc/swc-compiler.ts Updates SwcCompilerExtras.tsOptions type from ts.CompilerOptions to TsConfigProviderOutput['options']
actions/build.action.ts Updates runSwc parameter type; adds (duplicate) TsConfigProviderOutput import
test/lib/compiler/helpers/tsconfig-provider.spec.ts New tests for TsConfigProvider.getByConfigFilename, including exclude parsing logic
test/lib/compiler/defaults/swc-defaults.spec.ts New tests for swcDefaultsFactory, including the new ignore field behavior

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import { isModuleAvailable } from '../lib/utils/is-module-available';
import { AbstractAction } from './abstract.action';
import webpack = require('webpack');
import { TsConfigProviderOutput } from '../lib/compiler/helpers/tsconfig-provider';
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The TsConfigProviderOutput type is imported from '../lib/compiler/helpers/tsconfig-provider' at line 27, but TsConfigProvider is already imported from the same module at line 10. These two imports from the same module should be combined into a single import statement. Additionally, the new import is placed after the webpack require statement, breaking the grouping of ES module imports. It should be merged with the existing import at line 10.

Copilot uses AI. Check for mistakes.
expect(result.options.exclude).toEqual(['node_modules', 'dist']);
expect(result.fileNames).toBeDefined();

expect(fs.readFileSync).toHaveBeenCalledWith(configPath);
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The assertion expect(fs.readFileSync).toHaveBeenCalledWith(configPath) is fragile because TypeScript's internal sys.readFile calls fs.readFileSync with additional arguments (e.g., an encoding parameter like 'utf8'). The toHaveBeenCalledWith matcher requires an exact argument match, so this assertion will fail when TypeScript passes the encoding argument. Consider using expect(fs.readFileSync).toHaveBeenCalledWith(configPath, expect.anything()) or toHaveBeenCalledWith(expect.stringContaining('tsconfig.json'), expect.anything()), or replace the assertion with toHaveBeenCalled() since the actual content verification is done through the returned result.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants