UNPKG

ng-alain

Version:

Schematics specific to NG-ALAIN

215 lines (181 loc) 8.05 kB
import { JsonValue } from '@angular-devkit/core'; import { ProjectDefinition, WorkspaceDefinition } from '@angular-devkit/core/src/workspace'; import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics'; import { getWorkspace, updateWorkspace } from '@schematics/angular/utility/workspace'; import { modifyJSON, readJSON, writeJSON } from './json'; export const BUILD_TARGET_BUILD = 'build'; export const BUILD_TARGET_TEST = 'test'; export const BUILD_TARGET_SERVE = 'serve'; export const BUILD_TARGET_LINT = 'lint'; export const NG_ALAIN_JSON = `ng-alain.json`; export const DEFAULT_WORKSPACE_PATH = `/angular.json`; export interface NgAlainDefinition { projects?: Record<string, NgAlainProjectDefinition>; } export interface NgAlainProjectDefinition { routesRoot?: string; } export function getProjectName(workspace: WorkspaceDefinition, name?: string): string | null { if (name && workspace.projects.has(name)) { return name; } return Array.from(workspace.projects.keys()).pop() ?? null; } export function getNgAlainJson(tree: Tree): NgAlainDefinition | undefined { if (!tree.exists(NG_ALAIN_JSON)) return undefined; return readJSON(tree, NG_ALAIN_JSON); } export function writeNgAlainJson(tree: Tree, json: any): any { return writeJSON(tree, NG_ALAIN_JSON, json); } export function isMulitProject(tree: Tree): boolean { return !tree.exists('src/main.ts'); } export async function getProject( tree: Tree, projectName?: string ): Promise<{ project: ProjectDefinition; name: string; alainProject: NgAlainProjectDefinition }> { const workspace = await getWorkspace(tree); projectName = getProjectName(workspace, projectName); if (!projectName || !workspace.projects.has(projectName)) { throw new SchematicsException(`No project named "${projectName}" exists.`); } const project = getProjectFromWorkspace(workspace, projectName); const alainProject = (getNgAlainJson(tree)?.projects ?? {})[projectName] ?? {}; return { project, name: projectName, alainProject }; } export function addAssetsToTarget( resources: Array<{ type: 'style' | 'script'; value: string }>, behavior: 'add' | 'delete', types: string[] = [BUILD_TARGET_BUILD, BUILD_TARGET_TEST], projectName?: string, clean: boolean = false ): Rule { return updateWorkspace(async workspace => { const project = getProjectFromWorkspace(workspace, projectName); types.forEach(buildTarget => { const targetOptions = getProjectTarget(project, buildTarget); const styles = targetOptions.styles as Array<string | { input: string }>; const scripts = targetOptions.scripts as Array<string | { input: string }>; for (const item of resources) { const list = item.type === 'script' ? scripts : styles; if (clean === true) { list.length = 0; } if (behavior === 'add') { if (!list.includes(item.value)) { list.push(item.value); } } else { const idx = list.indexOf(item.value); if (idx !== -1) { list.splice(idx, 1); } } } }); }); } export function addAllowedCommonJsDependencies(items: string[], projectName?: string): Rule { return updateWorkspace(async workspace => { const project = getProjectFromWorkspace(workspace, projectName); const targetOptions = getProjectTarget(project, BUILD_TARGET_BUILD); let list = targetOptions.allowedCommonJsDependencies as string[]; if (!Array.isArray(list)) { list = []; } if (Array.isArray(items)) { list = [...list, ...items]; } const result = new Set<string>(list); ['ajv', 'ajv-formats', 'mockjs', 'file-saver', 'extend'].forEach(key => result.add(key)); targetOptions.allowedCommonJsDependencies = Array.from(result).sort(); }); } export function removeAllowedCommonJsDependencies(key: string, projectName?: string): Rule { return updateWorkspace(async workspace => { const project = getProjectFromWorkspace(workspace, projectName); const targetOptions = getProjectTarget(project, BUILD_TARGET_BUILD); const list = targetOptions.allowedCommonJsDependencies as string[]; if (!Array.isArray(list)) { return; } const pos = list.indexOf(key); if (pos === -1) return; list.splice(pos, 1); targetOptions.allowedCommonJsDependencies = list.sort(); }); } export function addAllowSyntheticDefaultImports(value: boolean = true): Rule { return (tree: Tree) => { modifyJSON(tree, 'tsconfig.json', { path: ['compilerOptions', 'allowSyntheticDefaultImports'], value }); return tree; }; } export function getProjectFromWorkspace(workspace: WorkspaceDefinition, projectName: string): ProjectDefinition { if (!projectName) { projectName = Array.from(workspace.projects.keys()).pop() ?? null; } const project = workspace.projects.get(projectName); if (!project) { throw new SchematicsException(`Could not find project in workspace: ${projectName}`); } return project; } export function getProjectTarget( project: ProjectDefinition, buildTarget: string, type: 'options' | 'configurations' = 'options' ): Record<string, JsonValue | undefined> { const options = project.targets?.get(buildTarget)?.[type]; if (!options) { throw new SchematicsException(`Cannot determine project target configuration for: ${buildTarget}.${type}.`); } return options; } export function addStylePreprocessorOptions(workspace: WorkspaceDefinition, projectName: string): void { const project = getProjectFromWorkspace(workspace, projectName); if (project == null) return; const build = project.targets.get(BUILD_TARGET_BUILD); if (build == null || build.options == null) return; if (build.options.stylePreprocessorOptions == null) { build.options.stylePreprocessorOptions = {}; } let includePaths: string[] = build.options.stylePreprocessorOptions['includePaths'] ?? []; if (!Array.isArray(includePaths)) includePaths = []; if (includePaths.includes(`node_modules/`)) return; includePaths.push(`node_modules/`); build.options.stylePreprocessorOptions['includePaths'] = includePaths; } export function addStyleResources(workspace: WorkspaceDefinition, projectName: string): void { const project = getProjectFromWorkspace(workspace, projectName); if (project == null) return; const build = project.targets.get(BUILD_TARGET_BUILD); if (build == null || build.options == null) return; if (!Array.isArray(build.options.assets)) build.options.assets = []; (build.options.assets as string[]).push(`src/assets`); } export function addSchematicCollections(workspace: WorkspaceDefinition): void { const cli = workspace.extensions.cli as Record<string, unknown>; if (cli && cli.schematicCollections) return; if (cli == null) workspace.extensions.cli = {}; let schematicCollections = workspace.extensions.cli['schematicCollections'] as string[]; if (!Array.isArray(schematicCollections)) schematicCollections = []; if (!schematicCollections.includes(`@schematics/angular`)) schematicCollections.push(`@schematics/angular`); if (!schematicCollections.includes(`ng-alain`)) schematicCollections.push(`ng-alain`); workspace.extensions.cli['schematicCollections'] = schematicCollections; } export function addFileReplacements(workspace: WorkspaceDefinition, projectName: string): void { const project = getProjectFromWorkspace(workspace, projectName); if (project == null) return; const build = project.targets.get(BUILD_TARGET_BUILD); if (build == null || build.options == null) return; if (build.configurations == null) build.configurations = {}; if (build.configurations.production == null) build.configurations.production = {}; if (!Array.isArray(build.configurations.production.fileReplacements)) build.configurations.production.fileReplacements = []; build.configurations.production.fileReplacements.push({ replace: 'src/environments/environment.ts', with: 'src/environments/environment.prod.ts' }); }