Skip to content

Commit

Permalink
fix(angular): adjust generated tsconfig path mapping for publ libs
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #2794
  • Loading branch information
juristr committed Jun 11, 2020
1 parent d17d437 commit d570127
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 16 deletions.
6 changes: 6 additions & 0 deletions docs/angular/api-angular/schematics/library.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Type: `string`

A directory where the lib is placed

### importPath

Type: `string`

The library name used to import it, like @myorg/my-awesome-lib

### lazy

Default: `false`
Expand Down
6 changes: 6 additions & 0 deletions docs/angular/api-react/schematics/library.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ Type: `string`

A directory where the lib is placed

### importPath

Type: `string`

The library name used to import it, like @myorg/my-awesome-lib

### js

Default: `false`
Expand Down
6 changes: 6 additions & 0 deletions docs/react/api-angular/schematics/library.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Type: `string`

A directory where the lib is placed

### importPath

Type: `string`

The library name used to import it, like @myorg/my-awesome-lib

### lazy

Default: `false`
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
}
},
"dependencies": {
"@nrwl/nx-cloud": "^9.3.2"
"@nrwl/nx-cloud": "^9.3.2",
"speakingurl": "^14.0.1"
}
}
14 changes: 12 additions & 2 deletions packages/angular/src/schematics/library/lib/normalize-options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Tree } from '@angular-devkit/schematics';
import { getNpmScope, toClassName, toFileName } from '@nrwl/workspace';
import { libsDir } from '@nrwl/workspace/src/utils/ast-utils';
import { getNpmScope, toClassName, toFileName, NxJson } from '@nrwl/workspace';
import { libsDir, readJsonInTree } from '@nrwl/workspace/src/utils/ast-utils';
import { Schema } from '../schema';
import { NormalizedSchema } from './normalized-schema';

Expand All @@ -24,6 +24,15 @@ export function normalizeOptions(
const modulePath = `${projectRoot}/src/lib/${fileName}.module.ts`;
const defaultPrefix = getNpmScope(host);

// adjust the import path, especially for publishable
// libs which need to respect the NPM package scoping rules
let importPath = options.importPath;
if (!importPath) {
importPath = options.publishable
? `@${defaultPrefix}/${projectName}`
: `@${defaultPrefix}/${projectDirectory}`;
}

return {
...options,
prefix: options.prefix ? options.prefix : defaultPrefix,
Expand All @@ -35,5 +44,6 @@ export function normalizeOptions(
modulePath,
parsedTags,
fileName,
importPath,
};
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Rule, Tree } from '@angular-devkit/schematics';
import { getNpmScope, updateJsonInTree } from '@nrwl/workspace';
import { updateJsonInTree } from '@nrwl/workspace';
import { NormalizedSchema } from './normalized-schema';

export function updateLibPackageNpmScope(options: NormalizedSchema): Rule {
return (host: Tree) => {
return updateJsonInTree(`${options.projectRoot}/package.json`, (json) => {
json.name = `@${getNpmScope(host)}/${options.name}`;
return json;
});
};
return updateJsonInTree(`${options.projectRoot}/package.json`, (json) => {
json.name = options.importPath;
return json;
});
}
17 changes: 12 additions & 5 deletions packages/angular/src/schematics/library/lib/update-tsconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ import {
chain,
Rule,
SchematicContext,
SchematicsException,
Tree,
} from '@angular-devkit/schematics';
import { NxJson, readJsonInTree, updateJsonInTree } from '@nrwl/workspace';
import { libsDir } from '@nrwl/workspace/src/utils/ast-utils';
import { updateJsonInTree } from '@nrwl/workspace';
import { NormalizedSchema } from './normalized-schema';

export function updateTsConfig(options: NormalizedSchema): Rule {
return chain([
(host: Tree, context: SchematicContext) => {
const nxJson = readJsonInTree<NxJson>(host, 'nx.json');
return updateJsonInTree('tsconfig.json', (json) => {
const c = json.compilerOptions;
c.paths = c.paths || {};
delete c.paths[options.name];
c.paths[`@${nxJson.npmScope}/${options.projectDirectory}`] = [
`${libsDir(host)}/${options.projectDirectory}/src/index.ts`,

if (c.paths[options.importPath]) {
throw new SchematicsException(
`You already have a library using the import path "${options.importPath}". Make sure to specify a unique one.`
);
}

c.paths[options.importPath] = [
`libs/${options.projectDirectory}/src/index.ts`,
];

return json;
})(host, context);
},
Expand Down
92 changes: 92 additions & 0 deletions packages/angular/src/schematics/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,21 @@ describe('lib', () => {
).toBeUndefined();
});

it('should use an npm friendly name in tsconfig.json when publishable', async () => {
const tree = await runSchematic(
'lib',
{ name: 'myLib', directory: 'myDir', publishable: true },
appTree
);
const tsconfigJson = readJsonInTree(tree, '/tsconfig.json');
expect(
tsconfigJson.compilerOptions.paths['@proj/my-dir-my-lib']
).toEqual(['libs/my-dir/my-lib/src/index.ts']);
expect(
tsconfigJson.compilerOptions.paths['my-dir-my-lib/*']
).toBeUndefined();
});

it('should update tsconfig.json (no existing path mappings)', async () => {
const updatedTree: any = updateJsonInTree('tsconfig.json', (json) => {
json.compilerOptions.paths = undefined;
Expand Down Expand Up @@ -1007,4 +1022,81 @@ describe('lib', () => {
).toEqual(['libs/my-lib/tsconfig.lib.json']);
});
});

describe('--importPath', () => {
it('should update the package.json & tsconfig with the given import path', async () => {
const tree = await runSchematic(
'lib',
{
name: 'myLib',
framework: 'angular',
publishable: true,
directory: 'myDir',
importPath: '@myorg/lib',
},
appTree
);
const packageJson = readJsonInTree(
tree,
'libs/my-dir/my-lib/package.json'
);
const tsconfigJson = readJsonInTree(tree, '/tsconfig.json');

expect(packageJson.name).toBe('@myorg/lib');
expect(
tsconfigJson.compilerOptions.paths[packageJson.name]
).toBeDefined();
});

it('should fail if the same importPath has already been used', async () => {
const tree1 = await runSchematic(
'lib',
{
name: 'myLib1',
framework: 'angular',
publishable: true,
importPath: '@myorg/lib',
},
appTree
);

try {
await runSchematic(
'lib',
{
name: 'myLib2',
framework: 'angular',
publishable: true,
importPath: '@myorg/lib',
},
tree1
);
} catch (e) {
expect(e.message).toContain(
'You already have a library using the import path'
);
}

expect.assertions(1);
});

it('should fail if we pass an invalid npm package name', async () => {
try {
await runSchematic(
'lib',
{
name: 'myLib',
framework: 'angular',
publishable: true,
importPath: '@myorg/shop/mylib',
},
appTree
);
} catch (e) {
expect(e.message).toContain('scoped package name has an extra');
}

expect.assertions(1);
});
});
});
5 changes: 5 additions & 0 deletions packages/angular/src/schematics/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { updateLibPackageNpmScope } from './lib/update-lib-package-npm-scope';
import { updateProject } from './lib/update-project';
import { updateTsConfig } from './lib/update-tsconfig';
import { Schema } from './schema';
import { validateNpmPackageName } from '@nrwl/workspace/src/utils/validate-npm-pkg-name';

export default function (schema: Schema): Rule {
return (host: Tree): Rule => {
Expand All @@ -23,6 +24,10 @@ export default function (schema: Schema): Rule {
throw new Error(`routing must be set`);
}

if (options.publishable === true) {
validateNpmPackageName(options.importPath);
}

return chain([
addLintFiles(options.projectRoot, Linter.TsLint, { onlyGlobal: true }),
addUnitTestRunner(options),
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/schematics/library/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface Schema {
directory?: string;
sourceDir?: string;
publishable: boolean;
importPath?: string;

spec?: boolean;
flat?: boolean;
Expand Down
4 changes: 4 additions & 0 deletions packages/angular/src/schematics/library/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
"enum": ["karma", "jest", "none"],
"description": "Test runner to use for unit tests",
"default": "jest"
},
"importPath": {
"type": "string",
"description": "The library name used to import it, like @myorg/my-awesome-lib"
}
},
"required": []
Expand Down
3 changes: 2 additions & 1 deletion packages/workspace/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"yargs": "^11.0.0",
"chalk": "2.4.2",
"@nrwl/cli": "*",
"axios": "0.19.2"
"axios": "0.19.2",
"speakingurl": "14.0.1"
}
}
40 changes: 40 additions & 0 deletions packages/workspace/src/utils/validate-npm-pkg-name.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { validateNpmPackageName } from './validate-npm-pkg-name';

/**
* Rules: https://docs.npmjs.com/files/package.json#name
*/

describe('Validate npm package name', () => {
['a'.repeat(214), '@myorg/awesomeness', 'awesomelib'].forEach((pkgName) => {
it(`should succeed for ${pkgName}`, () => {
expect(validateNpmPackageName(pkgName)).toBeTruthy();
});
});

[
{
name: 'a'.repeat(215),
error: /more than 214 characters/,
},
{ name: '_myawesomepkg', error: /cannot start with a dot nor underscore/ },
{ name: '.myawesomepkg', error: /cannot start with a dot nor underscore/ },
{ name: 'Mypackage', error: /uppercase letters/ },
{ name: '@my/super/org', error: /scoped package name has an extra/ },
{
name: '@my/SuperPkg',
error: /package name cannot have uppercase letters/,
},
].forEach((pkgTest) => {
it(`should fail for ${pkgTest.name}`, () => {
function execValidation() {
validateNpmPackageName(pkgTest.name);
}

if (pkgTest.error) {
expect(execValidation).toThrowError(pkgTest.error);
} else {
expect(execValidation).toThrowError();
}
});
});
});
Loading

0 comments on commit d570127

Please sign in to comment.