feat(schematics): create local project tsconfigs for typings
This commit is contained in:
parent
d0c9c5e05d
commit
cd2e311a32
@ -44,6 +44,11 @@
|
||||
"version": "7.1.0",
|
||||
"description": "Add generic affected command",
|
||||
"factory": "./update-7-1-0/update-7-1-0"
|
||||
},
|
||||
"update-7.2.0": {
|
||||
"version": "7.2.0",
|
||||
"description": "Create tsconfig.jsons in project roots",
|
||||
"factory": "./update-7-2-0/update-7-2-0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
398
packages/schematics/migrations/update-7-2-0/update-7-2-0.spec.ts
Normal file
398
packages/schematics/migrations/update-7-2-0/update-7-2-0.spec.ts
Normal file
@ -0,0 +1,398 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { serializeJson } from '../../src/utils/fileutils';
|
||||
|
||||
import * as path from 'path';
|
||||
import { readJsonInTree, updateJsonInTree } from '../../src/utils/ast-utils';
|
||||
|
||||
describe('Update 7.2.0', () => {
|
||||
let initialTree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
|
||||
beforeEach(() => {
|
||||
initialTree = Tree.empty();
|
||||
initialTree.create(
|
||||
'package.json',
|
||||
serializeJson({
|
||||
scripts: {}
|
||||
})
|
||||
);
|
||||
createJson('tsconfig.json', {});
|
||||
createJson('angular.json', {
|
||||
projects: {
|
||||
app1: {
|
||||
root: 'apps/app1',
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@angular-devkit/build-angular:browser',
|
||||
options: {
|
||||
tsConfig: 'apps/app1/tsconfig.app.json'
|
||||
}
|
||||
},
|
||||
test: {
|
||||
builder: '@angular-devkit/build-angular:karma',
|
||||
options: {
|
||||
tsConfig: 'apps/app1/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: [
|
||||
'apps/app1/tsconfig.app.json',
|
||||
'apps/app1/tsconfig.spec.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'app1-e2e': {
|
||||
root: 'apps/app1-e2e',
|
||||
architect: {
|
||||
e2e: {
|
||||
builder: '@angular-devkit/build-angular:protractor',
|
||||
options: {
|
||||
tsConfig: 'apps/app1-e2e/tsconfig.e2e.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: 'apps/app1-e2e/tsconfig.e2e.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
app2: {
|
||||
root: 'apps/app2',
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@angular-devkit/build-angular:browser',
|
||||
options: {
|
||||
tsConfig: 'apps/app2/tsconfig.app.json'
|
||||
}
|
||||
},
|
||||
test: {
|
||||
builder: '@nrwl/schematics:jest',
|
||||
options: {
|
||||
tsConfig: 'apps/app2/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: [
|
||||
'apps/app2/tsconfig.app.json',
|
||||
'apps/app2/tsconfig.spec.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'app2-e2e': {
|
||||
root: 'apps/app2-e2e',
|
||||
architect: {
|
||||
e2e: {
|
||||
builder: '@nrwl/builders:cypress',
|
||||
options: {
|
||||
tsConfig: 'apps/app2-e2e/tsconfig.e2e.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'node-app': {
|
||||
root: 'apps/node-app',
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@nrwl/builders:node-build',
|
||||
options: {
|
||||
tsConfig: 'apps/node-app/tsconfig.app.json'
|
||||
}
|
||||
},
|
||||
test: {
|
||||
builder: '@nrwl/schematics:jest',
|
||||
options: {
|
||||
tsConfig: 'apps/node-app/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: [
|
||||
'apps/node-app/tsconfig.app.json',
|
||||
'apps/node-app/tsconfig.spec.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'weird-app': {
|
||||
root: 'apps/weird/app',
|
||||
architect: {
|
||||
build: {
|
||||
builder: '@nrwl/builders:node-build',
|
||||
options: {
|
||||
tsConfig: 'apps/weird/app/src/tsconfig.app.json'
|
||||
}
|
||||
},
|
||||
test: {
|
||||
builder: '@nrwl/schematics:jest',
|
||||
options: {
|
||||
tsConfig: 'apps/weird/app/src/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: [
|
||||
'apps/weird/app/src/tsconfig.app.json',
|
||||
'apps/weird/app/src/tsconfig.spec.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
lib1: {
|
||||
root: 'libs/lib1',
|
||||
architect: {
|
||||
test: {
|
||||
builder: '@angular-devkit/build-angular:karma',
|
||||
options: {
|
||||
tsConfig: 'libs/lib1/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: [
|
||||
'libs/lib1/tsconfig.lib.json',
|
||||
'libs/lib1/tsconfig.spec.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
lib2: {
|
||||
root: 'libs/lib2',
|
||||
architect: {
|
||||
test: {
|
||||
builder: '@angular-devkit/build-angular:jest',
|
||||
options: {
|
||||
tsConfig: 'libs/lib2/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
builder: '@angular-devkit/build-angular:tslint',
|
||||
options: {
|
||||
tsConfig: [
|
||||
'libs/lib2/tsconfig.lib.json',
|
||||
'libs/lib2/tsconfig.spec.json'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
createJson('apps/app1/tsconfig.app.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jquery']
|
||||
}
|
||||
});
|
||||
createJson('apps/app1/tsconfig.spec.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jasmine', 'node', 'sinon']
|
||||
}
|
||||
});
|
||||
createJson('apps/app1-e2e/tsconfig.e2e.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jasmine', 'jasminewd2', 'node']
|
||||
}
|
||||
});
|
||||
createJson('apps/app2/tsconfig.app.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: []
|
||||
}
|
||||
});
|
||||
createJson('apps/app2/tsconfig.spec.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jest', 'node']
|
||||
}
|
||||
});
|
||||
createJson('apps/app2-e2e/tsconfig.e2e.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['cypress', 'node']
|
||||
}
|
||||
});
|
||||
createJson('apps/node-app/tsconfig.app.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['node']
|
||||
}
|
||||
});
|
||||
createJson('apps/node-app/tsconfig.spec.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jest', 'node']
|
||||
}
|
||||
});
|
||||
createJson('apps/weird/app/src/tsconfig.app.json', {
|
||||
extends: '../../../tsconfig.json',
|
||||
compilerOptions: {}
|
||||
});
|
||||
createJson('apps/weird/app/src/tsconfig.spec.json', {
|
||||
extends: '../../../tsconfig.json',
|
||||
compilerOptions: {}
|
||||
});
|
||||
createJson('libs/lib1/tsconfig.lib.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: []
|
||||
}
|
||||
});
|
||||
createJson('libs/lib1/tsconfig.spec.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jasmine', 'node']
|
||||
}
|
||||
});
|
||||
createJson('libs/lib2/tsconfig.lib.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: []
|
||||
}
|
||||
});
|
||||
createJson('libs/lib2/tsconfig.spec.json', {
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jest', 'node']
|
||||
}
|
||||
});
|
||||
|
||||
function createJson(path: string, value: any) {
|
||||
initialTree.create(path, serializeJson(value));
|
||||
}
|
||||
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/schematics',
|
||||
path.join(__dirname, '../migrations.json')
|
||||
);
|
||||
});
|
||||
|
||||
it('should create tsconfigs for existing projects', async () => {
|
||||
const result = await schematicRunner
|
||||
.runSchematicAsync('update-7.2.0', {}, initialTree)
|
||||
.toPromise();
|
||||
expect(result.files).toContain('/tsconfig.json');
|
||||
expect(result.files).toContain('/apps/app1/tsconfig.json');
|
||||
expect(result.files).toContain('/apps/app1-e2e/tsconfig.json');
|
||||
expect(result.files).toContain('/apps/app2/tsconfig.json');
|
||||
expect(result.files).toContain('/apps/app2-e2e/tsconfig.json');
|
||||
expect(result.files).toContain('/apps/node-app/tsconfig.json');
|
||||
expect(result.files).toContain('/apps/weird/app/tsconfig.json');
|
||||
expect(result.files).toContain('/libs/lib1/tsconfig.json');
|
||||
expect(result.files).toContain('/libs/lib2/tsconfig.json');
|
||||
[
|
||||
'/apps/app1/tsconfig.json',
|
||||
'/apps/app1-e2e/tsconfig.json',
|
||||
'/apps/app2/tsconfig.json',
|
||||
'/apps/app2-e2e/tsconfig.json',
|
||||
'/apps/node-app/tsconfig.json',
|
||||
'/libs/lib1/tsconfig.json',
|
||||
'/libs/lib2/tsconfig.json'
|
||||
].forEach(tsConfig => {
|
||||
const value = readJsonInTree(result, tsConfig);
|
||||
expect(value.extends).toEqual('../../tsconfig.json');
|
||||
});
|
||||
expect(
|
||||
readJsonInTree(result, 'apps/weird/app/tsconfig.json').extends
|
||||
).toEqual('../../../tsconfig.json');
|
||||
});
|
||||
|
||||
it('should edit existing tsconfigs to extend the new one', async () => {
|
||||
const result = await schematicRunner
|
||||
.runSchematicAsync('update-7.2.0', {}, initialTree)
|
||||
.toPromise();
|
||||
[
|
||||
'/apps/app1/tsconfig.app.json',
|
||||
'/apps/app1/tsconfig.spec.json',
|
||||
'/apps/app1-e2e/tsconfig.e2e.json',
|
||||
'/apps/app2/tsconfig.app.json',
|
||||
'/apps/app2/tsconfig.spec.json',
|
||||
'/apps/app2-e2e/tsconfig.e2e.json',
|
||||
'/apps/node-app/tsconfig.app.json',
|
||||
'/apps/node-app/tsconfig.spec.json',
|
||||
'/libs/lib1/tsconfig.lib.json',
|
||||
'/libs/lib1/tsconfig.spec.json',
|
||||
'/libs/lib2/tsconfig.lib.json',
|
||||
'/libs/lib2/tsconfig.spec.json'
|
||||
].forEach(tsConfig => {
|
||||
const value = readJsonInTree(result, tsConfig);
|
||||
expect(value.extends).toEqual('./tsconfig.json');
|
||||
});
|
||||
expect(
|
||||
readJsonInTree(result, 'apps/weird/app/src/tsconfig.app.json').extends
|
||||
).toEqual('../tsconfig.json');
|
||||
expect(
|
||||
readJsonInTree(result, 'apps/weird/app/src/tsconfig.spec.json').extends
|
||||
).toEqual('../tsconfig.json');
|
||||
});
|
||||
|
||||
it('should edit existing tsconfigs to have a union of all types being used', async () => {
|
||||
const result = await schematicRunner
|
||||
.runSchematicAsync('update-7.2.0', {}, initialTree)
|
||||
.toPromise();
|
||||
|
||||
function getTypes(path: string) {
|
||||
return readJsonInTree(result, path).compilerOptions.types;
|
||||
}
|
||||
|
||||
expect(getTypes('apps/app1/tsconfig.json')).toEqual([
|
||||
'jquery',
|
||||
'jasmine',
|
||||
'node',
|
||||
'sinon'
|
||||
]);
|
||||
expect(getTypes('apps/app1-e2e/tsconfig.json')).toEqual([
|
||||
'jasmine',
|
||||
'jasminewd2',
|
||||
'node'
|
||||
]);
|
||||
expect(getTypes('apps/app2/tsconfig.json')).toEqual(['jest', 'node']);
|
||||
expect(getTypes('apps/app2-e2e/tsconfig.json')).toEqual([
|
||||
'cypress',
|
||||
'node'
|
||||
]);
|
||||
expect(getTypes('apps/node-app/tsconfig.json')).toEqual(['node', 'jest']);
|
||||
expect(getTypes('apps/weird/app/tsconfig.json')).toBeUndefined();
|
||||
expect(getTypes('libs/lib1/tsconfig.json')).toEqual(['jasmine', 'node']);
|
||||
expect(getTypes('libs/lib2/tsconfig.json')).toEqual(['jest', 'node']);
|
||||
});
|
||||
|
||||
it("should not set types if one of the project's tsconfigs do not have types defined", async () => {
|
||||
initialTree = await schematicRunner
|
||||
.callRule(
|
||||
updateJsonInTree('apps/app1/tsconfig.app.json', json => {
|
||||
delete json.compilerOptions.types;
|
||||
return json;
|
||||
}),
|
||||
initialTree
|
||||
)
|
||||
.toPromise();
|
||||
const result = await schematicRunner
|
||||
.runSchematicAsync('update-7.2.0', {}, initialTree)
|
||||
.toPromise();
|
||||
|
||||
function getTypes(path: string) {
|
||||
return readJsonInTree(result, path).compilerOptions.types;
|
||||
}
|
||||
|
||||
expect(getTypes('apps/app1/tsconfig.json')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
160
packages/schematics/migrations/update-7-2-0/update-7-2-0.ts
Normal file
160
packages/schematics/migrations/update-7-2-0/update-7-2-0.ts
Normal file
@ -0,0 +1,160 @@
|
||||
import {
|
||||
Rule,
|
||||
chain,
|
||||
noop,
|
||||
SchematicContext,
|
||||
Tree
|
||||
} from '@angular-devkit/schematics';
|
||||
import { normalize, join, Path, dirname } from '@angular-devkit/core';
|
||||
|
||||
import { relative } from 'path';
|
||||
|
||||
import { updateJsonInTree, readJsonInTree } from '../../src/utils/ast-utils';
|
||||
import { getWorkspacePath } from '../../src/utils/cli-config-utils';
|
||||
import { offsetFromRoot } from '../../src/utils/common';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
|
||||
function getBuilders(project: any): string[] {
|
||||
return Array.from(
|
||||
new Set(Object.values<any>(project.architect).map(target => target.builder))
|
||||
);
|
||||
}
|
||||
|
||||
const builderTypes: { [key: string]: string[] } = {
|
||||
'@angular-devkit/build-angular:karma': ['jasmine'],
|
||||
'@angular-devkit/build-angular:protractor': ['jasmine', 'jasminewd2'],
|
||||
'@nrwl/builders:jest': ['jest', 'node'],
|
||||
'@nrwl/builers:cypress': ['cypress']
|
||||
};
|
||||
|
||||
function getTypes(host: Tree, project: any, context: SchematicContext) {
|
||||
let types = [];
|
||||
|
||||
const tsConfigs = getTsConfigs(project).map(tsconfigPath =>
|
||||
readJsonInTree(host, tsconfigPath)
|
||||
);
|
||||
|
||||
const tsConfigsWithNoTypes = getTsConfigs(project).filter(tsconfigPath => {
|
||||
const tsconfig = readJsonInTree(host, tsconfigPath);
|
||||
return !tsconfig.compilerOptions.types;
|
||||
});
|
||||
|
||||
if (tsConfigsWithNoTypes.length > 0) {
|
||||
context.logger.warn(
|
||||
stripIndents`The following tsconfigs had no types defined: ${tsConfigsWithNoTypes.join(
|
||||
','
|
||||
)}`
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
types = types.concat(
|
||||
...tsConfigs.map(tsconfig => tsconfig.compilerOptions.types || [])
|
||||
);
|
||||
|
||||
types = types.concat(
|
||||
...getBuilders(project)
|
||||
.filter(builder => builder in builderTypes)
|
||||
.map(builder => builderTypes[builder])
|
||||
);
|
||||
|
||||
return types.filter((type, i, arr) => arr.indexOf(type) === i); // dedupe the array;
|
||||
}
|
||||
|
||||
function createTsConfig(project: any): Rule {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const tsConfigPath = join(normalize(project.root), 'tsconfig.json');
|
||||
if (host.exists(tsConfigPath)) {
|
||||
return noop();
|
||||
}
|
||||
host.create(tsConfigPath, '{}');
|
||||
const types = getTypes(host, project, context);
|
||||
if (types === undefined) {
|
||||
context.logger.warn(
|
||||
stripIndents`No types array was added to ${tsConfigPath} meaning the editor might encounter conflicts for types.}`
|
||||
);
|
||||
}
|
||||
return updateJsonInTree(tsConfigPath, () => {
|
||||
return {
|
||||
extends: `${offsetFromRoot(project.root)}tsconfig.json`,
|
||||
compilerOptions: {
|
||||
types
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getTsConfigs(project: any): Path[] {
|
||||
return Array.from(
|
||||
new Set<Path>(
|
||||
Object.values<any>(project.architect)
|
||||
.reduce(
|
||||
(arr: any[], target) => {
|
||||
return [
|
||||
...arr,
|
||||
...(target.options ? [target.options] : []),
|
||||
...Object.values<any>(target.configurations || {})
|
||||
] as any[];
|
||||
},
|
||||
<any[]>[]
|
||||
)
|
||||
.reduce((arr: string[], options) => {
|
||||
if (!options.tsConfig) {
|
||||
return arr;
|
||||
}
|
||||
if (!Array.isArray(options.tsConfig)) {
|
||||
return arr.includes(options.tsConfig)
|
||||
? arr
|
||||
: [...arr, options.tsConfig];
|
||||
}
|
||||
return [
|
||||
...arr,
|
||||
...options.tsConfig.filter(tsconfig => !arr.includes(tsconfig))
|
||||
];
|
||||
}, [])
|
||||
.map(tsconfig => {
|
||||
return normalize(tsconfig);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function updateTsConfig(project: any, tsconfig: Path): Rule {
|
||||
return updateJsonInTree(tsconfig, json => {
|
||||
json.extends =
|
||||
dirname(tsconfig) === normalize(project.root)
|
||||
? './tsconfig.json'
|
||||
: relative(dirname(tsconfig), join(project.root, 'tsconfig.json'));
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
function updateTsConfigs(project: any): Rule {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
return chain(
|
||||
getTsConfigs(project).map(tsconfig => updateTsConfig(project, tsconfig))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function updateProjects(host: Tree) {
|
||||
const { projects } = readJsonInTree(host, getWorkspacePath(host));
|
||||
return chain(
|
||||
Object.values<any>(projects).map(project => {
|
||||
return chain([createTsConfig(project), updateTsConfigs(project)]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function displayInformation(host: Tree, context: SchematicContext) {
|
||||
context.logger
|
||||
.info(stripIndents`With this update, we are changing the structure of the tsconfig files.
|
||||
A tsconfig.json has been added to all project roots which is used by editors to provide intellisense.
|
||||
The tsconfig.(app|lib|spec|e2e).json files now all extend off of the tsconfig.json in the project root.
|
||||
To find out more, visit our wiki: https://github.com/nrwl/nx/wiki/Workspace-Organization#tsconfigs`);
|
||||
}
|
||||
|
||||
export default function(): Rule {
|
||||
return chain([updateProjects, displayInformation]);
|
||||
}
|
||||
@ -68,13 +68,17 @@ describe('app', () => {
|
||||
getFileContent(tree, 'apps/my-app/src/app/app.module.ts')
|
||||
).toContain('class AppModule');
|
||||
|
||||
const tsconfig = readJsonInTree(tree, 'apps/my-app/tsconfig.json');
|
||||
expect(tsconfig.extends).toEqual('../../tsconfig.json');
|
||||
expect(tsconfig.compilerOptions.types).toContain('jasmine');
|
||||
|
||||
const tsconfigApp = JSON.parse(
|
||||
stripJsonComments(getFileContent(tree, 'apps/my-app/tsconfig.app.json'))
|
||||
);
|
||||
expect(tsconfigApp.compilerOptions.outDir).toEqual(
|
||||
'../../dist/out-tsc/apps/my-app'
|
||||
);
|
||||
expect(tsconfigApp.extends).toEqual('../../tsconfig.json');
|
||||
expect(tsconfigApp.extends).toEqual('./tsconfig.json');
|
||||
|
||||
const tslintJson = JSON.parse(
|
||||
stripJsonComments(getFileContent(tree, 'apps/my-app/tslint.json'))
|
||||
@ -90,7 +94,7 @@ describe('app', () => {
|
||||
expect(tsconfigE2E.compilerOptions.outDir).toEqual(
|
||||
'../../dist/out-tsc/apps/my-app-e2e'
|
||||
);
|
||||
expect(tsconfigE2E.extends).toEqual('../../tsconfig.json');
|
||||
expect(tsconfigE2E.extends).toEqual('./tsconfig.json');
|
||||
});
|
||||
|
||||
it('should default the prefix to npmScope', () => {
|
||||
@ -216,11 +220,21 @@ describe('app', () => {
|
||||
|
||||
// Make sure these have properties
|
||||
[
|
||||
{
|
||||
path: 'apps/my-dir/my-app/tsconfig.json',
|
||||
lookupFn: json => json.extends,
|
||||
expectedValue: '../../../tsconfig.json'
|
||||
},
|
||||
{
|
||||
path: 'apps/my-dir/my-app/tsconfig.app.json',
|
||||
lookupFn: json => json.compilerOptions.outDir,
|
||||
expectedValue: '../../../dist/out-tsc/apps/my-dir/my-app'
|
||||
},
|
||||
{
|
||||
path: 'apps/my-dir/my-app-e2e/tsconfig.json',
|
||||
lookupFn: json => json.extends,
|
||||
expectedValue: '../../../tsconfig.json'
|
||||
},
|
||||
{
|
||||
path: 'apps/my-dir/my-app-e2e/tsconfig.e2e.json',
|
||||
lookupFn: json => json.compilerOptions.outDir,
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "<%= offsetFromRoot %>tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@ -5,7 +5,12 @@ import {
|
||||
Rule,
|
||||
Tree,
|
||||
SchematicContext,
|
||||
schematic
|
||||
schematic,
|
||||
mergeWith,
|
||||
apply,
|
||||
template,
|
||||
move as devkitMove,
|
||||
url
|
||||
} from '@angular-devkit/schematics';
|
||||
import { Schema } from './schema';
|
||||
import * as ts from 'typescript';
|
||||
@ -153,6 +158,27 @@ Nx is designed to help you create and build enterprise grade Angular application
|
||||
};
|
||||
}
|
||||
|
||||
function addTsconfigs(options: NormalizedSchema): Rule {
|
||||
return chain([
|
||||
mergeWith(
|
||||
apply(url('./files'), [
|
||||
template({
|
||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot)
|
||||
}),
|
||||
devkitMove(options.appProjectRoot)
|
||||
])
|
||||
),
|
||||
mergeWith(
|
||||
apply(url('./files'), [
|
||||
template({
|
||||
offsetFromRoot: offsetFromRoot(options.e2eProjectRoot)
|
||||
}),
|
||||
devkitMove(options.e2eProjectRoot)
|
||||
])
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
function updateProject(options: NormalizedSchema): Rule {
|
||||
return (host: Tree) => {
|
||||
return chain([
|
||||
@ -193,7 +219,7 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
updateJsonInTree(`${options.appProjectRoot}/tsconfig.app.json`, json => {
|
||||
return {
|
||||
...json,
|
||||
extends: `${offsetFromRoot(options.appProjectRoot)}tsconfig.json`,
|
||||
extends: `./tsconfig.json`,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
outDir: `${offsetFromRoot(options.appProjectRoot)}dist/out-tsc/${
|
||||
@ -208,14 +234,25 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
};
|
||||
}),
|
||||
options.unitTestRunner === 'karma'
|
||||
? updateJsonInTree(
|
||||
? chain([
|
||||
updateJsonInTree(
|
||||
`${options.appProjectRoot}/tsconfig.json`,
|
||||
json => {
|
||||
return {
|
||||
...json,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
types: [...json.compilerOptions.types, 'jasmine']
|
||||
}
|
||||
};
|
||||
}
|
||||
),
|
||||
updateJsonInTree(
|
||||
`${options.appProjectRoot}/tsconfig.spec.json`,
|
||||
json => {
|
||||
return {
|
||||
...json,
|
||||
extends: `${offsetFromRoot(
|
||||
options.appProjectRoot
|
||||
)}tsconfig.json`,
|
||||
extends: `./tsconfig.json`,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
outDir: `${offsetFromRoot(
|
||||
@ -225,6 +262,7 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
};
|
||||
}
|
||||
)
|
||||
])
|
||||
: host => {
|
||||
host.delete(`${options.appProjectRoot}/tsconfig.spec.json`);
|
||||
return host;
|
||||
@ -271,7 +309,7 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
host.delete(`${options.e2eProjectRoot}/protractor.conf.js`);
|
||||
}
|
||||
}
|
||||
])(host, null);
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
@ -302,10 +340,19 @@ function updateE2eProject(options: NormalizedSchema): Rule {
|
||||
json.projects[options.e2eProjectName] = project;
|
||||
return json;
|
||||
}),
|
||||
updateJsonInTree(`${options.e2eProjectRoot}/tsconfig.json`, json => {
|
||||
return {
|
||||
...json,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
types: [...json.compilerOptions.types, 'jasmine', 'jasminewd2']
|
||||
}
|
||||
};
|
||||
}),
|
||||
updateJsonInTree(`${options.e2eProjectRoot}/tsconfig.e2e.json`, json => {
|
||||
return {
|
||||
...json,
|
||||
extends: `${offsetFromRoot(options.e2eProjectRoot)}tsconfig.json`,
|
||||
extends: `./tsconfig.json`,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
outDir: `${offsetFromRoot(options.e2eProjectRoot)}dist/out-tsc/${
|
||||
@ -314,7 +361,7 @@ function updateE2eProject(options: NormalizedSchema): Rule {
|
||||
}
|
||||
};
|
||||
})
|
||||
])(host, null);
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
@ -343,6 +390,7 @@ export default function(schema: Schema): Rule {
|
||||
viewEncapsulation: options.viewEncapsulation,
|
||||
routing: false
|
||||
}),
|
||||
addTsconfigs(options),
|
||||
|
||||
move(e2eProjectRoot, options.e2eProjectRoot),
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { createEmptyWorkspace } from '@nrwl/schematics/src/utils/testing-utils';
|
||||
import { readJsonInTree } from '@nrwl/schematics/src/utils/ast-utils';
|
||||
import * as path from 'path';
|
||||
|
||||
describe('schematic:cypres-project', () => {
|
||||
describe('schematic:cypress-project', () => {
|
||||
const schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/schematics',
|
||||
path.join(__dirname, '../../collection.json')
|
||||
@ -123,6 +123,7 @@ describe('schematic:cypres-project', () => {
|
||||
'apps/my-app-e2e/tsconfig.e2e.json'
|
||||
);
|
||||
|
||||
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
||||
expect(tsconfigJson.compilerOptions.outDir).toEqual(
|
||||
'../../dist/out-tsc/apps/my-app-e2e/src'
|
||||
);
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { getGreeting } from '../support/app.po';
|
||||
|
||||
describe('Hello Nx', () => {
|
||||
|
||||
@ -1,3 +1 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
export const getGreeting = () => cy.get('h1');
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "<%= offsetFromRoot %>tsconfig.json",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc/<%= projectRoot %>/src",
|
||||
|
||||
@ -19,7 +19,6 @@ import {
|
||||
updateJsonInTree
|
||||
} from '../../utils/ast-utils';
|
||||
import { cypressVersion, nxVersion } from '../../lib-versions';
|
||||
import { replaceAppNameWithPath } from '../../utils/cli-config-utils';
|
||||
import { offsetFromRoot } from '../../utils/common';
|
||||
import { Schema } from '../application/schema';
|
||||
|
||||
@ -107,6 +106,21 @@ function generateFiles(options: CypressProjectSchema): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function updateTsConfig(options: CypressProjectSchema): Rule {
|
||||
return updateJsonInTree(
|
||||
join(normalize(options.e2eProjectRoot), 'tsconfig.json'),
|
||||
json => {
|
||||
return {
|
||||
...json,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
types: [...json.compilerOptions.types, 'cypress']
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateAngularJson(options: CypressProjectSchema): Rule {
|
||||
return updateJsonInTree('angular.json', json => {
|
||||
const projectConfig = json.projects[options.e2eProjectName];
|
||||
@ -139,6 +153,7 @@ export default function(options: CypressProjectSchema): Rule {
|
||||
checkArchitectTarget(options),
|
||||
checkDependenciesInstalled(),
|
||||
updateAngularJson(options),
|
||||
updateTsConfig(options),
|
||||
generateFiles(options)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "<%= offsetFromRoot %>tsconfig.json",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc/<%= projectRoot %>",
|
||||
"module": "commonjs",
|
||||
|
||||
@ -45,6 +45,21 @@ function generateFiles(options: JestProjectSchema): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function updateTsConfig(options: JestProjectSchema): Rule {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const projectConfig = getProjectConfig(host, options.project);
|
||||
return updateJsonInTree(join(projectConfig.root, 'tsconfig.json'), json => {
|
||||
return {
|
||||
...json,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
types: [...json.compilerOptions.types, 'node', 'jest']
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function updateAngularJson(options: JestProjectSchema): Rule {
|
||||
return updateJsonInTree('angular.json', json => {
|
||||
const projectConfig = json.projects[options.project];
|
||||
@ -90,6 +105,7 @@ export default function(options: JestProjectSchema): Rule {
|
||||
return chain([
|
||||
check(options),
|
||||
generateFiles(options),
|
||||
updateTsConfig(options),
|
||||
updateAngularJson(options)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -77,6 +77,19 @@ describe('lib', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should update the local tsconfig.json', () => {
|
||||
const resultTree = schematicRunner.runSchematic(
|
||||
'jest-project',
|
||||
{
|
||||
project: 'lib1'
|
||||
},
|
||||
appTree
|
||||
);
|
||||
const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.json');
|
||||
expect(tsConfig.compilerOptions.types).toContain('jest');
|
||||
expect(tsConfig.compilerOptions.types).toContain('node');
|
||||
});
|
||||
|
||||
it('should create a tsconfig.spec.json', () => {
|
||||
const resultTree = schematicRunner.runSchematic(
|
||||
'jest-project',
|
||||
@ -87,7 +100,7 @@ describe('lib', () => {
|
||||
);
|
||||
const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.spec.json');
|
||||
expect(tsConfig).toEqual({
|
||||
extends: '../../tsconfig.json',
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
module: 'commonjs',
|
||||
outDir: '../../dist/out-tsc/libs/lib1',
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "<%= offsetFromRoot %>tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@ -5,7 +5,12 @@ import {
|
||||
Rule,
|
||||
Tree,
|
||||
SchematicContext,
|
||||
schematic
|
||||
schematic,
|
||||
url,
|
||||
apply,
|
||||
mergeWith,
|
||||
move as devkitMove,
|
||||
template
|
||||
} from '@angular-devkit/schematics';
|
||||
import { Schema } from './schema';
|
||||
import * as path from 'path';
|
||||
@ -224,7 +229,7 @@ function updateNgPackage(options: NormalizedSchema): Rule {
|
||||
}
|
||||
|
||||
function updateProject(options: NormalizedSchema): Rule {
|
||||
return (host: Tree) => {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const libRoot = `${options.projectRoot}/src/lib/`;
|
||||
|
||||
host.delete(path.join(libRoot, `${options.name}.service.ts`));
|
||||
@ -295,6 +300,14 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
}
|
||||
|
||||
return chain([
|
||||
mergeWith(
|
||||
apply(url('./files'), [
|
||||
template({
|
||||
offsetFromRoot: offsetFromRoot(options.projectRoot)
|
||||
}),
|
||||
devkitMove(options.projectRoot)
|
||||
])
|
||||
),
|
||||
updateJsonInTree(getWorkspacePath(host), json => {
|
||||
const project = json.projects[options.name];
|
||||
const fixedProject = replaceAppNameWithPath(
|
||||
@ -334,7 +347,7 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
json.exclude = json.exclude || [];
|
||||
return {
|
||||
...json,
|
||||
extends: `${offsetFromRoot(options.projectRoot)}tsconfig.json`,
|
||||
extends: `./tsconfig.json`,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
outDir: `${offsetFromRoot(options.projectRoot)}dist/out-tsc/${
|
||||
@ -360,7 +373,7 @@ function updateProject(options: NormalizedSchema): Rule {
|
||||
}),
|
||||
updateNgPackage(options),
|
||||
options.unitTestRunner === 'karma' ? updateKarmaConfig(options) : noop()
|
||||
])(host, null);
|
||||
])(host, context);
|
||||
};
|
||||
}
|
||||
|
||||
@ -378,10 +391,19 @@ function updateKarmaConfig(options: NormalizedSchema) {
|
||||
)
|
||||
);
|
||||
},
|
||||
updateJsonInTree(`${options.projectRoot}/tsconfig.json`, json => {
|
||||
return {
|
||||
...json,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
types: [...json.compilerOptions.types, 'jasmine']
|
||||
}
|
||||
};
|
||||
}),
|
||||
updateJsonInTree(`${options.projectRoot}/tsconfig.spec.json`, json => {
|
||||
return {
|
||||
...json,
|
||||
extends: `${offsetFromRoot(options.projectRoot)}tsconfig.json`,
|
||||
extends: `./tsconfig.json`,
|
||||
compilerOptions: {
|
||||
...json.compilerOptions,
|
||||
outDir: `${offsetFromRoot(options.projectRoot)}dist/out-tsc/${
|
||||
|
||||
@ -115,7 +115,7 @@ describe('lib', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should update tsconfig.json', () => {
|
||||
it('should update root tsconfig.json', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
{ name: 'myLib' },
|
||||
@ -127,6 +127,51 @@ describe('lib', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create a local tsconfig.json', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
{ name: 'myLib' },
|
||||
appTree
|
||||
);
|
||||
|
||||
const tsconfigJson = readJsonInTree(tree, 'libs/my-lib/tsconfig.json');
|
||||
expect(tsconfigJson).toEqual({
|
||||
extends: '../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jasmine']
|
||||
},
|
||||
include: ['**/*.ts']
|
||||
});
|
||||
});
|
||||
|
||||
it('should extend the local tsconfig.json with tsconfig.spec.json', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
{ name: 'myLib' },
|
||||
appTree
|
||||
);
|
||||
|
||||
const tsconfigJson = readJsonInTree(
|
||||
tree,
|
||||
'libs/my-lib/tsconfig.spec.json'
|
||||
);
|
||||
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
||||
});
|
||||
|
||||
it('should extend the local tsconfig.json with tsconfig.lib.json', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
{ name: 'myLib' },
|
||||
appTree
|
||||
);
|
||||
|
||||
const tsconfigJson = readJsonInTree(
|
||||
tree,
|
||||
'libs/my-lib/tsconfig.lib.json'
|
||||
);
|
||||
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
|
||||
});
|
||||
|
||||
it('should generate files', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
@ -345,6 +390,26 @@ describe('lib', () => {
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should create a local tsconfig.json', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
{ name: 'myLib', directory: 'myDir' },
|
||||
appTree
|
||||
);
|
||||
|
||||
const tsconfigJson = readJsonInTree(
|
||||
tree,
|
||||
'libs/my-dir/my-lib/tsconfig.json'
|
||||
);
|
||||
expect(tsconfigJson).toEqual({
|
||||
extends: '../../../tsconfig.json',
|
||||
compilerOptions: {
|
||||
types: ['jasmine']
|
||||
},
|
||||
include: ['**/*.ts']
|
||||
});
|
||||
});
|
||||
|
||||
it('should not generate a module for --module false', () => {
|
||||
const tree = schematicRunner.runSchematic(
|
||||
'lib',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "<%= offset %>tsconfig.json",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offset %>dist/out-tsc/<%= root %>",
|
||||
"types": ["node"]
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "<%= offset %>tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node", "express"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@ -97,6 +97,12 @@ describe('node-app', () => {
|
||||
'res.send(`Welcome to my-node-app!`);'
|
||||
);
|
||||
|
||||
const tsconfig = readJsonInTree(tree, 'apps/my-node-app/tsconfig.json');
|
||||
expect(tsconfig.extends).toEqual('../../tsconfig.json');
|
||||
expect(tsconfig.compilerOptions.types).toContain('node');
|
||||
expect(tsconfig.compilerOptions.types).toContain('express');
|
||||
expect(tsconfig.compilerOptions.types).toContain('jest');
|
||||
|
||||
const tsconfigApp = JSON.parse(
|
||||
stripJsonComments(
|
||||
getFileContent(tree, 'apps/my-node-app/tsconfig.app.json')
|
||||
@ -105,7 +111,7 @@ describe('node-app', () => {
|
||||
expect(tsconfigApp.compilerOptions.outDir).toEqual(
|
||||
'../../dist/out-tsc/apps/my-node-app'
|
||||
);
|
||||
expect(tsconfigApp.extends).toEqual('../../tsconfig.json');
|
||||
expect(tsconfigApp.extends).toEqual('./tsconfig.json');
|
||||
|
||||
const tslintJson = JSON.parse(
|
||||
stripJsonComments(getFileContent(tree, 'apps/my-node-app/tslint.json'))
|
||||
@ -180,6 +186,11 @@ describe('node-app', () => {
|
||||
|
||||
// Make sure these have properties
|
||||
[
|
||||
{
|
||||
path: 'apps/my-dir/my-node-app/tsconfig.json',
|
||||
lookupFn: json => json.extends,
|
||||
expectedValue: '../../../tsconfig.json'
|
||||
},
|
||||
{
|
||||
path: 'apps/my-dir/my-node-app/tsconfig.app.json',
|
||||
lookupFn: json => json.compilerOptions.outDir,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user