diff --git a/packages/schematics/angular/application/files/common-files/tsconfig.app.json.template b/packages/schematics/angular/application/files/common-files/tsconfig.app.json.template index a65978e44714..c9064b22766b 100644 --- a/packages/schematics/angular/application/files/common-files/tsconfig.app.json.template +++ b/packages/schematics/angular/application/files/common-files/tsconfig.app.json.template @@ -3,13 +3,14 @@ { "extends": "<%= relativePathToWorkspaceRoot %>/tsconfig.json", "compilerOptions": { + "composite": true, "outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/app", "types": [] }, - "files": [ - "src/main.ts" - ], "include": [ - "src/**/*.d.ts" + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" ] } diff --git a/packages/schematics/angular/application/files/common-files/tsconfig.spec.json.template b/packages/schematics/angular/application/files/common-files/tsconfig.spec.json.template index 3a0a2b43e8f1..40b32b8bc458 100644 --- a/packages/schematics/angular/application/files/common-files/tsconfig.spec.json.template +++ b/packages/schematics/angular/application/files/common-files/tsconfig.spec.json.template @@ -3,13 +3,13 @@ { "extends": "<%= relativePathToWorkspaceRoot %>/tsconfig.json", "compilerOptions": { + "composite": true, "outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/spec", "types": [ "jasmine" ] }, "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" + "src/**/*.ts" ] } diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts index 2273afc311b1..b85ad2b41d36 100644 --- a/packages/schematics/angular/application/index.ts +++ b/packages/schematics/angular/application/index.ts @@ -26,12 +26,28 @@ import { import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { Schema as ComponentOptions } from '../component/schema'; import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies'; +import { JSONFile } from '../utility/json-file'; import { latestVersions } from '../utility/latest-versions'; import { relativePathToWorkspaceRoot } from '../utility/paths'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders, ProjectType } from '../utility/workspace-models'; import { Schema as ApplicationOptions, Style } from './schema'; +function addTsProjectReference(...paths: string[]) { + return (host: Tree) => { + if (!host.exists('tsconfig.json')) { + return host; + } + + const newReferences = paths.map((path) => ({ path })); + + const file = new JSONFile(host, 'tsconfig.json'); + const jsonPath = ['references']; + const value = file.get(jsonPath); + file.modify(jsonPath, Array.isArray(value) ? [...value, ...newReferences] : newReferences); + }; +} + export default function (options: ApplicationOptions): Rule { return async (host: Tree, context: SchematicContext) => { const { appDir, appRootSelector, componentOptions, folderName, sourceDir } = @@ -39,6 +55,10 @@ export default function (options: ApplicationOptions): Rule { return chain([ addAppToWorkspaceFile(options, appDir, folderName), + addTsProjectReference(join(normalize(appDir), 'tsconfig.app.json')), + options.skipTests + ? noop() + : addTsProjectReference(join(normalize(appDir), 'tsconfig.spec.json')), options.standalone ? noop() : schematic('module', { diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index 31c505a1548a..e9b84948db24 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -93,8 +93,13 @@ describe('Application Schematic', () => { it('should set the right paths in the tsconfig.app.json', async () => { const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree); - const { files, extends: _extends } = readJsonFile(tree, '/projects/foo/tsconfig.app.json'); - expect(files).toEqual(['src/main.ts']); + const { + include, + exclude, + extends: _extends, + } = readJsonFile(tree, '/projects/foo/tsconfig.app.json'); + expect(include).toEqual(['src/**/*.ts']); + expect(exclude).toEqual(['src/**/*.spec.ts']); expect(_extends).toBe('../../tsconfig.json'); }); @@ -105,6 +110,34 @@ describe('Application Schematic', () => { expect(_extends).toBe('../../tsconfig.json'); }); + it('should add project references in the root tsconfig.json', async () => { + const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree); + + const { references } = readJsonFile(tree, '/tsconfig.json'); + expect(references).toContain( + jasmine.objectContaining({ path: 'projects/foo/tsconfig.app.json' }), + ); + expect(references).toContain( + jasmine.objectContaining({ path: 'projects/foo/tsconfig.spec.json' }), + ); + }); + + it('should not add spec project reference in the root tsconfig.json with "skipTests" enabled', async () => { + const tree = await schematicRunner.runSchematic( + 'application', + { ...defaultOptions, skipTests: true }, + workspaceTree, + ); + + const { references } = readJsonFile(tree, '/tsconfig.json'); + expect(references).toContain( + jasmine.objectContaining({ path: 'projects/foo/tsconfig.app.json' }), + ); + expect(references).not.toContain( + jasmine.objectContaining({ path: 'projects/foo/tsconfig.spec.json' }), + ); + }); + it('should install npm dependencies when `skipInstall` is false', async () => { await schematicRunner.runSchematic( 'application', diff --git a/packages/schematics/angular/server/index.ts b/packages/schematics/angular/server/index.ts index a8baccf0d503..50f624e078cd 100644 --- a/packages/schematics/angular/server/index.ts +++ b/packages/schematics/angular/server/index.ts @@ -119,10 +119,6 @@ function updateConfigFileApplicationBuilder(options: ServerOptions): Rule { function updateTsConfigFile(tsConfigPath: string): Rule { return (host: Tree) => { const json = new JSONFile(host, tsConfigPath); - const filesPath = ['files']; - const files = new Set((json.get(filesPath) as string[] | undefined) ?? []); - files.add('src/' + serverMainEntryName); - json.modify(filesPath, [...files]); const typePath = ['compilerOptions', 'types']; const types = new Set((json.get(typePath) as string[] | undefined) ?? []); diff --git a/packages/schematics/angular/server/index_spec.ts b/packages/schematics/angular/server/index_spec.ts index 09dfbc73d2a1..f3e5e277d8ef 100644 --- a/packages/schematics/angular/server/index_spec.ts +++ b/packages/schematics/angular/server/index_spec.ts @@ -167,7 +167,6 @@ describe('Server Schematic', () => { const filePath = '/projects/bar/tsconfig.app.json'; const contents = parseJson(tree.readContent(filePath).toString()); expect(contents.compilerOptions.types).toEqual(['node']); - expect(contents.files).toEqual(['src/main.ts', 'src/main.server.ts']); }); it(`should add 'provideClientHydration' to the providers list`, async () => { diff --git a/packages/schematics/angular/ssr/index.ts b/packages/schematics/angular/ssr/index.ts index b81188340f0b..e589395dac73 100644 --- a/packages/schematics/angular/ssr/index.ts +++ b/packages/schematics/angular/ssr/index.ts @@ -154,6 +154,13 @@ function updateApplicationBuilderTsConfigRule(options: SSROptions): Rule { } const json = new JSONFile(host, tsConfigPath); + + // Skip adding the files entry if the server entry would already be included + const include = json.get(['include']); + if (Array.isArray(include) && include.includes('src/**/*.ts')) { + return; + } + const filesPath = ['files']; const files = new Set((json.get(filesPath) as string[] | undefined) ?? []); files.add('src/server.ts'); diff --git a/packages/schematics/angular/ssr/index_spec.ts b/packages/schematics/angular/ssr/index_spec.ts index a9f4eff7ac5e..97b534aba8e1 100644 --- a/packages/schematics/angular/ssr/index_spec.ts +++ b/packages/schematics/angular/ssr/index_spec.ts @@ -70,13 +70,28 @@ describe('SSR Schematic', () => { expect((schematicRunner.tasks[0].options as { command: string }).command).toBe('install'); }); - it(`should update 'tsconfig.app.json' files with Express main file`, async () => { + it(`should not update 'tsconfig.app.json' files with Express main file already included`, async () => { const tree = await schematicRunner.runSchematic('ssr', defaultOptions, appTree); const { files } = tree.readJson('/projects/test-app/tsconfig.app.json') as { files: string[]; }; - expect(files).toEqual(['src/main.ts', 'src/main.server.ts', 'src/server.ts']); + expect(files).toBeUndefined(); + }); + + it(`should update 'tsconfig.app.json' files with Express main file if not included`, async () => { + const appTsConfigContent = appTree.readJson('/projects/test-app/tsconfig.app.json') as { + include?: string[]; + }; + appTsConfigContent.include = []; + appTree.overwrite('/projects/test-app/tsconfig.app.json', JSON.stringify(appTsConfigContent)); + + const tree = await schematicRunner.runSchematic('ssr', defaultOptions, appTree); + const { files } = tree.readJson('/projects/test-app/tsconfig.app.json') as { + files: string[]; + }; + + expect(files).toContain('src/server.ts'); }); }); diff --git a/packages/schematics/angular/workspace/files/tsconfig.json.template b/packages/schematics/angular/workspace/files/tsconfig.json.template index 92d84123aeee..798ec8305a16 100644 --- a/packages/schematics/angular/workspace/files/tsconfig.json.template +++ b/packages/schematics/angular/workspace/files/tsconfig.json.template @@ -2,8 +2,7 @@ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "compileOnSave": false, - "compilerOptions": { - "outDir": "./dist/out-tsc",<% if (strict) { %> + "compilerOptions": {<% if (strict) { %> "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, @@ -22,5 +21,6 @@ "strictInputAccessModifiers": true, "typeCheckHostBindings": true, "strictTemplates": true<% } %> - } + }, + "files": [] }