diff --git a/schematics/src/ng-add/index.ts b/schematics/src/ng-add/index.ts index bd6cd8e91..4cc0eeede 100644 --- a/schematics/src/ng-add/index.ts +++ b/schematics/src/ng-add/index.ts @@ -1,30 +1,42 @@ import { Rule, SchematicContext, Tree, chain } from '@angular-devkit/schematics'; -import { addPackageToPackageJson } from './utils'; +import { addAssetToAngularJson, addPackageToPackageJson } from './utils'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; +import { IADTSchematicsOptions } from './models/schematics-options'; +import { ADT_SUPPORTED_STYLES, ADTStyleOptions } from './models/style-options'; -export default function (_options: any): Rule { +export default function (_options: IADTSchematicsOptions): Rule { return chain([ - addPackageJsonDependencies(), + addPackageJsonDependencies(_options), installPackageJsonDependencies(), - updateAngularJsonFile() + updateAngularJsonFile(_options) ]); } -function addPackageJsonDependencies() { +function addPackageJsonDependencies(options: IADTSchematicsOptions) { return (tree: Tree, context: SchematicContext) => { // Update package.json + const styleDeps = ADT_SUPPORTED_STYLES.find(e => e.style == options.style); + const dependencies = [ { version: '^3.4.1', name: 'jquery', isDev: false }, { version: '^1.10.20', name: 'datatables.net', isDev: false }, - { version: '^1.10.20', name: 'datatables.net-dt', isDev: false }, - { version: '^11.0.0', name: 'angular-datatables', isDev: false }, { version: '^3.3.33', name: '@types/jquery', isDev: true }, { version: '^1.10.18', name: '@types/datatables.net', isDev: true } ]; + if (styleDeps) { + if (styleDeps.style != ADTStyleOptions.DT) + context.logger.log('warn', 'Your project needs Bootstrap CSS installed and configured for changes to take affect.'); + styleDeps.packageJson.forEach(e => dependencies.push(e)); + } + dependencies.forEach(dependency => { - addPackageToPackageJson(tree, dependency.name, dependency.version, dependency.isDev); - context.logger.log('info', `✅️ Added "${dependency.name}" into "${dependency.isDev ? 'devDependencies' : 'dependencies' }"`); + const result = addPackageToPackageJson(tree, dependency.name, dependency.version, dependency.isDev); + if (result) { + context.logger.log('info', `✅️ Added "${dependency.name}" into "${dependency.isDev ? 'devDependencies' : 'dependencies'}"`); + } else { + context.logger.log('info', `ℹ️ Skipped adding "${dependency.name}" into package.json`); + } }); return tree; }; @@ -40,31 +52,27 @@ function installPackageJsonDependencies(): Rule { } -function updateAngularJsonFile() { +function updateAngularJsonFile(options: IADTSchematicsOptions) { return (tree: Tree, context: SchematicContext) => { - try { - const angularJsonFile = tree.read('angular.json'); - if (angularJsonFile) { - const angularJsonFileObject = JSON.parse(angularJsonFile.toString('utf-8')); - const project = Object.keys(angularJsonFileObject['projects'])[0]; - const projectObject = angularJsonFileObject.projects[project]; - const targets = projectObject.targets ? projectObject.targets : projectObject.architect; + const styleDeps = ADT_SUPPORTED_STYLES.find(e => e.style == options.style); - const styles = targets.build.options.styles; - const scripts = targets.build.options.scripts; + const assets = [ + { path: 'node_modules/jquery/dist/jquery.js', target: 'scripts', fancyName: 'jQuery Core' }, + { path: 'node_modules/datatables.net/js/jquery.dataTables.js', target: 'scripts', fancyName: 'DataTables.net Core JS' }, + ]; - styles.push('node_modules/datatables.net-dt/css/jquery.dataTables.css'); - scripts.push('node_modules/jquery/dist/jquery.js'); - scripts.push('node_modules/datatables.net/js/jquery.dataTables.js'); + if (styleDeps) { + styleDeps.angularJson.forEach(e => assets.push(e)); + } - tree.overwrite('angular.json', JSON.stringify(angularJsonFileObject, null, 2)); - context.logger.log('info', `✅️ Updated angular.json`); + assets.forEach(asset => { + const result = addAssetToAngularJson(tree, asset.target, asset.path); + if (result) { + context.logger.log('info', `✅️ Added "${asset.fancyName}" into angular.json`); } else { - context.logger.log('error', '🚫 Failed to locate angular.json else.'); + context.logger.log('info', `ℹ️ Skipped adding "${asset.fancyName}" into angular.json`); } - } catch (e) { - context.logger.log('error', `🚫 Failed to update angular.json foobar.`); - } + }); }; } diff --git a/schematics/src/ng-add/models/schematics-options.ts b/schematics/src/ng-add/models/schematics-options.ts new file mode 100644 index 000000000..c9ee73a61 --- /dev/null +++ b/schematics/src/ng-add/models/schematics-options.ts @@ -0,0 +1,6 @@ +import { ADTStyleOptions } from "./style-options"; + +export interface IADTSchematicsOptions { + project: string, + style: ADTStyleOptions +} diff --git a/schematics/src/ng-add/models/style-options.ts b/schematics/src/ng-add/models/style-options.ts new file mode 100644 index 000000000..2dac0736e --- /dev/null +++ b/schematics/src/ng-add/models/style-options.ts @@ -0,0 +1,37 @@ +export enum ADTStyleOptions { + DT="dt", + BS3="bs3", + BS4="bs4", +} + +export const ADT_SUPPORTED_STYLES = [ + { + style: ADTStyleOptions.DT, + packageJson: [ + { version: '^1.10.20', name: 'datatables.net-dt', isDev: false }, + ], + angularJson: [ + { path: 'node_modules/datatables.net-dt/css/jquery.dataTables.css', target: 'styles', fancyName: 'DataTables.net Core CSS' }, + ] + }, + { + style: ADTStyleOptions.BS3, + packageJson: [ + { version: '^1.10.20', name: 'datatables.net-bs', isDev: false }, + ], + angularJson: [ + { path: 'node_modules/datatables.net-bs/css/dataTables.bootstrap.min.css', target: 'styles', fancyName: 'DataTables.net Bootstrap 3 CSS' }, + { path: 'node_modules/datatables.net-bs/js/dataTables.bootstrap.min.js', target: 'scripts', fancyName: 'DataTables.net Bootstrap 3 JS' }, + ] + }, + { + style: ADTStyleOptions.BS4, + packageJson: [ + { version: '^1.10.20', name: 'datatables.net-bs4', isDev: false }, + ], + angularJson: [ + { path: 'node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css', target: 'styles', fancyName: 'DataTables.net Bootstrap 4 CSS' }, + { path: 'node_modules/datatables.net-bs4/js/dataTables.bootstrap4.min.js', target: 'scripts', fancyName: 'DataTables.net Bootstrap 4 JS' }, + ] + }, +] diff --git a/schematics/src/ng-add/schema.json b/schematics/src/ng-add/schema.json index af82ed5e6..be84a366f 100644 --- a/schematics/src/ng-add/schema.json +++ b/schematics/src/ng-add/schema.json @@ -11,6 +11,27 @@ "$default": { "$source": "projectName" } + }, + "style": { + "description": "The styling library to use for Datatables.", + "type": "string", + "default": "dt", + "enum": ["dt", "bs3", "bs4"], + "x-prompt": { + "message": "Which styling library would you like to use for DataTables?", + "type": "list", + "items": [ + { "value": "dt", "label": "DataTables (Default)" }, + { + "value": "bs3", + "label": "Bootstrap 3" + }, + { + "value": "bs4", + "label": "Bootstrap 4" + } + ] + } } } -} \ No newline at end of file +} diff --git a/schematics/src/ng-add/utils/index.ts b/schematics/src/ng-add/utils/index.ts index 37489ca07..b23895dd2 100644 --- a/schematics/src/ng-add/utils/index.ts +++ b/schematics/src/ng-add/utils/index.ts @@ -27,7 +27,7 @@ export function installPackageJsonDependencies(): Rule { } export function addStyleToTarget(project: WorkspaceProject, targetName: string, host: Tree, - assetPath: string, workspace: WorkspaceSchema) { + assetPath: string, workspace: WorkspaceSchema) { const targetOptions = getProjectTargetOptions(project, targetName); @@ -99,7 +99,7 @@ function sortObjectByKeys(obj: { [key: string]: string }) { * Note: This function accepts an additional parameter `isDevDependency` so we * can place a given dependency in the correct dependencies array inside package.json */ -export function addPackageToPackageJson(host: Tree, pkg: string, version: string, isDevDependency = false): Tree { +export function addPackageToPackageJson(host: Tree, pkg: string, version: string, isDevDependency = false): boolean { if (host.exists('package.json')) { /* tslint:disable-next-line: no-non-null-assertion */ @@ -114,20 +114,48 @@ export function addPackageToPackageJson(host: Tree, pkg: string, version: string json.dependencies = {}; } + // update UI that `pkg` wasn't re-added to package.json + if (json.dependencies[pkg] || json.devDependencies[pkg]) return false; + if (!json.dependencies[pkg] && !isDevDependency) { json.dependencies[pkg] = version; json.dependencies = sortObjectByKeys(json.dependencies); } - if(!json.devDependencies[pkg] && isDevDependency) { + if (!json.devDependencies[pkg] && isDevDependency) { json.devDependencies[pkg] = version; json.devDependencies = sortObjectByKeys(json.devDependencies); } host.overwrite('package.json', JSON.stringify(json, null, 2)); + return true; } - return host; + return false; +} + +export function addAssetToAngularJson(host: Tree, assetType: string, assetPath: string): boolean { + /* tslint:disable-next-line: no-non-null-assertion */ + const sourceText = host.read('angular.json')!.toString('utf-8'); + const json = JSON.parse(sourceText); + + if (!json) return false; + + const projectName = Object.keys(json['projects'])[0]; + const projectObject = json.projects[projectName]; + const targets = projectObject.targets || projectObject.architect; + + const targetLocation: string[] = targets.build.options[assetType]; + + // update UI that `assetPath` wasn't re-added to angular.json + if (targetLocation.indexOf(assetPath) != -1) return false; + + targetLocation.push(assetPath); + + host.overwrite('angular.json', JSON.stringify(json, null, 2)); + + return true; + } export function removePackageJsonDependency(tree: Tree, dependencyName: string) {