UNPKG

ng-alain

Version:

Schematics specific to NG-ALAIN

177 lines (157 loc) 6.17 kB
import { basename, dirname, normalize, relative, strings } from '@angular-devkit/core'; import { apply, applyTemplates, branchAndMerge, chain, filter, mergeWith, move, noop, Rule, SchematicsException, Tree, url } from '@angular-devkit/schematics'; import { addImportToModule, findNode } from '@schematics/angular/utility/ast-utils'; import { InsertChange } from '@schematics/angular/utility/change'; import { findModuleFromOptions, buildRelativePath } from '@schematics/angular/utility/find-module'; import { parseName } from '@schematics/angular/utility/parse-name'; import * as ts from 'typescript'; import { ROUTINS_FILENAME, addServiceToModuleOrStandalone, getProject, isStandalone, refreshPathRoot } from '../utils'; import { Schema as ModuleSchema } from './schema'; function addDeclarationToNgModule(options: ModuleSchema): Rule { return (tree: Tree) => { if (options.standalone) { return tree; } if (!options.module) { return tree; } const modulePath = normalize(`/${options.module}`); const text = tree.read(modulePath); if (text === null) { throw new SchematicsException(`File ${modulePath} does not exist.`); } const sourceText = text.toString('utf-8'); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const importModulePath = normalize( `/${options.path}/${options.flat ? '' : `${strings.dasherize(options.name)}/`}${strings.dasherize( options.name )}.module` ); const relativeDir = relative(dirname(modulePath), dirname(importModulePath)); const relativePath = `${relativeDir.startsWith('.') ? relativeDir : `./${relativeDir}`}/${basename( importModulePath )}`; const changes = addImportToModule(source, modulePath, strings.classify(`${options.name}Module`), relativePath); const recorder = tree.beginUpdate(modulePath); for (const change of changes) { if (change instanceof InsertChange) { recorder.insertLeft(change.pos, change.toAdd); } } tree.commitUpdate(recorder); return tree; }; } function addRoutingModuleToTop(options: ModuleSchema): Rule { return (tree: Tree) => { const modulePath = normalize( `${options.path}/${options.standalone ? ROUTINS_FILENAME : 'routes-routing.module.ts'}` ); if (!tree.exists(modulePath)) { return tree; } const sourceText = tree.read(modulePath).toString('utf-8'); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const routesNode = findNode(source, ts.SyntaxKind.Identifier, 'routes'); if (routesNode == null || routesNode.parent == null) { return tree; } const parentNode = routesNode.parent as ts.PropertyAssignment; if ( parentNode.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression || parentNode.initializer.getChildCount() === 0 ) { return tree; } let childrenNode = findNode(parentNode.initializer, ts.SyntaxKind.Identifier, 'children'); if (options.standalone && childrenNode == null) { childrenNode = parentNode; } if (childrenNode == null || childrenNode.parent == null) { return tree; } const recorder = tree.beginUpdate(modulePath); const moduleName = options.standalone ? 'routes' : strings.classify(`${options.name}Module`); let pos = childrenNode.parent.end; const validLines = childrenNode.parent .getText() .trim() .split('\n') .map(v => v.trim()) .filter(v => v.length > 1 && !v.startsWith('//')); const comma = validLines.pop()?.endsWith(',') === false ? ', ' : ''; const code = `${comma} { path: '${options.name}', loadChildren: () => import('./${options.name}/${ options.standalone ? 'routes' : `${options.name}.module` }').then((m) => m.${moduleName}) }`; // Insert it just before the `]`. recorder.insertRight(pos - 1, code); tree.commitUpdate(recorder); return tree; }; } function addServiceToNgModule(options: ModuleSchema): Rule { return (tree: Tree) => { if (options.service !== 'none') return tree; const basePath = `/${options.path}/${options.flat ? '' : `${strings.dasherize(options.name)}/`}`; const servicePath = normalize(`${basePath}${strings.dasherize(options.name)}.service`); const serviceName = strings.classify(`${options.name}Service`); const importModulePath = normalize( options.standalone ? `${basePath}${ROUTINS_FILENAME.split('.')[0]}` : `${basePath}${strings.dasherize(options.name)}.module` ); const importServicePath = buildRelativePath(importModulePath, servicePath); addServiceToModuleOrStandalone(tree, options.standalone, `${importModulePath}.ts`, serviceName, importServicePath); return tree; }; } export default function (schema: ModuleSchema): Rule { return async (tree: Tree) => { const proj = await getProject(tree, schema.project); refreshPathRoot(proj.project, schema, proj.alainProject); if (schema.module) { schema.module = findModuleFromOptions(tree, schema); } const parsedPath = parseName(schema.path, schema.name); schema.name = parsedPath.name; schema.path = parsedPath.path; schema.routing = true; schema.flat = false; // standalone schema.standalone = await isStandalone(tree, schema.standalone, proj.name); const templateSource = apply(url('./files'), [ schema.service === 'ignore' ? filter(filePath => !filePath.endsWith('.service.ts.template')) : noop(), schema.routing ? noop() : filter(path => !path.endsWith('-routing.module.ts')), schema.standalone ? filter(path => !path.includes('.module.ts')) : noop(), applyTemplates({ ...strings, 'if-flat': (s: string) => (schema.flat ? '' : s), ...schema }), move(parsedPath.path) ]); return chain([ branchAndMerge( chain([ mergeWith(templateSource), addDeclarationToNgModule(schema), addRoutingModuleToTop(schema), addServiceToNgModule(schema) ]) ) ]); }; }