Skip to content

Commit

Permalink
feat(schematics): added ng-add command for Mosaic
Browse files Browse the repository at this point in the history
(cherry picked from commit 3c2827e)
  • Loading branch information
pimenovoleg committed Aug 8, 2019
1 parent 1f5e8f1 commit bd831fe
Show file tree
Hide file tree
Showing 15 changed files with 520 additions and 0 deletions.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = {
'progress-bar',
'progress-spinner',
'radio',
'schematics',
'scrolling',
'select',
'sidepanel',
Expand Down
22 changes: 22 additions & 0 deletions packages/mosaic/schematics/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This is the root config file where the schematics are defined.
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
// Adds Mosaic to an application without changing any templates
"ng-add": {
"description": "Adds Mosaic to the application without affecting any templates",
"factory": "./ng-add/index",
"schema": "./ng-add/schema.json",
"aliases": [
"mosaic-shell",
"install"
]
},
"ng-add-setup-project": {
"description": "Sets up the specified project after the ng-add dependencies have been installed.",
"private": true,
"factory": "./ng-add/setup-project",
"schema": "./ng-add/schema.json"
}
}
}
6 changes: 6 additions & 0 deletions packages/mosaic/schematics/migration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {

}
}
17 changes: 17 additions & 0 deletions packages/mosaic/schematics/ng-add/fonts/roboto-fonts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Tree } from '@angular-devkit/schematics';
import { getProjectFromWorkspace } from '@angular/cdk/schematics';
import { getWorkspace } from '@schematics/angular/utility/config';

import { Schema } from '../schema';


//TODO: add Roboto Fonts
export function addRobotoFonts(options: Schema): (host: Tree) => Tree {

return (host: Tree) => {
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(workspace, options.project);

return host;
};
}
26 changes: 26 additions & 0 deletions packages/mosaic/schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask, RunSchematicTask } from '@angular-devkit/schematics/tasks';

import { addPackageToPackageJson, getPackageVersionFromPackageJson } from './package-config';
import { Schema } from './schema';
import { mosaicVersion, requiredAngularVersionRange } from './version-names';


export default function(options: Schema): Rule {

return (host: Tree, context: SchematicContext) => {

const ngCoreVersionTag = getPackageVersionFromPackageJson(host, '@angular/core');
const angularDependencyVersion = ngCoreVersionTag || requiredAngularVersionRange;

addPackageToPackageJson(host, '@angular/cdk', angularDependencyVersion);
addPackageToPackageJson(host, '@angular/forms', angularDependencyVersion);
addPackageToPackageJson(host, '@angular/animations', angularDependencyVersion);
addPackageToPackageJson(host, '@ptsecurity/cdk', `~${mosaicVersion}`);
addPackageToPackageJson(host, '@ptsecurity/mosaic', `~${mosaicVersion}`);

const installTaskId = context.addTask(new NodePackageInstallTask());

context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]);
};
}
42 changes: 42 additions & 0 deletions packages/mosaic/schematics/ng-add/package-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Tree } from '@angular-devkit/schematics';


function sortObjectByKeys(obj: object) {
return Object.keys(obj).sort().reduce((result, key) => (result[key] = obj[key]) && result, {});
}

export function getPackageVersionFromPackageJson(tree: Tree, name: string): string | null {
if (!tree.exists('package.json')) {
return null;
}

const packageJson = JSON.parse(tree.read('package.json')!.toString('utf8'));

if (packageJson.dependencies && packageJson.dependencies[name]) {
return packageJson.dependencies[name];
}

return null;
}

export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {

if (host.exists('package.json')) {

const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);

if (!json.dependencies) {
json.dependencies = {};
}

if (!json.dependencies[pkg]) {
json.dependencies[pkg] = version;
json.dependencies = sortObjectByKeys(json.dependencies);
}

host.overwrite('package.json', JSON.stringify(json, null, 4));
}

return host;
}
45 changes: 45 additions & 0 deletions packages/mosaic/schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "http://json-schema.org/schema",
"id": "mosaic-ng-add",
"title": "Mosaic ng-add schematic",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "Name of the project.",
"$default": {
"$source": "projectName"
}
},
"theme": {
"description": "The theme to apply",
"type": "string",
"default": "default-theme",
"x-prompt": {
"message": "Choose a prebuilt theme name, or \"custom\" for a custom theme:",
"type": "list",
"items": [
{
"value": "default-theme",
"label": "Blue/Gray"
},
{
"value": "dark-theme",
"label": "Dark Theme"
},
{
"value": "custom",
"label": "Custom"
}
]
}
},
"animations": {
"type": "boolean",
"default": true,
"description": "Whether Angular browser animations should be set up.",
"x-prompt": "Set up browser animations for Mosaic?"
}
},
"required": []
}
13 changes: 13 additions & 0 deletions packages/mosaic/schematics/ng-add/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface Schema {
/** Name of the project. */
project: string;

/** Whether gesture support should be set up. */
gestures: boolean;

/** Whether Angular browser animations should be set up. */
animations: boolean;

/** Name of pre-built theme to install. */
theme: 'default-theme' | 'dark-theme' | 'custom';
}
106 changes: 106 additions & 0 deletions packages/mosaic/schematics/ng-add/setup-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { chain, noop, Rule, Tree } from '@angular-devkit/schematics';
import {
addModuleImportToRootModule,
getProjectFromWorkspace,
getProjectMainFile, getProjectStyleFile,
hasNgModuleImport
} from '@angular/cdk/schematics';
import { getWorkspace } from '@schematics/angular/utility/config';
import { getAppModulePath } from '@schematics/angular/utility/ng-ast-utils';
import chalk from 'chalk';

import { addRobotoFonts } from './fonts/roboto-fonts';
import { Schema } from './schema';
import { addThemeToAppStyles } from './theming/theming';


/**
* 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.module
*/
// tslint:disable-next-line:no-default-export
export default function(options: Schema): Rule {
return chain([
noop(),
addAnimationsModule(options),
addThemeToAppStyles(options),
addRobotoFonts(options),
addMosaicAppStyles(options)
]);
}

/** Name of the Angular module that enables Angular browser animations. */
const browserAnimationsModuleName = 'BrowserAnimationsModule';

/** Name of the module that switches Angular animations to a noop implementation. */
const noopAnimationsModuleName = 'NoopAnimationsModule';

function addAnimationsModule(options: Schema) {
return (host: Tree) => {

const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(workspace, options.project);

const appModulePath = getAppModulePath(host, getProjectMainFile(project));

if (options.animations) {
if (hasNgModuleImport(host, appModulePath, noopAnimationsModuleName)) {
return console.warn(chalk.red(`Could not set up "${chalk.bold(browserAnimationsModuleName)}" ` +
`because "${chalk.bold(noopAnimationsModuleName)}" is already imported. Please manually ` +
`set up browser animations.`));
}

addModuleImportToRootModule(host, browserAnimationsModuleName,
'@angular/platform-browser/animations', project);
} else if (!hasNgModuleImport(host, appModulePath, browserAnimationsModuleName)) {
// Do not add the NoopAnimationsModule module if the project already explicitly uses
// the BrowserAnimationsModule.
addModuleImportToRootModule(host, noopAnimationsModuleName,
'@angular/platform-browser/animations', project);
}

return host;
};
}

function addMosaicAppStyles(options: Schema) {
return (host: Tree) => {
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(workspace, options.project);
const styleFilePath = getProjectStyleFile(project);

if (!styleFilePath) {
console.warn(chalk.red(`Could not find the default style file for this project.`));
console.warn(chalk.red(`Please consider manually setting up the Roboto font in your CSS.`));

return;
}

const buffer = host.read(styleFilePath);

if (!buffer) {
console.warn(chalk.red(`Could not read the default style file within the project ` +
`(${chalk.italic(styleFilePath)})`));
console.warn(chalk.red(`Please consider manually setting up the Robot font.`));

return;
}

const htmlContent = buffer.toString();
// tslint:disable-next-line:prefer-template
const insertion = '\n' +
`html, body { height: 100%; }\n` +
`body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }\n`;

if (htmlContent.includes(insertion)) {
return;
}

const recorder = host.beginUpdate(styleFilePath);

recorder.insertLeft(htmlContent.length, insertion);
host.commitUpdate(recorder);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


export function createCustomTheme(name: string = 'app') {
return `
//TODO:
add description for Custom Theming
`;
}
Loading

0 comments on commit bd831fe

Please sign in to comment.