import { virtualFs, workspaces, JsonValue } from '@angular-devkit/core'; import { Rule, Tree, SchematicContext, SchematicsException, } from '@angular-devkit/schematics'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; // import { mergeMap, Observable, of } from 'rxjs'; import { readFileSync } from 'fs'; interface NgAddOptions { projectName: string; } function createHost(tree: Tree): workspaces.WorkspaceHost { return { async readFile(path: string): Promise { const data = tree.read(path); if (!data) { throw new SchematicsException('File not found.'); } return virtualFs.fileBufferToString(data); }, async writeFile(path: string, data: string): Promise { return tree.overwrite(path, data); }, async isDirectory(path: string): Promise { return !tree.exists(path) && tree.getDir(path).subfiles.length > 0; }, async isFile(path: string): Promise { return tree.exists(path); }, }; } /** * * @param key string like 'parent.child.key' */ export function getJsonValue(key: string, obj: unknown): JsonValue { const keys = key.split('.'); let result: unknown = obj; for (const k of keys) { if (typeof result !== 'object') { return result as JsonValue; } if (!result || !(result as { [k: string]: JsonValue})[k]) { return null; } result = (result as { [k: string]: JsonValue})[k]; } return result as JsonValue; } export function updateJsonValue( key: string, _obj: unknown, updateValueFunc: (v: unknown) => unknown ) { const keys = ['root', ...key.split('.')]; const jsonMap: { [key: string]: unknown } = { root: _obj }; let current = _obj; for (const k of keys) { if (k === 'root') { continue; } current = (current as { [k: string]: JsonValue})[k]; jsonMap[k] = current; } const lastKey: string = keys[keys.length - 1]; jsonMap[lastKey] = updateValueFunc(jsonMap[lastKey]); const reversedKeys = keys.reverse(); let updatedValue: { [key: string]: unknown } = { [reversedKeys[0]]: jsonMap[reversedKeys[0]] }; reversedKeys.forEach((k, i) => { if (i > 0) { updatedValue = { [k]: { ...(jsonMap[k] as { [key: string]: unknown }), ...updatedValue } }; } }); return updatedValue['root']; } export function pushBuildAssetsItem(projectName: string, angularJsonObject: unknown) { const assetsSetting = { "glob": "**/*", "input": "node_modules/@crayon/player/assets", "output": "/assets" }; const updateValue = (v: unknown) => { if (Array.isArray(v) && !(v.find(item => item?.input === assetsSetting.input) || v.find(item => item === assetsSetting.input))) { return [...v, assetsSetting]; } else { return v; } } const updateJsonObject = updateJsonValue(`projects.${projectName}.architect.build.options.assets`, angularJsonObject, updateValue); if (typeof angularJsonObject !== 'object' || typeof updateJsonObject !== 'object') { throw new SchematicsException('Failed to update angular.json'); } else { return updateJsonObject; } } // Just return the tree export function ngAdd(options: NgAddOptions): Rule {// (tree: Tree, context: SchematicContext) => Observable { return async (tree: Tree, context: SchematicContext): Promise => { const host = createHost(tree); const { workspace } = await workspaces.readWorkspace('/', host); let projectName = ''; if (options?.projectName || typeof workspace.extensions['defaultProject'] === 'string') { projectName = options?.projectName || (workspace.extensions['defaultProject'] as string); } else { throw new Error('Project name is required. Set option --projectName or defaultProject in angular.json'); } const angularJsonRaw = readFileSync('./angular.json', { encoding: 'utf8' }); let angularJsonObject: unknown = JSON.parse(angularJsonRaw); angularJsonObject = pushBuildAssetsItem(projectName, angularJsonObject); tree.overwrite('angular.json', JSON.stringify(angularJsonObject, null, 2)); context.addTask(new NodePackageInstallTask()); }; }