Skip to content

Commit

Permalink
refactor(schematics): update schematics to use new workspace API #3714
Browse files Browse the repository at this point in the history
  • Loading branch information
alyahmedaly authored and maxokorokov committed Jul 6, 2020
1 parent d9a1d08 commit 7c81d8d
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 124 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"@commitlint/cli": "8.3.4",
"@commitlint/config-angular": "8.3.4",
"@nguniversal/express-engine": "~10.0.0",
"@schematics/angular": "~9.0.0",
"@schematics/angular": "~10.0.0",
"@types/express": "^4.16.1",
"@types/fs-extra": "^7.0.0",
"@types/glob": "^7.1.1",
Expand Down
11 changes: 4 additions & 7 deletions schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import {
RunSchematicTask,
} from '@angular-devkit/schematics/tasks';

import {getWorkspace} from '@schematics/angular/utility/config';
import {getProject} from '@schematics/angular/utility/project';
import {getWorkspace} from '@schematics/angular/utility/workspace';

import {Schema} from './schema';
import * as messages from './messages';
Expand All @@ -20,13 +19,13 @@ const BOOTSTRAP_VERSION = '4.4.0';
* It installs all dependencies in the 'package.json' and runs 'ng-add-setup-project' schematic.
*/
export default function ngAdd(options: Schema): Rule {
return (tree: Tree, context: SchematicContext) => {
return async(tree: Tree, context: SchematicContext) => {

// Checking that project exists
const {project} = options;
if (project) {
const workspace = getWorkspace(tree);
const projectWorkspace = getProject(workspace, project);
const workspace = await getWorkspace(tree);
const projectWorkspace = workspace.projects.get(project);

if (!projectWorkspace) {
throw new SchematicsException(messages.noProject(project));
Expand All @@ -47,7 +46,5 @@ export default function ngAdd(options: Schema): Rule {
context.addTask(new RunSchematicTask('ng-add-setup-project', options), [
context.addTask(new NodePackageInstallTask()),
]);

return tree;
};
}
24 changes: 12 additions & 12 deletions schematics/ng-add/setup-project.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
import {getWorkspace} from '@schematics/angular/utility/config';
import {getProject} from '@schematics/angular/utility/project';
import {WorkspaceProject} from '@schematics/angular/utility/workspace-models';
import {getProjectTargets} from '@schematics/angular/utility/project-targets';

import {getWorkspace} from '@schematics/angular/utility/workspace';
import {workspaces} from '@angular-devkit/core';
import {Schema} from './schema';
import * as messages from './messages';
import {createTestApp} from '../utils/testing';
Expand All @@ -14,15 +11,18 @@ import {createTestApp} from '../utils/testing';
let runner: SchematicTestRunner;
let log: string[] = [];

async function createAppWithOptions(appOptions = {}): Promise<{ tree: UnitTestTree, project: WorkspaceProject }> {
async function createAppWithOptions(appOptions = {}):
Promise<{ tree: UnitTestTree, project: workspaces.ProjectDefinition }> {
// 'app' is the default application, so we're not passing '--project' option
const options: Schema = {project: projectName};
let tree = await createTestApp(runner, appOptions);
tree = await runner.runSchematicAsync('ng-add-setup-project', options, tree).toPromise();

const workspace = getWorkspace(tree);
const project = getProject(workspace, projectName);
return {tree, project};
const workspace = await getWorkspace(tree);
const project = workspace.projects.get(projectName);
return {tree,
// @ts-ignore TODO: types is not compatible because of ngx-build-plus have old dependency on
// @angular/schematics version 8
project};
}

beforeEach(() => {
Expand All @@ -44,7 +44,7 @@ import {createTestApp} from '../utils/testing';

it(`should add 'bootstrap.min.css' to 'angular.json' by default`, async() => {
const {project} = await createAppWithOptions();
const targetOptions = getProjectTargets(project).build !.options;
const targetOptions = project.targets.get('build') !.options;

expect(targetOptions.styles).toContain('node_modules/bootstrap/dist/css/bootstrap.min.css');
});
Expand All @@ -70,7 +70,7 @@ import {createTestApp} from '../utils/testing';

it(`should add 'bootstrap.min.css' to 'angular.json' if style system is unsupported`, async() => {
const {project} = await createAppWithOptions({style: 'less'});
const targetOptions = getProjectTargets(project).build !.options;
const targetOptions = project.targets.get('build') !.options;

expect(targetOptions.styles).toContain('node_modules/bootstrap/dist/css/bootstrap.min.css');
expect(log).toEqual([messages.unsupportedStyles(`projects/${projectName}/src/styles.less`)]);
Expand Down
62 changes: 40 additions & 22 deletions schematics/ng-add/steps/add-bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as path from 'path';
import {Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
import {getWorkspace} from '@schematics/angular/utility/config';
import {getProject} from '@schematics/angular/utility/project';
import {WorkspaceProject, WorkspaceSchema} from '@schematics/angular/utility/workspace-models';
import {Rule, SchematicContext, Tree, SchematicsException} from '@angular-devkit/schematics';

import {Schema} from '../schema';
import * as messages from '../messages';
import {getProjectStyleFile, getProjectTargetOptions} from '../../utils/project';
import {getWorkspace, updateWorkspace} from '@schematics/angular/utility/workspace';
import {workspaces, JsonArray} from '@angular-devkit/core';


const BOOTSTRAP_CSS_FILEPATH = 'node_modules/bootstrap/dist/css/bootstrap.min.css';
Expand All @@ -26,57 +25,76 @@ const SUPPORTED_BOOTSTRAP_STYLE_IMPORTS = {
* If not possible, we're simply adding 'bootstrap.css' to the 'angular.json'
*/
export function addBootstrapStyles(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const workspace = getWorkspace(host);
const project = getProject(workspace, options.project || workspace.defaultProject !);
const styleFilePath = getProjectStyleFile(project) || '';
return async(host: Tree, context: SchematicContext) => {
const workspace = await getWorkspace(host);

const projectName = options.project || workspace.extensions.defaultProject !.toString();
const project = workspace.projects.get(projectName);
if (!project) {
throw new SchematicsException(messages.noProject(projectName));
}

const styleFilePath = getProjectStyleFile(
// @ts-ignore TODO: types is not compatible because of ngx-build-plus have old dependency
// on @angular/schematics version 8
project) ||
'';
const styleFileExtension = path.extname(styleFilePath);
const styleFilePatch = SUPPORTED_BOOTSTRAP_STYLE_IMPORTS[styleFileExtension];

// found supported styles
if (styleFilePatch) {
addBootstrapToStylesFile(host, styleFilePath, styleFilePatch);
return addBootstrapToStylesFile(styleFilePath, styleFilePatch);
} else {
// found some styles, but unsupported
if (styleFileExtension !== '.css' && styleFileExtension !== '') {
context.logger.warn(messages.unsupportedStyles(styleFilePath));
}

// just patching 'angular.json'
addBootstrapToAngularJson(workspace, project, host);
return addBootstrapToAngularJson(
// @ts-ignore TODO: types is not compatible because of ngx-build-plus have old dependency on
// @angular/schematics version 8
workspace, project, host);
}
return host;
};
}

/**
* Patches 'styles.scss' or 'styles.sass' to add Bootstrap snippet
*/
function addBootstrapToStylesFile(host: Tree, styleFilePath: string, styleFilePatch: string) {
const styleContent = host.read(styleFilePath) !.toString('utf-8');
function addBootstrapToStylesFile(styleFilePath: string, styleFilePatch: string): Rule {
return (host: Tree) => {
const styleContent = host.read(styleFilePath) !.toString('utf-8');

const recorder = host.beginUpdate(styleFilePath);
recorder.insertRight(styleContent.length, styleFilePatch);
const recorder = host.beginUpdate(styleFilePath);
recorder.insertRight(styleContent.length, styleFilePatch);

host.commitUpdate(recorder);
host.commitUpdate(recorder);
};
}

/**
* Patches 'angular.json' to add 'bootstrap.css' styles
*/
function addBootstrapToAngularJson(workspace: WorkspaceSchema, project: WorkspaceProject, host: Tree) {
function addBootstrapToAngularJson(
workspace: workspaces.WorkspaceDefinition, project: workspaces.ProjectDefinition, host: Tree): Rule {
const targetOptions = getProjectTargetOptions(project, 'build');
if (!targetOptions.styles) {
const styles = (targetOptions.styles as JsonArray | undefined);
if (!styles) {
targetOptions.styles = [BOOTSTRAP_CSS_FILEPATH];
} else {
const existingStyles = targetOptions.styles.map((s) => typeof s === 'string' ? s : s.input);
const existingStyles = styles.map((s) => typeof s === 'string' ? s : s !['input']);

for (const[, stylePath] of existingStyles.entries()) {
// If the given asset is already specified in the styles, we don't need to do anything.
if (stylePath === BOOTSTRAP_CSS_FILEPATH) {
return;
return () => host;
}
}
targetOptions.styles.unshift(BOOTSTRAP_CSS_FILEPATH);
styles.unshift(BOOTSTRAP_CSS_FILEPATH);
}
host.overwrite('angular.json', JSON.stringify(workspace, null, 2));
// @ts-ignore TODO: types is not compatible because of ngx-build-plus have old dependency
// on @angular/schematics version 8
return updateWorkspace(workspace);
}
22 changes: 14 additions & 8 deletions schematics/ng-add/steps/add-ngb-module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics';
import {getWorkspace} from '@schematics/angular/utility/config';
import {getProject} from '@schematics/angular/utility/project';
import {getAppModulePath} from '@schematics/angular/utility/ng-ast-utils';
import {addImportToModule} from '@schematics/angular/utility/ast-utils';
import {InsertChange} from '@schematics/angular/utility/change';
import * as ts from '@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript';

import {Schema} from '../schema';
import {getProjectTargetOptions} from '../../utils/project';
import {getWorkspace} from '@schematics/angular/utility/workspace';
import * as messages from '../messages';


const NG_BOOTSTRAP_MODULE_NAME = 'NgbModule';
Expand All @@ -17,12 +17,19 @@ const NG_BOOTSTRAP_PACKAGE_NAME = '@ng-bootstrap/ng-bootstrap';
* Patches main application module by adding 'NgbModule' import
*/
export function addNgbModuleToAppModule(options: Schema): Rule {
return (host: Tree) => {
const workspace = getWorkspace(host);
const project = getProject(workspace, options.project || workspace.defaultProject !);
const buildOptions = getProjectTargetOptions(project, 'build');
return async(host: Tree) => {
const workspace = await getWorkspace(host);
const projectName = options.project || (workspace.extensions.defaultProject as string);
const project = workspace.projects.get(projectName);
if (!project) {
throw new SchematicsException(messages.noProject(projectName));
}
const buildOptions = getProjectTargetOptions(
// @ts-ignore TODO: types is not compatible because of ngx-build-plus have old dependency on
// @angular/schematics version 8
project, 'build');

const modulePath = getAppModulePath(host, buildOptions.main);
const modulePath = getAppModulePath(host, (buildOptions.main as string));

const text = host.read(modulePath);
if (text === null) {
Expand All @@ -40,6 +47,5 @@ export function addNgbModuleToAppModule(options: Schema): Rule {
}
}
host.commitUpdate(recorder);
return host;
};
}
18 changes: 8 additions & 10 deletions schematics/utils/project.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {normalize} from '@angular-devkit/core';
import {WorkspaceProject} from '@schematics/angular/utility/workspace-models';
import {getProjectTargets} from '@schematics/angular/utility/project-targets';
import {normalize, workspaces} from '@angular-devkit/core';
import {SchematicsException} from '@angular-devkit/schematics';

// Regular expression that matches all possible Angular CLI default style files
Expand All @@ -12,10 +10,10 @@ const validStyleFileRegex = /\.(c|le|sc|sa)ss/;
/**
* Resolves options for the build target of the given project
*/
export function getProjectTargetOptions(project: WorkspaceProject, buildTarget: string) {
const targets = getProjectTargets(project);
if (targets && targets[buildTarget] && targets[buildTarget].options) {
return targets[buildTarget].options;
export function getProjectTargetOptions(project: workspaces.ProjectDefinition, buildTarget: string) {
const buildTargetObject = project.targets.get(buildTarget);
if (buildTargetObject && buildTargetObject.options) {
return buildTargetObject.options;
}

throw new SchematicsException(`Cannot determine project target configuration for: ${buildTarget}.`);
Expand All @@ -26,11 +24,11 @@ export function getProjectTargetOptions(project: WorkspaceProject, buildTarget:
* Gets a style file with the given extension in a project and returns its path. If no
* extension is specified, any style file with a valid extension will be returned.
*/
export function getProjectStyleFile(project: WorkspaceProject, extension?: string): string | null {
export function getProjectStyleFile(project: workspaces.ProjectDefinition, extension?: string): string | null {
const buildOptions = getProjectTargetOptions(project, 'build');

if (buildOptions.styles && buildOptions.styles.length) {
const styles = buildOptions.styles.map((s) => typeof s === 'string' ? s : s.input);
if (buildOptions.styles && Array.isArray(buildOptions.styles) && buildOptions.styles.length) {
const styles = buildOptions.styles.map((s) => typeof s === 'string' ? s : s !['input']);

// Look for the default style file that is generated for new projects by the Angular CLI. This
// default style file is usually called `styles.ext` unless it has been changed explicitly.
Expand Down
2 changes: 1 addition & 1 deletion schematics/utils/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function createWorkspace(runner: SchematicTestRunner): Promise<UnitTestTree> {
return runner
.runExternalSchematicAsync('@schematics/angular', 'workspace', {
name: 'workspace',
version: '9.0.0',
version: '10.0.0',
newProjectRoot: 'projects',
})
.toPromise();
Expand Down
Loading

0 comments on commit 7c81d8d

Please sign in to comment.