feat(angular): add preserveAngularCLILayout option to ng-add
This commit is contained in:
parent
690be207be
commit
48d953e4d7
@ -293,22 +293,6 @@ forEachCli('angular', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO(FrozenPandaz): reenable after angular 9
|
||||
xit('should convert a project with common libraries in the ecosystem', () => {
|
||||
// create a new AngularCLI app
|
||||
runNew();
|
||||
|
||||
// Add some Angular libraries
|
||||
runNgAdd('add @angular/elements');
|
||||
runNgAdd('add @angular/material');
|
||||
runNgAdd('add @angular/pwa');
|
||||
runNgAdd('add @ngrx/store');
|
||||
runNgAdd('add @ngrx/effects');
|
||||
|
||||
// Add Nx
|
||||
runNgAdd('add @nrwl/workspace --skip-install');
|
||||
});
|
||||
|
||||
it('should handle different types of errors', () => {
|
||||
// create a new AngularCLI app
|
||||
runNew();
|
||||
@ -353,6 +337,18 @@ forEachCli('angular', () => {
|
||||
// Put src back
|
||||
runCommand('mv src-bak src');
|
||||
});
|
||||
|
||||
it('should support preserveAngularCLILayout', () => {
|
||||
runNew('', false, false);
|
||||
runNgAdd('add @nrwl/workspace --preserveAngularCLILayout');
|
||||
|
||||
const updatedAngularCLIJson = readJson('angular.json');
|
||||
expect(updatedAngularCLIJson.projects.proj.root).toEqual('');
|
||||
expect(updatedAngularCLIJson.projects.proj.sourceRoot).toEqual('src');
|
||||
|
||||
const output = runCLI('build');
|
||||
expect(output).toContain(`> ng run proj:build`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -9,222 +9,258 @@ describe('workspace', () => {
|
||||
appTree = new UnitTestTree(Tree.empty());
|
||||
});
|
||||
|
||||
it('should error if no package.json is present', async () => {
|
||||
try {
|
||||
await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
fail('should throw');
|
||||
} catch (e) {
|
||||
expect(e.message).toContain('Cannot find package.json');
|
||||
}
|
||||
});
|
||||
describe('move to nx layout', () => {
|
||||
it('should error if no package.json is present', async () => {
|
||||
try {
|
||||
await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
fail('should throw');
|
||||
} catch (e) {
|
||||
expect(e.message).toContain('Cannot find package.json');
|
||||
}
|
||||
});
|
||||
|
||||
it('should error if no e2e/protractor.conf.js is present', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
proj1: {
|
||||
architect: {
|
||||
e2e: {
|
||||
options: {
|
||||
protractorConfig: 'e2e/protractor.conf.js',
|
||||
it('should error if no e2e/protractor.conf.js is present', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
proj1: {
|
||||
architect: {
|
||||
e2e: {
|
||||
options: {
|
||||
protractorConfig: 'e2e/protractor.conf.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
try {
|
||||
await runSchematic('ng-add', { name: 'proj1' }, appTree);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
'An e2e project was specified but e2e/protractor.conf.js could not be found.'
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should error if no angular.json is present', async () => {
|
||||
try {
|
||||
try {
|
||||
await runSchematic('ng-add', { name: 'proj1' }, appTree);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
'An e2e project was specified but e2e/protractor.conf.js could not be found.'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should error if no angular.json is present', async () => {
|
||||
try {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create('/e2e/protractor.conf.js', '');
|
||||
await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain('Cannot find angular.json');
|
||||
}
|
||||
});
|
||||
|
||||
it('should error if the angular.json specifies more than one app', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create('/e2e/protractor.conf.js', '');
|
||||
await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain('Cannot find angular.json');
|
||||
}
|
||||
});
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
proj1: {},
|
||||
'proj1-e2e': {},
|
||||
proj2: {},
|
||||
'proj2-e2e': {},
|
||||
},
|
||||
})
|
||||
);
|
||||
try {
|
||||
await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain('Can only convert projects with one app');
|
||||
}
|
||||
});
|
||||
|
||||
it('should error if the angular.json specifies more than one app', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create('/e2e/protractor.conf.js', '');
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
proj1: {},
|
||||
'proj1-e2e': {},
|
||||
proj2: {},
|
||||
'proj2-e2e': {},
|
||||
},
|
||||
})
|
||||
);
|
||||
try {
|
||||
await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain('Can only convert projects with one app');
|
||||
}
|
||||
});
|
||||
|
||||
it('should work without nested tsconfig files', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
defaultProject: 'myApp',
|
||||
projects: {
|
||||
myApp: {
|
||||
root: '',
|
||||
sourceRoot: 'src',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
it('should work without nested tsconfig files', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
defaultProject: 'myApp',
|
||||
projects: {
|
||||
myApp: {
|
||||
root: '',
|
||||
sourceRoot: 'src',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
test: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.spec.json',
|
||||
test: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.spec.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
lint: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
lint: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
e2e: {
|
||||
options: {
|
||||
protractorConfig: 'e2e/protractor.conf.js',
|
||||
e2e: {
|
||||
options: {
|
||||
protractorConfig: 'e2e/protractor.conf.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.app.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.spec.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create('/tsconfig.json', '{"compilerOptions": {}}');
|
||||
appTree.create('/tslint.json', '{"rules": {}}');
|
||||
appTree.create('/e2e/protractor.conf.js', '// content');
|
||||
appTree.create('/src/app/app.module.ts', '// content');
|
||||
const tree = await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
|
||||
});
|
||||
})
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.app.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.spec.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create('/tsconfig.json', '{"compilerOptions": {}}');
|
||||
appTree.create('/tslint.json', '{"rules": {}}');
|
||||
appTree.create('/e2e/protractor.conf.js', '// content');
|
||||
appTree.create('/src/app/app.module.ts', '// content');
|
||||
const tree = await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with nested (sub-dir) tsconfig files', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
defaultProject: 'myApp',
|
||||
projects: {
|
||||
myApp: {
|
||||
sourceRoot: 'src',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
tsConfig: 'src/tsconfig.app.json',
|
||||
it('should work with nested (sub-dir) tsconfig files', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
defaultProject: 'myApp',
|
||||
projects: {
|
||||
myApp: {
|
||||
sourceRoot: 'src',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
tsConfig: 'src/tsconfig.app.json',
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
test: {
|
||||
options: {
|
||||
tsConfig: 'src/tsconfig.spec.json',
|
||||
test: {
|
||||
options: {
|
||||
tsConfig: 'src/tsconfig.spec.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
lint: {
|
||||
options: {
|
||||
tsConfig: 'src/tsconfig.app.json',
|
||||
lint: {
|
||||
options: {
|
||||
tsConfig: 'src/tsconfig.app.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
e2e: {
|
||||
options: {
|
||||
protractorConfig: 'e2e/protractor.conf.js',
|
||||
e2e: {
|
||||
options: {
|
||||
protractorConfig: 'e2e/protractor.conf.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
appTree.create(
|
||||
'/src/tsconfig.app.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create(
|
||||
'/src/tsconfig.spec.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create('/tsconfig.json', '{"compilerOptions": {}}');
|
||||
appTree.create('/tslint.json', '{"rules": {}}');
|
||||
appTree.create('/e2e/protractor.conf.js', '// content');
|
||||
appTree.create('/src/app/app.module.ts', '// content');
|
||||
const tree = await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
|
||||
});
|
||||
})
|
||||
);
|
||||
appTree.create(
|
||||
'/src/tsconfig.app.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create(
|
||||
'/src/tsconfig.spec.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create('/tsconfig.json', '{"compilerOptions": {}}');
|
||||
appTree.create('/tslint.json', '{"rules": {}}');
|
||||
appTree.create('/e2e/protractor.conf.js', '// content');
|
||||
appTree.create('/src/app/app.module.ts', '// content');
|
||||
const tree = await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with missing e2e, lint, or test targets', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
defaultProject: 'myApp',
|
||||
projects: {
|
||||
myApp: {
|
||||
root: '',
|
||||
sourceRoot: 'src',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
it('should work with missing e2e, lint, or test targets', async () => {
|
||||
appTree.create('/package.json', JSON.stringify({}));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
defaultProject: 'myApp',
|
||||
projects: {
|
||||
myApp: {
|
||||
root: '',
|
||||
sourceRoot: 'src',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.app.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.spec.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create('/tsconfig.json', '{"compilerOptions": {}}');
|
||||
appTree.create('/tslint.json', '{"rules": {}}');
|
||||
appTree.create('/e2e/protractor.conf.js', '// content');
|
||||
appTree.create('/src/app/app.module.ts', '// content');
|
||||
appTree.create('/karma.conf.js', '// content');
|
||||
})
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.app.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create(
|
||||
'/tsconfig.spec.json',
|
||||
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
|
||||
);
|
||||
appTree.create('/tsconfig.json', '{"compilerOptions": {}}');
|
||||
appTree.create('/tslint.json', '{"rules": {}}');
|
||||
appTree.create('/e2e/protractor.conf.js', '// content');
|
||||
appTree.create('/src/app/app.module.ts', '// content');
|
||||
appTree.create('/karma.conf.js', '// content');
|
||||
|
||||
const tree = await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
const tree = await runSchematic('ng-add', { name: 'myApp' }, appTree);
|
||||
|
||||
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
|
||||
expect(tree.exists('/apps/myApp/karma.conf.js')).toBe(true);
|
||||
expect(tree.exists('/karma.conf.js')).toBe(true);
|
||||
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
|
||||
expect(tree.exists('/apps/myApp/karma.conf.js')).toBe(true);
|
||||
expect(tree.exists('/karma.conf.js')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('preserve angular cli layout', () => {
|
||||
beforeEach(() => {
|
||||
appTree.create('/package.json', JSON.stringify({ devDependencies: {} }));
|
||||
appTree.create(
|
||||
'/angular.json',
|
||||
JSON.stringify({ projects: { myproj: {} } })
|
||||
);
|
||||
});
|
||||
|
||||
it('should update package.json', async () => {
|
||||
const tree = await runSchematic(
|
||||
'ng-add',
|
||||
{ preserveAngularCLILayout: true },
|
||||
appTree
|
||||
);
|
||||
|
||||
const d = JSON.parse(tree.readContent('/package.json')).devDependencies;
|
||||
expect(d['@nrwl/workspace']).toBeDefined();
|
||||
expect(d['@nrwl/angular']).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should create nx.json', async () => {
|
||||
const tree = await runSchematic(
|
||||
'ng-add',
|
||||
{ preserveAngularCLILayout: true },
|
||||
appTree
|
||||
);
|
||||
|
||||
const nxJson = JSON.parse(tree.readContent('/nx.json'));
|
||||
expect(nxJson.projects).toEqual({ myproj: { tags: [] } });
|
||||
expect(nxJson.npmScope).toEqual('myproj');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -29,6 +29,7 @@ import {
|
||||
renameSyncInTree,
|
||||
renameDirSyncInTree,
|
||||
addInstallTask,
|
||||
addDepsToPackageJson,
|
||||
} from '@nrwl/workspace';
|
||||
import { DEFAULT_NRWL_PRETTIER_CONFIG } from '../workspace/workspace';
|
||||
import { JsonArray } from '@angular-devkit/core';
|
||||
@ -557,27 +558,70 @@ function checkCanConvertToWorkspace(options: Schema) {
|
||||
};
|
||||
}
|
||||
|
||||
const createNxJson = (host: Tree) => {
|
||||
const json = JSON.parse(host.read('angular.json').toString());
|
||||
if (Object.keys(json.projects || {}).length !== 1) {
|
||||
throw new Error(
|
||||
`The schematic can only be used with Angular CLI workspaces with a single project.`
|
||||
);
|
||||
}
|
||||
const name = Object.keys(json.projects)[0];
|
||||
host.create(
|
||||
'nx.json',
|
||||
serializeJson({
|
||||
npmScope: name,
|
||||
implicitDependencies: {
|
||||
'angular.json': '*',
|
||||
'package.json': '*',
|
||||
'tsconfig.json': '*',
|
||||
'tslint.json': '*',
|
||||
'nx.json': '*',
|
||||
},
|
||||
projects: {
|
||||
[name]: {
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
tasksRunnerOptions: {
|
||||
default: {
|
||||
runner: '@nrwl/workspace/tasks-runners/default',
|
||||
options: {
|
||||
cacheableOperations: ['build', 'lint', 'test', 'e2e'],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export default function (schema: Schema): Rule {
|
||||
const options = {
|
||||
...schema,
|
||||
npmScope: toFileName(schema.npmScope || schema.name),
|
||||
};
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
tmpl: '',
|
||||
}),
|
||||
]);
|
||||
return chain([
|
||||
checkCanConvertToWorkspace(options),
|
||||
moveExistingFiles(options),
|
||||
mergeWith(templateSource),
|
||||
createAdditionalFiles(options),
|
||||
updatePackageJson(),
|
||||
updateAngularCLIJson(options),
|
||||
updateTsLint(),
|
||||
updateProjectTsLint(options),
|
||||
updateTsConfig(options),
|
||||
updateTsConfigsJson(options),
|
||||
addInstallTask(options),
|
||||
]);
|
||||
if (schema.preserveAngularCLILayout) {
|
||||
return chain([
|
||||
addDepsToPackageJson({}, { '@nrwl/workspace': nxVersion }),
|
||||
createNxJson,
|
||||
]);
|
||||
} else {
|
||||
const options = {
|
||||
...schema,
|
||||
npmScope: toFileName(schema.npmScope || schema.name),
|
||||
};
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
tmpl: '',
|
||||
}),
|
||||
]);
|
||||
return chain([
|
||||
checkCanConvertToWorkspace(options),
|
||||
moveExistingFiles(options),
|
||||
mergeWith(templateSource),
|
||||
createAdditionalFiles(options),
|
||||
updatePackageJson(),
|
||||
updateAngularCLIJson(options),
|
||||
updateTsLint(),
|
||||
updateProjectTsLint(options),
|
||||
updateTsConfig(options),
|
||||
updateTsConfigsJson(options),
|
||||
addInstallTask(options),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,4 +2,5 @@ export interface Schema {
|
||||
name: string;
|
||||
skipInstall: boolean;
|
||||
npmScope?: string;
|
||||
preserveAngularCLILayout: boolean;
|
||||
}
|
||||
|
||||
@ -14,6 +14,11 @@
|
||||
"description": "Skip installing after adding @nrwl/workspace",
|
||||
"default": false
|
||||
},
|
||||
"preserveAngularCLILayout": {
|
||||
"type": "boolean",
|
||||
"description": "Preserve the Angular CLI layout instead of moving the app into apps.",
|
||||
"default": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Project name.",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user