ohayolibs
Version:
Ohayo is a set of essential modules for ohayojp.
484 lines (444 loc) • 16.5 kB
text/typescript
import { strings } from '@angular-devkit/core';
import {
apply,
chain,
filter,
MergeStrategy,
mergeWith,
move,
noop,
Rule,
SchematicContext,
template,
Tree,
url,
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import * as path from 'path';
import { getLangData } from '../core/lang.config';
import { tryAddFile } from '../utils/ohayo';
import { HMR_CONTENT } from '../utils/contents';
import { addFiles } from '../utils/file';
import { addHeadStyle, addHtmlToBody } from '../utils/html';
import {
addAllowedCommonJsDependencies,
addPackageToPackageJson,
getAngular,
getJSON,
getPackage,
overwriteAngular,
overwriteJSON,
overwritePackage,
scriptsToAngularJson,
} from '../utils/json';
import { VERSION, ZORROVERSION } from '../utils/lib-versions';
import { getProject, getProjectFromWorkspace, Project } from '../utils/project';
import { Schema as ApplicationOptions } from './schema';
const overwriteDataFileRoot = path.join(__dirname, 'overwrites');
let project: Project;
/** Remove files to be overwrite */
function removeOrginalFiles(): (host: Tree) => void {
return (host: Tree) => {
[
`${project.root}/README.md`,
`${project.root}/tslint.json`,
`${project.sourceRoot}/main.ts`,
`${project.sourceRoot}/environments/environment.prod.ts`,
`${project.sourceRoot}/environments/environment.ts`,
`${project.sourceRoot}/styles.less`,
`${project.sourceRoot}/app/app.module.ts`,
`${project.sourceRoot}/app/app.component.spec.ts`,
`${project.sourceRoot}/app/app.component.ts`,
`${project.sourceRoot}/app/app.component.html`,
`${project.sourceRoot}/app/app.component.less`,
]
.filter(p => host.exists(p))
.forEach(p => host.delete(p));
};
}
function fixMain(): (host: Tree) => void {
return (host: Tree) => {
// fix: main.ts using no hmr file
tryAddFile(host, `${project.sourceRoot}/main.ts`, HMR_CONTENT.NO_HMR_MAIN_DOT_TS);
};
}
function fixAngularJson(options: ApplicationOptions): (host: Tree) => void {
return (host: Tree) => {
const json = getAngular(host);
const _project = getProjectFromWorkspace(json, options.project);
// Add proxy.conf.json
(_project.targets || _project.architect)!.serve!.options.proxyConfig = 'proxy.conf.json';
overwriteAngular(host, json);
return host;
};
}
function addDependenciesToPackageJson(options: ApplicationOptions): (host: Tree) => void {
return (host: Tree) => {
// 3rd
addPackageToPackageJson(host, [
// allow ignore ng-zorro-antd becauce of @ohayo/theme dependency
`ng-zorro-antd@${ZORROVERSION}`,
// ng-zorro-antd need
'screenfull@DEP-0.0.0-PLACEHOLDER',
'ajv@DEP-0.0.0-PLACEHOLDER',
]);
// add ajv
scriptsToAngularJson(host, ['node_modules/ajv/dist/ajv.bundle.js'], 'add', ['build', 'test']);
// @ohayo/*
addPackageToPackageJson(
host,
['components', 'acl', 'auth', 'cache', 'form', 'mock', 'theme', 'util', 'chart'].map(pkg => `@ohayo/${pkg}@${VERSION}`),
);
// ohayojp
addPackageToPackageJson(
host,
[
`ohayojp@${VERSION}`,
`ohayojp-codelyzer@DEP-0.0.0-PLACEHOLDER`,
`ng-alain-plugin-theme@DEP-0.0.0-PLACEHOLDER`,
`@ohayo/testing@${VERSION}`,
],
'devDependencies',
);
// i18n
if (options.i18n) {
addPackageToPackageJson(host, [`@ngx-translate/core@DEP-0.0.0-PLACEHOLDER`, `@ngx-translate/http-loader@DEP-0.0.0-PLACEHOLDER`]);
}
// Configuring CommonJS dependencies
// https://angular.io/guide/build#configuring-commonjs-dependencies
addAllowedCommonJsDependencies(host);
return host;
};
}
function addRunScriptToPackageJson(): (host: Tree) => void {
return (host: Tree) => {
const json = getPackage(host, 'scripts');
if (json == null) return host;
json.scripts.start = `ng s -o`;
json.scripts.build = `node --max_old_space_size=5120 ./node_modules/@angular/cli/bin/ng build --prod`;
json.scripts.analyze = `node --max_old_space_size=5120 ./node_modules/@angular/cli/bin/ng build --prod --stats-json`;
json.scripts['test-coverage'] = `ng test --code-coverage --watch=false`;
json.scripts['color-less'] = `ng-alain-plugin-theme -t=colorLess`;
json.scripts.theme = `ng-alain-plugin-theme -t=themeCss`;
json.scripts.icon = `ng g ohayojp:plugin icon`;
overwritePackage(host, json);
return host;
};
}
function addPathsToTsConfig(): (host: Tree) => Tree {
return (host: Tree) => {
const json = getJSON(host, 'tsconfig.json', 'compilerOptions');
if (json == null) return host;
if (!json.compilerOptions) json.compilerOptions = {};
if (!json.compilerOptions.paths) json.compilerOptions.paths = {};
const paths = json.compilerOptions.paths;
paths['@shared'] = ['src/app/shared/index'];
paths['@core'] = ['src/app/core/index'];
paths['@env/*'] = ['src/environments/*'];
overwriteJSON(host, 'tsconfig.json', json);
return host;
};
}
function addCodeStylesToPackageJson(): (host: Tree) => Tree {
return (host: Tree) => {
const json = getPackage(host);
if (json == null) return host;
json.scripts.lint = `npm run lint:ts && npm run lint:style`;
json.scripts['lint:ts'] = `ng lint --fix`;
json.scripts['lint:style'] = `stylelint \"src/**/*.less\" --syntax less --fix`;
json.scripts['pretty-quick'] = `pretty-quick`;
json.scripts['tslint-check'] = `tslint-config-prettier-check ./tslint.json`;
overwritePackage(host, json);
// dependencies
addPackageToPackageJson(
host,
[
`tslint-config-prettier@DEP-0.0.0-PLACEHOLDER`,
`tslint-language-service@DEP-0.0.0-PLACEHOLDER`,
`pretty-quick@DEP-0.0.0-PLACEHOLDER`,
`husky@DEP-0.0.0-PLACEHOLDER`,
`prettier@DEP-0.0.0-PLACEHOLDER`,
`stylelint@DEP-0.0.0-PLACEHOLDER`,
`stylelint-config-prettier@DEP-0.0.0-PLACEHOLDER`,
`stylelint-config-rational-order@DEP-0.0.0-PLACEHOLDER`,
`stylelint-config-standard@DEP-0.0.0-PLACEHOLDER`,
`stylelint-declaration-block-no-ignored-properties@DEP-0.0.0-PLACEHOLDER`,
`stylelint-order@DEP-0.0.0-PLACEHOLDER`,
],
'devDependencies',
);
return host;
};
}
function addSchematics(): (host: Tree) => Tree {
return (host: Tree) => {
const angularJsonFile = 'angular.json';
const json = getJSON(host, angularJsonFile, 'schematics');
if (json == null) return host;
json.schematics['ohayojp:module'] = {
routing: true,
spec: false,
};
json.schematics['ohayojp:list'] = {
spec: false,
};
json.schematics['ohayojp:edit'] = {
spec: false,
modal: true,
};
json.schematics['ohayojp:view'] = {
spec: false,
modal: true,
};
json.schematics['ohayojp:curd'] = {
spec: false,
};
json.schematics['@schematics/angular:module'] = {
routing: true,
spec: false,
};
json.schematics['@schematics/angular:component'] = {
spec: false,
flat: false,
inlineStyle: true,
inlineTemplate: false,
};
json.schematics['@schematics/angular:directive'] = {
spec: false,
};
json.schematics['@schematics/angular:service'] = {
spec: false,
};
overwriteJSON(host, angularJsonFile, json);
};
}
function addNzLintRules(): (host: Tree) => void {
return (host: Tree) => {
addPackageToPackageJson(host, ['nz-tslint-rules@DEP-0.0.0-PLACEHOLDER'], 'devDependencies');
const json = getJSON(host, 'tslint.json');
if (json == null) return host;
json.rulesDirectory.push(`nz-tslint-rules`);
json.rules['nz-secondary-entry-imports'] = true;
overwriteJSON(host, 'tslint.json', json);
return host;
};
}
function forceLess(): (host: Tree) => void {
return (host: Tree) => {
scriptsToAngularJson(host, ['src/styles.less'], 'add', ['build'], null!, true);
};
}
function addStyle(): (host: Tree) => Tree {
return (host: Tree) => {
addHeadStyle(
host,
project,
` <style type="text/css">.preloader{position:fixed;top:0;left:0;width:100%;height:100%;overflow:hidden;background:#49a9ee;z-index:9999;transition:opacity .65s}.preloader-hidden-add{opacity:1;display:block}.preloader-hidden-add-active{opacity:0}.preloader-hidden{display:none}.cs-loader{position:absolute;top:0;left:0;height:100%;width:100%}.cs-loader-inner{transform:translateY(-50%);top:50%;position:absolute;width:100%;color:#fff;text-align:center}.cs-loader-inner label{font-size:20px;opacity:0;display:inline-block}@keyframes lol{0%{opacity:0;transform:translateX(-300px)}33%{opacity:1;transform:translateX(0)}66%{opacity:1;transform:translateX(0)}100%{opacity:0;transform:translateX(300px)}}.cs-loader-inner label:nth-child(6){animation:lol 3s infinite ease-in-out}.cs-loader-inner label:nth-child(5){animation:lol 3s .1s infinite ease-in-out}.cs-loader-inner label:nth-child(4){animation:lol 3s .2s infinite ease-in-out}.cs-loader-inner label:nth-child(3){animation:lol 3s .3s infinite ease-in-out}.cs-loader-inner label:nth-child(2){animation:lol 3s .4s infinite ease-in-out}.cs-loader-inner label:nth-child(1){animation:lol 3s .5s infinite ease-in-out}</style>`,
);
addHtmlToBody(
host,
project,
` <div class="preloader"><div class="cs-loader"><div class="cs-loader-inner"><label> ●</label><label> ●</label><label> ●</label><label> ●</label><label> ●</label><label> ●</label></div></div></div>\n`,
);
// add styles
addFiles(host, [`${project.sourceRoot}/styles/index.less`, `${project.sourceRoot}/styles/theme.less`], overwriteDataFileRoot);
return host;
};
}
function mergeFiles(options: ApplicationOptions, from: string, to: string): Rule {
return mergeWith(
apply(url(from), [
options.i18n ? noop() : filter(p => p.indexOf('i18n') === -1),
options.form ? noop() : filter(p => p.indexOf('json-schema') === -1),
template({
utils: strings,
...options,
dot: '.',
VERSION,
ZORROVERSION,
}),
move(to),
]),
);
}
function addCliTpl(): (host: Tree) => void {
const TPLS = {
'__name@dasherize__.component.html': `<page-header></page-header>`,
'__name@dasherize__.component.ts': `import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
import { _HttpClient } from '@ohayo/theme';
import { NzMessageService } from 'ng-zorro-antd/message';
@Component({
selector: '<%= selector %>',
templateUrl: './<%= dasherize(name) %>.component.html',<% if(!inlineStyle) { %><% } else { %>
styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
export class <%= componentName %> implements OnInit {
constructor(private http: _HttpClient, private msg: NzMessageService) { }
ngOnInit() { }
}
`,
'__name@dasherize__.component.spec.ts': `import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { <%= componentName %> } from './<%= dasherize(name) %>.component';
describe('<%= componentName %>', () => {
let component: <%= componentName %>;
let fixture: ComponentFixture<<%= componentName %>>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ <%= componentName %> ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(<%= componentName %>);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
`,
};
return (host: Tree) => {
const prefix = `${project.root}/_cli-tpl/test/__path__/__name@dasherize@if-flat__/`;
Object.keys(TPLS).forEach(name => {
const realPath = prefix + name;
if (host.exists(realPath)) {
host.overwrite(realPath, TPLS[name]);
} else {
host.create(realPath, TPLS[name]);
}
});
};
}
function addFilesToRoot(options: ApplicationOptions): Rule {
return chain([
mergeWith(
apply(url('./files/src'), [
options.i18n ? noop() : filter(p => p.indexOf('i18n') === -1),
options.form ? noop() : filter(p => p.indexOf('json-schema') === -1),
template({
utils: strings,
...options,
dot: '.',
VERSION,
ZORROVERSION,
}),
move(project.sourceRoot),
]),
),
mergeWith(
apply(url('./files/root'), [
options.i18n ? noop() : filter(p => p.indexOf('i18n') === -1),
options.form ? noop() : filter(p => p.indexOf('json-schema') === -1),
template({
utils: strings,
...options,
dot: '.',
VERSION,
ZORROVERSION,
}),
// move('/')
]),
MergeStrategy.Overwrite,
),
]);
}
function fixLang(options: ApplicationOptions): (host: Tree) => void {
return (host: Tree) => {
if (options.i18n) return;
const langs = getLangData(options.defaultLanguage!);
if (!langs) return;
console.log(`Translating, please wait...`);
host.visit(p => {
if (~p.indexOf(`/node_modules/`)) return;
fixLangInHtml(host, p, langs);
});
};
}
function fixLangInHtml(host: Tree, p: string, langs: {}): void {
let html = host.get(p)!.content.toString('utf8');
let matchCount = 0;
// {{(status ? 'menu.fullscreen.exit' : 'menu.fullscreen') | translate }}
// {{ (status ? 'menu.fullscreen.exit' : 'menu.fullscreen') | translate }}
html = html.replace(/\{\{[ ]?\(status \? '([^']+)' : '([^']+)'\) \| translate \}\}/g, (_word, key1, key2) => {
++matchCount;
return `{{ status ? '${langs[key1] || key1}' : '${langs[key2] || key2}' }}`;
});
// {{ 'app.register-result.msg' | translate: params }}
html = html.replace(/\{\{[ ]?'([^']+)'[ ]? \| translate: [^ ]+ \}\}/g, (_word, key) => {
++matchCount;
return langs[key] || key;
});
// {{ 'Please enter mobile number!' | translate }}
html = html.replace(/\{\{[ ]?'([^']+)' \| translate[ ]?\}\}/g, (_word, key) => {
++matchCount;
return langs[key] || key;
});
// [nzTitle]="'app.login.tab-login-credentials' | translate"
html = html.replace(/'([^']+)' \| translate[ ]?/g, (_word, key) => {
++matchCount;
const value = langs[key] || key;
return `'${value}'`;
});
// 'app.register.get-verification-code' | translate
html = html.replace(/'([^']+)' \| translate/g, (_word, key) => {
++matchCount;
return langs[key] || key;
});
// removed `header-i18n`
if (~html.indexOf(`<header-i18n [showLang]="false" class="langs"></header-i18n>`)) {
++matchCount;
html = html.replace(`<header-i18n [showLang]="false" class="langs"></header-i18n>`, ``);
}
if (matchCount > 0) {
host.overwrite(p, html);
}
}
function fixVsCode(): (host: Tree) => void {
return (host: Tree) => {
const filePath = '.vscode/extensions.json';
let json = getJSON(host, filePath);
if (json == null) {
host.create(filePath, '');
json = {};
}
json.recommendations = ['cipchk.ohayojp-extension-pack'];
overwriteJSON(host, filePath, json);
};
}
function installPackages(): (_host: Tree, context: SchematicContext) => void {
return (_host: Tree, context: SchematicContext) => {
context.addTask(new NodePackageInstallTask());
};
}
export default function (options: ApplicationOptions): Rule {
return (host: Tree, context: SchematicContext) => {
project = getProject(host, options.project);
return chain([
// @ohayo/* dependencies
addDependenciesToPackageJson(options),
// ci
addRunScriptToPackageJson(),
addPathsToTsConfig(),
// code style
addCodeStylesToPackageJson(),
addSchematics(),
addNzLintRules(),
// files
removeOrginalFiles(),
addFilesToRoot(options),
addCliTpl(),
fixMain(),
forceLess(),
addStyle(),
fixLang(options),
fixVsCode(),
fixAngularJson(options),
installPackages(),
// applyLintFix(),
])(host, context);
};
}