From c68b4bfb475677e6e3edd6b2062b62c2e5d4828c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Thu, 1 Jun 2023 15:22:53 +0100 Subject: [PATCH] fix(js): do not overwrite supported typescript version (#17350) --- packages/js/package.json | 1 + packages/js/src/generators/init/init.spec.ts | 37 ++++++++++++++- packages/js/src/generators/init/init.ts | 50 +++++++++++++++++++- packages/js/src/utils/versions.ts | 10 +++- 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/packages/js/package.json b/packages/js/package.json index 79cdfb2880802..8a3febf066837 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -49,6 +49,7 @@ "ignore": "^5.0.4", "js-tokens": "^4.0.0", "minimatch": "3.0.5", + "semver": "7.3.4", "source-map-support": "0.5.19", "tslib": "^2.3.0", "@nx/devkit": "file:../devkit", diff --git a/packages/js/src/generators/init/init.spec.ts b/packages/js/src/generators/init/init.spec.ts index b1aeaf4a7d681..b6ab997d39be5 100644 --- a/packages/js/src/generators/init/init.spec.ts +++ b/packages/js/src/generators/init/init.spec.ts @@ -1,6 +1,7 @@ -import { writeJson, readJson, Tree } from '@nx/devkit'; +import { writeJson, readJson, Tree, updateJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import init from './init'; +import { typescriptVersion } from '../../utils/versions'; describe('js init generator', () => { let tree: Tree; @@ -81,4 +82,38 @@ describe('js init generator', () => { expect(tree.exists('.vscode/extensions.json')).toBeFalsy(); }); + + it('should install typescript package when it is not already installed', async () => { + await init(tree, {}); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies['typescript']).toBeDefined(); + }); + + it('should overwrite installed typescript version when is not a supported version', async () => { + updateJson(tree, 'package.json', (json) => { + json.devDependencies = { ...json.devDependencies, typescript: '~4.5.0' }; + return json; + }); + + await init(tree, {}); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies['typescript']).toBe(typescriptVersion); + }); + + it('should not overwrite installed typescript version when is a supported version', async () => { + updateJson(tree, 'package.json', (json) => { + json.devDependencies = { ...json.devDependencies, typescript: '~4.7.0' }; + return json; + }); + + await init(tree, {}); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies['typescript']).toBe('~4.7.0'); + expect(packageJson.devDependencies['typescript']).not.toBe( + typescriptVersion + ); + }); }); diff --git a/packages/js/src/generators/init/init.ts b/packages/js/src/generators/init/init.ts index 96d132b95f02d..cdac8d1d57827 100644 --- a/packages/js/src/generators/init/init.ts +++ b/packages/js/src/generators/init/init.ts @@ -6,19 +6,58 @@ import { generateFiles, GeneratorCallback, joinPathFragments, + readJson, stripIndents, Tree, updateJson, writeJson, } from '@nx/devkit'; +import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver'; +import { readModulePackageJson } from 'nx/src/utils/package-json'; +import { satisfies, valid } from 'semver'; import { getRootTsConfigFileName } from '../../utils/typescript/ts-config'; import { nxVersion, prettierVersion, + supportedTypescriptVersions, typescriptVersion, } from '../../utils/versions'; import { InitSchema } from './schema'; +async function getInstalledTypescriptVersion( + tree: Tree +): Promise { + const rootPackageJson = readJson(tree, 'package.json'); + const tsVersionInRootPackageJson = + rootPackageJson.devDependencies?.['typescript'] ?? + rootPackageJson.dependencies?.['typescript']; + + if (!tsVersionInRootPackageJson) { + return null; + } + if (valid(tsVersionInRootPackageJson)) { + // it's a pinned version, return it + return tsVersionInRootPackageJson; + } + + // it's a version range, check whether the installed version matches it + try { + const tsPackageJson = readModulePackageJson('typescript').packageJson; + const installedTsVersion = + tsPackageJson.devDependencies?.['typescript'] ?? + tsPackageJson.dependencies?.['typescript']; + // the installed version matches the package.json version range + if ( + installedTsVersion && + satisfies(installedTsVersion, tsVersionInRootPackageJson) + ) { + return installedTsVersion; + } + } finally { + return checkAndCleanWithSemver('typescript', tsVersionInRootPackageJson); + } +} + export async function initGenerator( tree: Tree, schema: InitSchema @@ -36,7 +75,16 @@ export async function initGenerator( }; if (!schema.js) { - devDependencies['typescript'] = typescriptVersion; + const installedTsVersion = await getInstalledTypescriptVersion(tree); + + if ( + !installedTsVersion || + !satisfies(installedTsVersion, supportedTypescriptVersions, { + includePrerelease: true, + }) + ) { + devDependencies['typescript'] = typescriptVersion; + } } // https://prettier.io/docs/en/configuration.html diff --git a/packages/js/src/utils/versions.ts b/packages/js/src/utils/versions.ts index ac29ce97ddde7..588075bb28eab 100644 --- a/packages/js/src/utils/versions.ts +++ b/packages/js/src/utils/versions.ts @@ -8,5 +8,13 @@ export const swcHelpersVersion = '~0.5.0'; export const swcNodeVersion = '~1.4.2'; export const tsLibVersion = '^2.3.0'; export const typesNodeVersion = '18.7.1'; -export const typescriptVersion = '~5.0.2'; export const verdaccioVersion = '^5.0.4'; + +// Typescript +export const typescriptVersion = '~5.0.2'; +/** + * The minimum version is currently determined from the lowest version + * that's supported by the lowest Angular supported version, e.g. + * `npm view @angular/compiler-cli@14.0.0 peerDependencies.typescript` + */ +export const supportedTypescriptVersions = '>=4.6.2';