Skip to content

feat(schematics): add shell schematic #9883

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 18, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions schematics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Angular Material Schematics
A collection of Schemaatics for Angular Material.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schemaatics -> typo


## Collection
- [Shell](shell/README.md)
9 changes: 8 additions & 1 deletion schematics/collection.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// This is the root config file where the schematics are defined.
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {}
"schematics": {
"materialShell": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI we're probably going to change this name at some point but it's still TBD

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be ng-add.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding an alias for it gives this error: Error: Schematics/alias "ng-add" collides with another alias or schematic name.

"description": "Create a Material shell",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand the description to something like "Adds Angular Material to an application without changing any templates"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, comments are technically not supported in JSON. ;P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The collection.json are read as JSON5; http://json5.org/

"factory": "./shell/index",
"schema": "./shell/schema.json",
"aliases": [ "material-shell" ]
}
}
}
219 changes: 219 additions & 0 deletions schematics/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions schematics/shell/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Material Shell
Adds Angular Material and its depedencies and pre-configures the application.

It does the following:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could omit the "It does the following: " ; the list by itself is good


- Adds Material and CDK to `package.json`
- Adds Material Icons Stylesheet to `index.html`
- Adds Roboto Font to `index.html`
- Ensure `BrowserAnimationsModule` is installed and included in root module
- Adds pre-configured theme to `.angular-cli.json` file

Command: `ng generate material-shell --collection=material-schematics`
91 changes: 91 additions & 0 deletions schematics/shell/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
Rule,
SchematicContext,
Tree,
chain,
noop,
SchematicsException
} from '@angular-devkit/schematics';
import {Schema} from './schema';
import {materialVersion, cdkVersion, angularVersion} from '../utils/lib-versions';
import {getConfig} from '../utils/devkit-utils/config';
import {addModuleImportToRootModule} from '../utils/ast';
import {addHeadLink} from '../utils/html';
import {addPackageToPackageJson} from '../utils/package';

/**
* Scaffolds the basics of a Angular Material application, this includes:
* - Add Packages to package.json
* - Adds pre-built themes to styles.ext
* - Adds Browser Animation to app.momdule
*/
export default function(options: Schema): Rule {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have to use a default export? These are generally strongly discouraged / banned inside Google (and these schematics will someday be used inside Google)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hansl - All of the schematics seem to use default exports (ref: https://github.com/angular/devkit/blob/master/packages/schematics/angular/app-shell/index.ts#L313). Thoughts on @jelbourn comment?

return chain([
options && options.skipPackageJson ? noop() : addMaterialToPackageJson(options),
addImportToStyles(options),
addAnimationRootConfig(),
addFontsToIndex()
]);
}

/**
* Add material, cdk, annimations to package.json
*/
function addMaterialToPackageJson(options: Schema) {
return (host: Tree) => {
addPackageToPackageJson(host, 'dependencies', '@angular/cdk', cdkVersion);
addPackageToPackageJson(host, 'dependencies', '@angular/material', materialVersion);
addPackageToPackageJson(host, 'dependencies', '@angular/animations', angularVersion);
return host;
};
}

/**
* Add pre-built styles to style.ext file
*/
function addImportToStyles(options: Schema) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addThemeToAppStyles?

return (host: Tree) => {
const config = getConfig(host);
config.apps.forEach(app => {
const themeName = options && options.theme ? options.theme : 'indigo-pink';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making it create a custom theme file by default? I believe that most people will want to customize the theme to one of the non-standard options, so it will be one less step when they want to swap it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, I'll add an option for custom theme :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jelbourn - Custom theme requires SCSS be enabled. If they don't have this do we want to configure it for them automatically?

const themeSrc = `../node_modules/@angular/material/prebuilt-themes/${themeName}.css`;
const hasCurrentTheme = app.styles.find((s: string) => s.indexOf(themeSrc) > -1);
const hasOtherTheme = app.styles.find((s: string) =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer breaking at the higher syntactic level:

const hasOtherTheme = 
    app.styles.find((s: string) => s.indexOf('@angular/material/prebuilt-themes') > -1);

s.indexOf('@angular/material/prebuilt-themes') > -1);

if (!hasCurrentTheme && !hasOtherTheme) {
app.styles.splice(0, 0, themeSrc);
}

if (hasOtherTheme) {
throw new SchematicsException(`Another theme is already defined.`);
}
});
host.overwrite('.angular-cli.json', JSON.stringify(config, null, 2));
return host;
};
}

/**
* Add browser animation module to app.module
*/
function addAnimationRootConfig() {
return (host: Tree) => {
addModuleImportToRootModule(host,
'BrowserAnimationsModule', '@angular/platform-browser/animations');
return host;
};
}

/**
* Adds fonts to the index.ext file
*/
function addFontsToIndex() {
return (host: Tree) => {
addHeadLink(host,
`<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">`);
addHeadLink(host,
`<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">`);
return host;
};
}
44 changes: 44 additions & 0 deletions schematics/shell/index_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {Tree} from '@angular-devkit/schematics';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {join} from 'path';
import {getFileContent} from '@schematics/angular/utility/test';
import {createTestApp} from '../utils/testing';
import {getConfig} from '@schematics/angular/utility/config';
import {getIndexHtmlPath} from '../utils/ast';

const collectionPath = join(__dirname, '../collection.json');

describe('material-shell-schematic', () => {
let runner: SchematicTestRunner;
let appTree: Tree;

beforeEach(() => {
appTree = createTestApp();
runner = new SchematicTestRunner('schematics', collectionPath);
});

it('should update package.json', () => {
const tree = runner.runSchematic('materialShell', {}, appTree);
const packageJson = JSON.parse(getFileContent(tree, '/package.json'));

expect(packageJson.dependencies['@angular/material']).toBeDefined();
expect(packageJson.dependencies['@angular/cdk']).toBeDefined();
});

it('should add default theme', () => {
const tree = runner.runSchematic('materialShell', {}, appTree);
const config = getConfig(tree);
config.apps.forEach(app => {
expect(app.styles).toContain(
'../node_modules/@angular/material/prebuilt-themes/indigo-pink.css');
});
});

it('should add font links', () => {
const tree = runner.runSchematic('materialShell', {}, appTree);
const indexPath = getIndexHtmlPath(tree);
const buffer = tree.read(indexPath);
const indexSrc = buffer.toString();
expect(indexSrc.indexOf('fonts.googleapis.com')).toBeGreaterThan(-1);
});
});
Loading