feat(node): migrate @nrwl/node schematics to devkit (#4958)
This commit is contained in:
parent
aba5c44ec1
commit
4df3b66152
2
nx.json
2
nx.json
@ -71,7 +71,7 @@
|
|||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"implicitDependencies": ["workspace", "jest"]
|
"implicitDependencies": ["workspace", "jest", "linter"]
|
||||||
},
|
},
|
||||||
"next": {
|
"next": {
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
|||||||
@ -75,7 +75,7 @@ export function generateFiles(
|
|||||||
newContent = fs.readFileSync(filePath);
|
newContent = fs.readFileSync(filePath);
|
||||||
} else {
|
} else {
|
||||||
const template = fs.readFileSync(filePath).toString();
|
const template = fs.readFileSync(filePath).toString();
|
||||||
newContent = ejs.render(template, substitutions);
|
newContent = ejs.render(template, substitutions, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
host.write(computedPath, newContent);
|
host.write(computedPath, newContent);
|
||||||
|
|||||||
@ -46,7 +46,9 @@ describe('app', () => {
|
|||||||
);
|
);
|
||||||
expect(eslintrcJson).toMatchInlineSnapshot(`
|
expect(eslintrcJson).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"extends": "../../.eslintrc.json",
|
"extends": Array [
|
||||||
|
"../../.eslintrc.json",
|
||||||
|
],
|
||||||
"ignorePatterns": Array [
|
"ignorePatterns": Array [
|
||||||
"!**/*",
|
"!**/*",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -24,7 +24,9 @@ describe('app', () => {
|
|||||||
);
|
);
|
||||||
expect(eslintrcJson).toMatchInlineSnapshot(`
|
expect(eslintrcJson).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"extends": "../../.eslintrc.json",
|
"extends": Array [
|
||||||
|
"../../.eslintrc.json",
|
||||||
|
],
|
||||||
"ignorePatterns": Array [
|
"ignorePatterns": Array [
|
||||||
"!**/*",
|
"!**/*",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -2,23 +2,44 @@
|
|||||||
"name": "nx/node",
|
"name": "nx/node",
|
||||||
"version": "0.1",
|
"version": "0.1",
|
||||||
"extends": ["@nrwl/workspace"],
|
"extends": ["@nrwl/workspace"],
|
||||||
"schematics": {
|
"generators": {
|
||||||
"init": {
|
"init": {
|
||||||
"factory": "./src/schematics/init/init",
|
"factory": "./src/generators/init/init",
|
||||||
"schema": "./src/schematics/init/schema.json",
|
"schema": "./src/generators/init/schema.json",
|
||||||
"description": "Initialize the @nrwl/node plugin",
|
"description": "Initialize the @nrwl/node plugin",
|
||||||
"aliases": ["ng-add"],
|
"aliases": ["ng-add"],
|
||||||
"hidden": true
|
"hidden": true
|
||||||
},
|
},
|
||||||
"application": {
|
"application": {
|
||||||
"factory": "./src/schematics/application/application",
|
"factory": "./src/generators/application/application",
|
||||||
"schema": "./src/schematics/application/schema.json",
|
"schema": "./src/generators/application/schema.json",
|
||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"description": "Create a node application"
|
"description": "Create a node application"
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"factory": "./src/schematics/library/library",
|
"factory": "./src/generators/library/library",
|
||||||
"schema": "./src/schematics/library/schema.json",
|
"schema": "./src/generators/library/schema.json",
|
||||||
|
"aliases": ["lib"],
|
||||||
|
"description": "Create a library"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schematics": {
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init#initSchematic",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Initialize the @nrwl/node plugin",
|
||||||
|
"aliases": ["ng-add"],
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"application": {
|
||||||
|
"factory": "./src/generators/application/application#applicationSchematic",
|
||||||
|
"schema": "./src/generators/application/schema.json",
|
||||||
|
"aliases": ["app"],
|
||||||
|
"description": "Create a node application"
|
||||||
|
},
|
||||||
|
"library": {
|
||||||
|
"factory": "./src/generators/library/library#librarySchematic",
|
||||||
|
"schema": "./src/generators/library/schema.json",
|
||||||
"aliases": ["lib"],
|
"aliases": ["lib"],
|
||||||
"description": "Create a library"
|
"description": "Create a library"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export { applicationGenerator } from './src/schematics/application/application';
|
export { applicationGenerator } from './src/generators/application/application';
|
||||||
export { libraryGenerator } from './src/schematics/library/library';
|
export { libraryGenerator } from './src/generators/library/library';
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"migrations": "./migrations.json"
|
"migrations": "./migrations.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@nrwl/workspace": "*",
|
||||||
"@nrwl/devkit": "*",
|
"@nrwl/devkit": "*",
|
||||||
"@nrwl/jest": "*",
|
"@nrwl/jest": "*",
|
||||||
"@nrwl/linter": "*",
|
"@nrwl/linter": "*",
|
||||||
|
|||||||
@ -1,25 +1,37 @@
|
|||||||
import { Tree } from '@angular-devkit/schematics';
|
import { NxJsonConfiguration, readJson, Tree } from '@nrwl/devkit';
|
||||||
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
import { runSchematic } from '../../utils/testing';
|
|
||||||
import { NxJson, readJsonInTree } from '@nrwl/workspace';
|
|
||||||
// to break the dependency
|
|
||||||
const createApp = require('../../../../angular/' + 'src/utils/testing')
|
|
||||||
.createApp;
|
|
||||||
|
|
||||||
|
import { applicationGenerator as angularApplicationGenerator } from '@nrwl/angular/generators';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
import { applicationGenerator } from './application';
|
||||||
|
import { overrideCollectionResolutionForTesting } from '@nrwl/devkit/ngcli-adapter';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
describe('app', () => {
|
describe('app', () => {
|
||||||
let appTree: Tree;
|
let tree: Tree;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
appTree = Tree.empty();
|
tree = createTreeWithEmptyWorkspace();
|
||||||
appTree = createEmptyWorkspace(appTree);
|
|
||||||
|
overrideCollectionResolutionForTesting({
|
||||||
|
'@nrwl/cypress': join(__dirname, '../../../../cypress/collection.json'),
|
||||||
|
'@nrwl/jest': join(__dirname, '../../../../jest/collection.json'),
|
||||||
|
'@nrwl/workspace': join(
|
||||||
|
__dirname,
|
||||||
|
'../../../../workspace/collection.json'
|
||||||
|
),
|
||||||
|
'@nrwl/angular': join(__dirname, '../../../../angular/collection.json'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
overrideCollectionResolutionForTesting(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('not nested', () => {
|
describe('not nested', () => {
|
||||||
it('should update workspace.json', async () => {
|
it('should update workspace.json', async () => {
|
||||||
const tree = await runSchematic('app', { name: 'myNodeApp' }, appTree);
|
await applicationGenerator(tree, { name: 'myNodeApp' });
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
const project = workspaceJson.projects['my-node-app'];
|
const project = workspaceJson.projects['my-node-app'];
|
||||||
expect(project.root).toEqual('apps/my-node-app');
|
expect(project.root).toEqual('apps/my-node-app');
|
||||||
expect(project.architect).toEqual(
|
expect(project.architect).toEqual(
|
||||||
@ -67,12 +79,8 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update nx.json', async () => {
|
it('should update nx.json', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, { name: 'myNodeApp', tags: 'one,two' });
|
||||||
'app',
|
const nxJson = readJson<NxJsonConfiguration>(tree, '/nx.json');
|
||||||
{ name: 'myNodeApp', tags: 'one,two' },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
|
|
||||||
expect(nxJson.projects).toEqual({
|
expect(nxJson.projects).toEqual({
|
||||||
'my-node-app': {
|
'my-node-app': {
|
||||||
tags: ['one', 'two'],
|
tags: ['one', 'two'],
|
||||||
@ -81,11 +89,11 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should generate files', async () => {
|
it('should generate files', async () => {
|
||||||
const tree = await runSchematic('app', { name: 'myNodeApp' }, appTree);
|
await applicationGenerator(tree, { name: 'myNodeApp' });
|
||||||
expect(tree.exists(`apps/my-node-app/jest.config.js`)).toBeTruthy();
|
expect(tree.exists(`apps/my-node-app/jest.config.js`)).toBeTruthy();
|
||||||
expect(tree.exists('apps/my-node-app/src/main.ts')).toBeTruthy();
|
expect(tree.exists('apps/my-node-app/src/main.ts')).toBeTruthy();
|
||||||
|
|
||||||
const tsconfig = readJsonInTree(tree, 'apps/my-node-app/tsconfig.json');
|
const tsconfig = readJson(tree, 'apps/my-node-app/tsconfig.json');
|
||||||
expect(tsconfig).toMatchInlineSnapshot(`
|
expect(tsconfig).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
@ -102,17 +110,16 @@ describe('app', () => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const tsconfigApp = readJsonInTree(
|
const tsconfigApp = readJson(tree, 'apps/my-node-app/tsconfig.app.json');
|
||||||
tree,
|
|
||||||
'apps/my-node-app/tsconfig.app.json'
|
|
||||||
);
|
|
||||||
expect(tsconfigApp.compilerOptions.outDir).toEqual('../../dist/out-tsc');
|
expect(tsconfigApp.compilerOptions.outDir).toEqual('../../dist/out-tsc');
|
||||||
expect(tsconfigApp.extends).toEqual('./tsconfig.json');
|
expect(tsconfigApp.extends).toEqual('./tsconfig.json');
|
||||||
|
|
||||||
const eslintrc = readJsonInTree(tree, 'apps/my-node-app/.eslintrc.json');
|
const eslintrc = readJson(tree, 'apps/my-node-app/.eslintrc.json');
|
||||||
expect(eslintrc).toMatchInlineSnapshot(`
|
expect(eslintrc).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"extends": "../../.eslintrc.json",
|
"extends": Array [
|
||||||
|
"../../.eslintrc.json",
|
||||||
|
],
|
||||||
"ignorePatterns": Array [
|
"ignorePatterns": Array [
|
||||||
"!**/*",
|
"!**/*",
|
||||||
],
|
],
|
||||||
@ -153,12 +160,11 @@ describe('app', () => {
|
|||||||
|
|
||||||
describe('nested', () => {
|
describe('nested', () => {
|
||||||
it('should update workspace.json', async () => {
|
it('should update workspace.json', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', directory: 'myDir' },
|
directory: 'myDir',
|
||||||
appTree
|
});
|
||||||
);
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
|
||||||
|
|
||||||
expect(workspaceJson.projects['my-dir-my-node-app'].root).toEqual(
|
expect(workspaceJson.projects['my-dir-my-node-app'].root).toEqual(
|
||||||
'apps/my-dir/my-node-app'
|
'apps/my-dir/my-node-app'
|
||||||
@ -178,12 +184,12 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update nx.json', async () => {
|
it('should update nx.json', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', directory: 'myDir', tags: 'one,two' },
|
directory: 'myDir',
|
||||||
appTree
|
tags: 'one,two',
|
||||||
);
|
});
|
||||||
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
|
const nxJson = readJson<NxJsonConfiguration>(tree, '/nx.json');
|
||||||
expect(nxJson.projects).toEqual({
|
expect(nxJson.projects).toEqual({
|
||||||
'my-dir-my-node-app': {
|
'my-dir-my-node-app': {
|
||||||
tags: ['one', 'two'],
|
tags: ['one', 'two'],
|
||||||
@ -193,15 +199,14 @@ describe('app', () => {
|
|||||||
|
|
||||||
it('should generate files', async () => {
|
it('should generate files', async () => {
|
||||||
const hasJsonValue = ({ path, expectedValue, lookupFn }) => {
|
const hasJsonValue = ({ path, expectedValue, lookupFn }) => {
|
||||||
const config = readJsonInTree(tree, path);
|
const config = readJson(tree, path);
|
||||||
|
|
||||||
expect(lookupFn(config)).toEqual(expectedValue);
|
expect(lookupFn(config)).toEqual(expectedValue);
|
||||||
};
|
};
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', directory: 'myDir' },
|
directory: 'myDir',
|
||||||
appTree
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure these exist
|
// Make sure these exist
|
||||||
[
|
[
|
||||||
@ -226,7 +231,7 @@ describe('app', () => {
|
|||||||
{
|
{
|
||||||
path: 'apps/my-dir/my-node-app/.eslintrc.json',
|
path: 'apps/my-dir/my-node-app/.eslintrc.json',
|
||||||
lookupFn: (json) => json.extends,
|
lookupFn: (json) => json.extends,
|
||||||
expectedValue: '../../../.eslintrc.json',
|
expectedValue: ['../../../.eslintrc.json'],
|
||||||
},
|
},
|
||||||
].forEach(hasJsonValue);
|
].forEach(hasJsonValue);
|
||||||
});
|
});
|
||||||
@ -234,17 +239,16 @@ describe('app', () => {
|
|||||||
|
|
||||||
describe('--unit-test-runner none', () => {
|
describe('--unit-test-runner none', () => {
|
||||||
it('should not generate test configuration', async () => {
|
it('should not generate test configuration', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', unitTestRunner: 'none' },
|
unitTestRunner: 'none',
|
||||||
appTree
|
});
|
||||||
);
|
|
||||||
expect(tree.exists('jest.config.js')).toBeFalsy();
|
expect(tree.exists('jest.config.js')).toBeFalsy();
|
||||||
expect(tree.exists('apps/my-node-app/src/test-setup.ts')).toBeFalsy();
|
expect(tree.exists('apps/my-node-app/src/test-setup.ts')).toBeFalsy();
|
||||||
expect(tree.exists('apps/my-node-app/src/test.ts')).toBeFalsy();
|
expect(tree.exists('apps/my-node-app/src/test.ts')).toBeFalsy();
|
||||||
expect(tree.exists('apps/my-node-app/tsconfig.spec.json')).toBeFalsy();
|
expect(tree.exists('apps/my-node-app/tsconfig.spec.json')).toBeFalsy();
|
||||||
expect(tree.exists('apps/my-node-app/jest.config.js')).toBeFalsy();
|
expect(tree.exists('apps/my-node-app/jest.config.js')).toBeFalsy();
|
||||||
const workspaceJson = readJsonInTree(tree, 'workspace.json');
|
const workspaceJson = readJson(tree, 'workspace.json');
|
||||||
expect(
|
expect(
|
||||||
workspaceJson.projects['my-node-app'].architect.test
|
workspaceJson.projects['my-node-app'].architect.test
|
||||||
).toBeUndefined();
|
).toBeUndefined();
|
||||||
@ -264,59 +268,53 @@ describe('app', () => {
|
|||||||
|
|
||||||
describe('--frontendProject', () => {
|
describe('--frontendProject', () => {
|
||||||
it('should configure proxy', async () => {
|
it('should configure proxy', async () => {
|
||||||
appTree = createApp(appTree, 'my-frontend');
|
await angularApplicationGenerator(tree, { name: 'my-frontend' });
|
||||||
|
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', frontendProject: 'my-frontend' },
|
frontendProject: 'my-frontend',
|
||||||
appTree
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
|
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
|
||||||
const serve = readJsonInTree(tree, 'workspace.json').projects[
|
const serve = readJson(tree, 'workspace.json').projects['my-frontend']
|
||||||
'my-frontend'
|
.architect.serve;
|
||||||
].architect.serve;
|
|
||||||
expect(serve.options.proxyConfig).toEqual(
|
expect(serve.options.proxyConfig).toEqual(
|
||||||
'apps/my-frontend/proxy.conf.json'
|
'apps/my-frontend/proxy.conf.json'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should configure proxies for multiple node projects with the same frontend app', async () => {
|
it('should configure proxies for multiple node projects with the same frontend app', async () => {
|
||||||
appTree = createApp(appTree, 'my-frontend');
|
await angularApplicationGenerator(tree, { name: 'my-frontend' });
|
||||||
|
|
||||||
appTree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'cart',
|
||||||
{ name: 'cart', frontendProject: 'my-frontend' },
|
frontendProject: 'my-frontend',
|
||||||
appTree
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'billing',
|
||||||
{ name: 'billing', frontendProject: 'my-frontend' },
|
frontendProject: 'my-frontend',
|
||||||
appTree
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
|
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
|
||||||
|
|
||||||
expect(readJsonInTree(tree, 'apps/my-frontend/proxy.conf.json')).toEqual({
|
expect(readJson(tree, 'apps/my-frontend/proxy.conf.json')).toEqual({
|
||||||
'/api': { target: 'http://localhost:3333', secure: false },
|
'/api': { target: 'http://localhost:3333', secure: false },
|
||||||
'/billing-api': { target: 'http://localhost:3333', secure: false },
|
'/billing-api': { target: 'http://localhost:3333', secure: false },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with unnormalized project names', async () => {
|
it('should work with unnormalized project names', async () => {
|
||||||
appTree = createApp(appTree, 'myFrontend');
|
await angularApplicationGenerator(tree, { name: 'myFrontend' });
|
||||||
|
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', frontendProject: 'myFrontend' },
|
frontendProject: 'myFrontend',
|
||||||
appTree
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
|
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
|
||||||
const serve = readJsonInTree(tree, 'workspace.json').projects[
|
const serve = readJson(tree, 'workspace.json').projects['my-frontend']
|
||||||
'my-frontend'
|
.architect.serve;
|
||||||
].architect.serve;
|
|
||||||
expect(serve.options.proxyConfig).toEqual(
|
expect(serve.options.proxyConfig).toEqual(
|
||||||
'apps/my-frontend/proxy.conf.json'
|
'apps/my-frontend/proxy.conf.json'
|
||||||
);
|
);
|
||||||
@ -325,22 +323,22 @@ describe('app', () => {
|
|||||||
|
|
||||||
describe('--babelJest', () => {
|
describe('--babelJest', () => {
|
||||||
it('should use babel for jest', async () => {
|
it('should use babel for jest', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', tags: 'one,two', babelJest: true } as Schema,
|
tags: 'one,two',
|
||||||
appTree
|
babelJest: true,
|
||||||
);
|
} as Schema);
|
||||||
|
|
||||||
expect(tree.readContent(`apps/my-node-app/jest.config.js`))
|
expect(tree.read(`apps/my-node-app/jest.config.js`).toString())
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"module.exports = {
|
"module.exports = {
|
||||||
displayName: 'my-node-app',
|
displayName: 'my-node-app',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\\\\\\\.[tj]s$': 'babel-jest',
|
'^.+\\\\\\\\.[tj]s$': 'babel-jest'
|
||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
coverageDirectory: '../../coverage/apps/my-node-app',
|
coverageDirectory: '../../coverage/apps/my-node-app'
|
||||||
};
|
};
|
||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
@ -348,38 +346,30 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
describe('--js flag', () => {
|
describe('--js flag', () => {
|
||||||
it('should generate js files instead of ts files', async () => {
|
it('should generate js files instead of ts files', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
|
||||||
{
|
|
||||||
name: 'myNodeApp',
|
name: 'myNodeApp',
|
||||||
js: true,
|
js: true,
|
||||||
} as Schema,
|
} as Schema);
|
||||||
appTree
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(tree.exists(`apps/my-node-app/jest.config.js`)).toBeTruthy();
|
expect(tree.exists(`apps/my-node-app/jest.config.js`)).toBeTruthy();
|
||||||
expect(tree.exists('apps/my-node-app/src/main.js')).toBeTruthy();
|
expect(tree.exists('apps/my-node-app/src/main.js')).toBeTruthy();
|
||||||
|
|
||||||
const tsConfig = readJsonInTree(tree, 'apps/my-node-app/tsconfig.json');
|
const tsConfig = readJson(tree, 'apps/my-node-app/tsconfig.json');
|
||||||
expect(tsConfig.compilerOptions).toEqual({
|
expect(tsConfig.compilerOptions).toEqual({
|
||||||
allowJs: true,
|
allowJs: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tsConfigApp = readJsonInTree(
|
const tsConfigApp = readJson(tree, 'apps/my-node-app/tsconfig.app.json');
|
||||||
tree,
|
|
||||||
'apps/my-node-app/tsconfig.app.json'
|
|
||||||
);
|
|
||||||
expect(tsConfigApp.include).toEqual(['**/*.ts', '**/*.js']);
|
expect(tsConfigApp.include).toEqual(['**/*.ts', '**/*.js']);
|
||||||
expect(tsConfigApp.exclude).toEqual(['**/*.spec.ts', '**/*.spec.js']);
|
expect(tsConfigApp.exclude).toEqual(['**/*.spec.ts', '**/*.spec.js']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update workspace.json', async () => {
|
it('should update workspace.json', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', js: true } as Schema,
|
js: true,
|
||||||
appTree
|
} as Schema);
|
||||||
);
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
|
||||||
const project = workspaceJson.projects['my-node-app'];
|
const project = workspaceJson.projects['my-node-app'];
|
||||||
const buildTarget = project.architect.build;
|
const buildTarget = project.architect.build;
|
||||||
|
|
||||||
@ -393,11 +383,11 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should generate js files for nested libs as well', async () => {
|
it('should generate js files for nested libs as well', async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', directory: 'myDir', js: true } as Schema,
|
directory: 'myDir',
|
||||||
appTree
|
js: true,
|
||||||
);
|
} as Schema);
|
||||||
expect(
|
expect(
|
||||||
tree.exists(`apps/my-dir/my-node-app/jest.config.js`)
|
tree.exists(`apps/my-dir/my-node-app/jest.config.js`)
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
@ -407,11 +397,10 @@ describe('app', () => {
|
|||||||
|
|
||||||
describe('--pascalCaseFiles', () => {
|
describe('--pascalCaseFiles', () => {
|
||||||
it(`should notify that this flag doesn't do anything`, async () => {
|
it(`should notify that this flag doesn't do anything`, async () => {
|
||||||
const tree = await runSchematic(
|
await applicationGenerator(tree, {
|
||||||
'app',
|
name: 'myNodeApp',
|
||||||
{ name: 'myNodeApp', pascalCaseFiles: true } as Schema,
|
pascalCaseFiles: true,
|
||||||
appTree
|
} as Schema);
|
||||||
);
|
|
||||||
|
|
||||||
// @TODO how to spy on context ?
|
// @TODO how to spy on context ?
|
||||||
// expect(contextLoggerSpy).toHaveBeenCalledWith('NOTE: --pascalCaseFiles is a noop')
|
// expect(contextLoggerSpy).toHaveBeenCalledWith('NOTE: --pascalCaseFiles is a noop')
|
||||||
240
packages/node/src/generators/application/application.ts
Normal file
240
packages/node/src/generators/application/application.ts
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
convertNxGenerator,
|
||||||
|
formatFiles,
|
||||||
|
generateFiles,
|
||||||
|
GeneratorCallback,
|
||||||
|
getWorkspaceLayout,
|
||||||
|
joinPathFragments,
|
||||||
|
logger,
|
||||||
|
names,
|
||||||
|
NxJsonProjectConfiguration,
|
||||||
|
offsetFromRoot,
|
||||||
|
ProjectConfiguration,
|
||||||
|
readProjectConfiguration,
|
||||||
|
readWorkspaceConfiguration,
|
||||||
|
TargetConfiguration,
|
||||||
|
toJS,
|
||||||
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
updateTsConfigsToJs,
|
||||||
|
updateWorkspaceConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
||||||
|
import { jestProjectGenerator } from '@nrwl/jest';
|
||||||
|
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||||
|
|
||||||
|
import { Schema } from './schema';
|
||||||
|
import { initGenerator } from '../init/init';
|
||||||
|
|
||||||
|
interface NormalizedSchema extends Schema {
|
||||||
|
appProjectRoot: string;
|
||||||
|
parsedTags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBuildConfig(
|
||||||
|
project: ProjectConfiguration,
|
||||||
|
options: NormalizedSchema
|
||||||
|
): TargetConfiguration {
|
||||||
|
return {
|
||||||
|
executor: '@nrwl/node:build',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
options: {
|
||||||
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
|
main: joinPathFragments(
|
||||||
|
project.sourceRoot,
|
||||||
|
'main' + (options.js ? '.js' : '.ts')
|
||||||
|
),
|
||||||
|
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||||
|
assets: [joinPathFragments(project.sourceRoot, 'assets')],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
optimization: true,
|
||||||
|
extractLicenses: true,
|
||||||
|
inspect: false,
|
||||||
|
fileReplacements: [
|
||||||
|
{
|
||||||
|
replace: joinPathFragments(
|
||||||
|
project.sourceRoot,
|
||||||
|
'environments/environment' + (options.js ? '.js' : '.ts')
|
||||||
|
),
|
||||||
|
with: joinPathFragments(
|
||||||
|
project.sourceRoot,
|
||||||
|
'environments/environment.prod' + (options.js ? '.js' : '.ts')
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServeConfig(options: NormalizedSchema): TargetConfiguration {
|
||||||
|
return {
|
||||||
|
executor: '@nrwl/node:execute',
|
||||||
|
options: {
|
||||||
|
buildTarget: `${options.name}:build`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addProject(tree: Tree, options: NormalizedSchema) {
|
||||||
|
const project: ProjectConfiguration & NxJsonProjectConfiguration = {
|
||||||
|
root: options.appProjectRoot,
|
||||||
|
sourceRoot: join(options.appProjectRoot, 'src'),
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {},
|
||||||
|
tags: options.parsedTags,
|
||||||
|
};
|
||||||
|
project.targets.build = getBuildConfig(project, options);
|
||||||
|
project.targets.serve = getServeConfig(options);
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, options.name, project);
|
||||||
|
|
||||||
|
const workspace = readWorkspaceConfiguration(tree);
|
||||||
|
|
||||||
|
if (!workspace.defaultProject) {
|
||||||
|
workspace.defaultProject = options.name;
|
||||||
|
updateWorkspaceConfiguration(tree, workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAppFiles(tree: Tree, options: NormalizedSchema) {
|
||||||
|
generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, {
|
||||||
|
tmpl: '',
|
||||||
|
name: options.name,
|
||||||
|
root: options.appProjectRoot,
|
||||||
|
offset: offsetFromRoot(options.appProjectRoot),
|
||||||
|
});
|
||||||
|
if (options.js) {
|
||||||
|
toJS(tree);
|
||||||
|
}
|
||||||
|
if (options.pascalCaseFiles) {
|
||||||
|
logger.warn('NOTE: --pascalCaseFiles is a noop');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addProxy(tree: Tree, options: NormalizedSchema) {
|
||||||
|
const projectConfig = readProjectConfiguration(tree, options.frontendProject);
|
||||||
|
if (projectConfig.targets && projectConfig.targets.serve) {
|
||||||
|
const pathToProxyFile = `${projectConfig.root}/proxy.conf.json`;
|
||||||
|
projectConfig.targets.serve.options.proxyConfig = pathToProxyFile;
|
||||||
|
|
||||||
|
if (!tree.exists(pathToProxyFile)) {
|
||||||
|
tree.write(
|
||||||
|
pathToProxyFile,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:3333',
|
||||||
|
secure: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
//add new entry to existing config
|
||||||
|
const proxyFileContent = tree.read(pathToProxyFile).toString();
|
||||||
|
|
||||||
|
const proxyModified = {
|
||||||
|
...JSON.parse(proxyFileContent),
|
||||||
|
[`/${options.name}-api`]: {
|
||||||
|
target: 'http://localhost:3333',
|
||||||
|
secure: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.write(pathToProxyFile, JSON.stringify(proxyModified, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProjectConfiguration(tree, options.frontendProject, projectConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applicationGenerator(tree: Tree, schema: Schema) {
|
||||||
|
const options = normalizeOptions(tree, schema);
|
||||||
|
|
||||||
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
const initTask = await initGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
tasks.push(initTask);
|
||||||
|
|
||||||
|
addAppFiles(tree, options);
|
||||||
|
addProject(tree, options);
|
||||||
|
|
||||||
|
if (options.linter !== Linter.None) {
|
||||||
|
const lintTask = await lintProjectGenerator(tree, {
|
||||||
|
linter: options.linter,
|
||||||
|
project: options.name,
|
||||||
|
tsConfigPaths: [
|
||||||
|
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||||
|
],
|
||||||
|
eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`],
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
tasks.push(lintTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.unitTestRunner === 'jest') {
|
||||||
|
const jestTask = await jestProjectGenerator(tree, {
|
||||||
|
project: options.name,
|
||||||
|
setupFile: 'none',
|
||||||
|
skipSerializers: true,
|
||||||
|
supportTsx: options.js,
|
||||||
|
babelJest: options.babelJest,
|
||||||
|
});
|
||||||
|
tasks.push(jestTask);
|
||||||
|
}
|
||||||
|
if (options.js) {
|
||||||
|
updateTsConfigsToJs(tree, { projectRoot: options.appProjectRoot });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.frontendProject) {
|
||||||
|
addProxy(tree, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
||||||
|
const { appsDir } = getWorkspaceLayout(host);
|
||||||
|
|
||||||
|
const appDirectory = options.directory
|
||||||
|
? `${names(options.directory).fileName}/${names(options.name).fileName}`
|
||||||
|
: names(options.name).fileName;
|
||||||
|
|
||||||
|
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
|
||||||
|
|
||||||
|
const appProjectRoot = joinPathFragments(appsDir, appDirectory);
|
||||||
|
|
||||||
|
const parsedTags = options.tags
|
||||||
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
name: names(appProjectName).fileName,
|
||||||
|
frontendProject: options.frontendProject
|
||||||
|
? names(options.frontendProject).fileName
|
||||||
|
: undefined,
|
||||||
|
appProjectRoot,
|
||||||
|
parsedTags,
|
||||||
|
linter: options.linter ?? Linter.EsLint,
|
||||||
|
unitTestRunner: options.unitTestRunner ?? 'jest',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default applicationGenerator;
|
||||||
|
export const applicationSchematic = convertNxGenerator(applicationGenerator);
|
||||||
15
packages/node/src/generators/application/schema.d.ts
vendored
Normal file
15
packages/node/src/generators/application/schema.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Linter } from '@nrwl/linter';
|
||||||
|
|
||||||
|
export interface Schema {
|
||||||
|
name: string;
|
||||||
|
skipFormat?: boolean;
|
||||||
|
skipPackageJson?: boolean;
|
||||||
|
directory?: string;
|
||||||
|
unitTestRunner?: 'jest' | 'none';
|
||||||
|
linter?: Linter;
|
||||||
|
tags?: string;
|
||||||
|
frontendProject?: string;
|
||||||
|
babelJest?: boolean;
|
||||||
|
js?: boolean;
|
||||||
|
pascalCaseFiles?: boolean;
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"cli": "nx",
|
||||||
"id": "SchematicsNxNodeApp",
|
"id": "SchematicsNxNodeApp",
|
||||||
"title": "Nx Application Options Schema",
|
"title": "Nx Application Options Schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
49
packages/node/src/generators/init/init.spec.ts
Normal file
49
packages/node/src/generators/init/init.spec.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { addDependenciesToPackageJson, readJson, Tree } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
|
||||||
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
import { initGenerator } from './init';
|
||||||
|
|
||||||
|
describe('init', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add dependencies', async () => {
|
||||||
|
const existing = 'existing';
|
||||||
|
const existingVersion = '1.0.0';
|
||||||
|
|
||||||
|
addDependenciesToPackageJson(
|
||||||
|
tree,
|
||||||
|
{
|
||||||
|
'@nrwl/node': nxVersion,
|
||||||
|
[existing]: existingVersion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[existing]: existingVersion,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await initGenerator(tree, {});
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.dependencies['@nrwl/node']).toBeUndefined();
|
||||||
|
expect(packageJson.dependencies[existing]).toBeDefined();
|
||||||
|
expect(packageJson.devDependencies['@nrwl/node']).toBeDefined();
|
||||||
|
expect(packageJson.devDependencies[existing]).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('defaultCollection', () => {
|
||||||
|
it('should be set if none was set before', async () => {
|
||||||
|
await initGenerator(tree, {});
|
||||||
|
const workspaceJson = readJson(tree, 'workspace.json');
|
||||||
|
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/node');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add jest config if unitTestRunner is none', async () => {
|
||||||
|
await initGenerator(tree, { unitTestRunner: 'none' });
|
||||||
|
expect(tree.exists('jest.config.js')).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
53
packages/node/src/generators/init/init.ts
Normal file
53
packages/node/src/generators/init/init.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
convertNxGenerator,
|
||||||
|
formatFiles,
|
||||||
|
GeneratorCallback,
|
||||||
|
Tree,
|
||||||
|
updateJson,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
import { Schema } from './schema';
|
||||||
|
import { setDefaultCollection } from '@nrwl/workspace/src/utilities/set-default-collection';
|
||||||
|
import { jestInitGenerator } from '@nrwl/jest';
|
||||||
|
|
||||||
|
function updateDependencies(tree: Tree) {
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
delete json.dependencies['@nrwl/node'];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
return addDependenciesToPackageJson(tree, {}, { '@nrwl/node': nxVersion });
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOptions(schema: Schema) {
|
||||||
|
return {
|
||||||
|
...schema,
|
||||||
|
unitTestRunner: schema.unitTestRunner ?? 'jest',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initGenerator(tree: Tree, schema: Schema) {
|
||||||
|
const options = normalizeOptions(schema);
|
||||||
|
|
||||||
|
setDefaultCollection(tree, '@nrwl/node');
|
||||||
|
|
||||||
|
let jestInstall: GeneratorCallback;
|
||||||
|
if (options.unitTestRunner === 'jest') {
|
||||||
|
jestInstall = await jestInitGenerator(tree, {});
|
||||||
|
}
|
||||||
|
const installTask = await updateDependencies(tree);
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
if (jestInstall) {
|
||||||
|
await jestInstall();
|
||||||
|
}
|
||||||
|
await installTask();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default initGenerator;
|
||||||
|
export const initSchematic = convertNxGenerator(initGenerator);
|
||||||
4
packages/node/src/generators/init/schema.d.ts
vendored
Normal file
4
packages/node/src/generators/init/schema.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface Schema {
|
||||||
|
unitTestRunner?: 'jest' | 'none';
|
||||||
|
skipFormat?: boolean;
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"cli": "nx",
|
||||||
"id": "NxNodeInit",
|
"id": "NxNodeInit",
|
||||||
"title": "Init Node Plugin",
|
"title": "Init Node Plugin",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -1,21 +1,20 @@
|
|||||||
import { Tree } from '@angular-devkit/schematics';
|
import { NxJsonConfiguration, readJson, Tree } from '@nrwl/devkit';
|
||||||
import { NxJson, readJsonInTree } from '@nrwl/workspace';
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
|
||||||
import { runSchematic } from '../../utils/testing';
|
|
||||||
import { Schema } from './schema.d';
|
import { Schema } from './schema.d';
|
||||||
|
import { libraryGenerator } from './library';
|
||||||
|
|
||||||
describe('lib', () => {
|
describe('lib', () => {
|
||||||
let appTree: Tree;
|
let tree: Tree;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
appTree = Tree.empty();
|
tree = createTreeWithEmptyWorkspace();
|
||||||
appTree = createEmptyWorkspace(appTree);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('not nested', () => {
|
describe('not nested', () => {
|
||||||
it('should update workspace.json', async () => {
|
it('should update workspace.json', async () => {
|
||||||
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
|
await libraryGenerator(tree, { name: 'myLib' });
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
||||||
expect(workspaceJson.projects['my-lib'].architect.build).toBeUndefined();
|
expect(workspaceJson.projects['my-lib'].architect.build).toBeUndefined();
|
||||||
expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
|
expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
|
||||||
@ -35,12 +34,12 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('adds srcRootForCompilationRoot in workspace.json', async () => {
|
it('adds srcRootForCompilationRoot in workspace.json', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', rootDir: './src', buildable: true },
|
rootDir: './src',
|
||||||
appTree
|
buildable: true,
|
||||||
);
|
});
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
expect(
|
expect(
|
||||||
workspaceJson.projects['my-lib'].architect.build.options
|
workspaceJson.projects['my-lib'].architect.build.options
|
||||||
.srcRootForCompilationRoot
|
.srcRootForCompilationRoot
|
||||||
@ -48,12 +47,8 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update nx.json', async () => {
|
it('should update nx.json', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', tags: 'one,two' });
|
||||||
'lib',
|
const nxJson = readJson<NxJsonConfiguration>(tree, '/nx.json');
|
||||||
{ name: 'myLib', tags: 'one,two' },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
|
|
||||||
expect(nxJson.projects).toEqual({
|
expect(nxJson.projects).toEqual({
|
||||||
'my-lib': {
|
'my-lib': {
|
||||||
tags: ['one', 'two'],
|
tags: ['one', 'two'],
|
||||||
@ -62,16 +57,16 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update root tsconfig.base.json', async () => {
|
it('should update root tsconfig.base.json', async () => {
|
||||||
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
|
await libraryGenerator(tree, { name: 'myLib' });
|
||||||
const tsconfigJson = readJsonInTree(tree, '/tsconfig.base.json');
|
const tsconfigJson = readJson(tree, '/tsconfig.base.json');
|
||||||
expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([
|
expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([
|
||||||
'libs/my-lib/src/index.ts',
|
'libs/my-lib/src/index.ts',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a local tsconfig.json', async () => {
|
it('should create a local tsconfig.json', async () => {
|
||||||
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
|
await libraryGenerator(tree, { name: 'myLib' });
|
||||||
const tsconfigJson = readJsonInTree(tree, 'libs/my-lib/tsconfig.json');
|
const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.json');
|
||||||
expect(tsconfigJson).toMatchInlineSnapshot(`
|
expect(tsconfigJson).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
@ -90,30 +85,24 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should extend the local tsconfig.json with tsconfig.spec.json', async () => {
|
it('should extend the local tsconfig.json with tsconfig.spec.json', async () => {
|
||||||
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
|
await libraryGenerator(tree, { name: 'myLib' });
|
||||||
const tsconfigJson = readJsonInTree(
|
const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.spec.json');
|
||||||
tree,
|
|
||||||
'libs/my-lib/tsconfig.spec.json'
|
|
||||||
);
|
|
||||||
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should extend the local tsconfig.json with tsconfig.lib.json', async () => {
|
it('should extend the local tsconfig.json with tsconfig.lib.json', async () => {
|
||||||
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
|
await libraryGenerator(tree, { name: 'myLib' });
|
||||||
const tsconfigJson = readJsonInTree(
|
const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.lib.json');
|
||||||
tree,
|
|
||||||
'libs/my-lib/tsconfig.lib.json'
|
|
||||||
);
|
|
||||||
expect(tsconfigJson.compilerOptions.types).toContain('node');
|
expect(tsconfigJson.compilerOptions.types).toContain('node');
|
||||||
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate files', async () => {
|
it('should generate files', async () => {
|
||||||
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
|
await libraryGenerator(tree, { name: 'myLib' });
|
||||||
expect(tree.exists(`libs/my-lib/jest.config.js`)).toBeTruthy();
|
expect(tree.exists(`libs/my-lib/jest.config.js`)).toBeTruthy();
|
||||||
expect(tree.exists('libs/my-lib/src/index.ts')).toBeTruthy();
|
expect(tree.exists('libs/my-lib/src/index.ts')).toBeTruthy();
|
||||||
|
|
||||||
const eslintrc = readJsonInTree(tree, 'libs/my-lib/.eslintrc.json');
|
const eslintrc = readJson(tree, 'libs/my-lib/.eslintrc.json');
|
||||||
expect(eslintrc).toMatchInlineSnapshot(`
|
expect(eslintrc).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"extends": Array [
|
"extends": Array [
|
||||||
@ -159,32 +148,24 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe('nested', () => {
|
describe('nested', () => {
|
||||||
it('should update nx.json', async () => {
|
it('should update nx.json', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib',
|
name: 'myLib',
|
||||||
directory: 'myDir',
|
directory: 'myDir',
|
||||||
tags: 'one',
|
tags: 'one',
|
||||||
},
|
});
|
||||||
appTree
|
const nxJson = readJson<NxJsonConfiguration>(tree, '/nx.json');
|
||||||
);
|
|
||||||
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
|
|
||||||
expect(nxJson.projects).toEqual({
|
expect(nxJson.projects).toEqual({
|
||||||
'my-dir-my-lib': {
|
'my-dir-my-lib': {
|
||||||
tags: ['one'],
|
tags: ['one'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const tree2 = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib2',
|
name: 'myLib2',
|
||||||
directory: 'myDir',
|
directory: 'myDir',
|
||||||
tags: 'one,two',
|
tags: 'one,two',
|
||||||
},
|
});
|
||||||
tree
|
const nxJson2 = readJson<NxJsonConfiguration>(tree, '/nx.json');
|
||||||
);
|
|
||||||
const nxJson2 = readJsonInTree<NxJson>(tree2, '/nx.json');
|
|
||||||
expect(nxJson2.projects).toEqual({
|
expect(nxJson2.projects).toEqual({
|
||||||
'my-dir-my-lib': {
|
'my-dir-my-lib': {
|
||||||
tags: ['one'],
|
tags: ['one'],
|
||||||
@ -196,22 +177,14 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should generate files', async () => {
|
it('should generate files', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', directory: 'myDir' });
|
||||||
'lib',
|
|
||||||
{ name: 'myLib', directory: 'myDir' },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
expect(tree.exists(`libs/my-dir/my-lib/jest.config.js`)).toBeTruthy();
|
expect(tree.exists(`libs/my-dir/my-lib/jest.config.js`)).toBeTruthy();
|
||||||
expect(tree.exists('libs/my-dir/my-lib/src/index.ts')).toBeTruthy();
|
expect(tree.exists('libs/my-dir/my-lib/src/index.ts')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update workspace.json', async () => {
|
it('should update workspace.json', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', directory: 'myDir' });
|
||||||
'lib',
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
{ name: 'myLib', directory: 'myDir' },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
|
||||||
|
|
||||||
expect(workspaceJson.projects['my-dir-my-lib'].root).toEqual(
|
expect(workspaceJson.projects['my-dir-my-lib'].root).toEqual(
|
||||||
'libs/my-dir/my-lib'
|
'libs/my-dir/my-lib'
|
||||||
@ -225,12 +198,8 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update tsconfig.json', async () => {
|
it('should update tsconfig.json', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', directory: 'myDir' });
|
||||||
'lib',
|
const tsconfigJson = readJson(tree, '/tsconfig.base.json');
|
||||||
{ name: 'myLib', directory: 'myDir' },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
const tsconfigJson = readJsonInTree(tree, '/tsconfig.base.json');
|
|
||||||
expect(
|
expect(
|
||||||
tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']
|
tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']
|
||||||
).toEqual(['libs/my-dir/my-lib/src/index.ts']);
|
).toEqual(['libs/my-dir/my-lib/src/index.ts']);
|
||||||
@ -243,15 +212,11 @@ describe('lib', () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib',
|
name: 'myLib',
|
||||||
directory: 'myDir',
|
directory: 'myDir',
|
||||||
publishable: true,
|
publishable: true,
|
||||||
},
|
});
|
||||||
appTree
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.message).toContain(
|
expect(e.message).toContain(
|
||||||
'For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)'
|
'For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)'
|
||||||
@ -260,16 +225,9 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create a local tsconfig.json', async () => {
|
it('should create a local tsconfig.json', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', directory: 'myDir' });
|
||||||
'lib',
|
|
||||||
{ name: 'myLib', directory: 'myDir' },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
|
|
||||||
const tsconfigJson = readJsonInTree(
|
const tsconfigJson = readJson(tree, 'libs/my-dir/my-lib/tsconfig.json');
|
||||||
tree,
|
|
||||||
'libs/my-dir/my-lib/tsconfig.json'
|
|
||||||
);
|
|
||||||
expect(tsconfigJson.extends).toEqual('../../../tsconfig.base.json');
|
expect(tsconfigJson.extends).toEqual('../../../tsconfig.base.json');
|
||||||
expect(tsconfigJson.references).toEqual([
|
expect(tsconfigJson.references).toEqual([
|
||||||
{
|
{
|
||||||
@ -284,20 +242,13 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe('--unit-test-runner none', () => {
|
describe('--unit-test-runner none', () => {
|
||||||
it('should not generate test configuration', async () => {
|
it('should not generate test configuration', async () => {
|
||||||
const resultTree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', unitTestRunner: 'none' });
|
||||||
'lib',
|
expect(tree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy();
|
||||||
{ name: 'myLib', unitTestRunner: 'none' },
|
expect(tree.exists('libs/my-lib/jest.config.js')).toBeFalsy();
|
||||||
appTree
|
expect(tree.exists('libs/my-lib/lib/my-lib.spec.ts')).toBeFalsy();
|
||||||
);
|
const workspaceJson = readJson(tree, 'workspace.json');
|
||||||
expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy();
|
|
||||||
expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeFalsy();
|
|
||||||
expect(resultTree.exists('libs/my-lib/lib/my-lib.spec.ts')).toBeFalsy();
|
|
||||||
const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
|
|
||||||
expect(workspaceJson.projects['my-lib'].architect.test).toBeUndefined();
|
expect(workspaceJson.projects['my-lib'].architect.test).toBeUndefined();
|
||||||
const tsconfigJson = readJsonInTree(
|
const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.json');
|
||||||
resultTree,
|
|
||||||
'libs/my-lib/tsconfig.json'
|
|
||||||
);
|
|
||||||
expect(tsconfigJson.extends).toEqual('../../tsconfig.base.json');
|
expect(tsconfigJson.extends).toEqual('../../tsconfig.base.json');
|
||||||
expect(tsconfigJson.references).toEqual([
|
expect(tsconfigJson.references).toEqual([
|
||||||
{
|
{
|
||||||
@ -320,12 +271,8 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe('buildable package', () => {
|
describe('buildable package', () => {
|
||||||
it('should have a builder defined', async () => {
|
it('should have a builder defined', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', buildable: true });
|
||||||
'lib',
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
{ name: 'myLib', buildable: true },
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
|
||||||
|
|
||||||
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
||||||
|
|
||||||
@ -352,12 +299,12 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe('publishable package', () => {
|
describe('publishable package', () => {
|
||||||
it('should have a builder defined', async () => {
|
it('should have a builder defined', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', publishable: true, importPath: '@proj/mylib' },
|
publishable: true,
|
||||||
appTree
|
importPath: '@proj/mylib',
|
||||||
);
|
});
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
|
|
||||||
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
||||||
|
|
||||||
@ -365,16 +312,13 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update package.json', async () => {
|
it('should update package.json', async () => {
|
||||||
const publishableTree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'mylib',
|
||||||
{ name: 'mylib', publishable: true, importPath: '@proj/mylib' },
|
publishable: true,
|
||||||
appTree
|
importPath: '@proj/mylib',
|
||||||
);
|
});
|
||||||
|
|
||||||
let packageJsonContent = readJsonInTree(
|
let packageJsonContent = readJson(tree, 'libs/mylib/package.json');
|
||||||
publishableTree,
|
|
||||||
'libs/mylib/package.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(packageJsonContent.name).toEqual('@proj/mylib');
|
expect(packageJsonContent.name).toEqual('@proj/mylib');
|
||||||
});
|
});
|
||||||
@ -382,21 +326,14 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe('--importPath', () => {
|
describe('--importPath', () => {
|
||||||
it('should update the package.json & tsconfig with the given import path', async () => {
|
it('should update the package.json & tsconfig with the given import path', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib',
|
name: 'myLib',
|
||||||
publishable: true,
|
publishable: true,
|
||||||
directory: 'myDir',
|
directory: 'myDir',
|
||||||
importPath: '@myorg/lib',
|
importPath: '@myorg/lib',
|
||||||
},
|
});
|
||||||
appTree
|
const packageJson = readJson(tree, 'libs/my-dir/my-lib/package.json');
|
||||||
);
|
const tsconfigJson = readJson(tree, '/tsconfig.base.json');
|
||||||
const packageJson = readJsonInTree(
|
|
||||||
tree,
|
|
||||||
'libs/my-dir/my-lib/package.json'
|
|
||||||
);
|
|
||||||
const tsconfigJson = readJsonInTree(tree, '/tsconfig.base.json');
|
|
||||||
|
|
||||||
expect(packageJson.name).toBe('@myorg/lib');
|
expect(packageJson.name).toBe('@myorg/lib');
|
||||||
expect(
|
expect(
|
||||||
@ -405,26 +342,18 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the same importPath has already been used', async () => {
|
it('should fail if the same importPath has already been used', async () => {
|
||||||
const tree1 = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib1',
|
name: 'myLib1',
|
||||||
publishable: true,
|
publishable: true,
|
||||||
importPath: '@myorg/lib',
|
importPath: '@myorg/lib',
|
||||||
},
|
});
|
||||||
appTree
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib2',
|
name: 'myLib2',
|
||||||
publishable: true,
|
publishable: true,
|
||||||
importPath: '@myorg/lib',
|
importPath: '@myorg/lib',
|
||||||
},
|
});
|
||||||
tree1
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.message).toContain(
|
expect(e.message).toContain(
|
||||||
'You already have a library using the import path'
|
'You already have a library using the import path'
|
||||||
@ -437,22 +366,21 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe(`--babelJest`, () => {
|
describe(`--babelJest`, () => {
|
||||||
it('should use babel for jest', async () => {
|
it('should use babel for jest', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', babelJest: true } as Schema,
|
babelJest: true,
|
||||||
appTree
|
} as Schema);
|
||||||
);
|
|
||||||
|
|
||||||
expect(tree.readContent(`libs/my-lib/jest.config.js`))
|
expect(tree.read(`libs/my-lib/jest.config.js`).toString())
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"module.exports = {
|
"module.exports = {
|
||||||
displayName: 'my-lib',
|
displayName: 'my-lib',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\\\\\\\.[tj]sx?$': 'babel-jest',
|
'^.+\\\\\\\\.[tj]sx?$': 'babel-jest'
|
||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
coverageDirectory: '../../coverage/libs/my-lib',
|
coverageDirectory: '../../coverage/libs/my-lib'
|
||||||
};
|
};
|
||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
@ -460,14 +388,10 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
describe('--js flag', () => {
|
describe('--js flag', () => {
|
||||||
it('should generate js files instead of ts files', async () => {
|
it('should generate js files instead of ts files', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
|
||||||
{
|
|
||||||
name: 'myLib',
|
name: 'myLib',
|
||||||
js: true,
|
js: true,
|
||||||
} as Schema,
|
} as Schema);
|
||||||
appTree
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(tree.exists(`libs/my-lib/jest.config.js`)).toBeTruthy();
|
expect(tree.exists(`libs/my-lib/jest.config.js`)).toBeTruthy();
|
||||||
expect(tree.exists('libs/my-lib/src/index.js')).toBeTruthy();
|
expect(tree.exists('libs/my-lib/src/index.js')).toBeTruthy();
|
||||||
@ -475,37 +399,35 @@ describe('lib', () => {
|
|||||||
expect(tree.exists('libs/my-lib/src/lib/my-lib.spec.js')).toBeTruthy();
|
expect(tree.exists('libs/my-lib/src/lib/my-lib.spec.js')).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
readJsonInTree(tree, 'libs/my-lib/tsconfig.json').compilerOptions
|
readJson(tree, 'libs/my-lib/tsconfig.json').compilerOptions
|
||||||
).toEqual({
|
).toEqual({
|
||||||
allowJs: true,
|
allowJs: true,
|
||||||
});
|
});
|
||||||
expect(
|
expect(readJson(tree, 'libs/my-lib/tsconfig.lib.json').include).toEqual([
|
||||||
readJsonInTree(tree, 'libs/my-lib/tsconfig.lib.json').include
|
'**/*.ts',
|
||||||
).toEqual(['**/*.ts', '**/*.js']);
|
'**/*.js',
|
||||||
expect(
|
]);
|
||||||
readJsonInTree(tree, 'libs/my-lib/tsconfig.lib.json').exclude
|
expect(readJson(tree, 'libs/my-lib/tsconfig.lib.json').exclude).toEqual([
|
||||||
).toEqual(['**/*.spec.ts', '**/*.spec.js']);
|
'**/*.spec.ts',
|
||||||
|
'**/*.spec.js',
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update root tsconfig.json with a js file path', async () => {
|
it('should update root tsconfig.json with a js file path', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, { name: 'myLib', js: true } as Schema);
|
||||||
'lib',
|
const tsconfigJson = readJson(tree, '/tsconfig.base.json');
|
||||||
{ name: 'myLib', js: true } as Schema,
|
|
||||||
appTree
|
|
||||||
);
|
|
||||||
const tsconfigJson = readJsonInTree(tree, '/tsconfig.base.json');
|
|
||||||
expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([
|
expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([
|
||||||
'libs/my-lib/src/index.js',
|
'libs/my-lib/src/index.js',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update architect builder when --buildable', async () => {
|
it('should update architect builder when --buildable', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', buildable: true, js: true } as Schema,
|
buildable: true,
|
||||||
appTree
|
js: true,
|
||||||
);
|
} as Schema);
|
||||||
const workspaceJson = readJsonInTree(tree, '/workspace.json');
|
const workspaceJson = readJson(tree, '/workspace.json');
|
||||||
|
|
||||||
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
|
||||||
|
|
||||||
@ -515,11 +437,11 @@ describe('lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should generate js files for nested libs as well', async () => {
|
it('should generate js files for nested libs as well', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', directory: 'myDir', js: true } as Schema,
|
directory: 'myDir',
|
||||||
appTree
|
js: true,
|
||||||
);
|
} as Schema);
|
||||||
expect(tree.exists(`libs/my-dir/my-lib/jest.config.js`)).toBeTruthy();
|
expect(tree.exists(`libs/my-dir/my-lib/jest.config.js`)).toBeTruthy();
|
||||||
expect(tree.exists('libs/my-dir/my-lib/src/index.js')).toBeTruthy();
|
expect(tree.exists('libs/my-dir/my-lib/src/index.js')).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
@ -533,21 +455,20 @@ describe('lib', () => {
|
|||||||
|
|
||||||
describe('--pascalCaseFiles', () => {
|
describe('--pascalCaseFiles', () => {
|
||||||
it('should generate files with upper case names', async () => {
|
it('should generate files with upper case names', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', pascalCaseFiles: true } as Schema,
|
pascalCaseFiles: true,
|
||||||
appTree
|
} as Schema);
|
||||||
);
|
|
||||||
expect(tree.exists('libs/my-lib/src/lib/MyLib.ts')).toBeTruthy();
|
expect(tree.exists('libs/my-lib/src/lib/MyLib.ts')).toBeTruthy();
|
||||||
expect(tree.exists('libs/my-lib/src/lib/MyLib.spec.ts')).toBeTruthy();
|
expect(tree.exists('libs/my-lib/src/lib/MyLib.spec.ts')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate files with upper case names for nested libs as well', async () => {
|
it('should generate files with upper case names for nested libs as well', async () => {
|
||||||
const tree = await runSchematic(
|
await libraryGenerator(tree, {
|
||||||
'lib',
|
name: 'myLib',
|
||||||
{ name: 'myLib', directory: 'myDir', pascalCaseFiles: true } as Schema,
|
directory: 'myDir',
|
||||||
appTree
|
pascalCaseFiles: true,
|
||||||
);
|
} as Schema);
|
||||||
expect(
|
expect(
|
||||||
tree.exists('libs/my-dir/my-lib/src/lib/MyDirMyLib.ts')
|
tree.exists('libs/my-dir/my-lib/src/lib/MyDirMyLib.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
140
packages/node/src/generators/library/library.ts
Normal file
140
packages/node/src/generators/library/library.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import {
|
||||||
|
convertNxGenerator,
|
||||||
|
formatFiles,
|
||||||
|
generateFiles,
|
||||||
|
getWorkspaceLayout,
|
||||||
|
joinPathFragments,
|
||||||
|
names,
|
||||||
|
offsetFromRoot,
|
||||||
|
readProjectConfiguration,
|
||||||
|
toJS,
|
||||||
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
updateTsConfigsToJs,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { Schema } from './schema';
|
||||||
|
import { libraryGenerator as workspaceLibraryGenerator } from '@nrwl/workspace/generators';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
export interface NormalizedSchema extends Schema {
|
||||||
|
name: string;
|
||||||
|
prefix: string;
|
||||||
|
fileName: string;
|
||||||
|
projectRoot: string;
|
||||||
|
projectDirectory: string;
|
||||||
|
parsedTags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function libraryGenerator(tree: Tree, schema: Schema) {
|
||||||
|
const options = normalizeOptions(tree, schema);
|
||||||
|
|
||||||
|
if (options.publishable === true && !schema.importPath) {
|
||||||
|
throw new Error(
|
||||||
|
`For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const libraryInstall = await workspaceLibraryGenerator(tree, {
|
||||||
|
...schema,
|
||||||
|
importPath: options.importPath,
|
||||||
|
});
|
||||||
|
createFiles(tree, options);
|
||||||
|
|
||||||
|
if (options.js) {
|
||||||
|
updateTsConfigsToJs(tree, options);
|
||||||
|
}
|
||||||
|
updateProject(tree, options);
|
||||||
|
await formatFiles(tree);
|
||||||
|
|
||||||
|
return libraryInstall;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default libraryGenerator;
|
||||||
|
export const librarySchematic = convertNxGenerator(libraryGenerator);
|
||||||
|
|
||||||
|
function normalizeOptions(tree: Tree, options: Schema): NormalizedSchema {
|
||||||
|
const { npmScope, libsDir } = getWorkspaceLayout(tree);
|
||||||
|
const defaultPrefix = npmScope;
|
||||||
|
const name = names(options.name).fileName;
|
||||||
|
const projectDirectory = options.directory
|
||||||
|
? `${names(options.directory).fileName}/${name}`
|
||||||
|
: name;
|
||||||
|
|
||||||
|
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
||||||
|
const fileName = projectName;
|
||||||
|
const projectRoot = joinPathFragments(libsDir, projectDirectory);
|
||||||
|
|
||||||
|
const parsedTags = options.tags
|
||||||
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const importPath =
|
||||||
|
options.importPath || `@${defaultPrefix}/${projectDirectory}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
prefix: defaultPrefix, // we could also allow customizing this
|
||||||
|
fileName,
|
||||||
|
name: projectName,
|
||||||
|
projectRoot,
|
||||||
|
projectDirectory,
|
||||||
|
parsedTags,
|
||||||
|
importPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFiles(tree: Tree, options: NormalizedSchema) {
|
||||||
|
const nameFormats = names(options.name);
|
||||||
|
return generateFiles(
|
||||||
|
tree,
|
||||||
|
join(__dirname, './files/lib'),
|
||||||
|
options.projectRoot,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
...nameFormats,
|
||||||
|
tmpl: '',
|
||||||
|
offsetFromRoot: offsetFromRoot(options.projectRoot),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (options.unitTestRunner === 'none') {
|
||||||
|
tree.delete(
|
||||||
|
join(options.projectRoot, `./src/lib/${nameFormats.fileName}.spec.ts`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!options.publishable && !options.buildable) {
|
||||||
|
tree.delete(join(options.projectRoot, 'package.json'));
|
||||||
|
}
|
||||||
|
if (options.js) {
|
||||||
|
toJS(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProject(tree: Tree, options: NormalizedSchema) {
|
||||||
|
if (!options.publishable && !options.buildable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const project = readProjectConfiguration(tree, options.name);
|
||||||
|
const { libsDir } = getWorkspaceLayout(tree);
|
||||||
|
|
||||||
|
project.targets = project.targets || {};
|
||||||
|
project.targets.build = {
|
||||||
|
executor: '@nrwl/node:package',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
options: {
|
||||||
|
outputPath: `dist/${libsDir}/${options.projectDirectory}`,
|
||||||
|
tsConfig: `${options.projectRoot}/tsconfig.lib.json`,
|
||||||
|
packageJson: `${options.projectRoot}/package.json`,
|
||||||
|
main: `${options.projectRoot}/src/index` + (options.js ? '.js' : '.ts'),
|
||||||
|
assets: [`${options.projectRoot}/*.md`],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.rootDir) {
|
||||||
|
project.targets.build.options.srcRootForCompilationRoot = options.rootDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProjectConfiguration(tree, options.name, project);
|
||||||
|
}
|
||||||
@ -3,18 +3,18 @@ import { Linter } from '@nrwl/workspace';
|
|||||||
export interface Schema {
|
export interface Schema {
|
||||||
name: string;
|
name: string;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
skipTsConfig: boolean;
|
skipTsConfig?: boolean;
|
||||||
skipFormat: boolean;
|
skipFormat?: boolean;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
unitTestRunner: 'jest' | 'none';
|
unitTestRunner?: 'jest' | 'none';
|
||||||
linter: Linter;
|
linter?: Linter;
|
||||||
buildable?: boolean;
|
buildable?: boolean;
|
||||||
publishable?: boolean;
|
publishable?: boolean;
|
||||||
importPath?: string;
|
importPath?: string;
|
||||||
testEnvironment: 'jsdom' | 'node';
|
testEnvironment?: 'jsdom' | 'node';
|
||||||
rootDir?: string;
|
rootDir?: string;
|
||||||
babelJest?: boolean;
|
babelJest?: boolean;
|
||||||
js: boolean;
|
js?: boolean;
|
||||||
pascalCaseFiles: boolean;
|
pascalCaseFiles?: boolean;
|
||||||
strict: boolean;
|
strict?: boolean;
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"cli": "nx",
|
||||||
"id": "NxNodeLibrary",
|
"id": "NxNodeLibrary",
|
||||||
"title": "Create a Node Library for Nx",
|
"title": "Create a Node Library for Nx",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -1,247 +0,0 @@
|
|||||||
import {
|
|
||||||
apply,
|
|
||||||
chain,
|
|
||||||
externalSchematic,
|
|
||||||
mergeWith,
|
|
||||||
move,
|
|
||||||
noop,
|
|
||||||
Rule,
|
|
||||||
SchematicContext,
|
|
||||||
template,
|
|
||||||
Tree,
|
|
||||||
url,
|
|
||||||
} from '@angular-devkit/schematics';
|
|
||||||
import { join, normalize, Path } from '@angular-devkit/core';
|
|
||||||
import { Schema } from './schema';
|
|
||||||
import {
|
|
||||||
updateJsonInTree,
|
|
||||||
updateWorkspaceInTree,
|
|
||||||
generateProjectLint,
|
|
||||||
addLintFiles,
|
|
||||||
formatFiles,
|
|
||||||
} from '@nrwl/workspace';
|
|
||||||
import { getProjectConfig } from '@nrwl/workspace';
|
|
||||||
import init from '../init/init';
|
|
||||||
import { appsDir } from '@nrwl/workspace/src/utils/ast-utils';
|
|
||||||
import {
|
|
||||||
toJS,
|
|
||||||
updateTsConfigsToJs,
|
|
||||||
maybeJs,
|
|
||||||
} from '@nrwl/workspace/src/utils/rules/to-js';
|
|
||||||
import { names, offsetFromRoot } from '@nrwl/devkit';
|
|
||||||
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
|
|
||||||
|
|
||||||
interface NormalizedSchema extends Schema {
|
|
||||||
appProjectRoot: Path;
|
|
||||||
parsedTags: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNxJson(options: NormalizedSchema): Rule {
|
|
||||||
return updateJsonInTree(`/nx.json`, (json) => {
|
|
||||||
return {
|
|
||||||
...json,
|
|
||||||
projects: {
|
|
||||||
...json.projects,
|
|
||||||
[options.name]: { tags: options.parsedTags },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBuildConfig(project: any, options: NormalizedSchema) {
|
|
||||||
return {
|
|
||||||
builder: '@nrwl/node:build',
|
|
||||||
outputs: ['{options.outputPath}'],
|
|
||||||
options: {
|
|
||||||
outputPath: join(normalize('dist'), options.appProjectRoot),
|
|
||||||
main: maybeJs(options, join(project.sourceRoot, 'main.ts')),
|
|
||||||
tsConfig: join(options.appProjectRoot, 'tsconfig.app.json'),
|
|
||||||
assets: [join(project.sourceRoot, 'assets')],
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
production: {
|
|
||||||
optimization: true,
|
|
||||||
extractLicenses: true,
|
|
||||||
inspect: false,
|
|
||||||
fileReplacements: [
|
|
||||||
{
|
|
||||||
replace: maybeJs(
|
|
||||||
options,
|
|
||||||
join(project.sourceRoot, 'environments/environment.ts')
|
|
||||||
),
|
|
||||||
with: maybeJs(
|
|
||||||
options,
|
|
||||||
join(project.sourceRoot, 'environments/environment.prod.ts')
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getServeConfig(options: NormalizedSchema) {
|
|
||||||
return {
|
|
||||||
builder: '@nrwl/node:execute',
|
|
||||||
options: {
|
|
||||||
buildTarget: `${options.name}:build`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateWorkspaceJson(options: NormalizedSchema): Rule {
|
|
||||||
return updateWorkspaceInTree((workspaceJson) => {
|
|
||||||
const project = {
|
|
||||||
root: options.appProjectRoot,
|
|
||||||
sourceRoot: join(options.appProjectRoot, 'src'),
|
|
||||||
projectType: 'application',
|
|
||||||
prefix: options.name,
|
|
||||||
architect: <any>{},
|
|
||||||
};
|
|
||||||
|
|
||||||
project.architect.build = getBuildConfig(project, options);
|
|
||||||
project.architect.serve = getServeConfig(options);
|
|
||||||
project.architect.lint = generateProjectLint(
|
|
||||||
normalize(project.root),
|
|
||||||
join(normalize(project.root), 'tsconfig.app.json'),
|
|
||||||
options.linter,
|
|
||||||
[`${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`]
|
|
||||||
);
|
|
||||||
|
|
||||||
workspaceJson.projects[options.name] = project;
|
|
||||||
|
|
||||||
workspaceJson.defaultProject = workspaceJson.defaultProject || options.name;
|
|
||||||
|
|
||||||
return workspaceJson;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addAppFiles(options: NormalizedSchema): Rule {
|
|
||||||
return chain([
|
|
||||||
mergeWith(
|
|
||||||
apply(url(`./files/app`), [
|
|
||||||
template({
|
|
||||||
tmpl: '',
|
|
||||||
name: options.name,
|
|
||||||
root: options.appProjectRoot,
|
|
||||||
offset: offsetFromRoot(options.appProjectRoot),
|
|
||||||
}),
|
|
||||||
move(options.appProjectRoot),
|
|
||||||
options.js ? toJS() : noop(),
|
|
||||||
])
|
|
||||||
),
|
|
||||||
options.pascalCaseFiles
|
|
||||||
? (tree, context) => {
|
|
||||||
context.logger.warn('NOTE: --pascalCaseFiles is a noop');
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
: noop(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProxy(options: NormalizedSchema): Rule {
|
|
||||||
return (host: Tree, context: SchematicContext) => {
|
|
||||||
const projectConfig = getProjectConfig(host, options.frontendProject);
|
|
||||||
if (projectConfig.architect && projectConfig.architect.serve) {
|
|
||||||
const pathToProxyFile = `${projectConfig.root}/proxy.conf.json`;
|
|
||||||
|
|
||||||
if (!host.exists(pathToProxyFile)) {
|
|
||||||
host.create(
|
|
||||||
pathToProxyFile,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
'/api': {
|
|
||||||
target: 'http://localhost:3333',
|
|
||||||
secure: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
//add new entry to existing config
|
|
||||||
const proxyFileContent = host.get(pathToProxyFile).content.toString();
|
|
||||||
|
|
||||||
const proxyModified = {
|
|
||||||
...JSON.parse(proxyFileContent),
|
|
||||||
[`/${options.name}-api`]: {
|
|
||||||
target: 'http://localhost:3333',
|
|
||||||
secure: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
host.overwrite(pathToProxyFile, JSON.stringify(proxyModified, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateWorkspaceInTree((json) => {
|
|
||||||
projectConfig.architect.serve.options.proxyConfig = pathToProxyFile;
|
|
||||||
json.projects[options.frontendProject] = projectConfig;
|
|
||||||
return json;
|
|
||||||
})(host, context);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function addJest(options: NormalizedSchema) {
|
|
||||||
return options.unitTestRunner === 'jest'
|
|
||||||
? externalSchematic('@nrwl/jest', 'jest-project', {
|
|
||||||
project: options.name,
|
|
||||||
setupFile: 'none',
|
|
||||||
skipSerializers: true,
|
|
||||||
supportTsx: options.js,
|
|
||||||
babelJest: options.babelJest,
|
|
||||||
})
|
|
||||||
: noop();
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (schema: Schema): Rule {
|
|
||||||
return (host: Tree, context: SchematicContext) => {
|
|
||||||
const options = normalizeOptions(host, schema);
|
|
||||||
return chain([
|
|
||||||
init({
|
|
||||||
...options,
|
|
||||||
skipFormat: true,
|
|
||||||
}),
|
|
||||||
addLintFiles(options.appProjectRoot, options.linter),
|
|
||||||
addAppFiles(options),
|
|
||||||
options.js
|
|
||||||
? updateTsConfigsToJs({ projectRoot: options.appProjectRoot })
|
|
||||||
: noop,
|
|
||||||
updateWorkspaceJson(options),
|
|
||||||
updateNxJson(options),
|
|
||||||
addJest(options),
|
|
||||||
options.frontendProject ? addProxy(options) : noop(),
|
|
||||||
formatFiles(options),
|
|
||||||
])(host, context);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
|
||||||
const appDirectory = options.directory
|
|
||||||
? `${names(options.directory).fileName}/${names(options.name).fileName}`
|
|
||||||
: names(options.name).fileName;
|
|
||||||
|
|
||||||
const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');
|
|
||||||
|
|
||||||
const appProjectRoot = join(normalize(appsDir(host)), appDirectory);
|
|
||||||
|
|
||||||
const parsedTags = options.tags
|
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
...options,
|
|
||||||
name: names(appProjectName).fileName,
|
|
||||||
frontendProject: options.frontendProject
|
|
||||||
? names(options.frontendProject).fileName
|
|
||||||
: undefined,
|
|
||||||
appProjectRoot,
|
|
||||||
parsedTags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const applicationGenerator = wrapAngularDevkitSchematic(
|
|
||||||
'@nrwl/node',
|
|
||||||
'application'
|
|
||||||
);
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { Linter } from '@nrwl/workspace';
|
|
||||||
|
|
||||||
export interface Schema {
|
|
||||||
name: string;
|
|
||||||
skipFormat: boolean;
|
|
||||||
skipPackageJson: boolean;
|
|
||||||
directory?: string;
|
|
||||||
unitTestRunner: 'jest' | 'none';
|
|
||||||
linter: Linter;
|
|
||||||
tags?: string;
|
|
||||||
frontendProject?: string;
|
|
||||||
babelJest?: boolean;
|
|
||||||
js: boolean;
|
|
||||||
pascalCaseFiles: boolean;
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
import { Tree } from '@angular-devkit/schematics';
|
|
||||||
import { addDepsToPackageJson, readJsonInTree } from '@nrwl/workspace';
|
|
||||||
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
|
|
||||||
import { callRule, runSchematic } from '../../utils/testing';
|
|
||||||
import { nxVersion } from '../../utils/versions';
|
|
||||||
|
|
||||||
describe('init', () => {
|
|
||||||
let tree: Tree;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
tree = Tree.empty();
|
|
||||||
tree = createEmptyWorkspace(tree);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add dependencies', async () => {
|
|
||||||
const existing = 'existing';
|
|
||||||
const existingVersion = '1.0.0';
|
|
||||||
await callRule(
|
|
||||||
addDepsToPackageJson(
|
|
||||||
{ '@nrwl/node': nxVersion, [existing]: existingVersion },
|
|
||||||
{ [existing]: existingVersion },
|
|
||||||
false
|
|
||||||
),
|
|
||||||
tree
|
|
||||||
);
|
|
||||||
const result = await runSchematic('init', {}, tree);
|
|
||||||
const packageJson = readJsonInTree(result, 'package.json');
|
|
||||||
expect(packageJson.dependencies['@nrwl/node']).toBeUndefined();
|
|
||||||
expect(packageJson.dependencies[existing]).toBeDefined();
|
|
||||||
expect(packageJson.devDependencies['@nrwl/node']).toBeDefined();
|
|
||||||
expect(packageJson.devDependencies[existing]).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('defaultCollection', () => {
|
|
||||||
it('should be set if none was set before', async () => {
|
|
||||||
const result = await runSchematic('init', {}, tree);
|
|
||||||
const workspaceJson = readJsonInTree(result, 'workspace.json');
|
|
||||||
expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/node');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add jest config if unitTestRunner is none', async () => {
|
|
||||||
const result = await runSchematic(
|
|
||||||
'init',
|
|
||||||
{
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
},
|
|
||||||
tree
|
|
||||||
);
|
|
||||||
expect(result.exists('jest.config.js')).toEqual(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import { chain, noop, Rule } from '@angular-devkit/schematics';
|
|
||||||
import {
|
|
||||||
addPackageWithInit,
|
|
||||||
formatFiles,
|
|
||||||
setDefaultCollection,
|
|
||||||
updateJsonInTree,
|
|
||||||
} from '@nrwl/workspace';
|
|
||||||
import { nxVersion } from '../../utils/versions';
|
|
||||||
import { Schema } from './schema';
|
|
||||||
|
|
||||||
function updateDependencies(): Rule {
|
|
||||||
return updateJsonInTree('package.json', (json) => {
|
|
||||||
delete json.dependencies['@nrwl/node'];
|
|
||||||
json.devDependencies['@nrwl/node'] = nxVersion;
|
|
||||||
return json;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (schema: Schema) {
|
|
||||||
return chain([
|
|
||||||
setDefaultCollection('@nrwl/node'),
|
|
||||||
schema.unitTestRunner === 'jest'
|
|
||||||
? addPackageWithInit('@nrwl/jest')
|
|
||||||
: noop(),
|
|
||||||
updateDependencies(),
|
|
||||||
formatFiles(schema),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export interface Schema {
|
|
||||||
unitTestRunner: 'jest' | 'none';
|
|
||||||
skipFormat: boolean;
|
|
||||||
}
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
import { normalize, Path } from '@angular-devkit/core';
|
|
||||||
import {
|
|
||||||
apply,
|
|
||||||
chain,
|
|
||||||
externalSchematic,
|
|
||||||
filter,
|
|
||||||
MergeStrategy,
|
|
||||||
mergeWith,
|
|
||||||
move,
|
|
||||||
noop,
|
|
||||||
Rule,
|
|
||||||
SchematicContext,
|
|
||||||
SchematicsException,
|
|
||||||
template,
|
|
||||||
Tree,
|
|
||||||
url,
|
|
||||||
} from '@angular-devkit/schematics';
|
|
||||||
import {
|
|
||||||
formatFiles,
|
|
||||||
getNpmScope,
|
|
||||||
updateWorkspaceInTree,
|
|
||||||
} from '@nrwl/workspace';
|
|
||||||
import { Schema } from './schema';
|
|
||||||
import { libsDir } from '@nrwl/workspace/src/utils/ast-utils';
|
|
||||||
import {
|
|
||||||
maybeJs,
|
|
||||||
toJS,
|
|
||||||
updateTsConfigsToJs,
|
|
||||||
} from '@nrwl/workspace/src/utils/rules/to-js';
|
|
||||||
import { names, offsetFromRoot } from '@nrwl/devkit';
|
|
||||||
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
|
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
|
||||||
name: string;
|
|
||||||
prefix: string;
|
|
||||||
fileName: string;
|
|
||||||
projectRoot: Path;
|
|
||||||
projectDirectory: string;
|
|
||||||
parsedTags: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (schema: NormalizedSchema): Rule {
|
|
||||||
return (host: Tree, context: SchematicContext) => {
|
|
||||||
const options = normalizeOptions(host, schema);
|
|
||||||
|
|
||||||
if (options.publishable === true && !schema.importPath) {
|
|
||||||
throw new SchematicsException(
|
|
||||||
`For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return chain([
|
|
||||||
externalSchematic('@nrwl/workspace', 'lib', {
|
|
||||||
...schema,
|
|
||||||
importPath: options.importPath,
|
|
||||||
}),
|
|
||||||
createFiles(options),
|
|
||||||
options.js ? updateTsConfigsToJs(options) : noop(),
|
|
||||||
addProject(options),
|
|
||||||
formatFiles(options),
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
|
||||||
const defaultPrefix = getNpmScope(host);
|
|
||||||
const name = names(options.name).fileName;
|
|
||||||
const projectDirectory = options.directory
|
|
||||||
? `${names(options.directory).fileName}/${name}`
|
|
||||||
: name;
|
|
||||||
|
|
||||||
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
|
||||||
const fileName = projectName;
|
|
||||||
const projectRoot = normalize(`${libsDir(host)}/${projectDirectory}`);
|
|
||||||
|
|
||||||
const parsedTags = options.tags
|
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const importPath =
|
|
||||||
options.importPath || `@${defaultPrefix}/${projectDirectory}`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...options,
|
|
||||||
prefix: defaultPrefix, // we could also allow customizing this
|
|
||||||
fileName,
|
|
||||||
name: projectName,
|
|
||||||
projectRoot,
|
|
||||||
projectDirectory,
|
|
||||||
parsedTags,
|
|
||||||
importPath,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFiles(options: NormalizedSchema): Rule {
|
|
||||||
return mergeWith(
|
|
||||||
apply(url(`./files/lib`), [
|
|
||||||
template({
|
|
||||||
...options,
|
|
||||||
...names(options.name),
|
|
||||||
tmpl: '',
|
|
||||||
offsetFromRoot: offsetFromRoot(options.projectRoot),
|
|
||||||
}),
|
|
||||||
move(options.projectRoot),
|
|
||||||
options.unitTestRunner === 'none'
|
|
||||||
? filter((file) => !file.endsWith('spec.ts'))
|
|
||||||
: noop(),
|
|
||||||
options.publishable || options.buildable
|
|
||||||
? noop()
|
|
||||||
: filter((file) => !file.endsWith('package.json')),
|
|
||||||
options.js ? toJS() : noop(),
|
|
||||||
]),
|
|
||||||
MergeStrategy.Overwrite
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProject(options: NormalizedSchema): Rule {
|
|
||||||
if (!options.publishable && !options.buildable) {
|
|
||||||
return noop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateWorkspaceInTree((json, context, host) => {
|
|
||||||
const { architect } = json.projects[options.name];
|
|
||||||
if (architect) {
|
|
||||||
architect.build = {
|
|
||||||
builder: '@nrwl/node:package',
|
|
||||||
outputs: ['{options.outputPath}'],
|
|
||||||
options: {
|
|
||||||
outputPath: `dist/${libsDir(host)}/${options.projectDirectory}`,
|
|
||||||
tsConfig: `${options.projectRoot}/tsconfig.lib.json`,
|
|
||||||
packageJson: `${options.projectRoot}/package.json`,
|
|
||||||
main: maybeJs(options, `${options.projectRoot}/src/index.ts`),
|
|
||||||
assets: [`${options.projectRoot}/*.md`],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options.rootDir) {
|
|
||||||
architect.build.options.srcRootForCompilationRoot = options.rootDir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const libraryGenerator = wrapAngularDevkitSchematic(
|
|
||||||
'@nrwl/node',
|
|
||||||
'library'
|
|
||||||
);
|
|
||||||
Loading…
x
Reference in New Issue
Block a user