@datorama/akita
Version:
State Management Tailored-Made for JS Applications
222 lines (185 loc) • 7.51 kB
text/typescript
import { Rule, SchematicContext, Tree, noop, chain, SchematicsException } from '@angular-devkit/schematics';
import { NodeDependency, addPackageJsonDependency, NodeDependencyType, getWorkspace, getProjectFromWorkspace, addModuleImportToRootModule, getAppModulePath, InsertChange } from 'schematics-utilities';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { Schema } from './schema';
import * as ts from 'typescript';
import { isImported, insertImport, addProviderToModule, getModuleFile, applyChanges } from './utils';
function addPackageJsonDependencies(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const dependencies: NodeDependency[] = [
{
type: NodeDependencyType.Default,
version: '^4.0.0',
name: '@datorama/akita'
}
];
if (options.withRouter || options.router) {
dependencies.push({
type: NodeDependencyType.Dev,
version: '^3.1.3',
name: '@datorama/akita-ng-router-store'
});
}
if (options.devtools) {
dependencies.push({
type: NodeDependencyType.Dev,
version: '^3.0.2',
name: '@datorama/akita-ngdevtools'
});
}
if (options.httpEntityService) {
dependencies.push({
type: NodeDependencyType.Default,
version: '^1.0.0',
name: '@datorama/akita-ng-entity-service'
});
}
if (options.firebaseEntityService) {
dependencies.push({
type: NodeDependencyType.Default,
version: '^1.0.0',
name: 'akita-ng-fire'
});
}
dependencies.forEach(dependency => {
addPackageJsonDependency(host, dependency);
context.logger.log('info', `✅️ Added "${dependency.name}" into ${dependency.type}`);
});
return host;
};
}
function installPackageJsonDependencies(): Rule {
return (host: Tree, context: SchematicContext) => {
context.addTask(new NodePackageInstallTask());
context.logger.log('info', `🔍 Installing packages...`);
return host;
};
}
function getTsSourceFile(host: Tree, path: string): ts.SourceFile {
const buffer = host.read(path);
if (!buffer) {
throw new SchematicsException(`Could not read file (${path}).`);
}
const content = buffer.toString();
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
return source;
}
function injectImports(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
if (!options.router && !options.devtools && !options.httpEntityService && !options.firebaseEntityService) {
return;
}
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(
workspace,
// Takes the first project in case it's not provided by CLI
options.project ? options.project : Object.keys(workspace['projects'])[0]
);
const modulePath = getAppModulePath(host, (project as any).architect.build.options.main);
let moduleSource = getTsSourceFile(host, modulePath);
let importModule = 'environment';
let importPath = '../environments/environment';
if (!isImported(moduleSource, importModule, importPath)) {
const change = insertImport(moduleSource, modulePath, importModule, importPath);
if (change) {
const recorder = host.beginUpdate(modulePath);
recorder.insertLeft((change as InsertChange).pos, (change as InsertChange).toAdd);
host.commitUpdate(recorder);
}
}
if (options.withRouter || options.router) {
const routerChange = insertImport(moduleSource, modulePath, 'AkitaNgRouterStoreModule', '@datorama/akita-ng-router-store');
if (routerChange) {
const recorder = host.beginUpdate(modulePath);
recorder.insertLeft((routerChange as InsertChange).pos, (routerChange as InsertChange).toAdd);
host.commitUpdate(recorder);
}
}
if (options.devtools) {
const devtoolsChange = insertImport(moduleSource, modulePath, 'AkitaNgDevtools', '@datorama/akita-ngdevtools');
if (devtoolsChange) {
const recorder = host.beginUpdate(modulePath);
recorder.insertLeft((devtoolsChange as InsertChange).pos, (devtoolsChange as InsertChange).toAdd);
host.commitUpdate(recorder);
}
}
if (options.httpEntityService) {
const entityServiceChange = insertImport(moduleSource, modulePath, 'NG_ENTITY_SERVICE_CONFIG', '@datorama/akita-ng-entity-service');
if (entityServiceChange) {
const recorder = host.beginUpdate(modulePath);
recorder.insertLeft((entityServiceChange as InsertChange).pos, (entityServiceChange as InsertChange).toAdd);
host.commitUpdate(recorder);
}
}
return host;
};
}
function setSchematicsAsDefault(): Rule {
return (host: Tree, context: SchematicContext) => {
const exec = require('child_process').exec;
exec('ng config cli.defaultCollection @datorama/akita', () => {
context.logger.log('info', `✅️ Setting Akita schematics as default`);
});
return host;
};
}
function addModuleToImports(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(
workspace,
// Takes the first project in case it's not provided by CLI
options.project ? options.project : Object.keys(workspace['projects'])[0]
);
let importDevtools = '';
let importRouter = '';
let provideEntityServiceConfig = '';
if ((options.withRouter || options.router) && options.devtools) {
importRouter = `AkitaNgRouterStoreModule.forRoot()`;
}
if (options.devtools) {
importDevtools = `environment.production ? [] : AkitaNgDevtools.forRoot()`;
}
if (options.httpEntityService) {
provideEntityServiceConfig = `{ provide: NG_ENTITY_SERVICE_CONFIG, useValue: { baseUrl: 'https://jsonplaceholder.typicode.com' }}`;
}
if (importDevtools) {
addModuleImportToRootModule(host, importDevtools, null as any, project);
}
if (importRouter) {
addModuleImportToRootModule(host, importRouter, null as any, project);
}
if (provideEntityServiceConfig) {
const modulePath = getAppModulePath(host, project.architect.build.options.main);
const module = getModuleFile(host, modulePath);
const providerChanges = addProviderToModule(module, modulePath, provideEntityServiceConfig, null);
applyChanges(host, modulePath, providerChanges as InsertChange[]);
}
if (options.devtools) {
context.logger.log('info', `🔥 AkitaNgDevtools is imported`);
}
if (options.withRouter || options.router) {
context.logger.log('info', `🦄 AkitaNgRouterStoreModule is imported`);
}
if (options.httpEntityService) {
context.logger.log('info', `🌈 NgEntityService is imported`);
}
return host;
};
}
function log(): Rule {
return (host: Tree, context: SchematicContext) => {
context.logger.log('info', `👏 Create your first entity store by running: ng g af todos/todos`);
return host;
};
}
export function akitaNgAdd(options: Schema): Rule {
return chain([
options && options.skipPackageJson ? noop() : addPackageJsonDependencies(options),
options && options.skipPackageJson ? noop() : installPackageJsonDependencies(),
addModuleToImports(options),
injectImports(options),
setSchematicsAsDefault(),
log()
]);
}