From c6a44c49cde7c9f1ec8069512470f6330873ce3b Mon Sep 17 00:00:00 2001 From: Juri Date: Wed, 9 Feb 2022 22:15:31 +0100 Subject: [PATCH] fixup! feat(linter): automatic fixes for noRelativeOrAbsoluteImportsAcrossLibraries and noSelfCircularDependencies --- e2e/linter/src/linter.test.ts | 213 +++++++++++++----- .../eslint-plugin-nx/src/utils/ast-utils.ts | 8 +- 2 files changed, 157 insertions(+), 64 deletions(-) diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index fedba0a6aefc16..853bb47d16f547 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -253,7 +253,6 @@ describe('Linter', () => { expect(out).toContain( 'Projects cannot be imported by a relative or absolute path, and must begin with a npm scope' ); - // expect(out).toContain('Imports of lazy-loaded libraries are forbidden'); expect(out).toContain('Imports of apps are forbidden'); expect(out).toContain( 'A project tagged with "validtag" can only depend on libs tagged with "validtag"' @@ -261,9 +260,9 @@ describe('Linter', () => { }, 1000000); describe('workspace boundary rules', () => { - const libA = uniq('lib-a'); - const libB = uniq('lib-b'); - const libC = uniq('lib-c'); + const libA = uniq('tslib-a'); + const libB = uniq('tslib-b'); + const libC = uniq('tslib-c'); let projScope; beforeAll(() => { @@ -272,79 +271,169 @@ describe('Linter', () => { runCLI(`generate @nrwl/workspace:lib ${libB}`); runCLI(`generate @nrwl/workspace:lib ${libC}`); - // updateFile(`apps/${myapp}/src/main.ts`, `console.log("should fail");`); - }); + /** + * create tslib-a structure + */ + createFile( + `libs/${libA}/src/lib/tslib-a.ts`, + ` + export function libASayHello(): string { + return 'Hi from tslib-a'; + } + ` + ); - xdescribe('aaashould autofix noSelfCircularDependencies', () => { - beforeAll(() => { - /* - import { func1, func2 } from '@scope/same-lib'; + createFile( + `libs/${libA}/src/lib/some-non-exported-function.ts`, + ` + export function someNonPublicLibFunction() { + return 'this function is exported, but not via the libs barrel file'; + } + + export function someSelectivelyExportedFn() { + return 'this fn is exported selectively in the barrel file'; + } + ` + ); - should be transformed into + createFile( + `libs/${libA}/src/index.ts`, + ` + export * from './lib/tslib-a'; - import { func1 } from './func1'; - import { func2 } from './func2'; - */ + export { someSelectivelyExportedFn } from './lib/some-non-exported-function'; + ` + ); - createFile( - `libs/${libC}/src/lib/another-func.ts`, - ` - export function anotherFunc() { - return 'hi'; - } - ` - ); - - updateFile( - `libs/${libC}/src/lib/index.ts`, - ` - export * from './lib/${names(libC).fileName}'; - export * from './lib/another-func'; + /** + * create tslib-b structure + */ + createFile( + `libs/${libB}/src/index.ts`, ` - ); + export * from './lib/tslib-b'; + ` + ); - createFile( - `libs/${libC}/src/lib/lib-c-another.ts`, - ` -import { ${ - names(libC).propertyName - }, anotherFunc } from '@${projScope}/${libC}'; + createFile( + `libs/${libB}/src/lib/tslib-b.ts`, + ` + import { libASayHello } from '../../../${libA}/src/lib/tslib-a'; + // import { someNonPublicLibFunction } from '../../../${libA}/src/lib/some-non-exported-function'; + import { someSelectivelyExportedFn } from '../../../${libA}/src/lib/some-non-exported-function'; + + export function tslibB(): string { + // someNonPublicLibFunction(); + someSelectivelyExportedFn(); + libASayHello(); + return 'hi there'; + } + ` + ); + + /** + * create tslib-c structure + */ + + createFile( + `libs/${libC}/src/index.ts`, + ` + export * from './lib/tslib-c'; + export * from './lib/constant'; + + ` + ); + + createFile( + `libs/${libC}/src/lib/constant.ts`, + ` + export const SOME_CONSTANT = 'some constant value'; + export const someFunc1 = () => 'hi'; + export function someFunc2() { + return 'hi2'; + } + ` + ); + + createFile( + `libs/${libC}/src/lib/tslib-c-another.ts`, + ` +import { tslibC, SOME_CONSTANT, someFunc1, someFunc2 } from '@${projScope}/${libC}'; export function someStuff() { - anotherFunc(); - return ${names(libC).propertyName}(); + someFunc1(); + someFunc2(); + tslibC(); + console.log(SOME_CONSTANT); + return 'hi'; } + + ` + ); + + createFile( + `libs/${libC}/src/lib/tslib-c.ts`, ` - ); +import { someFunc1, someFunc2, SOME_CONSTANT } from '@${projScope}/${libC}'; + +export function tslibC(): string { + someFunc1(); + someFunc2(); + console.log(SOME_CONSTANT); + return 'tslib-c'; +} + + ` + ); + }); - // scenario 2 + it('should fix noSelfCircularDependencies', () => { + const stdout = runCLI(`lint ${libC}`, { + silenceError: true, }); + expect(stdout).toContain( + 'Projects should use relative imports to import from other files within the same project' + ); - it('should fix a circular self reference', () => { - const stdout = runCLI(`lint ${libC}`, { - silenceError: true, - }); - expect(stdout).toContain( - 'Projects should use relative imports to import from other files within the same project' - ); + // fix them + const fixedStout = runCLI(`lint ${libC} --fix`, { + silenceError: true, + }); + expect(fixedStout).toContain('Successfully ran target lint for project'); + + const fileContent = readFile(`libs/${libC}/src/lib/tslib-c-another.ts`); + expect(fileContent).toContain(` + import { tslibC } from './tslib-c'; + import { SOME_CONSTANT, someFunc1, someFunc2 } from './constant'; + `); + + const fileContentTslibC = readFile(`libs/${libC}/src/lib/tslib-c.ts`); + expect(fileContentTslibC).toContain(` + import { someFunc1, someFunc2, SOME_CONSTANT } from './constant'; + `); + }); - // fix them - const fixedStout = runCLI(`lint ${libC} --fix`, { - silenceError: true, - }); - expect(fixedStout).not.toContain( - 'Projects should use relative imports to import from other files within the same project' - ); - const fileContent = readFile(`libs/${libC}/src/lib/lib-c-another.ts`); - expect(fileContent).toContain( - `import { ${names(libC).propertyName} } from './${ - names(libC).fileName - }';` - ); - expect(fileContent).toContain( - `import { anotherFunc } from './another-func';` - ); + it('should fix noRelativeOrAbsoluteImportsAcrossLibraries', () => { + const stdout = runCLI(`lint ${libB}`, { + silenceError: true, + }); + expect(stdout).toContain( + 'Projects cannot be imported by a relative or absolute path, and must begin with a npm scope' + ); + + // fix them + const fixedStout = runCLI(`lint ${libB} --fix`, { + silenceError: true, }); + expect(fixedStout).toContain('Successfully ran target lint for project'); + + const fileContent = readFile(`libs/${libB}/src/lib/tslib-b.ts`); + expect(fileContent).toContain(` + import { libASayHello } from '@${projScope}/${libA}'; + `); + expect(fileContent).toContain(` + import { someSelectivelyExportedFn } from '@${projScope}/${libA}'; + `); }); }); }); diff --git a/packages/eslint-plugin-nx/src/utils/ast-utils.ts b/packages/eslint-plugin-nx/src/utils/ast-utils.ts index 534505d94eddc3..af2128df7de242 100644 --- a/packages/eslint-plugin-nx/src/utils/ast-utils.ts +++ b/packages/eslint-plugin-nx/src/utils/ast-utils.ts @@ -5,13 +5,17 @@ import { existsSync, readFileSync } from 'fs'; import { dirname } from 'path'; import ts = require('typescript'); import { logger } from '@nrwl/devkit'; +import { appRootPath } from '@nrwl/tao/src/utils/app-root'; function tryReadBaseJson() { try { - return JSON.parse(readFileSync('tsconfig.base.json').toString('utf-8')); + return JSON.parse( + readFileSync( + joinPathFragments(appRootPath, 'tsconfig.base.json') + ).toString('utf-8') + ); } catch (e) { logger.warn(`Error reading tsconfig.base.json: \n${JSON.stringify(e)}`); - } finally { return null; } }