@ngneat/transloco
Version:
The internationalization (i18n) library for Angular
116 lines (101 loc) • 4.75 kB
text/typescript
import { dasherize } from '@angular-devkit/core/src/utils/strings';
import {
Rule,
Tree,
SchematicContext,
externalSchematic,
mergeWith,
source,
EmptyTree
} from '@angular-devkit/schematics';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { ScriptTarget, createSourceFile } from 'typescript';
import { applyChanges } from '../ng-add';
import { LIB_NAME } from '../schematics.consts';
import { stringifyList } from '../utils/array';
import { addProviderToModule, insertImport, addImportToModule } from '../utils/ast-utils';
import { findModuleFromOptions } from '../utils/find-module';
import { getProject, getProjectPath } from '../utils/projects';
import { createTranslateFilesFromOptions } from '../utils/translations';
import { getConfig } from '../utils/transloco';
import { SchemaOptions } from './schema';
const p = require('path');
function getProviderValue(options: SchemaOptions) {
const name = dasherize(options.name);
if (!options.inlineLoader) return `'${name}'`;
return `{ scope: '${name}', loader }`;
}
function addScopeToModule(tree: Tree, modulePath: string, options: SchemaOptions) {
const module = tree.read(modulePath);
const moduleSource = createSourceFile(modulePath, module.toString('utf-8'), ScriptTarget.Latest, true);
const provider = `{ provide: TRANSLOCO_SCOPE, useValue: ${getProviderValue(options)} }`;
const changes = [];
changes.push(addProviderToModule(moduleSource, modulePath, provider, LIB_NAME)[0]);
changes.push(addImportToModule(moduleSource, modulePath, 'TranslocoModule', LIB_NAME)[0]);
changes.push(insertImport(moduleSource, modulePath, 'TRANSLOCO_SCOPE, TranslocoModule', LIB_NAME));
if (options.inlineLoader) {
changes.push(insertImport(moduleSource, modulePath, 'loader', './transloco.loader'));
}
applyChanges(tree, modulePath, changes as any);
}
function getTranslationFilesFromAssets(host, translationsPath) {
const langFiles = host.root.dir(translationsPath as any).subfiles;
return Array.from(new Set(langFiles.map(file => file.split('.')[0])));
}
function getTranslationFiles(options, host, translationsPath): string[] {
return options.langs || getConfig().langs || getTranslationFilesFromAssets(host, translationsPath);
}
function addInlineLoader(tree: Tree, modulePath: any, name: string, langs) {
const loader = `export const loader = [${stringifyList(langs)}].reduce((acc: any, lang: string) => {
acc[lang] = () => import(\`./i18n/\${lang}.json\`);
return acc;
}, {});
`;
const path = p.join(p.dirname(modulePath), 'transloco.loader.ts');
tree.create(path, loader);
}
function createTranslationFiles(options, rootPath, modulePath, host: Tree): Tree {
if (options.skipCreation) {
return new EmptyTree();
}
const defaultPath = options.inlineLoader
? p.join(p.dirname(modulePath), 'i18n')
: p.join(rootPath, 'assets', 'i18n', dasherize(options.name));
const translationsPath = options.translationPath ? p.join(rootPath, options.translationPath) : defaultPath;
return createTranslateFilesFromOptions(host, options, translationsPath);
}
export default function(options: SchemaOptions): Rule {
return (host: Tree, context: SchematicContext) => {
const project = getProject(host, options.project);
const rootPath = (project && project.sourceRoot) || 'src';
const assetsPath = p.join(rootPath, 'assets', 'i18n');
options.langs = getTranslationFiles(options, host, assetsPath);
if (options.module) {
const projectPath = getProjectPath(host, project, options);
const modulePath = findModuleFromOptions(host, options, projectPath);
if (options.inlineLoader) {
addInlineLoader(host, modulePath, options.name, options.langs);
}
if (modulePath) {
addScopeToModule(host, modulePath, options);
return mergeWith(source(createTranslationFiles(options, rootPath, modulePath, host)))(host, context);
}
}
const cmpRule = externalSchematic('@schematics/angular', 'module', options);
const tree$ = (cmpRule(host, context) as Observable<Tree>).pipe(
tap(tree => {
const modulePath = tree.actions.find(
action => !!action.path.match(/\.module\.ts/) && !action.path.match(/-routing\.module\.ts/)
).path;
addScopeToModule(tree, modulePath, options);
if (options.inlineLoader) {
addInlineLoader(tree, modulePath, options.name, options.langs);
}
const translationRule = createTranslationFiles(options, rootPath, modulePath, host);
tree.merge(translationRule);
})
);
return tree$;
};
}