fix(node): set correct compilerOptions for Nest applications (#29725)

This PR fixes and issue where generating Nest app in the new TS setup
results in a build error due to missing `experimentalDecorators` option
in tsconfig. Decorators are required for Nest to work, but we do not set
it anymore in `tsconfig.base.json` by default.


## Current Behavior
Nest apps are broken

## Expected Behavior
Nest apps work

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Jack Hsu 2025-01-22 23:20:25 -05:00 committed by GitHub
parent 8357a2270a
commit a0cfe88f5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 62 additions and 8 deletions

View File

@ -131,6 +131,45 @@ describe('Node Applications', () => {
expect(err).toBeFalsy();
}
}, 300_000);
it('should be able to generate a nest application', async () => {
const nestapp = uniq('nodeapp');
const port = getRandomPort();
process.env.PORT = `${port}`;
runCLI(
`generate @nx/nest:app apps/${nestapp} --linter=eslint --unitTestRunner=jest`
);
expect(() => runCLI(`lint ${nestapp}`)).not.toThrow();
expect(() => runCLI(`test ${nestapp}`)).not.toThrow();
runCLI(`build ${nestapp}`);
checkFilesExist(`dist/apps/${nestapp}/main.js`);
const p = await runCommandUntil(
`serve ${nestapp}`,
(output) =>
output.includes(
`Application is running on: http://localhost:${port}/api`
),
{
env: {
NX_DAEMON: 'true',
},
}
);
const result = await getData(port, '/api');
expect(result.message).toMatch('Hello');
try {
await promisifiedTreeKill(p.pid, 'SIGKILL');
expect(await killPorts(port)).toBeTruthy();
} catch (err) {
expect(err).toBeFalsy();
}
}, 300_000);
});
function getRandomPort() {

View File

@ -278,6 +278,7 @@ describe('application generator', () => {
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "out-tsc/myapp",
@ -307,6 +308,8 @@ describe('application generator', () => {
expect(readJson(tree, 'myapp/tsconfig.spec.json')).toMatchInlineSnapshot(`
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "./out-tsc/jest",

View File

@ -1,12 +1,14 @@
import type { Tree } from '@nx/devkit';
import { joinPathFragments, updateJson } from '@nx/devkit';
import type { NormalizedOptions } from '../schema';
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
export function updateTsConfig(tree: Tree, options: NormalizedOptions): void {
updateJson(
tree,
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
(json) => {
json.compilerOptions.experimentalDecorators = true;
json.compilerOptions.emitDecoratorMetadata = true;
json.compilerOptions.target = 'es2021';
if (options.strict) {
@ -22,4 +24,20 @@ export function updateTsConfig(tree: Tree, options: NormalizedOptions): void {
return json;
}
);
// For TS solution, we don't extend from shared tsconfig.json, so we need to make sure decorators are also turned on for spec tsconfig.
if (isUsingTsSolutionSetup(tree)) {
const tsconfigSpecPath = joinPathFragments(
options.appProjectRoot,
'tsconfig.spec.json'
);
if (tree.exists(tsconfigSpecPath)) {
updateJson(tree, tsconfigSpecPath, (json) => {
json.compilerOptions ??= {};
json.compilerOptions.experimentalDecorators = true;
json.compilerOptions.emitDecoratorMetadata = true;
return json;
});
}
}
}

View File

@ -153,11 +153,7 @@ describe('e2eProjectGenerator', () => {
"jest.config.ts",
"src/**/*.ts",
],
"references": [
{
"path": "../api",
},
],
"references": [],
}
`);
});

View File

@ -10,7 +10,5 @@
"jest.config.ts",
"src/**/*.ts"
],
"references": [
{ "path": "<%= relativeProjectReferencePath %>" }
]
"references": []
}