fix(linter): use ng-packager for checking secondary entry points in linter (#14425)
This commit is contained in:
parent
5cfd357e8f
commit
c23b74ef89
@ -313,7 +313,7 @@ export default createESLintRule<Options, MessageIds>({
|
|||||||
if (
|
if (
|
||||||
!allowCircularSelfDependency &&
|
!allowCircularSelfDependency &&
|
||||||
!isRelativePath(imp) &&
|
!isRelativePath(imp) &&
|
||||||
!isAngularSecondaryEntrypoint(targetProjectLocator, imp)
|
!isAngularSecondaryEntrypoint(imp, sourceFilePath)
|
||||||
) {
|
) {
|
||||||
context.report({
|
context.report({
|
||||||
node,
|
node,
|
||||||
|
|||||||
@ -8,8 +8,15 @@ import {
|
|||||||
findTransitiveExternalDependencies,
|
findTransitiveExternalDependencies,
|
||||||
hasBannedDependencies,
|
hasBannedDependencies,
|
||||||
hasBannedImport,
|
hasBannedImport,
|
||||||
|
isAngularSecondaryEntrypoint,
|
||||||
isTerminalRun,
|
isTerminalRun,
|
||||||
} from './runtime-lint-utils';
|
} from './runtime-lint-utils';
|
||||||
|
import { vol } from 'memfs';
|
||||||
|
|
||||||
|
jest.mock('nx/src/utils/workspace-root', () => ({
|
||||||
|
workspaceRoot: '/root',
|
||||||
|
}));
|
||||||
|
jest.mock('fs', () => require('memfs').fs);
|
||||||
|
|
||||||
describe('hasBannedImport', () => {
|
describe('hasBannedImport', () => {
|
||||||
const source: ProjectGraphProjectNode = {
|
const source: ProjectGraphProjectNode = {
|
||||||
@ -313,3 +320,65 @@ describe('is terminal run', () => {
|
|||||||
expect(isTerminalRun()).toBe(false);
|
expect(isTerminalRun()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isAngularSecondaryEntrypoint', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const tsConfig = {
|
||||||
|
compilerOptions: {
|
||||||
|
baseUrl: '.',
|
||||||
|
resolveJsonModule: true,
|
||||||
|
paths: {
|
||||||
|
'@project/standard': ['libs/standard/src/index.ts'],
|
||||||
|
'@project/standard/secondary': [
|
||||||
|
'libs/standard/secondary/src/index.ts',
|
||||||
|
],
|
||||||
|
'@project/standard/tertiary': [
|
||||||
|
'libs/standard/tertiary/src/public_api.ts',
|
||||||
|
],
|
||||||
|
'@project/features': ['libs/features/src/index.ts'],
|
||||||
|
'@project/features/*': ['libs/features/*/random/folder/api.ts'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const fsJson = {
|
||||||
|
'tsconfig.base.json': JSON.stringify(tsConfig),
|
||||||
|
'apps/app.ts': '',
|
||||||
|
'libs/standard/package.json': '{ "version": "0.0.0" }',
|
||||||
|
'libs/standard/secondary/ng-package.json': JSON.stringify({
|
||||||
|
version: '0.0.0',
|
||||||
|
ngPackage: { lib: { entryFile: 'src/index.ts' } },
|
||||||
|
}),
|
||||||
|
'libs/standard/secondary/src/index.ts': 'const bla = "foo"',
|
||||||
|
'libs/standard/tertiary/ng-package.json': JSON.stringify({
|
||||||
|
version: '0.0.0',
|
||||||
|
ngPackage: { lib: { entryFile: 'src/public_api.ts' } },
|
||||||
|
}),
|
||||||
|
'libs/standard/tertiary/src/public_api.ts': 'const bla = "foo"',
|
||||||
|
'libs/features/package.json': '{ "version": "0.0.0" }',
|
||||||
|
'libs/features/secondary/ng-package.json': JSON.stringify({
|
||||||
|
version: '0.0.0',
|
||||||
|
ngPackage: { lib: { entryFile: 'random/folder/api.ts' } },
|
||||||
|
}),
|
||||||
|
'libs/features/secondary/random/folder/api.ts': 'const bla = "foo"',
|
||||||
|
};
|
||||||
|
vol.fromJSON(fsJson, '/root');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for secondary entrypoints', () => {
|
||||||
|
expect(
|
||||||
|
isAngularSecondaryEntrypoint('@project/standard', 'apps/app.ts')
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
isAngularSecondaryEntrypoint('@project/standard/secondary', 'apps/app.ts')
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isAngularSecondaryEntrypoint('@project/standard/tertiary', 'apps/app.ts')
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isAngularSecondaryEntrypoint('@project/features', 'apps/app.ts')
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
isAngularSecondaryEntrypoint('@project/features/secondary', 'apps/app.ts')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -12,13 +12,16 @@ import {
|
|||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { getPath, pathExists } from './graph-utils';
|
import { getPath, pathExists } from './graph-utils';
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { readFileIfExisting } from 'nx/src/project-graph/file-utils';
|
import { readFileIfExisting } from 'nx/src/project-graph/file-utils';
|
||||||
import { TargetProjectLocator } from 'nx/src/utils/target-project-locator';
|
import { TargetProjectLocator } from 'nx/src/utils/target-project-locator';
|
||||||
import {
|
import {
|
||||||
findProjectForPath,
|
findProjectForPath,
|
||||||
ProjectRootMappings,
|
ProjectRootMappings,
|
||||||
} from 'nx/src/project-graph/utils/find-project-for-path';
|
} from 'nx/src/project-graph/utils/find-project-for-path';
|
||||||
|
import {
|
||||||
|
getRootTsConfigFileName,
|
||||||
|
resolveModuleByImport,
|
||||||
|
} from 'nx/src/utils/typescript';
|
||||||
|
|
||||||
export type Deps = { [projectName: string]: ProjectGraphDependency[] };
|
export type Deps = { [projectName: string]: ProjectGraphDependency[] };
|
||||||
type SingleSourceTagConstraint = {
|
type SingleSourceTagConstraint = {
|
||||||
@ -415,20 +418,31 @@ export function groupImports(
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function isAngularSecondaryEntrypoint(
|
export function isAngularSecondaryEntrypoint(
|
||||||
targetProjectLocator: TargetProjectLocator,
|
importExpr: string,
|
||||||
importExpr: string
|
filePath: string
|
||||||
): boolean {
|
): boolean {
|
||||||
const targetFiles = targetProjectLocator.findPaths(importExpr);
|
const resolvedModule = resolveModuleByImport(
|
||||||
return (
|
importExpr,
|
||||||
targetFiles &&
|
filePath,
|
||||||
targetFiles.some(
|
join(workspaceRoot, getRootTsConfigFileName())
|
||||||
(file) =>
|
|
||||||
// The `ng-packagr` defaults to the `src/public_api.ts` entry file to
|
|
||||||
// the public API if the `lib.entryFile` is not specified explicitly.
|
|
||||||
(file.endsWith('src/public_api.ts') || file.endsWith('src/index.ts')) &&
|
|
||||||
existsSync(
|
|
||||||
joinPathFragments(workspaceRoot, file, '../../', 'ng-package.json')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return !!resolvedModule && fileIsSecondaryEntryPoint(resolvedModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileIsSecondaryEntryPoint(file: string): boolean {
|
||||||
|
let parent = joinPathFragments(file, '../');
|
||||||
|
while (parent !== './') {
|
||||||
|
// we need to find closest existing ng-package.json
|
||||||
|
// in order to determine if the file matches the secondary entry point
|
||||||
|
const ngPackageContent = readFileIfExisting(
|
||||||
|
joinPathFragments(workspaceRoot, parent, 'ng-package.json')
|
||||||
|
);
|
||||||
|
if (ngPackageContent) {
|
||||||
|
const entryFile = parseJson(ngPackageContent)?.ngPackage?.lib?.entryFile;
|
||||||
|
return entryFile && file === joinPathFragments(parent, entryFile);
|
||||||
|
}
|
||||||
|
parent = joinPathFragments(parent, '../');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user