feat(angular): support multi-app for ng-add schematic (#20768)

* chore(angular): Add missing argument type information.

* chore(angular): Add missing semicolon.

* fix(angular): Properly detect the Angular project name for multi-app repositories.

* fix(angular): Fix generated angular.json when using ng-add  in multi-app repositories.

* chore(angular): Improve reported warnings when using ng-add with invalid workspace configuration.
This commit is contained in:
Walter S
2020-03-18 18:46:14 +02:00
committed by GitHub
parent 3514dd8362
commit 39e6c8f55b
3 changed files with 40 additions and 52 deletions

View File

@@ -2,7 +2,7 @@ import { join, Path } from '@angular-devkit/core';
import { apply, chain, mergeWith, move, Rule, SchematicContext, SchematicsException, template, Tree, url } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { addModuleImportToRootModule } from './../utils/ast';
import { addArchitectBuilder, addStyle, getWorkspace, addAsset, WorkspaceProject, WorkspaceSchema } from './../utils/config';
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName, getWorkspace, WorkspaceProject, WorkspaceSchema } from './../utils/config';
import { addPackageToPackageJson } from './../utils/package';
import { Schema as IonAddOptions } from './schema';
@@ -37,7 +37,7 @@ function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
};
}
function addIonicStyles(projectSourceRoot: Path): Rule {
function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
return (host: Tree) => {
const ionicStyles = [
'node_modules/@ionic/angular/css/normalize.css',
@@ -51,27 +51,27 @@ function addIonicStyles(projectSourceRoot: Path): Rule {
'node_modules/@ionic/angular/css/flex-utils.css',
`${projectSourceRoot}/theme/variables.css`
].forEach(entry => {
addStyle(host, entry);
addStyle(host, projectName, entry);
});
return host;
};
}
function addIonicons(): Rule {
function addIonicons(projectName: string): Rule {
return (host: Tree) => {
const ioniconsGlob = {
glob: '**/*.svg',
input: 'node_modules/ionicons/dist/ionicons/svg',
output: './svg'
};
addAsset(host, ioniconsGlob);
addAsset(host, projectName, ioniconsGlob);
return host;
};
}
function addIonicBuilder(projectName: string): Rule {
return (host: Tree) => {
addArchitectBuilder(host, 'ionic-cordova-serve', {
addArchitectBuilder(host, projectName, 'ionic-cordova-serve', {
builder: '@ionic/angular-toolkit:cordova-serve',
options: {
cordovaBuildTarget: `${projectName}:ionic-cordova-build`,
@@ -84,7 +84,7 @@ function addIonicBuilder(projectName: string): Rule {
}
}
});
addArchitectBuilder(host, 'ionic-cordova-build', {
addArchitectBuilder(host, projectName, 'ionic-cordova-build', {
builder: '@ionic/angular-toolkit:cordova-build',
options: {
browserTarget: `${projectName}:build`
@@ -109,7 +109,7 @@ export default function ngAdd(options: IonAddOptions): Rule {
return (host: Tree) => {
const workspace: WorkspaceSchema = getWorkspace(host);
if (!options.project) {
options.project = Object.keys(workspace.projects)[0];
options.project = getDefaultAngularAppName(workspace);
}
const project: WorkspaceProject = workspace.projects[options.project];
if (project.projectType !== 'application') {
@@ -128,8 +128,8 @@ export default function ngAdd(options: IonAddOptions): Rule {
addIonicAngularToolkitToPackageJson(),
addIonicAngularModuleToAppModule(sourcePath),
addIonicBuilder(options.project),
addIonicStyles(sourcePath),
addIonicons(),
addIonicStyles(options.project, sourcePath),
addIonicons(options.project),
mergeWith(rootTemplateSource),
// install freshly added dependencies
installNodeDeps()

View File

@@ -27,7 +27,7 @@ export function getSourceFile(host: Tree, path: string): ts.SourceFile {
*/
export function addModuleImportToRootModule(
host: Tree,
projectSourceRoot,
projectSourceRoot: string,
moduleName: string,
importSrc: string
) {

View File

@@ -21,7 +21,7 @@ function isAngularBrowserProject(projectConfig: any) {
return false;
}
export function getAngularAppName(config: any): string | null {
export function getDefaultAngularAppName(config: any): string {
const projects = config.projects;
const projectNames = Object.keys(projects);
@@ -32,60 +32,48 @@ export function getAngularAppName(config: any): string | null {
}
}
return null;
return projectNames[0];
}
export function getAngularAppConfig(config: any): any | null {
const projects = config.projects;
const projectNames = Object.keys(projects);
for (const projectName of projectNames) {
const projectConfig = projects[projectName];
if (isAngularBrowserProject(projectConfig)) {
return projectConfig;
}
export function getAngularAppConfig(config: any, projectName: string): any | never {
if (!config.projects.hasOwnProperty(projectName)) {
throw new SchematicsException(`Could not find project: ${projectName}`);
}
return null;
}
const projectConfig = config.projects[projectName];
if (isAngularBrowserProject(projectConfig)) {
return projectConfig;
}
export function addStyle(host: Tree, stylePath: string) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config);
if (appConfig) {
appConfig.architect.build.options.styles.push({
input: stylePath
});
writeConfig(host, config);
if (config.projectType !== 'application') {
throw new SchematicsException(`Invalid projectType for ${projectName}: ${config.projectType}`);
} else {
throw new SchematicsException(`Cannot find valid app`);
const buildConfig = projectConfig.architect.build;
throw new SchematicsException(`Invalid builder for ${projectName}: ${buildConfig.builder}`);
}
}
export function addAsset(host: Tree, asset: string | {glob: string; input: string; output: string}) {
export function addStyle(host: Tree, projectName: string, stylePath: string) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config);
if (appConfig) {
appConfig.architect.build.options.assets.push(asset);
writeConfig(host, config);
} else {
throw new SchematicsException(`Cannot find valid app`);
}
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect.build.options.styles.push({
input: stylePath
});
writeConfig(host, config);
}
export function addArchitectBuilder(host: Tree, builderName: string, builderOpts: any){
export function addAsset(host: Tree, projectName: string, asset: string | {glob: string; input: string; output: string}) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect.build.options.assets.push(asset);
writeConfig(host, config);
}
if (appConfig) {
appConfig.architect[builderName] = builderOpts
writeConfig(host, config);
} else {
throw new SchematicsException(`Cannot find valid app`);
}
export function addArchitectBuilder(host: Tree, projectName: string, builderName: string, builderOpts: any): void | never {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect[builderName] = builderOpts;
writeConfig(host, config);
}
export type WorkspaceSchema = experimental.workspace.WorkspaceSchema;