From 56261d4a224d0869cef67bb8e162626c23853da7 Mon Sep 17 00:00:00 2001 From: cipchk Date: Wed, 4 Apr 2018 18:55:46 +0800 Subject: [PATCH] refactor: refactor doc generator --- .travis.yml | 1 + .vscode/launch.json | 10 +- docs/getting-started.md | 2 +- docs/how-to-start.md | 2 - docs/module.md | 2 - package.json | 14 +- packages/abc/simple-table/index.md | 39 +- .../simple-table/simple-table.component.ts | 2 +- .../token/simple/simple.interceptor.spec.ts | 4 +- .../site/converters/highlight.ts | 0 .../gulp => scripts}/site/converters/site.ts | 0 scripts/site/generate.ts | 164 +++++++ scripts/site/index.ts | 1 + gulpfile.js => scripts/site/main.js | 4 +- {tools/gulp => scripts}/site/plugins/toc.ts | 2 +- scripts/site/tsconfig.json | 26 ++ scripts/site/utils/generate-demo.ts | 109 +++++ scripts/site/utils/generate-md.ts | 80 ++++ scripts/site/utils/group-files.ts | 32 ++ scripts/site/utils/parse-md.ts | 20 + {tools/gulp => scripts}/site/utils/utils.ts | 36 ++ site.sh | 4 +- site/app/core/meta.service.ts | 6 +- site/app/layout/header/header.component.html | 14 +- site/app/layout/header/header.component.ts | 24 +- .../code-box/code-box.component.html | 12 +- .../components/code-box/code-box.component.ts | 20 +- .../components/content/content.component.html | 35 +- .../components/docs/docs.component.html | 10 +- .../shared/components/docs/docs.component.ts | 58 +-- .../edit-button/edit-button.component.ts | 2 +- site/app/styles/markdown.less | 1 + site/site.config.js | 32 +- site/templates/{doc.ts => content.ts} | 4 +- site/templates/meta.ts | 1 + tools/gulp/build/index.ts | 182 -------- tools/gulp/gulpfile.ts | 2 - tools/gulp/site/converters/index.ts | 2 - tools/gulp/site/index.ts | 66 --- tools/gulp/site/plugins/index.ts | 1 - tools/gulp/site/utils/process.ts | 439 ------------------ tools/gulp/tsconfig.json | 22 - tools/gulp/util/inline-resources.js | 140 ------ tools/gulp/util/task_helpers.ts | 32 -- 44 files changed, 627 insertions(+), 1032 deletions(-) rename {tools/gulp => scripts}/site/converters/highlight.ts (100%) rename {tools/gulp => scripts}/site/converters/site.ts (100%) create mode 100644 scripts/site/generate.ts create mode 100644 scripts/site/index.ts rename gulpfile.js => scripts/site/main.js (73%) rename {tools/gulp => scripts}/site/plugins/toc.ts (92%) create mode 100644 scripts/site/tsconfig.json create mode 100644 scripts/site/utils/generate-demo.ts create mode 100644 scripts/site/utils/generate-md.ts create mode 100644 scripts/site/utils/group-files.ts create mode 100644 scripts/site/utils/parse-md.ts rename {tools/gulp => scripts}/site/utils/utils.ts (56%) rename site/templates/{doc.ts => content.ts} (76%) create mode 100644 site/templates/meta.ts delete mode 100644 tools/gulp/build/index.ts delete mode 100644 tools/gulp/gulpfile.ts delete mode 100644 tools/gulp/site/converters/index.ts delete mode 100644 tools/gulp/site/index.ts delete mode 100644 tools/gulp/site/plugins/index.ts delete mode 100644 tools/gulp/site/utils/process.ts delete mode 100644 tools/gulp/tsconfig.json delete mode 100644 tools/gulp/util/inline-resources.js delete mode 100644 tools/gulp/util/task_helpers.ts diff --git a/.travis.yml b/.travis.yml index 5009bcf66e..f717766cf4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ env: - TASK=release - TASK=test - TASK=lint + - TASK=site:release - TASK=scaffold:release addons: diff --git a/.vscode/launch.json b/.vscode/launch.json index 9f1a71fc6d..2ef1767223 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,10 +13,14 @@ { "type": "node", "request": "launch", - "name": "Gulp task [site:build]", - "program": "${workspaceRoot}/node_modules/gulp/bin/gulp.js", + "name": "site generate", + "runtimeArgs": ["-r", "ts-node/register"], + "cwd": "${workspaceRoot}", + "protocol": "inspector", + "internalConsoleOptions": "openOnSessionStart", "args": [ - "site:build" + "${workspaceFolder}/scripts/site/generate.ts", + "architecture" ] } ] diff --git a/docs/getting-started.md b/docs/getting-started.md index 497f7ec38f..0bb7befcc5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -73,7 +73,7 @@ ng-alain 是一个标准的 Angular cli 构建的项目,因此你会非常熟 $ npm install ``` -如果网络状况不佳,可以使用 [cnpm](//cnpmjs.org/) 进行加速。 +如果网络状况不佳,可以使用 [nrm](https://www.npmjs.com/package/nrm) 并切换国内NPM镜像进行加速,请务直接使用 **cnpm** 会导致关联包无法找到问题。 ```bash $ npm start diff --git a/docs/how-to-start.md b/docs/how-to-start.md index dd4759d69d..8ede7ba6fc 100644 --- a/docs/how-to-start.md +++ b/docs/how-to-start.md @@ -16,8 +16,6 @@ type: Dev ng-alain 的基础组件库来自 ng-zorro-antd,因此对于这类组件的使用方式你可以通过其官网得到很细说 [API](//ng.ant.design/) 文档说明,而对于 ng-alain 提供的组件则通过[组件页](/components)获取。 -> ng-alain 依赖了一个 [ng-zorro-antd-extra](//cipchk.github.io/ng-zorro-antd-extra/) 类库,这是它和 ng-zorro-antd 一样,只是提供了一些 3.0 才会有的组件临时解决方案。 - ## 二、启动 项目从启动开始就需要从远程获取到一些配置信息(例如:菜单数据、用户数据等),脚手架的启动始于 [startup.service.ts](//github.com/cipchk/ng-alain/blob/master/src/app/core/startup/startup.service.ts);它返回的是一个 `Promise` 对象,因此你可以在这个方法体内做你想做的事,默认情况下做了很多有意思的事: diff --git a/docs/module.md b/docs/module.md index c201483af3..e7c2a72fb4 100644 --- a/docs/module.md +++ b/docs/module.md @@ -41,7 +41,6 @@ type: Other + HTTP 拦截器 + Angular 启动服务 + `ng-zorro-antd` 基础组件服务 -+ `ng-zorro-antd-extra` 基础组件扩展服务 + `@delon/abc` 业务组件服务 **作用:** 贯穿整个应用的定义。 @@ -62,7 +61,6 @@ type: Other + Angular 通用模块:`CommonModule`、`FormsModule`、`RouterModule`、`ReactiveFormsModule` + `ng-zorro-antd` 基础组件模块 -+ `ng-zorro-antd-extra` 基础扩展组件模块 + `@delon/abc` 业务组件模块 + 第三方通用依赖组件模块 diff --git a/package.json b/package.json index 529d0d72b2..391e2553c1 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "lint:style": "stylelint \"{src}/**/*.less\" --syntax less", "lint-staged": "lint-staged", "test": "ng test -sr -cc", + "site:gen": "node scripts/site/main", "site:release": "bash ./site.sh -b", "site:gh": "bash ./site.sh -b -d", "release": "bash ./build.sh", @@ -70,6 +71,7 @@ "@angular/compiler-cli": "^5.0.0", "@angular/language-service": "^5.0.0", "@schematics/schematics": "^0.4.6", + "@types/fs-extra": "^5.0.1", "@types/gulp": "^4.0.4", "@types/htmlparser2": "^3.7.31", "@types/jasmine": "~2.8.6", @@ -85,17 +87,6 @@ "domhandler": "^2.4.1", "fs-extra": "^5.0.0", "gh-pages": "^1.1.0", - "gulp": "^3.9.1", - "gulp-bump": "^3.0.0", - "gulp-clean": "^0.4.0", - "gulp-cli": "^2.0.0", - "gulp-gh-pages": "^0.5.4", - "gulp-rename": "^1.2.2", - "gulp-replace": "^0.6.0", - "gulp-rollup": "^2.15.0", - "gulp-transform": "^3.0.5", - "gulp-util": "^3.0.8", - "gulp-wrap": "^0.13.0", "htmlparser2": "^3.9.2", "jasmine-core": "~2.8.0", "jasmine-spec-reporter": "~4.2.1", @@ -105,6 +96,7 @@ "karma-coverage-istanbul-reporter": "^1.4.2", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", + "klaw-sync": "^3.0.2", "mark-twain": "^2.0.2", "mkdirp": "^0.5.1", "mockjs": "^1.0.1-beta3", diff --git a/packages/abc/simple-table/index.md b/packages/abc/simple-table/index.md index 4c8b66abbf..4e783ea7c9 100644 --- a/packages/abc/simple-table/index.md +++ b/packages/abc/simple-table/index.md @@ -75,14 +75,34 @@ radioChange | radio变化时回调,参数为当前所选 | `EventEmitter` | - sortChange | 排序回调 | `EventEmitter` | - filterChange | Filter回调 | `EventEmitter` | - -**方法** +### 组件方法 -参数 | 说明 -----|------ -`export(urlOrData?: string | any[], opt?: STExportOptions)` | 导出Excel,确保已经注册 `AdXlsxModule` | `Function` | - +参数 | 返回值 | 说明 +----|-----|----- +`load(pi = 1, extraParams?: any)` | `void` | 根据页码重新加载数据 +`reset(extraParams?: any)` | `void` | 重置且重新设置 `pi` 为 `1` +`clearCheck()` | `void` | 清除所有 `checkbox` +`clearRadio()` | `void` | 清除所有 `radio` +`export(urlOrData?: string | any[], opt?: STExportOptions)` | `void` | 导出Excel,确保已经注册 `AdXlsxModule` > `urlOrData` 一般用于需要导出所有页时重新指定带有所有数据的URL地址或本地数据数组,当所有参数都不指定时自动读书当前页数据。 +**使用方式** + +```ts +@Component({ + template: ` + + + + ` +}) +class TestComponent { + @ViewChild('st') comp: SimpleTableComponent; + // this.comp.load(); +} +``` + *STExportOptions* 参数 | 说明 | 类型 | 默认值 @@ -97,7 +117,6 @@ callback | 保存前的回调 | `(wb: WorkBook) => void` | - ----|------|-----|------ checked? | 选择框或单选框状态值 | `boolean` | - disabled? | 选择框或单选框 `disabled` 值 | `boolean` | - -[key: string]: any | - | - | - ### SimpleTableColumn @@ -209,13 +228,3 @@ reqReName | 重命名请求参数 `pi`、`ps`;例如:`{ pi: 'Page' }` `pi` resReName | 重命名返回参数 `total`、`list`;例如:`{ total: 'Total' }` Total 会被当作 `total` | `Object` | - sortReName | 重命名排序值,`columns` 的重命名高于属性 | `{ ascend?: string, descend?: string }` | - modalParamsName | 指定模态框目标组件的接收参数名 | `string` | record - -## 刷新表格 - -一般用于表单搜索时。 - -```html - - - -``` diff --git a/packages/abc/simple-table/simple-table.component.ts b/packages/abc/simple-table/simple-table.component.ts index 488957bdff..37ad4a25fa 100644 --- a/packages/abc/simple-table/simple-table.component.ts +++ b/packages/abc/simple-table/simple-table.component.ts @@ -251,7 +251,7 @@ export class SimpleTableComponent implements OnInit, OnChanges, AfterViewInit, O } /** - * 重新加载列表 + * 根据页码重新加载数据 * * @param pi 指定当前页码,默认:`1` * @param extraParams 重新指定 `extraParams` 值 diff --git a/packages/auth/token/simple/simple.interceptor.spec.ts b/packages/auth/token/simple/simple.interceptor.spec.ts index dfe530e165..136c424dcf 100644 --- a/packages/auth/token/simple/simple.interceptor.spec.ts +++ b/packages/auth/token/simple/simple.interceptor.spec.ts @@ -97,8 +97,8 @@ describe('auth: simple.interceptor', () => { http.get('/test', { responseType: 'text' }).subscribe(value => { done(); }); - const req = httpBed.expectOne('/test?token=123') as TestRequest; - expect(req.request.url).toContain(`token=123`); + const req = httpBed.expectOne(() => true) as TestRequest; + expect(req.request.url).toBe('/test?token=123'); req.flush('ok!'); }); }); diff --git a/tools/gulp/site/converters/highlight.ts b/scripts/site/converters/highlight.ts similarity index 100% rename from tools/gulp/site/converters/highlight.ts rename to scripts/site/converters/highlight.ts diff --git a/tools/gulp/site/converters/site.ts b/scripts/site/converters/site.ts similarity index 100% rename from tools/gulp/site/converters/site.ts rename to scripts/site/converters/site.ts diff --git a/scripts/site/generate.ts b/scripts/site/generate.ts new file mode 100644 index 0000000000..f7cf5530b1 --- /dev/null +++ b/scripts/site/generate.ts @@ -0,0 +1,164 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import * as fse from 'fs-extra'; +import { parseMd } from './utils/parse-md'; +import { genUpperName, genUrl, generateDoc, includeAttributes, genComponentName, genSelector } from './utils/utils'; +import { groupFiles } from './utils/group-files'; +import { generateDemo } from './utils/generate-demo'; + +const target = process.argv[2]; +const isSyncSpecific = !!target && (target !== 'init'); + +if (!target) throw new Error(`Should specify the generation type, 'init' is all module`); + +const rootDir = path.resolve(__dirname, '../../'); +const siteConfig = require(path.join(rootDir, 'site/site.config.js')); +const defaultLang = siteConfig.theme.defaultLang; + +const AllMetas: any = Object.assign({ types: [] }, includeAttributes(siteConfig.theme, {})); + +function generateModule(config: any) { + const srcPath = path.join(rootDir, config.src); + const targetPath = path.join(rootDir, config.dist); + const tpl = { + module: fs.readFileSync(path.join(rootDir, config.template.module)).toString('utf8'), + content: fs.readFileSync(path.join(rootDir, config.template.content)).toString('utf8') + }; + + const files = groupFiles(srcPath, config, isSyncSpecific, target, siteConfig); + const metas: any[] = []; + const components: any[] = []; + + const modules: any = { + imports: [], + components: [], + routes: [] + }; + + function appendToModule(componentName: string, name: string, filename: string, needRouter: boolean = true) { + modules.imports.push(`import { ${componentName} } from './${name}/${filename}';`); + modules.components.push(componentName); + if (needRouter) { + if (modules.routes.length <= 0 && config.defaultRoute) { + modules.routes.push(`{ path: '', redirectTo: '${config.defaultRoute}', pathMatch: 'full' }`); + } + modules.routes.push(`{ path: '${name}', component: ${componentName} }`); + } + } + + files.forEach(item => { + // region: generate document file + + const content = {}; + const urls = {}; + const contentMetas = {}; + const i18n = Object.keys(item.data).length > 1; + // const titleBuffer = {}; + Object.keys(item.data).forEach(lang => { + const filePath = item.data[lang]; + content[lang] = parseMd(filePath, siteConfig); + urls[lang] = genUrl(rootDir, filePath); + // // split langs in meta + // if (typeof content[lang].meta.title === 'object') { + // Object.keys(content[lang].meta.title).forEach(titleLang => { + // if (!titleBuffer[titleLang]) { + // titleBuffer[titleLang] = content[lang].meta.title[titleLang]; + // } + // }); + // } + // content[lang].meta.title = typeof content[lang].meta.title === 'string' ? + // content[lang].meta.title : titleBuffer[lang]; + contentMetas[lang] = content[lang].meta; + }); + const meta = { + name: item.key, + i18n, + order: content[defaultLang].meta.order || 100, + cols: content[defaultLang].meta.cols || 1, + meta: contentMetas + }; + metas.push(meta); + + // endregion + + // region: generate demo files + const demos = generateDemo( + rootDir, + item.key, + path.join(path.dirname(item.data[defaultLang]), 'demo'), + meta.cols, + config, + siteConfig + ); + // endregion + + // region: generate document file + const fileObject: any = { + componentName: genComponentName(config.name, meta.name), + selector: genSelector(config.name, meta.name), + item: JSON.stringify({ + cols: meta.cols, + urls, + content, + demo: demos.data.length > 0 + }), + demo: demos.tpl.left.length > 0 || demos.tpl.right.length > 0 + }; + const demoHTML: string[] = [ ]; + if (fileObject.demo) { + demoHTML.push(``); + if (demos.tpl.left.length > 0 && demos.tpl.right.length > 0) { + demoHTML.push(`${demos.tpl.left.join('')}`); + demoHTML.push(`${demos.tpl.right.join('')}`); + } else { + demoHTML.push(`${demos.tpl.left.join('')}${demos.tpl.right.join('')}`); + } + + demoHTML.push(''); + } + fileObject.demos = demoHTML.join(''); + fileObject.codes = JSON.stringify(demos.data); + generateDoc( + fileObject, + fs.readFileSync(path.join(rootDir, config.template.content)).toString('utf8'), + path.join(rootDir, config.dist, item.key, `index.ts`) + ); + // endregion + + // region: register module + appendToModule(fileObject.componentName, item.key, 'index'); + // demo + demos.data.forEach(demo => { + appendToModule(demo.componentName, item.key, demo.name, false); + }); + // endregion + }); + + AllMetas.types.push(Object.assign({ list: metas }, includeAttributes(config, {}))); + + // region: generate module file + const moduleObj = { + name: config.name, + moduleName: genUpperName(config.name), + imports: modules.imports.join('\r\n'), + components: modules.components.join(',\r\n'), + routes: modules.routes.join(',\r\n') + }; + generateDoc( + moduleObj, + fs.readFileSync(path.join(rootDir, config.template.module)).toString('utf8'), + path.join(rootDir, config.dist, `${config.name}.module.ts`) + ); + // endregion +} + +for (const m of siteConfig.modules) { + generateModule(m); +} + +// generate meta file +generateDoc( + { data: JSON.stringify(AllMetas) }, + fs.readFileSync(path.join(rootDir, siteConfig.theme.meta.template)).toString('utf8'), + path.join(rootDir, siteConfig.theme.meta.dist, `meta.ts`) +); diff --git a/scripts/site/index.ts b/scripts/site/index.ts new file mode 100644 index 0000000000..7263b816b6 --- /dev/null +++ b/scripts/site/index.ts @@ -0,0 +1 @@ +import './generate'; diff --git a/gulpfile.js b/scripts/site/main.js similarity index 73% rename from gulpfile.js rename to scripts/site/main.js index 897982a014..931cef48aa 100644 --- a/gulpfile.js +++ b/scripts/site/main.js @@ -8,7 +8,7 @@ const path = require('path'); // Register TS compilation. require('ts-node').register({ - project: path.join(__dirname, 'tools/gulp/tsconfig.json') + project: path.join(__dirname, 'tsconfig.json') }); -require('./tools/gulp/gulpfile'); +require('./index'); diff --git a/tools/gulp/site/plugins/toc.ts b/scripts/site/plugins/toc.ts similarity index 92% rename from tools/gulp/site/plugins/toc.ts rename to scripts/site/plugins/toc.ts index 466d94a5b8..cc8fb36ec7 100644 --- a/tools/gulp/site/plugins/toc.ts +++ b/scripts/site/plugins/toc.ts @@ -2,7 +2,7 @@ const JsonML = require('jsonml.js/lib/utils'); import { isHeading, generateSluggedId } from '../utils/utils'; export function toc(markdownData: any, config: any) { - const maxDepth = config.theme.tocMaxDepth || 6; + const maxDepth = config.theme.tocMaxDepth || 3; const listItems = JsonML.getChildren(markdownData.content).filter((node: any) => { const tagName = JsonML.getTagName(node); return isHeading(tagName) && +tagName.charAt(1) <= maxDepth; diff --git a/scripts/site/tsconfig.json b/scripts/site/tsconfig.json new file mode 100644 index 0000000000..f855f8a633 --- /dev/null +++ b/scripts/site/tsconfig.json @@ -0,0 +1,26 @@ +// TypeScript config file that will be used to compile the gulp tasks. The normal gulpfile will +// use TS-Node to compile the gulp tasks at runtime. +{ + "compilerOptions": { + "experimentalDecorators": true, + "lib": [ + "es2015", + "dom", + "es2016.array.include" + ], + "module": "commonjs", + "moduleResolution": "node", + "outDir": "./dist", + "strictNullChecks": true, + "noEmitOnError": true, + "noImplicitAny": true, + "target": "es5", + "types": [ + "node" + ], + "baseUrl": "." + }, + "files": [ + "**/*.ts" + ] +} diff --git a/scripts/site/utils/generate-demo.ts b/scripts/site/utils/generate-demo.ts new file mode 100644 index 0000000000..29ab49b158 --- /dev/null +++ b/scripts/site/utils/generate-demo.ts @@ -0,0 +1,109 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import * as fse from 'fs-extra'; +import { parseMd } from './parse-md'; +import { getCode, genUpperName, genUrl } from './utils'; +import { toHtml } from './generate-md'; +const MT = require('mark-twain'); +const JsonML = require('jsonml.js/lib/utils'); + +export function generateDemo(rootDir: string, key: string, dir: string, cols: number, config: any, siteConfig: any) { + const ret: { tpl: { left: string[], right: string[] }, data: any[] } = { + tpl: { + left: [], + right: [] + }, + data: [] + }; + + if (!fse.pathExistsSync(dir)) return ret; + + const demos: any[] = fse.readdirSync(dir).map(name => { + const filePath = path.join(dir, name); + let mt = null; + try { + mt = MT(fse.readFileSync(filePath, { encoding: 'utf-8' })); + } catch (err) { + console.error(`invalid ${filePath}`, err); + return; + } + mt.name = name.replace('.md', ''); + mt.filePath = filePath; + return mt; + }); + + if (demos.length <= 0) return ret; + const isTwo = cols > 1; + let point = 0; + demos.sort((a: any, b: any) => a.meta.order - b.meta.order).forEach((markdownData: any, index: number) => { + const item: any = { + id: `${config.name}-${key}-${markdownData.name}`, + meta: markdownData.meta, + summary: ``, + code: ``, + name: markdownData.name, + urls: genUrl(rootDir, markdownData.filePath) + }; + + const contentChildren = JsonML.getChildren(markdownData.content); + const codeStartIndex = contentChildren.findIndex((node: any) => JsonML.getTagName(node) === 'pre'); + + if (codeStartIndex > -1) { + item.summary = [''].concat(contentChildren.slice(0, codeStartIndex)); + + const codeNodes = contentChildren.slice(codeStartIndex); + if (codeNodes.length > 0) { + const attr = JsonML.getAttributes(codeNodes[0]); + item.code = getCode(codeNodes[0]); + item.lang = attr.lang; + } + } else { + item.summary = markdownData.content; + } + // parse languages + const summaryChildren = JsonML.getChildren(item.summary); + const summaryLangIdx = summaryChildren.findIndex((node: any) => JsonML.getTagName(node) === 'h2'); + if (summaryLangIdx !== -1) { + const summaryRet: any = {}; + for (let i = 0; i < summaryChildren.length; i++) { + const summaryNode = summaryChildren[i]; + const summaryLang = '' + summaryNode[1]; + if (JsonML.getTagName(summaryNode) === 'h2' && ~siteConfig.theme.langs.indexOf(summaryLang)) { + const nextLangPos = summaryChildren.slice(i + 1).findIndex((node: any) => JsonML.getTagName(node) === 'h2'); + summaryRet[summaryLang] = [''].concat(nextLangPos === -1 ? summaryChildren.slice(i + 1) : summaryChildren.slice(i + 1, nextLangPos + 1)); + if (nextLangPos === -1) break; + i = nextLangPos; + } + } + item.summary = summaryRet; + // tslint:disable-next-line:forin + for (const lang in item.summary) { + item.summary[lang] = toHtml(item.summary[lang]); + } + } else { + item.summary = toHtml(item.summary); + } + + // replace component name + item.componentName = `${genUpperName(item.id)}Component`; + item.code = ('' + item.code) + .replace(/selector:[ ]?(['|"|`])([^'"`]+)/g, `selector: $1${item.id}`) + .replace(/export class ([^ {]+)/g, `export class ${item.componentName}`); + // save demo component + const filePath = path.join(rootDir, config.dist, key, `${markdownData.name}.ts`); + fse.ensureDirSync(path.dirname(filePath)); + fs.writeFileSync(filePath, item.code, { flag: 'w+' }); + // generate doc component template + const pos = isTwo ? index % 2 === 0 ? 'left' : 'right' : 'left'; + ret.tpl[pos].push(` + + <${item.id}> + + `); + item.point = point; + ret.data.push(item); + + point++; + }); + return ret; +} diff --git a/scripts/site/utils/generate-md.ts b/scripts/site/utils/generate-md.ts new file mode 100644 index 0000000000..168ac616b1 --- /dev/null +++ b/scripts/site/utils/generate-md.ts @@ -0,0 +1,80 @@ +import { escapeHTML, genAttr, isStandalone } from './utils'; +import { highlight } from '../converters/highlight'; +import { site } from '../converters/site'; +const JsonML = require('jsonml.js/lib/utils'); + +const converters = [ + highlight() +].concat(site()).concat([ + [ + (node: any) => typeof node === 'string', + (node: any) => node + ], + [ + (node: any) => JsonML.isElement(node) && JsonML.getTagName(node) === 'a', + (node: any, index: number) => { + const attrs = Object.assign({ }, JsonML.getAttributes(node)); + let target = attrs.href.startsWith('//') || attrs.href.startsWith('http') ? ' target="_blank"' : ''; + if (~attrs.href.indexOf('cipchk.github.io/ngx-weui')) target = ''; + return `${JsonML.getChildren(node).map(toHtml).join('')}`; + } + ], + [ + () => true, + (node: any) => { + const tagName = JsonML.getTagName(node); + const attrs = genAttr(Object.assign({ }, JsonML.getAttributes(node))); + return `${tagName ? `<${tagName}${attrs ? ' ' + attrs : ''}>` : ''}${isStandalone(tagName) ? '' : JsonML.getChildren(node).map(toHtml).join('') + (tagName ? `` : '')}`; + } + ] +]); + +export function toHtml(markdownData: any, codeEscape: boolean = true) { + const pair: any = converters.filter((converter: any) => { + return converter[0](markdownData); + })[0]; + const ret: string = pair[1](markdownData); + if (codeEscape) { + return ret.replace(/
([\s\S]*)<\/code><\/pre>/g, (fullWord: any, lang: any, code: any) => {
+            return `
${escapeHTML(code)}
`; + }); + } + return ret; +} + +function fixAngular(html: string): string { + return html.replace(/(.*?)<\/code>/img, (fullWord: string, content: string) => { + if (~content.indexOf('<')) { + return `${content.replace('<', '<')}`; + } + return fullWord; + }); +} + +export function generateMd(markdownData: any) { + const contentChildren = JsonML.getChildren(markdownData.content); + const apiStartIndex = contentChildren.findIndex((node: any) => + JsonML.getTagName(node) === 'h2' && + /^API/.test(JsonML.getChildren(node)[0]) + ); + + const ret: any = { }; + + if (apiStartIndex > -1) { + const content = contentChildren.slice(0, apiStartIndex); + ret.content = [''].concat(content); + + const api = contentChildren.slice(apiStartIndex); + ret.api = [''].concat(api); + } else { + ret.content = markdownData.content; + } + + if (ret.content) + ret.content = fixAngular(toHtml(ret.content)); + + if (ret.api) + ret.api = fixAngular(toHtml(ret.api)); + + return ret; +} diff --git a/scripts/site/utils/group-files.ts b/scripts/site/utils/group-files.ts new file mode 100644 index 0000000000..6a812eb7d2 --- /dev/null +++ b/scripts/site/utils/group-files.ts @@ -0,0 +1,32 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import * as fse from 'fs-extra'; +const klawSync = require('klaw-sync'); + +export function groupFiles( + srcPath: string, config: any, isSyncSpecific: boolean, target: string, siteConfig: any +) { + const files: { key: string, data: { [ key: string ]: string } }[] = []; + const langRe = new RegExp(`.(${siteConfig.theme.langs.join('|')}){1}`, 'i'); + klawSync(srcPath, { + nodir: true, + filter: (item) => path.extname(item.path) === '.md' && item.stats.size > 1 && !item.path.includes(`${path.sep}demo${path.sep}`) + }).forEach(item => { + const key = path.relative(srcPath, config.hasSubDir ? path.dirname(item.path) : item.path.split('.')[0]).trim(); + if (key.length === 0) return; + if (isSyncSpecific && key !== target) return; + if (config.ignores && ~config.ignores.indexOf(key)) return; + + let sourceItem = files.find(w => w.key === key); + if (!sourceItem) { + sourceItem = { + key, + data: {} + }; + files.push(sourceItem); + } + const langMatch = item.path.match(langRe); + sourceItem.data[langMatch ? langMatch[1] : siteConfig.theme.defaultLang] = item.path; + }); + return files; +} diff --git a/scripts/site/utils/parse-md.ts b/scripts/site/utils/parse-md.ts new file mode 100644 index 0000000000..049a262c68 --- /dev/null +++ b/scripts/site/utils/parse-md.ts @@ -0,0 +1,20 @@ +import * as fse from 'fs-extra'; +const MT = require('mark-twain'); +import { generateMd } from './generate-md'; +import { toc } from '../plugins/toc'; + +export function parseMd(file: string, config: any) { + let mt = null; + try { + mt = MT(fse.readFileSync(file, { encoding: 'utf-8' })); + } catch (err) { + console.error(`invalid ${file}`, err); + return; + } + + const result = generateMd(mt); + result.meta = mt.meta; + result.toc = toc(mt, config); + + return result; +} diff --git a/tools/gulp/site/utils/utils.ts b/scripts/site/utils/utils.ts similarity index 56% rename from tools/gulp/site/utils/utils.ts rename to scripts/site/utils/utils.ts index 0abedf26a4..c0371c08e9 100644 --- a/tools/gulp/site/utils/utils.ts +++ b/scripts/site/utils/utils.ts @@ -1,5 +1,9 @@ // tslint:disable const JsonML = require('jsonml.js/lib/utils'); +const mustache = require('mustache'); +import * as path from 'path'; +import * as fs from 'fs'; +import * as fse from 'fs-extra'; export function isHeading(node: any) { return /h[1-6]/i.test(typeof node === 'string' ? node : JsonML.getTagName(node)); @@ -55,3 +59,35 @@ export function genAttr(attr: any) { } return ret.join(' '); } + +export function generateDoc(data: any, tpl: string, savePath: string) { + fse.ensureDirSync(path.dirname(savePath)); + const content = mustache.render(tpl, data); + fs.writeFileSync(savePath, content, { flag: 'w+' }); +} + +export function genUpperName(name: string) { + return name.split('-').map(v => v.charAt(0).toUpperCase() + v.slice(1)).join(''); +} + +export function includeAttributes(config: any, targetMeta: any) { + if (!config.meta || !config.meta.includeAttributes || !Array.isArray(config.meta.includeAttributes)) return; + + targetMeta = targetMeta || {}; + for (const key of config.meta.includeAttributes) { + targetMeta[key] = config[key]; + } + return targetMeta; +} + +export function genUrl(rootDir: string, filePath: string) { + return path.relative(rootDir, filePath).replace(/\\/g, `/`); +} + +export function genComponentName(...names) { + return `${ names.map(key => genUpperName(key)).join('') }Component`; +} + +export function genSelector(...names) { + return `app-${ names.join('-') }`; +} diff --git a/site.sh b/site.sh index 6b1a50d04c..91479ec8d6 100644 --- a/site.sh +++ b/site.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env bash set -u -e -o pipefail @@ -23,7 +23,7 @@ echo 'BUILD(-b):' $BUILD ', DEPLOY(-d):' $DEPLOY if [[ ${BUILD} == true ]]; then echo 'Generate site...' - $(npm bin)/gulp site:gen + npm run site:gen init echo 'Generate site color less...' rm -rf .tmp diff --git a/site/app/core/meta.service.ts b/site/app/core/meta.service.ts index f3040f9b7c..1f0551735d 100644 --- a/site/app/core/meta.service.ts +++ b/site/app/core/meta.service.ts @@ -24,6 +24,10 @@ export class MetaService { name: data.name, module_name: category.module || '' }); + // fix title + if (typeof this._data.title === 'object') { + this._data.title = this._data.title[this.i18n.lang] || this._data.title[this.i18n.defaultLang]; + } this.refPage(url); @@ -109,7 +113,7 @@ export class MetaService { } const entry: any = Object.assign({ url: `/${category.name}/${item.name}`, - title: meta.title, + title: this.i18n.get(meta.title), subtitle: meta.subtitle, order: item.order }); diff --git a/site/app/layout/header/header.component.html b/site/app/layout/header/header.component.html index 06d25d7201..3a643a8bb4 100644 --- a/site/app/layout/header/header.component.html +++ b/site/app/layout/header/header.component.html @@ -1,6 +1,6 @@ -
- -
+ +
-
- + + diff --git a/site/app/layout/header/header.component.ts b/site/app/layout/header/header.component.ts index 341061b02d..60cde941b3 100644 --- a/site/app/layout/header/header.component.ts +++ b/site/app/layout/header/header.component.ts @@ -26,22 +26,24 @@ export class HeaderComponent implements OnInit { list: any[] = []; genData() { - const ret: any[] = []; - this.meta.data.types.forEach((item: any) => { - const typeRet: any[] = item.list; - if (typeRet.length === 0) return; - ret.push({ + this.list = this.meta.data.types.filter(w => w.list.length > 0).map((item: any) => { + return { type: item.name, - list: typeRet - }); + list: item.list.map(i => { + const ret: any = { + name: i.name, + meta: this.i18n.get(i.meta) + }; + ret.meta.title = this.i18n.get(ret.meta.title); + return ret; + }) + }; }); - this.list = ret; this.searching = false; } - genTitle(item: any) { - const meta = item.meta[this.i18n.lang] || item.meta[this.i18n.defaultLang]; - return meta ? `${meta.title}${meta.subtitle ? '-' + meta.subtitle : ''}` : ``; + genTitle(itemMeta: any) { + return itemMeta ? `${itemMeta.title}${itemMeta.subtitle ? '-' + itemMeta.subtitle : ''}` : ``; } select(url: string) { diff --git a/site/app/shared/components/code-box/code-box.component.html b/site/app/shared/components/code-box/code-box.component.html index 6d5aa01de4..7cb9a18e69 100644 --- a/site/app/shared/components/code-box/code-box.component.html +++ b/site/app/shared/components/code-box/code-box.component.html @@ -1,16 +1,16 @@ -
-
-
+
+
+
- {{i18n.get(item.meta.title)}} + {{ item.title }}
-
+
-
{{item.code}}
+
{{item.code}}
diff --git a/site/app/shared/components/code-box/code-box.component.ts b/site/app/shared/components/code-box/code-box.component.ts index 8f37d0add5..62409ff64e 100644 --- a/site/app/shared/components/code-box/code-box.component.ts +++ b/site/app/shared/components/code-box/code-box.component.ts @@ -17,16 +17,16 @@ export class CodeBoxComponent implements OnInit, OnDestroy { private _item: any; @Input() set item(value: any) { - if (typeof value.meta.title === 'string') { - const defTitle = '' + value.meta.title; - value.meta.title = {}; - for (const lang of this.i18n.langs) { - value.meta.title[lang] = defTitle; - } - } - value.code = value.code.trim(); - value.meta.browser = +value.meta.browser > 0 ? +value.meta.browser : null; - this._item = value; + const ret: any = { + meta: value.meta, + code: value.code.trim(), + title: this.i18n.get(value.meta.title), + summary: this.i18n.get(value.summary), + browser: +value.meta.browser > 0 ? +value.meta.browser : null, + bg: value.meta.bg, + urls: value.urls + }; + this._item = ret; } get item() { return this._item; } diff --git a/site/app/shared/components/content/content.component.html b/site/app/shared/components/content/content.component.html index 87755906cb..3a26dc560c 100644 --- a/site/app/shared/components/content/content.component.html +++ b/site/app/shared/components/content/content.component.html @@ -1,25 +1,34 @@ - - + + diff --git a/site/app/shared/components/docs/docs.component.html b/site/app/shared/components/docs/docs.component.html index 28ed75fb12..1d07e3192f 100644 --- a/site/app/shared/components/docs/docs.component.html +++ b/site/app/shared/components/docs/docs.component.html @@ -1,7 +1,7 @@ -
+ @@ -17,7 +17,7 @@

{{meta.item.subtitle}}

-
+

{{'app.component.refer.title' | translate}}

import { {{meta.item.module}} } from '{{meta.item.module_name}}';
@@ -33,4 +33,4 @@

-
+
diff --git a/site/app/shared/components/docs/docs.component.ts b/site/app/shared/components/docs/docs.component.ts index 40a70d9942..5cddcef2da 100644 --- a/site/app/shared/components/docs/docs.component.ts +++ b/site/app/shared/components/docs/docs.component.ts @@ -1,6 +1,5 @@ import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; -import { DomSanitizer } from '@angular/platform-browser'; import { I18NService } from '../../../i18n/service'; import { MetaService } from '../../../core/meta.service'; @@ -16,47 +15,25 @@ export class DocsComponent implements OnInit, OnDestroy { @Input() set item(value: any) { - if (Array.isArray(value.toc)) { - const toc = [ ...value.toc ]; - value.toc = {}; - for (const lang of this.i18n.langs) { - value.toc[lang] = [ ...toc ]; - } - } - - // region: source - if (typeof value.source === 'string') { - const source = '' + value.source; - value.source = {}; - for (const lang of this.i18n.langs) { - value.source[lang] = source; - } - } - // endregion + const ret: any = { + demo: value.demo, + urls: value.urls, // [this.i18n.lang] || value.urls[this.i18n.defaultLang], + con: value.content[this.i18n.lang] || value.content[this.i18n.defaultLang] + }; // region: demo toc - if (value.demo && this.codes && this.codes.length) { - // tslint:disable-next-line:forin - for (const lang in value.toc) { - const demoTocs: any[] = this.codes.map((item: any) => { - return { - h: 3, - href: '#' + item.id, - title: this.i18n.get(item.meta.title) - }; - }); - value.toc[lang] = demoTocs; - // const demoTitle = this.i18n.fanyi('app.component.examples'); - // value.toc[lang].splice(0, 0, { - // h: 2, - // href: '#' + demoTitle, - // title: demoTitle - // }, ...demoTocs); - } + if (ret.demo && this.codes && this.codes.length) { + ret.con.toc = this.codes.map((item: any) => { + return { + h: 3, + href: '#' + item.id, + title: this.i18n.get(item.meta.title) + }; + }); } // endregion - this._item = value; + this._item = ret; // goTo setTimeout(() => { @@ -71,8 +48,7 @@ export class DocsComponent implements OnInit, OnDestroy { constructor( public i18n: I18NService, public meta: MetaService, - private router: Router, - protected sanitizer: DomSanitizer + private router: Router ) { } @@ -91,10 +67,6 @@ export class DocsComponent implements OnInit, OnDestroy { }, 250); } - safeHtml(html: string) { - return this.sanitizer.bypassSecurityTrustHtml(html); - } - i18NChange$: any; ngOnInit(): void { this.i18NChange$ = this.i18n.change.subscribe(() => { diff --git a/site/app/shared/components/edit-button/edit-button.component.ts b/site/app/shared/components/edit-button/edit-button.component.ts index bb54b89848..86d2048558 100644 --- a/site/app/shared/components/edit-button/edit-button.component.ts +++ b/site/app/shared/components/edit-button/edit-button.component.ts @@ -16,7 +16,7 @@ export class EditButtonComponent { @Input() set item(data: any) { - this._full = `${this.meta.github}/edit/master/${this.i18n.get(data.source)}`; + this._full = `${this.meta.github}/edit/master/${this.i18n.get(data.urls)}`; } constructor(private meta: MetaService, private i18n: I18NService) {} diff --git a/site/app/styles/markdown.less b/site/app/styles/markdown.less index 07ca938a57..6d04c1e7bb 100644 --- a/site/app/styles/markdown.less +++ b/site/app/styles/markdown.less @@ -207,6 +207,7 @@ td:first-child { font-weight: 500; width: 20%; + white-space: nowrap; } td:nth-child(3) { width: 30%; diff --git a/site/site.config.js b/site/site.config.js index d2c8d4e8b3..2025d7e7bb 100644 --- a/site/site.config.js +++ b/site/site.config.js @@ -4,14 +4,27 @@ module.exports = { github: 'https://github.com/cipchk/delon', defaultLang: 'zh-CN', langs: [ 'zh-CN', 'en-US' ], - dist: './site/app/routes/gen', tocMaxDepth: 3, // toc max depth + meta: { + dist: './site/app/routes/gen', + template: './site/templates/meta.ts', + includeAttributes: [ 'github', 'langs', 'defaultLang', 'tocMaxDepth' ] + } }, - router: [ + modules: [ { name: 'docs', src: './docs', - defaultRoute: 'getting-started', + dist: './site/app/routes/gen/docs', + defaultRoute: 'architecture', + template: { + content: './site/templates/content.ts', + module: './site/templates/module.ts' + }, + meta: { + includeAttributes: [ 'name', 'types' ] + }, + hasSubDir: false, types: [ { 'zh-CN': '入门', @@ -33,9 +46,18 @@ module.exports = { }, { name: 'components', - src: [ './packages/abc' ], - ignores: [ './packages/abc/README.md' ], + src: './packages/abc', + dist: './site/app/routes/gen/components', + ignores: [ 'README.md' ], + template: { + content: './site/templates/content.ts', + module: './site/templates/module.ts' + }, + meta: { + includeAttributes: [ 'name', 'types', 'module' ] + }, module: '@delon/abc', + hasSubDir: true, defaultRoute: 'avatar-list', types: [ { diff --git a/site/templates/doc.ts b/site/templates/content.ts similarity index 76% rename from site/templates/doc.ts rename to site/templates/content.ts index 9b8b901579..7a70949776 100644 --- a/site/templates/doc.ts +++ b/site/templates/content.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core'; @Component({ - selector: `app-{{directoryName}}-{{name}}`, + selector: `{{selector}}`, template: `{{{demos}}}`, styles: [ `:host { display: block } `] }) export class {{componentName}} { - item: any = {{{data}}}; + item: any = {{{item}}}; codes: any[] = {{{codes}}}; } diff --git a/site/templates/meta.ts b/site/templates/meta.ts new file mode 100644 index 0000000000..ea70080059 --- /dev/null +++ b/site/templates/meta.ts @@ -0,0 +1 @@ +export const META: any = {{{data}}}; diff --git a/tools/gulp/build/index.ts b/tools/gulp/build/index.ts deleted file mode 100644 index 989bf6c0f3..0000000000 --- a/tools/gulp/build/index.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { task, src, dest } from 'gulp'; -const rename = require('gulp-rename'); -const replace = require('gulp-replace'); -const rollup = require('gulp-rollup'); -const wrap = require('gulp-wrap'); -const bump = require('gulp-bump'); -const runSequence = require('run-sequence'); -const inline_recources = require('../util/inline-resources'); -// @ts-ignore -const VERSION = require('../../../package.json').version_laster; -const NEXTVERSION = require('../../../package.json').version_next; - -const paths = { - build: './.ng_build', - lib: './.lib', - scaffold: './scaffold/', - scaffoldSrc: './scaffold/src/', - scaffoldApp: './scaffold/src/app/' -}; - -task('copy-sources', copySources); -task('inline-resources', copyResources); -task('bundle:umd:theme', bundleUmdTheme); -task('bundle:umd:abc', bundleUmdABC); -task('bundle:umd:acl', bundleUmdACL); -task('bundle:umd:auth', bundleUmdAuth); -task('bundle:umd:mock', bundleUmdMock); -task('bundle:umd:cache', bundleUmdCache); -// @ts-ignore -task('bundle', [ - 'bundle:umd:theme', 'bundle:umd:abc', 'bundle:umd:acl', 'bundle:umd:auth', 'bundle:umd:mock', - 'bundle:umd:cache' ]); -task('bump', bumpVersions); -task('bump:next', bumpNextVersions); -// cli -task('cli', cliResources); - -const versionPaths = [ - './package.json', - './scaffold/package.json', - './packages/theme/package.json', - './packages/abc/package.json', - './packages/acl/package.json', - './packages/auth/package.json', - './packages/mock/package.json', - './packages/cache/package.json' -]; - -function bumpVersions() { - src(versionPaths, {base: './'}) - .pipe(bump({ - version: VERSION - })) - .pipe(dest('./')); -} - -function bumpNextVersions() { - src(versionPaths, {base: './'}) - .pipe(bump({ - version: NEXTVERSION - })) - .pipe(dest('./')); -} - -function copySources() { - src(['./packages/**/*', '!./packages/cli/**/*']) - .pipe(dest(paths.build)) - ; -} - -function cliResources() { - // others - - src([ - 'utility', 'class', 'directive', 'enum', 'interface', 'pipe', 'service', 'guard', 'app-shell' - ].map(v => `node_modules/@schematics/angular/${v}/**`), - { base: 'node_modules/@schematics/angular/' }).pipe(dest('./packages/cli')); - // .vscode - src([ - `${paths.scaffold}.vscode/**` - ], { base: paths.scaffold + '.vscode/' }).pipe(dest('./packages/cli/application/files/__dot__vscode')); - // normal files - src([ - `${paths.scaffold}_nginx/**`, - `${paths.scaffold}e2e/**`, - `${paths.scaffold}docker-compose.debug.yml`, - `${paths.scaffold}docker-compose.yml`, - `${paths.scaffold}Dockerfile`, - `${paths.scaffold}LICENSE`, - `${paths.scaffold}README.md`, - `${paths.scaffold}README-zh_CN.md`, - `${paths.scaffold}tslint.json`, - `${paths.scaffold}karma.conf.js`, - `${paths.scaffold}tsconfig.json`, - `${paths.scaffold}protractor.conf.js`, - `${paths.scaffold}package-lock.json` - ], { base: paths.scaffold }).pipe(dest('./packages/cli/application/files')); - // source - src([ - `${paths.scaffoldSrc}styles/**`, - `${paths.scaffoldSrc}testing/**`, - `${paths.scaffoldSrc}environments/environment.hmr.ts`, - `${paths.scaffoldSrc}environments/environment.prod.ts`, - `${paths.scaffoldSrc}environments/environment.ts`, - `${paths.scaffoldSrc}favicon.ico`, - `${paths.scaffoldSrc}hmr.ts`, - `${paths.scaffoldSrc}main.ts`, - `${paths.scaffoldSrc}test.ts`, - `${paths.scaffoldSrc}tsconfig.spec.json`, - `${paths.scaffoldSrc}typings.d.ts` - ], { base: paths.scaffoldSrc }).pipe(dest('./packages/cli/application/files/__sourcedir__/')); - // app - src([ - `${paths.scaffoldApp}/core/i18n/**`, - `${paths.scaffoldApp}/core/net/**`, - `${paths.scaffoldApp}/core/module-import-guard.ts`, - `${paths.scaffoldApp}/core/README.md`, - `${paths.scaffoldApp}/layout/fullscreen/**`, - `${paths.scaffoldApp}/layout/passport/**`, - `${paths.scaffoldApp}/layout/default/default.component.html`, - `${paths.scaffoldApp}/layout/default/default.component.spec.ts`, - `${paths.scaffoldApp}/layout/default/default.component.ts`, - `${paths.scaffoldApp}/layout/default/header/components/i18n.component.ts`, - `${paths.scaffoldApp}/layout/default/header/components/icon.component.ts`, - `${paths.scaffoldApp}/layout/default/header/components/notify.component.ts`, - `${paths.scaffoldApp}/layout/default/header/components/task.component.ts`, - `${paths.scaffoldApp}/layout/default/header/components/user.component.ts`, - `${paths.scaffoldApp}/shared/README.md`, - `${paths.scaffoldApp}/routes/callback/**`, - `${paths.scaffoldApp}/routes/exception/**`, - `${paths.scaffoldApp}/routes/passport/**` - ], { base: paths.scaffoldApp }).pipe(dest('./packages/cli/application/other-files/')); -} - -function copyResources() { - src([ - `${paths.build}/**/*.html`, - `${paths.build}/**/*.css`, - `${paths.build}/**/*.less`, - `${paths.build}/**/LICENSE.txt`, - `${paths.build}/**/README.md`, - `${paths.build}/**/rollup.config.js`, - `${paths.build}/**/package.json` - ]) - .pipe(dest(paths.lib)) - .on('end', () => inline_recources(paths.lib)); -} - -function bundleUmdTheme() { - bundle(`${paths.lib}/theme/`); -} - -function bundleUmdABC() { - bundle(`${paths.lib}/abc/`); -} - -function bundleUmdACL() { - bundle(`${paths.lib}/acl/`); -} - -function bundleUmdAuth() { - bundle(`${paths.lib}/auth/`); -} - -function bundleUmdMock() { - bundle(`${paths.lib}/mock/`); -} - -function bundleUmdCache() { - bundle(`${paths.lib}/cache/`); -} - -function bundle(path: string) { - const config = require(`../../../${path}rollup.config.js`); - src(path + `**/*.js`) - .pipe(rollup(Object.assign({}, config, { - name: config.name, - input: `${path}index.js` - }))) - .pipe(rename(config.output)) - .pipe(dest(`${path}bundles`)); -} diff --git a/tools/gulp/gulpfile.ts b/tools/gulp/gulpfile.ts deleted file mode 100644 index 52004e1313..0000000000 --- a/tools/gulp/gulpfile.ts +++ /dev/null @@ -1,2 +0,0 @@ -import './build'; -import './site'; diff --git a/tools/gulp/site/converters/index.ts b/tools/gulp/site/converters/index.ts deleted file mode 100644 index 12042e7205..0000000000 --- a/tools/gulp/site/converters/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { highlight } from './highlight'; -export { site } from './site'; diff --git a/tools/gulp/site/index.ts b/tools/gulp/site/index.ts deleted file mode 100644 index 63823e9a59..0000000000 --- a/tools/gulp/site/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -// tslint:disable -import { task, src, dest } from 'gulp'; -import * as path from 'path'; -import * as fs from 'fs'; -import chalk from 'chalk'; -import { File, PluginError } from "gulp-util"; -const del = require('del'); -const runSequence = require('run-sequence'); -const transform = require('gulp-transform'); -const gulpClean = require('gulp-clean'); -const rename = require('gulp-rename'); -const ghPages = require('gulp-gh-pages'); - -import { gen } from './utils/process'; -import { cleanTask, copyTask } from '../util/task_helpers'; -const siteRoot = `../../../site/`; -const config = require(siteRoot + 'site.config.js'); -// region: load template -const tpl = { - doc: fs.readFileSync(`${config.theme.templates}/doc.ts`).toString('utf8'), - module: fs.readFileSync(`${config.theme.templates}/module.ts`).toString('utf8') -}; -// endregion - -const allBuild: string[] = []; -config.router.forEach((item: any) => { - const taskName = `site:build:${item.name}`; - if (!Array.isArray(item.src)) item.src = [ item.src ]; - item.full_src = item.src.map((p: string) => path.normalize(p)); - item.theme = config.theme; - item.tpl = tpl; - - task(taskName, () => { - console.info(chalk.blue(`=====begin build: ${item.name}=====`)); - const config = Object.assign({}, item); - src(config.src.map((p: string) => `${p}/**/*.md`)) - .pipe(gen(config)) - ; - }); - - // todo: test - // if (item.name === 'components') - allBuild.push(taskName); -}); - -task('site:clean', () => { - return src(config.theme.dist, { read: false }).pipe(gulpClean(null)); -}); - -task('site:404', () => { - return src(siteRoot + 'dist/index.html').pipe(rename('404.html')).pipe(dest('.')); -}); - -task('site:cname', copyTask(siteRoot + 'CNAME', siteRoot + 'dist')); - -task('site:ghpages', () => { - return src(siteRoot + 'dist/**/*').pipe(ghPages()); -}); - -task('site:gen', (cb) => { - runSequence('site:clean', allBuild); -}); - -task('site:deploy', (cb) => { - runSequence([ 'site:404', 'site:cname' ], 'site:ghpages'); -}); diff --git a/tools/gulp/site/plugins/index.ts b/tools/gulp/site/plugins/index.ts deleted file mode 100644 index 3766aac7a6..0000000000 --- a/tools/gulp/site/plugins/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { toc } from './toc'; diff --git a/tools/gulp/site/utils/process.ts b/tools/gulp/site/utils/process.ts deleted file mode 100644 index 51f717b7da..0000000000 --- a/tools/gulp/site/utils/process.ts +++ /dev/null @@ -1,439 +0,0 @@ -// tslint:disable -import chalk from 'chalk'; -import * as path from 'path'; -import * as fs from 'fs'; -import { highlight, site } from '../converters'; -import { toc } from '../plugins'; -import { isStandalone, getCode, escapeHTML, genAttr } from '../utils/utils'; -const mkdirp = require('mkdirp'); -const through = require('through2'); -const MT = require('mark-twain'); -const mustache = require('mustache'); -const JsonML = require('jsonml.js/lib/utils'); - -const converters = [ - highlight() -].concat(site()).concat([ - [ - (node: any) => typeof node === 'string', - (node: any) => node - ], - [ - (node: any) => JsonML.isElement(node) && JsonML.getTagName(node) === 'a', - (node: any, index: number) => { - const attrs = Object.assign({ }, JsonML.getAttributes(node)); - let target = attrs.href.startsWith('//') || attrs.href.startsWith('http') ? ' target="_blank"' : ''; - if (~attrs.href.indexOf('ng-alain.com')) target = ''; - return `${JsonML.getChildren(node).map(toHtml).join('')}`; - } - ], - [ - () => true, - (node: any) => { - const tagName = JsonML.getTagName(node); - const attrs = genAttr(Object.assign({ }, JsonML.getAttributes(node))); - return `${tagName ? `<${tagName}${attrs ? ' ' + attrs : ''}>` : ''}${isStandalone(tagName) ? '' : JsonML.getChildren(node).map(toHtml).join('') + (tagName ? `` : '')}`; - } - ] -]); - -function toHtml(markdownData: any, codeEscape: boolean = true) { - const pair:any = converters.filter((converter: any) => { - return converter[0](markdownData); - })[0]; - let ret: string = pair[1](markdownData); - if (codeEscape) { - return ret.replace(/
([\s\S]*)<\/code><\/pre>/g, (fullWord: any, lang: any, code: any) => {
-            return `
${escapeHTML(code)}
`; - }); - } - return ret; -} - -export function gen(config: any) { - const files: any[] = []; - const results: any[] = []; - if (!config.ignores) - config.ignores = []; - else { - const newIgnores = []; - for (const ignorePath of config.ignores) { - newIgnores.push(path.join(process.cwd(), ignorePath)); - } - config.ignores = newIgnores; - } - - function bufferContents(file: any, enc: any, cb: any) { - // ignore empty files - if (file.isNull()) { - cb(); - return; - } - - // ignore streams - if (file.isStream()) { - this.emit('error', new Error('Streaming not supported')); - cb(); - return; - } - - if (~config.ignores.indexOf(file.path)) { - cb(); - return; - } - - let mt: any = null; - try { - mt = MT(file.contents); - } catch (err) { - console.log(chalk.red(`markdown解析失败:${file.relative}`, err)); - this.emit('error', new Error(`markdown解析失败:${file.relative}`)); - cb(); - return; - } - - // todo: test - // if (file.path.indexOf('getting-started') === -1) { - // cb(); - // return; - // } - - // 同一目录下,不同语言文件解析至同一个对象中 - const arr = file.relative.split(path.sep); - const nameArr = arr[0].split('.'); - const name = nameArr[0] === `index` ? path.dirname(file.relative) : nameArr[0]; - let item = files.find(w => w.path === file.base && w.name === name); - if (!item) { - item = { - data: {}, - cwd: file.cwd, - path: file.base, - name: name, - i18n: nameArr.length === 3, - demos: [] - }; - files.push(item); - } - if (arr.indexOf('demo') === -1) - item.data[item.i18n ? nameArr[1] : config.theme.defaultLang] = mt; - else { - mt.name = arr[arr.length - 1].split('.')[0]; - mt.source = path.relative(file.cwd, file.path).replace(new RegExp('\\' + path.sep, 'g'), '/'); - item.demos.push(mt); - } - - cb(); - } - - function convert() { - files.forEach((i: any) => { - - for (const lang in i.data) { - const item = i.data[lang]; - i.data[lang].toc = toc(item, config); - } - - }); - - genMeta(); - genPage(); - } - - function genMeta() { - const tpl = { - begin: `export const META: any = `, - end: `;` - }; - config.theme.meta = `${config.theme.dist}/meta.ts`; - mkdirp.sync(path.dirname(config.theme.meta)); - let metaContent = fs.readFileSync(config.theme.meta, { flag: 'a+' }).toString().trim(); - if (metaContent.startsWith(tpl.begin)) - metaContent = metaContent.substr(tpl.begin.length); - if (metaContent.endsWith(tpl.end)) - metaContent = metaContent.substr(0, metaContent.length - tpl.end.length); - let json: any; - try { - json = JSON.parse(metaContent) || {}; - } catch (err) { - json = {}; - } - json.github = config.theme.github; - if (!json.types || !Array.isArray(json.types)) - json.types = []; - - let group = (json.types).find(w => w.name === config.name); - if (!group) { - group = { - name: config.name, - module: config.module, - types: config.types - }; - json.types.push(group); - } - // clear all data - group.list = []; - - files.forEach((i: any) => { - const langItem = i.data[config.theme.defaultLang]; - if (!langItem) return; - - const defLangMeta = langItem.meta || {}; - const groupItem: any = { - name: i.name, - i18n: i.i18n, - module: config.module, - order: defLangMeta.order || 0, - cols: defLangMeta.cols || 1, - meta: {} - }; - - for (const lang in i.data) { - groupItem.meta[lang] = i.data[lang].meta; - const metaItem = Object.assign({}, i.data[lang].meta); - if (typeof metaItem.title === 'object') { - groupItem.meta[lang].title = '' + metaItem.title[lang]; - if (!groupItem.i18n) { - for (const metaLang in metaItem.title) { - if (metaLang === lang) continue; - groupItem.meta[metaLang] = Object.assign({}, groupItem.meta[lang], { - title: metaItem.title[metaLang] - }); - } - } - } - } - - group.list.push(groupItem); - }); - - fs.writeFileSync(config.theme.meta, tpl.begin + JSON.stringify(json) + tpl.end); - } - - function genUpperName(name: string) { - return name.split('-').map(v => v.charAt(0).toUpperCase() + v.slice(1)).join(''); - } - - function parseContent(markdownData: any, item: any) { - const contentChildren = JsonML.getChildren(markdownData.content); - const apiStartIndex = contentChildren.findIndex((node: any) => - JsonML.getTagName(node) === 'h2' && - /^API/.test(JsonML.getChildren(node)[0]) - ); - - const ret: any = { }; - - if (apiStartIndex > -1) { - const content = contentChildren.slice(0, apiStartIndex); - ret.content = [''].concat(content); - - const api = contentChildren.slice(apiStartIndex); - ret.api = [''].concat(api); - } else { - ret.content = markdownData.content; - } - - if (ret.content) - ret.content = toHtml(ret.content); - - if (ret.api) - ret.api = toHtml(ret.api); - - return ret; - } - - function genContentObject(i: any): any { - const ret: any = { - cols: i.data[config.theme.defaultLang].meta.cols, - source: {}, - content: {}, - toc: {} - }; - const relPath = path.relative(i.cwd, i.path).replace(new RegExp('\\' + path.sep, 'g'), '/'); - - // i18n - if (i.i18n) { - for (const lang in i.data) { - ret.source[lang] = `${relPath}/${i.name}.${lang}.md`; - ret.content[lang] = parseContent(i.data[lang], i); - ret.toc[lang] = i.data[lang].toc; - } - } else { - ret.source = `${relPath}/${i.name}${config.name === 'components' ? '/index' : ''}.md`; - ret.content[config.theme.defaultLang] = parseContent(i.data[config.theme.defaultLang], i); - ret.toc = i.data[config.theme.defaultLang].toc; - } - - return ret; - } - - function genDocFile(i: any) { - const content = mustache.render(config.tpl.doc, i); - const filePath = path.join(config.theme.dist, i.directoryName, i.name, `index.ts`); - mkdirp.sync(path.dirname(filePath)); - fs.writeFileSync(filePath, content, { flag: 'w+' }); - } - - const modules: any = { - imports: [], - components: [], - routes: [] - }; - - function appendToModule(componentName: string, name: string, filename: string = 'index', needRouter: boolean = true) { - modules.imports.push(`import { ${componentName} } from './${name}/${filename}';`); - modules.components.push(componentName); - if (needRouter) { - if (modules.routes.length <= 0 && config.defaultRoute) { - modules.routes.push(`{ path: '', redirectTo: '${config.defaultRoute}', pathMatch: 'full' }`); - } - modules.routes.push(`{ path: '${name}', component: ${componentName} }`); - } - } - - function genModule(i: any) { - const content = mustache.render(config.tpl.module, i); - const filePath = path.join(config.theme.dist, i.name, `${i.name}.module.ts`); - mkdirp.sync(path.dirname(filePath)); - fs.writeFileSync(filePath, content, { flag: 'w+' }); - } - - function genPage() { - - const moduleName = genUpperName(config.name); - - files.forEach((i: any) => { - - // gen demo - const demos = genDemo(i); - - const fileDataObj = genContentObject(i); - fileDataObj.demo = demos.tpl.left.length > 0 || demos.tpl.right.length > 0; - const demoHTML: string[] = [ ]; - if (fileDataObj.demo) { - demoHTML.push(`
`); - if (demos.tpl.left.length > 0 && demos.tpl.right.length > 0) { - demoHTML.push(`
${demos.tpl.left.join('')}
`); - demoHTML.push(`
${demos.tpl.right.join('')}
`); - } else { - demoHTML.push(`
${demos.tpl.left.join('')}${demos.tpl.right.join('')}
`); - } - - demoHTML.push('
'); - } - - const fileObj = { - name: i.name, - directoryName: config.name, - componentName: `${moduleName}${genUpperName(i.name)}Component`, - data: JSON.stringify(fileDataObj), - demos: demoHTML.join(''), - codes: JSON.stringify(demos.data) - }; - // gen file - genDocFile(fileObj); - // append module - appendToModule(fileObj.componentName, fileObj.name); - }); - - const moduleObj = { - name: config.name, - moduleName: moduleName, - imports: modules.imports.join('\r\n'), - components: modules.components.join(',\r\n'), - routes: modules.routes.join(',\r\n') - }; - genModule(moduleObj); - } - - function genDemo(data: any): { tpl: { left: string[], right: string[] }, data: any[] } { - const ret: { tpl: { left: string[], right: string[] }, data: any[] } = { - tpl: { - left: [], - right: [] - }, - data: [] - }; - if (data.demos.length <= 0) return ret; - const isTwo = data.data[config.theme.defaultLang].meta.cols > 1; - let point = 0; - data.demos.sort((a: any, b: any) => a.meta.order - b.meta.order).forEach((markdownData: any, index: number) => { - const item: any = { - id: `${config.name}-${data.name}-${markdownData.name}`, - meta: markdownData.meta, - source: markdownData.source, - lang: 'ts', - summary: ``, - code: `` - }; - - const contentChildren = JsonML.getChildren(markdownData.content); - const codeStartIndex = contentChildren.findIndex((node: any) => JsonML.getTagName(node) === 'pre'); - - if (codeStartIndex > -1) { - item.summary = [''].concat(contentChildren.slice(0, codeStartIndex)); - - const codeNodes = contentChildren.slice(codeStartIndex); - if (codeNodes.length > 0) { - const attr = JsonML.getAttributes(codeNodes[0]); - item.code = getCode(codeNodes[0]); - item.lang = attr.lang; - } - } else { - item.summary = markdownData.content; - } - // parse languages - const summaryChildren = JsonML.getChildren(item.summary); - let summaryLangIdx = summaryChildren.findIndex((node: any) => JsonML.getTagName(node) === 'h2'); - if (summaryLangIdx !== -1) { - let summaryRet: any = {}; - for (let i = 0; i < summaryChildren.length; i++) { - const summaryNode = summaryChildren[i]; - const summaryLang = '' + summaryNode[1]; - if (JsonML.getTagName(summaryNode) === 'h2' && ~config.theme.langs.indexOf(summaryLang)) { - const nextLangPos = summaryChildren.slice(i + 1).findIndex((node: any) => JsonML.getTagName(node) === 'h2'); - summaryRet[summaryLang] = [''].concat(nextLangPos === -1 ? summaryChildren.slice(i + 1) : summaryChildren.slice(i + 1, nextLangPos + 1)); - if (nextLangPos === -1) break; - i = nextLangPos; - } - } - item.summary = summaryRet; - for (const lang in item.summary) { - item.summary[lang] = toHtml(item.summary[lang]); - } - } else { - item.summary = toHtml(item.summary); - } - - ret.data.push(item); - // replace component name - const componentName = `${genUpperName(item.id)}Component`; - const code = ('' + item.code) - .replace(/selector:[ ]?(['|"|`])([^'"`]+)/g, `selector: $1${item.id}`) - .replace(/export class ([^ {]+)/g, `export class ${componentName}`); - // save demo component - const filePath = path.join(config.theme.dist, config.name, data.name, `${markdownData.name}.ts`); - mkdirp.sync(path.dirname(filePath)); - fs.writeFileSync(filePath, code, { flag: 'w+' }); - // generate doc component template - const pos = isTwo ? index % 2 === 0 ? 'left' : 'right' : 'left'; - ret.tpl[pos].push(` - - <${item.id}> - - `); - // import component - appendToModule(componentName, data.name, markdownData.name, false); - - point++; - }); - return ret; - } - - function end(cb: any) { - convert(); - this.push(...results); - cb(); - } - - return through.obj(bufferContents, end); -} diff --git a/tools/gulp/tsconfig.json b/tools/gulp/tsconfig.json deleted file mode 100644 index 94e72616d8..0000000000 --- a/tools/gulp/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -// TypeScript config file that will be used to compile the gulp tasks. The normal gulpfile will -// use TS-Node to compile the gulp tasks at runtime. -{ - "compilerOptions": { - "experimentalDecorators": true, - "lib": ["es2015", "dom", "es2016.array.include"], - "module": "commonjs", - "moduleResolution": "node", - "outDir": "./dist", - "strictNullChecks": true, - "noEmitOnError": true, - "noImplicitAny": true, - "target": "es5", - "types": [ - "node" - ], - "baseUrl": "." - }, - "files": [ - "gulpfile.ts" - ] -} diff --git a/tools/gulp/util/inline-resources.js b/tools/gulp/util/inline-resources.js deleted file mode 100644 index b826c2ffb2..0000000000 --- a/tools/gulp/util/inline-resources.js +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env node - -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const glob = require('glob'); - -/** - * Simple Promiseify function that takes a Node API and return a version that supports promises. - * We use promises instead of synchronized functions to make the process less I/O bound and - * faster. It also simplify the code. - */ -function promiseify(fn) { - return function () { - const args = [].slice.call(arguments, 0); - return new Promise((resolve, reject) => { - fn.apply(this, args.concat([function (err, value) { - if (err) { - reject(err); - } else { - resolve(value); - } - }])); - }); - }; -} - -const readFile = promiseify(fs.readFile); -const writeFile = promiseify(fs.writeFile); - - -function inlineResources(globs) { - if (typeof globs == 'string') { - globs = [globs]; - } - - /** - * For every argument, inline the templates and styles under it and write the new file. - */ - return Promise.all(globs.map(pattern => { - if (pattern.indexOf('*') < 0) { - // Argument is a directory target, add glob patterns to include every files. - pattern = path.join(pattern, '**', '*'); - } - - const files = glob.sync(pattern, {}) - .filter(name => /\.js$/.test(name)); // Matches only JavaScript files. - - // Generate all files content with inlined templates. - return Promise.all(files.map(filePath => { - return readFile(filePath, 'utf-8') - .then(content => inlineResourcesFromString(content, url => { - return path.join(path.dirname(filePath), url); - })) - .then(content => writeFile(filePath, content)) - .catch(err => { - console.error('An error occurred: ', err); - }); - })); - })); -} - -/** - * Inline resources from a string content. - * @param content {string} The source file's content. - * @param urlResolver {Function} A resolver that takes a URL and return a path. - * @returns {string} The content with resources inlined. - */ -function inlineResourcesFromString(content, urlResolver) { - // Curry through the inlining functions. - return [ - inlineTemplate, - inlineStyle, - removeModuleId - ].reduce((content, fn) => fn(content, urlResolver), content); -} - -if (require.main === module) { - inlineResources(process.argv.slice(2)); -} - - -/** - * Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and - * replace with `template: ...` (with the content of the file included). - * @param content {string} The source file's content. - * @param urlResolver {Function} A resolver that takes a URL and return a path. - * @return {string} The content with all templates inlined. - */ -function inlineTemplate(content, urlResolver) { - return content.replace(/templateUrl:\s*'([^']+?\.html)'/g, function (m, templateUrl) { - const templateFile = urlResolver(templateUrl); - const templateContent = fs.readFileSync(templateFile, 'utf-8'); - const shortenedTemplate = templateContent - .replace(/([\n\r]\s*)+/gm, ' ') - .replace(/"/g, '\\"'); - return `template: "${shortenedTemplate}"`; - }); -} - - -/** - * Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and - * replace with `styles: [...]` (with the content of the file included). - * @param urlResolver {Function} A resolver that takes a URL and return a path. - * @param content {string} The source file's content. - * @return {string} The content with all styles inlined. - */ -function inlineStyle(content, urlResolver) { - return content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, function (m, styleUrls) { - const urls = eval(styleUrls); - return 'styles: [' + - urls.map(styleUrl => { - const styleFile = urlResolver(styleUrl); - const styleContent = fs.readFileSync(styleFile, 'utf-8'); - const shortenedStyle = styleContent - .replace(/([\n\r]\s*)+/gm, ' ') - .replace(/content: "([^"]+)"/g, 'content: "\\$1"') - .replace(/"/g, '\\"'); - return `"${shortenedStyle}"`; - }) - .join(',\n') + - ']'; - }); -} - - -/** - * Remove every mention of `moduleId: module.id`. - * @param content {string} The source file's content. - * @returns {string} The content with all moduleId: mentions removed. - */ -function removeModuleId(content) { - return content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, ''); -} - - -module.exports = inlineResources; -module.exports.inlineResourcesFromString = inlineResourcesFromString; diff --git a/tools/gulp/util/task_helpers.ts b/tools/gulp/util/task_helpers.ts deleted file mode 100644 index f3b9ce0eae..0000000000 --- a/tools/gulp/util/task_helpers.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as fs from 'fs'; -import { src, dest } from 'gulp'; -import * as path from 'path'; -const gulpClean = require('gulp-clean'); - -/** If the string passed in is a glob, returns it, otherwise append '**\/*' to it. */ -function _globify(maybeGlob: string, suffix = '**/*') { - if (maybeGlob.indexOf('*') !== -1) { - return maybeGlob; - } - try { - const stat = fs.statSync(maybeGlob); - if (stat.isFile()) { - return maybeGlob; - } - } catch (e) {} - return path.join(maybeGlob, suffix); -} - -/** Copy files from a glob to a destination. */ -export function copyTask(srcGlobOrDir: string | string[], outRoot: string) { - if (typeof srcGlobOrDir === 'string') { - return () => src(_globify(srcGlobOrDir)).pipe(dest(outRoot)); - } else { - return () => src(srcGlobOrDir.map(name => _globify(name))).pipe(dest(outRoot)); - } -} - -/** Delete files. */ -export function cleanTask(glob: string) { - return () => src(glob, { read: false }).pipe(gulpClean(null)); -}