From cae14c631eae5fc3522183e70fd8b36ef5356fb0 Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Tue, 18 Apr 2023 18:01:24 +0200 Subject: [PATCH] feat(tools): implement tsconfig-base-all generator --- tools/generators/tsconfig-base-all/README.md | 20 ++- .../files/constants.ts__tmpl__ | 1 - .../tsconfig-base-all/index.spec.ts | 115 ++++++++++++++++-- tools/generators/tsconfig-base-all/index.ts | 49 +++----- .../tsconfig-base-all/lib/utils.spec.ts | 7 -- .../generators/tsconfig-base-all/lib/utils.ts | 39 +++++- .../generators/tsconfig-base-all/schema.json | 13 +- tools/generators/tsconfig-base-all/schema.ts | 4 +- 8 files changed, 182 insertions(+), 66 deletions(-) delete mode 100644 tools/generators/tsconfig-base-all/files/constants.ts__tmpl__ delete mode 100644 tools/generators/tsconfig-base-all/lib/utils.spec.ts diff --git a/tools/generators/tsconfig-base-all/README.md b/tools/generators/tsconfig-base-all/README.md index 25c3f7714b001..bf5861795bcb7 100644 --- a/tools/generators/tsconfig-base-all/README.md +++ b/tools/generators/tsconfig-base-all/README.md @@ -1,20 +1,22 @@ # tsconfig-base-all -Workspace Generator ...TODO... +Workspace Generator for generating/updating `/tsconfig.base.all.json`. + +> `tsconfig.base.all.json` contains all monorepo project path aliases, and reflect source of truth for all monorepo packages. - [Usage](#usage) - [Examples](#examples) - [Options](#options) - - [`name`](#name) + - [`verify`](#verify) ## Usage ```sh -yarn nx workspace-generator tsconfig-base-all ... +yarn nx workspace-generator tsconfig-base-all ``` Show what will be generated without writing to disk: @@ -31,8 +33,14 @@ yarn nx workspace-generator tsconfig-base-all ## Options -#### `name` +#### `verify` + +Type: `boolean` -Type: `string` +use this option on CI to check if base.all.json is up to date and in sync with all other base configs -TODO... +Following will throw an error if `tsconfig.base.all.json` is out of date + +```sh +yarn nx workspace-generator tsconfig-base-all --verify +``` diff --git a/tools/generators/tsconfig-base-all/files/constants.ts__tmpl__ b/tools/generators/tsconfig-base-all/files/constants.ts__tmpl__ deleted file mode 100644 index d9913111a9dc7..0000000000000 --- a/tools/generators/tsconfig-base-all/files/constants.ts__tmpl__ +++ /dev/null @@ -1 +0,0 @@ -export const variable = "<%= name %>"; \ No newline at end of file diff --git a/tools/generators/tsconfig-base-all/index.spec.ts b/tools/generators/tsconfig-base-all/index.spec.ts index 219a5b3cbf3bb..956d08ad247a0 100644 --- a/tools/generators/tsconfig-base-all/index.spec.ts +++ b/tools/generators/tsconfig-base-all/index.spec.ts @@ -1,20 +1,121 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; -import { Tree, readProjectConfiguration } from '@nrwl/devkit'; +import { Tree, writeJson, readJson, updateJson } from '@nrwl/devkit'; import generator from './index'; import { TsconfigBaseAllGeneratorSchema } from './schema'; describe('tsconfig-base-all generator', () => { - let appTree: Tree; - const options: TsconfigBaseAllGeneratorSchema = { name: 'test' }; + let tree: Tree; + const options: TsconfigBaseAllGeneratorSchema = {}; beforeEach(() => { - appTree = createTreeWithEmptyWorkspace(); + tree = createTreeWithEmptyWorkspace(); + writeJson(tree, '/tsconfig.base.v0.json', { + compilerOptions: { + paths: { + '@proj/v0-one': ['packages/v0-one/src/index.ts'], + '@proj/v0-two': ['packages/v0-two/src/index.ts'], + }, + }, + }); + writeJson(tree, '/tsconfig.base.v8.json', { + compilerOptions: { + paths: { + '@proj/v8-one': ['packages/v8-one/src/index.ts'], + '@proj/v8-two': ['packages/v8-two/src/index.ts'], + }, + }, + }); + writeJson(tree, '/tsconfig.base.json', { + compilerOptions: { + paths: { + '@proj/one': ['packages/one/src/index.ts'], + '@proj/two': ['packages/two/src/index.ts'], + }, + }, + }); }); it('should run successfully', async () => { - await generator(appTree, options); - const config = readProjectConfiguration(appTree, 'test'); - expect(config).toBeDefined(); + await generator(tree, options); + const baseAllJson = readJson(tree, '/tsconfig.base.all.json'); + + expect(baseAllJson).toMatchInlineSnapshot(` + Object { + "compilerOptions": Object { + "baseUrl": ".", + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "moduleResolution": "node", + "paths": Object { + "@proj/one": Array [ + "packages/one/src/index.ts", + ], + "@proj/two": Array [ + "packages/two/src/index.ts", + ], + "@proj/v0-one": Array [ + "packages/v0-one/src/index.ts", + ], + "@proj/v0-two": Array [ + "packages/v0-two/src/index.ts", + ], + "@proj/v8-one": Array [ + "packages/v8-one/src/index.ts", + ], + "@proj/v8-two": Array [ + "packages/v8-two/src/index.ts", + ], + }, + "preserveConstEnums": true, + "pretty": true, + "rootDir": ".", + "skipLibCheck": true, + "sourceMap": true, + "typeRoots": Array [ + "node_modules/@types", + "./typings", + ], + }, + } + `); + }); + + describe(`--verify`, () => { + it(`should pass if base all config is up to date`, async () => { + expect.assertions(1); + await generator(tree, {}); + + updateJson(tree, '/tsconfig.base.json', json => { + json.compilerOptions.paths['@proj/three'] = ['packages/three/src/index.ts']; + return json; + }); + + await generator(tree, {}); + + await expect(generator(tree, { verify: true })).resolves.toBe(undefined); + }); + + it(`should fail if base all config is not up to date`, async () => { + expect.assertions(1); + await generator(tree, {}); + + updateJson(tree, '/tsconfig.base.json', json => { + json.compilerOptions.paths['@proj/three'] = ['packages/three/src/index.ts']; + return json; + }); + + try { + await generator(tree, { verify: true }); + } catch (err) { + expect((err as Error).message).toMatchInlineSnapshot(` + " + 🚨 /tsconfig.base.all.json is out of date. + + Please update it by running 'yarn nx workspace-generator tsconfig-base-all'. + " + `); + } + }); }); }); diff --git a/tools/generators/tsconfig-base-all/index.ts b/tools/generators/tsconfig-base-all/index.ts index 2c9c8df40214a..fdad039a5f875 100644 --- a/tools/generators/tsconfig-base-all/index.ts +++ b/tools/generators/tsconfig-base-all/index.ts @@ -1,50 +1,33 @@ -import * as path from 'path'; -import { Tree, formatFiles, installPackagesTask, names, generateFiles } from '@nrwl/devkit'; -import { libraryGenerator } from '@nrwl/workspace/generators'; - -import { getProjectConfig } from '../../utils'; +import { Tree, formatFiles, writeJson } from '@nrwl/devkit'; +import { isEqual } from 'lodash'; import { TsconfigBaseAllGeneratorSchema } from './schema'; +import { createPathAliasesConfig } from './lib/utils'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars interface NormalizedSchema extends ReturnType {} export default async function (tree: Tree, schema: TsconfigBaseAllGeneratorSchema) { - await libraryGenerator(tree, { name: schema.name }); - const normalizedOptions = normalizeOptions(tree, schema); - addFiles(tree, normalizedOptions); + const { existingTsConfig, mergedTsConfig, tsConfigAllPath } = createPathAliasesConfig(tree); - await formatFiles(tree); + if (normalizedOptions.verify && !isEqual(existingTsConfig, mergedTsConfig)) { + throw new Error(` + 🚨 ${tsConfigAllPath} is out of date. - return () => { - installPackagesTask(tree); - }; + Please update it by running 'yarn nx workspace-generator tsconfig-base-all'. + `); + } + + writeJson(tree, tsConfigAllPath, mergedTsConfig); + await formatFiles(tree); } function normalizeOptions(tree: Tree, options: TsconfigBaseAllGeneratorSchema) { - const project = getProjectConfig(tree, { packageName: options.name }); - + const defaults = { verify: false }; return { + ...defaults, ...options, - ...project, - ...names(options.name), - }; -} - -/** - * NOTE: remove this if your generator doesn't process any static/dynamic templates - */ -function addFiles(tree: Tree, options: NormalizedSchema) { - const templateOptions = { - ...options, - tmpl: '', }; - - generateFiles( - tree, - path.join(__dirname, 'files'), - path.join(options.projectConfig.root, options.name), - templateOptions, - ); } diff --git a/tools/generators/tsconfig-base-all/lib/utils.spec.ts b/tools/generators/tsconfig-base-all/lib/utils.spec.ts deleted file mode 100644 index 91e05a54a86f5..0000000000000 --- a/tools/generators/tsconfig-base-all/lib/utils.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { dummyHelper } from './utils'; - -describe(`utils`, () => { - it(`should behave...`, () => { - expect(dummyHelper()).toBe(undefined); - }); -}); diff --git a/tools/generators/tsconfig-base-all/lib/utils.ts b/tools/generators/tsconfig-base-all/lib/utils.ts index c340a27c29c8b..f9a246d4f26da 100644 --- a/tools/generators/tsconfig-base-all/lib/utils.ts +++ b/tools/generators/tsconfig-base-all/lib/utils.ts @@ -1,5 +1,40 @@ // use this module to define any kind of generic utilities that are used in more than 1 place within the generator implementation +import * as path from 'path'; +import { readJson, Tree } from '@nrwl/devkit'; -export function dummyHelper() { - return; +/** + * + * Create tsconfig.json with merged "compilerOptions.paths" from v0,v8,v9 tsconfigs. + */ +export function createPathAliasesConfig(tree: Tree) { + const tsConfigAllPath = '/tsconfig.base.all.json'; + const existingTsConfig = tree.exists(tsConfigAllPath) ? readJson(tree, tsConfigAllPath) : null; + + const baseConfigs = { + v0: readJson(tree, path.join('/tsconfig.base.v0.json')), + v8: readJson(tree, path.join('/tsconfig.base.v8.json')), + v9: readJson(tree, path.join('/tsconfig.base.json')), + }; + const tsConfigBase = '.'; + const mergedTsConfig = { + compilerOptions: { + moduleResolution: 'node', + forceConsistentCasingInFileNames: true, + skipLibCheck: true, + typeRoots: ['node_modules/@types', './typings'], + isolatedModules: true, + preserveConstEnums: true, + sourceMap: true, + pretty: true, + rootDir: tsConfigBase, + baseUrl: tsConfigBase, + paths: { + ...baseConfigs.v0.compilerOptions.paths, + ...baseConfigs.v8.compilerOptions.paths, + ...baseConfigs.v9.compilerOptions.paths, + }, + }, + }; + + return { tsConfigAllPath, mergedTsConfig, existingTsConfig }; } diff --git a/tools/generators/tsconfig-base-all/schema.json b/tools/generators/tsconfig-base-all/schema.json index 31c8558e1db3e..78f52b5970b63 100644 --- a/tools/generators/tsconfig-base-all/schema.json +++ b/tools/generators/tsconfig-base-all/schema.json @@ -2,16 +2,13 @@ "$schema": "http://json-schema.org/schema", "cli": "nx", "id": "tsconfig-base-all", + "description": "Create tsconfig.json with merged 'compilerOptions.paths' from v0,v8,v9 tsconfigs", "type": "object", "properties": { - "name": { - "type": "string", - "description": "Library name", - "$default": { - "$source": "argv", - "index": 0 - } + "verify": { + "type": "boolean", + "description": "Verify integrity of tsconfig.base.all.json path aliases" } }, - "required": ["name"] + "required": [] } diff --git a/tools/generators/tsconfig-base-all/schema.ts b/tools/generators/tsconfig-base-all/schema.ts index 9107aef11a743..8188c3342d5ab 100644 --- a/tools/generators/tsconfig-base-all/schema.ts +++ b/tools/generators/tsconfig-base-all/schema.ts @@ -1,6 +1,6 @@ export interface TsconfigBaseAllGeneratorSchema { /** - * Library name + * Verify integrity of tsconfig.base.all.json path aliases */ - name: string; + verify?: boolean; }