Skip to content

Commit

Permalink
fix(angular): importPath migration to fix broken paths (#6902)
Browse files Browse the repository at this point in the history
Fixes broken importPaths and package.json names due to incorrect logic that has already been fixed

ISSUES CLOSED: #6648
  • Loading branch information
Coly010 committed Sep 1, 2021
1 parent bafabb3 commit aa81561
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/angular/migrations.json
Expand Up @@ -84,6 +84,12 @@
"version": "12.3.5-beta.0",
"description": "Convert targets using @nrwl/angular:webpack-browser with the buildTarget option set to use the @nrwl/angular:delegate-build executor instead.",
"factory": "./src/migrations/update-12-3-0/convert-webpack-browser-build-target-to-delegate-build"
},
"update-invalid-import-paths": {
"cli": "nx",
"version": "12.9.0",
"description": "Fixes invalid importPaths for buildable and publishable libs.",
"factory": "./src/migrations/update-12-9-0/update-invalid-import-paths"
}
},
"packageJsonUpdates": {
Expand Down
@@ -0,0 +1,85 @@
import type { Tree } from '@nrwl/devkit';
import { updateJson, joinPathFragments, readJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';

import libGenerator from '../../generators/library/library';
import updateInvalidImportPaths from './update-invalid-import-paths';

describe('Migration to fix invalid import paths in affected workspaces', () => {
let tree: Tree;

beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();

// set up some libs

await libGenerator(tree, {
name: 'buildable1',
buildable: true,
directory: 'dir1',
});

await libGenerator(tree, {
name: 'buildable2',
buildable: true,
directory: 'dir1',
});

await libGenerator(tree, {
name: 'publishable1',
publishable: true,
directory: 'dir1',
importPath: '@proj/publishable1',
});

await libGenerator(tree, {
name: 'publishable2',
publishable: true,
directory: 'dir1',
importPath: '@proj/publishable2',
});

// break one of each kind
updateJson(
tree,
joinPathFragments('libs/dir1/buildable1', 'package.json'),
(pkgJson) => {
pkgJson.name = '@proj/dir1-buildable1';
return pkgJson;
}
);

updateJson(tree, 'tsconfig.base.json', (tsconfig) => {
const srcPath = tsconfig['@proj/publishable2'];
tsconfig['@proj/publishable2'] = undefined;

tsconfig['@proj/dir1/publishable2'] = srcPath;

return tsconfig;
});
});

it('should fix the invalid libraries', async () => {
// ACT
await updateInvalidImportPaths(tree);

// ASSERT
const fixedBuildable = readJson(
tree,
joinPathFragments('libs/dir1/buildable1', 'package.json')
);

const { compilerOptions } = readJson<{
compilerOptions: { paths: Record<string, string[]> };
}>(tree, 'tsconfig.base.json');
const { paths: tsConfigPaths } = compilerOptions;
const fixedPublishable = Boolean(tsConfigPaths['@proj/publishable2']);
const brokenPublishableShouldntExist = !Boolean(
tsConfigPaths['@proj/publishable2']
);

expect(fixedBuildable.name).toEqual('@proj/dir1/buildable1');
expect(fixedPublishable).toBeTruthy();
expect(brokenPublishableShouldntExist).toBeFalsy();
});
});
@@ -0,0 +1,140 @@
import type {
NxJsonProjectConfiguration,
ProjectConfiguration,
Tree,
} from '@nrwl/devkit';
import {
formatFiles,
getProjects,
readJson,
joinPathFragments,
updateJson,
logger,
} from '@nrwl/devkit';

type AffectedLib = ProjectConfiguration & NxJsonProjectConfiguration;
type InvalidLibs = {
buildableLibs: AffectedLib[];
publishableLibs: AffectedLib[];
};

export default async function (tree: Tree) {
const possibleAffectedLibs = findBuildableAndPublishableLibs(tree);
const invalidLibs = findInvalidLibs(tree, possibleAffectedLibs);
fixLibs(tree, invalidLibs);

await formatFiles(tree);
}

export function findBuildableAndPublishableLibs(tree: Tree): InvalidLibs {
const projects = getProjects(tree);
const buildableLibs: AffectedLib[] = [];
const publishableLibs: AffectedLib[] = [];

for (const [name, project] of projects) {
for (const target of Object.values(project.targets || {})) {
if (target.executor === '@nrwl/angular:package') {
publishableLibs.push(project);
} else if (target.executor === '@nrwl/angular:ng-packagr-lite') {
buildableLibs.push(project);
}
}
}

return { buildableLibs, publishableLibs };
}

export function findInvalidLibs(tree: Tree, libs: InvalidLibs): InvalidLibs {
const { compilerOptions } = readJson(tree, 'tsconfig.base.json');
const { paths: tsConfigPaths } = compilerOptions;

const invalidBuildableLibs = libs.buildableLibs.filter((lib) =>
checkInvalidLib(tree, lib, tsConfigPaths)
);
const invalidPublishableLibs = libs.publishableLibs.filter((lib) =>
checkInvalidLib(tree, lib, tsConfigPaths)
);
return {
buildableLibs: invalidBuildableLibs,
publishableLibs: invalidPublishableLibs,
};
}

function checkInvalidLib(
tree: Tree,
lib: AffectedLib,
tsConfigPaths: Record<string, string>
) {
const { name } = readJson(tree, joinPathFragments(lib.root, 'package.json'));
return !tsConfigPaths[name];
}

function fixLibs(tree: Tree, { buildableLibs, publishableLibs }: InvalidLibs) {
const { compilerOptions } = readJson(tree, 'tsconfig.base.json');
const { paths: tsConfigPaths } = compilerOptions;

buildableLibs.map((lib) => fixBuildableLib(tree, lib, tsConfigPaths));
publishableLibs.map((lib) => fixPublishableLib(tree, lib, tsConfigPaths));
}

function fixBuildableLib(
tree: Tree,
lib: AffectedLib,
tsConfigPaths: Record<string, string>
) {
const srcRoot = joinPathFragments(lib.sourceRoot, 'index.ts');

for (const [validPackageName, tsLibSrcRoot] of Object.entries(
tsConfigPaths
)) {
if (tsLibSrcRoot[0] === srcRoot) {
updateJson(
tree,
joinPathFragments(lib.root, 'package.json'),
(pkgJson) => {
pkgJson.name = validPackageName;

return pkgJson;
}
);
break;
}
}
}

function fixPublishableLib(
tree: Tree,
lib: AffectedLib,
tsConfigPaths: Record<string, string>
) {
const srcRoot = joinPathFragments(lib.sourceRoot, 'index.ts');
const { name: pkgName } = readJson(
tree,
joinPathFragments(lib.root, 'package.json')
);

const pkgNameParts = pkgName.split('/');
if (Array.isArray(pkgNameParts) && pkgNameParts.length > 2) {
logger.warn(
`Your publishable package ${pkgName} is an invalid NPM Package name. Please ensure it only contains one '/'.`
);
logger.warn(
`The affected package.json is at '${joinPathFragments(
lib.root,
'package.json'
)}'`
);
}

for (const [invalidPathKey, tsLibSrcRoot] of Object.entries(tsConfigPaths)) {
if (tsLibSrcRoot[0] === srcRoot) {
updateJson(tree, 'tsconfig.base.json', (tsconfig) => {
tsconfig.compilerOptions.paths[invalidPathKey] = undefined;
tsconfig.compilerOptions.paths[pkgName] = tsLibSrcRoot;

return tsconfig;
});
break;
}
}
}

0 comments on commit aa81561

Please sign in to comment.