From 91cee712214289d9192c9361a3cd3fcc2f77d0bc Mon Sep 17 00:00:00 2001 From: Ilya Surmay Date: Mon, 21 Aug 2017 18:20:48 +0300 Subject: [PATCH] feat(docs): add versioning implementation (#2350) * feat(docs): add versioning implementation * refactor(docs): remove rimraf, simplify package.json commands * feat(docs): add docs.archive to gh-pages script * refactor(docs): remove else, reduce nesting * feat(docs): docs versioning works with demo.serve * refactor(docs): deploy and serve from gh-pages, add version check * refactor(docs): clone/pull via js * chore(build): simplifed archieve script * chore(build): more tests --- .gitignore | 1 + README.md | 2 +- demo/src/app/app.module.ts | 24 ++-- .../common/top-menu/top-menu.component.html | 12 +- .../app/common/top-menu/top-menu.component.ts | 46 ++++++-- .../app/getting-started/getting-started.md | 2 +- demo/src/assets/css/style.css | 11 ++ demo/src/assets/json/current-version.json | 1 + demo/src/assets/json/versions.json | 1 + demo/src/index-bs4.html | 2 +- demo/src/index.html | 2 +- package.json | 15 ++- scripts/archive.js | 110 ++++++++++++++++++ {demo => scripts}/bs-config.js | 2 +- scripts/fetch-docs.js | 17 +++ 15 files changed, 214 insertions(+), 34 deletions(-) create mode 100644 demo/src/assets/json/current-version.json create mode 100644 demo/src/assets/json/versions.json create mode 100644 scripts/archive.js rename {demo => scripts}/bs-config.js (77%) create mode 100644 scripts/fetch-docs.js diff --git a/.gitignore b/.gitignore index 7012cf719c..a7587b9fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ npm-debug.log /demo/dist /demo/temp /logs +/gh-pages #System Files .DS_Store diff --git a/README.md b/README.md index 85c4e279b2..066e2d4bc7 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ You will need bootstrap styles (Bootstrap 3) Or Bootstrap 4 ``` - + ``` To enable bootstrap 4 theme templates in ngx-bootstrap, please read [here](https://github.com/valor-software/ngx-bootstrap/blob/development/docs/getting-started/bootstrap4.md) diff --git a/demo/src/app/app.module.ts b/demo/src/app/app.module.ts index 143ef6255f..651b0ef6a4 100644 --- a/demo/src/app/app.module.ts +++ b/demo/src/app/app.module.ts @@ -1,19 +1,21 @@ -import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { FormsModule} from '@angular/forms'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { BrowserModule } from '@angular/platform-browser'; import { RouterModule } from '@angular/router'; import { Ng2PageScrollModule } from 'ng2-page-scroll/ng2-page-scroll'; +import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; +import { ngdoc } from '../ng-api-doc'; +import { NgApiDoc } from './api-docs/api-docs.model'; + +import { NgApiDocModule } from './api-docs/index'; import { AppComponent } from './app.component'; import { routes } from './app.routing'; -import { GettingStartedComponent } from './getting-started/getting-started.component'; +import { AppFooterComponent } from './common/app-footer/app-footer.component'; import { MainMenuComponent } from './common/main-menu/main-menu.component'; -import { TopMenuComponent } from './common/top-menu/top-menu.component'; import { SearchFilterPipe } from './common/main-menu/search-filter.pipe'; -import { AppFooterComponent } from './common/app-footer/app-footer.component'; - -import { NgApiDocModule } from './api-docs/index'; -import { NgApiDoc } from './api-docs/api-docs.model'; -import { ngdoc } from '../ng-api-doc'; +import { TopMenuComponent } from './common/top-menu/top-menu.component'; +import { GettingStartedComponent } from './getting-started/getting-started.component'; @NgModule({ declarations: [ @@ -28,8 +30,10 @@ import { ngdoc } from '../ng-api-doc'; NgApiDocModule, BrowserModule, FormsModule, + HttpModule, RouterModule.forRoot(routes, {useHash: true}), - Ng2PageScrollModule.forRoot() + Ng2PageScrollModule.forRoot(), + BsDropdownModule.forRoot() ], providers: [ {provide: NgApiDoc, useValue: ngdoc} diff --git a/demo/src/app/common/top-menu/top-menu.component.html b/demo/src/app/common/top-menu/top-menu.component.html index 83c3eb9d1d..08e82e1d04 100644 --- a/demo/src/app/common/top-menu/top-menu.component.html +++ b/demo/src/app/common/top-menu/top-menu.component.html @@ -1,6 +1,16 @@
-

ngx-bootstrap

+

ngx-bootstrap {{currentVersion ? 'v' + currentVersion : ''}}

+
+
diff --git a/demo/src/app/common/top-menu/top-menu.component.ts b/demo/src/app/common/top-menu/top-menu.component.ts index e34985d8ac..78ef5f744b 100644 --- a/demo/src/app/common/top-menu/top-menu.component.ts +++ b/demo/src/app/common/top-menu/top-menu.component.ts @@ -1,35 +1,57 @@ import { AfterViewInit, Component, Inject, Renderer } from '@angular/core'; +import { Http } from '@angular/http'; import { DOCUMENT } from '@angular/platform-browser'; -import { NavigationEnd, Router } from '@angular/router'; +import { NavigationEnd, Router, UrlSerializer } from '@angular/router'; +import 'rxjs/add/operator/map'; @Component({ selector: 'top-menu', templateUrl: './top-menu.component.html' }) export class TopMenuComponent implements AfterViewInit { - public isShown: boolean = false; + public isShown = false; + public appUrl: string; + public appHash: string; + public currentVersion: string; + public previousDocs: string[] = []; + public isLocalhost = false; - private renderer: Renderer; - private document: any; - private router: Router; - - public constructor(renderer: Renderer, @Inject(DOCUMENT) document: any, router: Router) { - this.router = router; - this.renderer = renderer; - this.document = document; + public constructor(private renderer: Renderer, + @Inject(DOCUMENT) private document: any, + private router: Router, + private http: Http) { } public ngAfterViewInit(): any { // todo: remove this sh** - const getUrl = (router: Router) => router.routerState.snapshot.url.slice(0, router.routerState.snapshot.url.indexOf('#')); + this.isLocalhost = location.hostname === 'localhost'; + const getUrl = (router: Router) => { + const indexOfHash = router.routerState.snapshot.url.indexOf('#'); + + return router.routerState.snapshot.url.slice(0, indexOfHash); + }; let _prev = getUrl(this.router); this.router.events.subscribe((event: any) => { - let _cur = getUrl(this.router); + const _cur = getUrl(this.router); + this.appHash = location.hash === '#/' ? '' : location.hash; if (event instanceof NavigationEnd && _cur !== _prev) { _prev = _cur; this.toggle(false); } }); + + this.http.get('assets/json/versions.json') + .map(res => res.json()) + .subscribe((data: any) => { + this.previousDocs = data; + }); + this.http.get('assets/json/current-version.json') + .map(res => res.json()) + .subscribe((data: any) => { + this.currentVersion = data.version; + }); + + this.appUrl = location.protocol + '//' + location.hostname + (this.isLocalhost ? ':' + location.port + '/' : '/'); } public toggle(isShown?: boolean): void { diff --git a/demo/src/app/getting-started/getting-started.md b/demo/src/app/getting-started/getting-started.md index 587a2217c5..51ce3888f2 100644 --- a/demo/src/app/getting-started/getting-started.md +++ b/demo/src/app/getting-started/getting-started.md @@ -35,7 +35,7 @@ You will need bootstrap styles (Bootstrap 3) Or Bootstrap 4 ``` - + ``` To enable bootstrap 4 theme templates in ngx-bootstrap, please read [here](https://github.com/valor-software/ngx-bootstrap/blob/development/docs/getting-started/bootstrap4.md) diff --git a/demo/src/assets/css/style.css b/demo/src/assets/css/style.css index bfe8be9b52..e3d0918931 100644 --- a/demo/src/assets/css/style.css +++ b/demo/src/assets/css/style.css @@ -52,6 +52,17 @@ a:hover { float: left; } +#top-menu .prev-docs { + position: relative; +} + #top-menu .prev-docs .dropdown-toggle { + color: #fff; + font-size: 18px; + padding-top: 22px; + cursor: pointer; + } + + #top-menu .title { border-right: 1px solid #191924; min-width: 270px; diff --git a/demo/src/assets/json/current-version.json b/demo/src/assets/json/current-version.json new file mode 100644 index 0000000000..1a5b93f0bf --- /dev/null +++ b/demo/src/assets/json/current-version.json @@ -0,0 +1 @@ +{"version":"1.8.1"} \ No newline at end of file diff --git a/demo/src/assets/json/versions.json b/demo/src/assets/json/versions.json new file mode 100644 index 0000000000..cd25a5309c --- /dev/null +++ b/demo/src/assets/json/versions.json @@ -0,0 +1 @@ +[{"version":"Current","url":"ngx-bootstrap","unprefixedUrl":""},{"version":"1.8.5","url":"ngx-bootstrap/old/1.8.5","unprefixedUrl":"old/1.8.5"}] diff --git a/demo/src/index-bs4.html b/demo/src/index-bs4.html index 7e6a3eabaf..6788cccf92 100644 --- a/demo/src/index-bs4.html +++ b/demo/src/index-bs4.html @@ -16,7 +16,7 @@ - + diff --git a/demo/src/index.html b/demo/src/index.html index 09f19a40e4..e16ef7d6bc 100644 --- a/demo/src/index.html +++ b/demo/src/index.html @@ -19,7 +19,7 @@ - + diff --git a/package.json b/package.json index 0dc93af7a5..1505b9b71d 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,18 @@ "description": "Native Angular Bootstrap Components", "private": true, "scripts": { - "lite-server": "lite-server -c demo/bs-config.js", + "lite-server": "lite-server -c scripts/bs-config.js", "typedoc": "typedoc --exclude '**/*.spec.ts' ./src/", "demo.docs": "node scripts/docs/get-doc.js", - "demo.serve": "run-s build link demo.docs demo.build lite-server", - "demo.gh-pages": "run-s build demo.docs demo.build demo.deploy", + "demo.serve": "run-s build link demo.docs demo.build docs.archive lite-server", + "demo.serve-fast": "run-s build link demo.docs demo.build-fast docs.archive lite-server", + "demo.gh-pages": "run-s build demo.docs demo.build docs.archive demo.deploy", "demo.build": "ng build -prod --aot --build-optimizer && npm run generate-bs4", - "demo.deploy": "gh-pages -d demo/dist", - "demo-beta-deploy": "gh-pages -d demo/dist -r git@github.com:valorkin/ngx-bootstrap.git -b gh-pages", + "demo.build-fast": "ng build && npm run generate-bs4", + "demo.deploy": "gh-pages -d gh-pages", + "demo-beta-deploy": "gh-pages -d gh-pages -r git@github.com:valorkin/ngx-bootstrap.git -b gh-pages", + "docs.archive": "npm run docs.fetch && node scripts/archive.js", + "docs.fetch": "node scripts/fetch-docs.js", "link": "ngm link -p src --here", "lint": "exit 0", "disable-lint": "tslint \"**/*.ts\" -c tslint.json --fix --type-check -t prose -e \"node_modules/**\" -e \"dist/**\" -e \"temp/**\" -e \"scripts/docs/**\"", @@ -88,7 +92,6 @@ "core-js": "^2.4.1", "cpy": "5.1.0", "cpy-cli": "1.0.1", - "del-cli": "1.1.0", "gh-pages": "1.0.0", "gitignore-to-glob": "0.3.0", "google-code-prettify": "1.0.5", diff --git a/scripts/archive.js b/scripts/archive.js new file mode 100644 index 0000000000..05636f0a27 --- /dev/null +++ b/scripts/archive.js @@ -0,0 +1,110 @@ +const fs = require('fs-extra'); +const path = require('path'); + +const ghPagesDir = 'gh-pages/'; +const oldVersionDir = 'gh-pages/old/'; +const versionsFilePath = 'assets/json/versions.json'; +const currentVersionFilePath = 'assets/json/current-version.json'; + +const demoSrcDir = 'demo/src'; +const demoDistDir = 'demo/dist'; +const hostname = 'ngx-bootstrap'; + +let prevVersion; +const newVersion = require('../package.json').version; + +if (!fs.existsSync(ghPagesDir)) { + throw new Error('gh-pages dir wasn\'t found. Run `npm run docs.fetch`'); +} + +if (!fs.existsSync(demoDistDir)) { + throw new Error('demo/dist dir wasn\'t found. Run `npm run demo.build`'); +} + +try { + prevVersion = require(path.join('../', ghPagesDir, currentVersionFilePath)).version; +} catch (e) { + prevVersion = require(path.join('../', demoSrcDir, currentVersionFilePath)).version; +} + +console.log('Previous version:', prevVersion); +console.log('New version:', newVersion); +const isVersionChanged = prevVersion !== newVersion; + + +fs.readdir('gh-pages') +// filter files to operate on + .then(filterFileToMove) + // for local development, if version not changed, clean gh-pages folder + .then(files => { + if (isVersionChanged) { + return files; + } + console.log('Version hasn\'t changed. Current gh-pages version will be replaced with the one from demo/dist'); + return Promise.all(files.map(file => fs.remove(path.join(ghPagesDir, file)))) + .then(() => files) + }) + // move old files to corresponding folder, skip for local dev + .then(files => { + if (!isVersionChanged) { + return files; + } + + fs.ensureDirSync(path.join(oldVersionDir, prevVersion)); + + return Promise.all(files + .map(file => ({ + from: path.join(ghPagesDir, file), + to: path.join(oldVersionDir, prevVersion, file) + })) + .map((move) => fs.rename(move.from, move.to))); + }) + // copy demo dist to gh-pages folder + .then(() => fs.copy(demoDistDir, ghPagesDir)) + // generate new version json files + .then(() => { + if (isVersionChanged) { + return generateJson(); + } + }) + .catch(console.error.bind(console)); + +function filterFileToMove(files) { + return files.filter((file) => file !== 'old' && file !== '.git'); +} + +function generateJson() { + return fs.readdir(oldVersionDir) + .then(files => { + let savedVersions = files.map(file => ({ + version: file, + url: hostname + '/old/' + file, + unprefixedUrl: 'old/' + file + })); + + savedVersions.unshift({version: 'Current', url: hostname, unprefixedUrl: ''}); + + return savedVersions; + }) + .then(versions => { + const content = JSON.stringify(versions); + + return Promise + .all(versions.map((ver) => { + if (ver.version === 'Current') { + return Promise.resolve(); + } + + const _path = path.join(oldVersionDir, ver.version, versionsFilePath); + fs.ensureFileSync(_path); + return writeFile(_path, content); + })) + .then(() => writeFile(path.join(ghPagesDir, versionsFilePath), content)) + .then(() => writeFile(path.join(ghPagesDir, currentVersionFilePath), JSON.stringify({version: newVersion}))) + .then(() => writeFile(path.join(demoSrcDir, currentVersionFilePath), JSON.stringify({version: newVersion}))); + }); +} + +function writeFile(path, content) { + return fs.writeFile(path, content, 'utf8'); +} diff --git a/demo/bs-config.js b/scripts/bs-config.js similarity index 77% rename from demo/bs-config.js rename to scripts/bs-config.js index c90664a268..2519331582 100644 --- a/demo/bs-config.js +++ b/scripts/bs-config.js @@ -1,7 +1,7 @@ module.exports = { "port": 4200, "server": { - "baseDir": "./demo/dist", + "baseDir": "./gh-pages", middleware : { 1 : require('compression')()} } }; diff --git a/scripts/fetch-docs.js b/scripts/fetch-docs.js new file mode 100644 index 0000000000..0cf5a63590 --- /dev/null +++ b/scripts/fetch-docs.js @@ -0,0 +1,17 @@ +const fs = require('fs-extra'); +const exec = require('child_process').exec; + +if (!fs.existsSync('gh-pages')) { + console.log('Cloning the latest version of gh-pages'); + runCmd("git clone -b gh-pages --single-branch --depth 1 git@github.com:valor-software/ngx-bootstrap.git gh-pages"); + return; +} +console.log('Pulling the latest version of gh-pages'); +runCmd("cd gh-pages && git pull && cd ../"); + +function runCmd(cmd) { + exec(cmd, function (err) { + if (err) console.log('exec err: ' + err); + console.log('Done'); + }); +}