import { join, normalize, Path } from '@angular-devkit/core'; import { apply, chain, FileEntry, filter, forEach, mergeWith, move, Rule, SchematicContext, template, Tree, url } from '@angular-devkit/schematics'; import { existsSync } from 'fs'; import { updateJsonInTree } from '../../utils/ast-utils'; import { offsetFromRoot } from '../../utils/common'; import { toFileName } from '../../utils/name-utils'; import { formatFiles } from '../../utils/rules/format-files'; import { dasherize } from '../../utils/strings'; import { Schema } from './schema'; interface NormalizedSchema extends Schema { basePath: string; appDirectory: string; workspaceProjectRoot: Path; projectRoot: Path; } function contentReplacer(options: NormalizedSchema) { const toRemoveStrings = []; const toRemoveObject: any = {}; toRemoveStrings.forEach(key => (toRemoveObject[key] = '')); return { ...toRemoveObject, '/demo-ionic-e2e': '/' + options.name, 'demo-ionic': options.name, '-e2e-e2e': '-e2e' }; } function pathReplacer(options: NormalizedSchema) { return { '/demo-ionic-e2e': '/' + options.name }; } function updateSourceFiles( path: string, content: string, options: NormalizedSchema ): string { return undefined; } function filterFiles(path: Path) { const ignoredPaths = []; return ( ignoredPaths.filter(ignoredPath => path.startsWith(ignoredPath)).length === 0 ); } function templateSources(options: NormalizedSchema) { return [ `../../../files/empty/apps/demo-ionic-e2e`.replace('{basePath}', options.basePath) ]; } export default function (schema: any): Rule { return (tree: Tree, context: SchematicContext) => { const options = normalizeOptions(tree, schema); const appProjectRootPath = join( normalize(options.basePath), options.workspaceProjectRoot ); return chain([ ...( !existsSync(appProjectRootPath) ? templateSources(options).map(templateSource => addAppFiles(templateSource, tree, options) ) : [] ), updateNxJson(options), updateAngularJson(options), formatFiles() ])(tree, context); }; } function updateFileFileEntry( tree: Tree, fileEntry: FileEntry, options: NormalizedSchema ): FileEntry { let contentReplaced = false, pathReplaced = false, content = fileEntry.content.toString(), path = fileEntry.path.toString(); const updatedContent = updateSourceFiles(path, content, options); const contentReplacerResult = contentReplacer(options), pathReplacerResult = pathReplacer(options); if (updatedContent !== undefined) { content = updatedContent; contentReplaced = true; } Object.keys(contentReplacerResult).forEach(word => { if (content.indexOf(word) !== -1) { content = content.split(word).join(contentReplacerResult[word]); contentReplaced = true; } }); Object.keys(pathReplacerResult).forEach(word => { if (path.indexOf(word) !== -1) { path = path.split(word).join(pathReplacerResult[word]); pathReplaced = true; } }); if (contentReplaced || pathReplaced) { return { path: path, content: Buffer.from(content) }; } return fileEntry; } function normalizeOptions(tree: Tree, options: Schema): NormalizedSchema { const basePath = (tree)._backend._root; const appDirectory = options.org ? `${toFileName(options.org)}/${toFileName(options.name)}` : toFileName(options.name); const appProjectRoot = join( normalize(options.workspace), normalize('apps'), appDirectory ); const appProject = join( normalize('apps'), appDirectory ); return { ...options, name: toFileName( options.name .split('.') .map(word => dasherize(word)) .join('.') ), appDirectory, workspaceProjectRoot: appProjectRoot, projectRoot: appProject, basePath }; } function addAppFiles( templateSource: string, tree: Tree, options: NormalizedSchema ): Rule { return mergeWith( apply(url(templateSource), [ filter(path => filterFiles(path)), template({ tmpl: '', name: options.name, root: options.projectRoot, offset: offsetFromRoot(options.projectRoot) }), forEach((fileEntry: FileEntry) => { return updateFileFileEntry(tree, fileEntry, options); }), move(options.workspaceProjectRoot) ]) ); } function updateNxJson(options: NormalizedSchema): Rule { return updateJsonInTree(join(normalize(options.workspace), 'nx.json'), nxJson => { if (!nxJson) { nxJson = {}; } if (!nxJson.projects) { nxJson.projects = {}; } const name = options.name; nxJson.projects[name] = { tags: ['angular', 'client', 'application', 'ionic', 'e2e', name] }; return nxJson; }); } function updateAngularJson(options: NormalizedSchema): Rule { return updateJsonInTree(join(normalize(options.workspace), 'angular.json'), angularJson => { const project = { root: options.projectRoot, projectType: 'application', prefix: '', architect: {} }; project.architect.e2e = getE2eConfig(project, options); project.architect.lint = getLintConfig(project); angularJson.projects[options.name] = project; return angularJson; }); } function getE2eConfig(project: any, options: NormalizedSchema) { return { builder: '@angular-devkit/build-angular:protractor', options: { protractorConfig: join(project.root, 'protractor.conf.js'), devServerTarget: options.name.replace('-e2e', '') + ':serve' }, configurations: { production: { devServerTarget: options.name.replace('-e2e', '') + ':serve:production' } } }; } function getLintConfig(project: any) { return { builder: '@angular-devkit/build-angular:tslint', options: { tsConfig: join(project.root, 'tsconfig.e2e.json'), exclude: ['**/node_modules/**'] } }; }