From adf27776a81f5ea04a2c175ed74ef9510533bc5b Mon Sep 17 00:00:00 2001 From: Kilian Panot Date: Mon, 23 Dec 2024 19:32:01 +0900 Subject: [PATCH] fix(workspace): add tsconfig.json dedicated to the application --- codecov.yml | 3 +++ .../schematics/application/index.spec.ts | 25 +++++++++++++------ .../workspace/schematics/application/index.ts | 23 +++++++++++++++-- .../templates/tsconfig.json.template | 8 ++++++ 4 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 packages/@o3r/workspace/schematics/application/templates/tsconfig.json.template diff --git a/codecov.yml b/codecov.yml index 01ea2268e2..395c7b5734 100644 --- a/codecov.yml +++ b/codecov.yml @@ -32,6 +32,9 @@ ignore: - 'packages/@o3r-training/showcase-sdk/src/spec/**/*' - 'tools/github-actions/*/packaged-action/**/*' + # Templates + - '**/*.template' + comment: layout: "condensed_header, condensed_files, condensed_footer" behavior: default diff --git a/packages/@o3r/workspace/schematics/application/index.spec.ts b/packages/@o3r/workspace/schematics/application/index.spec.ts index f67ad422eb..298894e488 100644 --- a/packages/@o3r/workspace/schematics/application/index.spec.ts +++ b/packages/@o3r/workspace/schematics/application/index.spec.ts @@ -29,19 +29,19 @@ const collectionPath = path.join(__dirname, '../../collection.json'); let runner: SchematicTestRunner; describe('generateApplication', () => { - let initalTree: UnitTestTree; + let initialTree: UnitTestTree; beforeEach(() => { - initalTree = new UnitTestTree(Tree.empty()); - initalTree.create('angular.json', fs.readFileSync(path.resolve(__dirname, '..', '..', 'testing', 'mocks', 'angular.mocks.json'))); - initalTree.create('package.json', fs.readFileSync(path.resolve(__dirname, '..', '..', 'testing', 'mocks', 'package.mocks.json'))); + initialTree = new UnitTestTree(Tree.empty()); + initialTree.create('angular.json', fs.readFileSync(path.resolve(__dirname, '..', '..', 'testing', 'mocks', 'angular.mocks.json'))); + initialTree.create('package.json', fs.readFileSync(path.resolve(__dirname, '..', '..', 'testing', 'mocks', 'package.mocks.json'))); runner = new SchematicTestRunner('schematics', collectionPath); const angularPackageJson = require.resolve('@schematics/angular/package.json'); runner.registerCollection('@schematics/angular', path.resolve(path.dirname(angularPackageJson), require(angularPackageJson).schematics)); }); it('should generate an application', async () => { - const tree = await runner.runSchematic('application', { name: 'test', routing: true }, initalTree); + const tree = await runner.runSchematic('application', { name: 'test', routing: true }, initialTree); const mockExternalSchematic = require('@angular-devkit/schematics').externalSchematic as jest.Mock; expect(mockExternalSchematic).toHaveBeenCalledWith('@schematics/angular', 'application', expect.objectContaining({ name: 'test', @@ -55,7 +55,7 @@ describe('generateApplication', () => { }); it('should generate an application with provided path', async () => { - const tree = await runner.runSchematic('application', { name: 'test', path: '/apps' }, initalTree); + const tree = await runner.runSchematic('application', { name: 'test', path: '/apps' }, initialTree); const mockExternalSchematic = require('@angular-devkit/schematics').externalSchematic as jest.Mock; expect(mockExternalSchematic).toHaveBeenCalledWith('@schematics/angular', 'application', expect.objectContaining({ name: 'test', @@ -67,15 +67,24 @@ describe('generateApplication', () => { }); }); + it('should register the application in tsconfig', async () => { + initialTree.create('/tsconfig.base.json', JSON.stringify({})); + const tree = await runner.runSchematic('application', { name: 'test', path: '/apps' }, initialTree); + const mockExternalSchematic = require('@angular-devkit/schematics').externalSchematic as jest.Mock; + expect(mockExternalSchematic).toHaveBeenCalled(); + expect(tree.exists('/apps/test/tsconfig.json')).toBe(true); + expect(tree.readJson('/apps/test/tsconfig.json')).toMatchObject({ extends: '../../tsconfig.base.json' }); + }); + it('should throw error if NX context is detected', async () => { require('@o3r/schematics').isNxContext.mockReturnValueOnce(true); - await expect(runner.runSchematic('application', { name: 'test' }, initalTree)).rejects.toThrow( + await expect(runner.runSchematic('application', { name: 'test' }, initialTree)).rejects.toThrow( 'NX is not currently supported. Feature tracked via https://github.com/AmadeusITGroup/otter/issues/720'); }); it('should throw error if no workspace configuration is found', async () => { require('@o3r/schematics').getWorkspaceConfig.mockReturnValueOnce(null); - await expect(runner.runSchematic('application', { name: 'test' }, initalTree)).rejects.toThrow( + await expect(runner.runSchematic('application', { name: 'test' }, initialTree)).rejects.toThrow( 'The `path` option is not provided and no workspace configuration file found to define it.'); }); }); diff --git a/packages/@o3r/workspace/schematics/application/index.ts b/packages/@o3r/workspace/schematics/application/index.ts index daa643b217..a036fd9cf1 100644 --- a/packages/@o3r/workspace/schematics/application/index.ts +++ b/packages/@o3r/workspace/schematics/application/index.ts @@ -12,6 +12,7 @@ import { Schematic, strings, template, + type Tree, url, } from '@angular-devkit/schematics'; import { @@ -42,6 +43,21 @@ import type { NgGenerateApplicationSchema, } from './schema'; +/** + * Find the relative path to a configuration file at the monorepo root + * @param tree + * @param files List of files to look for, the first of the list will be used + * @param originPath Path from where to calculate the relative path + */ +function findConfigFileRelativePath(tree: Tree, files: string[], originPath: string) { + const foundFile = files.find((file) => tree.exists(`/${file}`)); + if (foundFile === undefined) { + return ''; + } + + return path.posix.relative(originPath, `/${foundFile}`); +} + /** * Add an Otter application to a monorepo * @param options Schematic options @@ -51,10 +67,11 @@ function generateApplicationFn(options: NgGenerateApplicationSchema): Rule { const packageJsonName = strings.dasherize(options.name); const cleanName = packageJsonName.replace(/^@/, '').replaceAll(/\//g, '-'); - const addProjectSpecificFiles = (targetPath: string, rootDependencies: Record) => { + const addProjectSpecificFiles = (targetPath: string, rootDependencies: Record, tsconfigBasePath: string) => { return mergeWith(apply(url('./templates'), [ template({ ...options, + tsconfigBasePath, enforceTildeRange, name: packageJsonName, rootDependencies @@ -109,13 +126,15 @@ function generateApplicationFn(options: NgGenerateApplicationSchema): Rule { } } as const satisfies Record; + const tsconfigBasePath = findConfigFileRelativePath(tree, ['tsconfig.base.json', 'tsconfig.json'], targetPath); + return chain([ externalSchematic>('@schematics/angular', 'application', { ...Object.entries(extendedOptions).reduce((acc, [key, value]) => (angularOptions.includes(key) ? { ...acc, [key]: value } : acc), {}), name: cleanName, projectRoot, style: Style.Scss }), - addProjectSpecificFiles(targetPath, rootDependencies), + addProjectSpecificFiles(targetPath, rootDependencies, tsconfigBasePath), updateProjectTsConfig(targetPath, 'tsconfig.app.json', { updateInputFiles: true }), setupDependencies({ dependencies, diff --git a/packages/@o3r/workspace/schematics/application/templates/tsconfig.json.template b/packages/@o3r/workspace/schematics/application/templates/tsconfig.json.template new file mode 100644 index 0000000000..1f6327668f --- /dev/null +++ b/packages/@o3r/workspace/schematics/application/templates/tsconfig.json.template @@ -0,0 +1,8 @@ +/* For IDE usage only */ +{ + "extends": "<%= tsconfigBasePath %>", + "references": [ + {"path": "./tsconfig.app.json"}, + {"path": "./tsconfig.spec.json"} + ] +}