Gh pages deploy fix (#5669)

* fix(app):redirect after 404

* fix(app):docs building script

* fix(app):add gulpfile for docs

* fix(app): directory splitting

* fix(app): build docs script

* fix(app): build docs script

* fix(app): build docs script

* fix(app): relative path for scss

* fix(app): relative path for scss

* fix(app): relative path for scss

* fix(app):fix footer icons

* fix(app):fix footer icons

* fix(app):fix footer icons

* fix(app):config

* fix(app):remove small-social

* fix(docs): footer icons style

* fix(docs): material page index file

* fix(docs): descriptions and keyword

* fix(docs): og description

* fix(docs): og description and title

* fix(docs): dem,o branch url

* fix(docs): dynamic title for docs page

* fix(docs): remove unused script

* fix(docs): static title

Co-authored-by: Evgeny Lupanov <e.lupanov@akveo.com>
This commit is contained in:
Alex Kurbako 2020-05-08 13:01:24 +03:00 committed by GitHub
parent 690382ea35
commit 87f18ecaba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 3638 additions and 64 deletions

View file

@ -13,7 +13,6 @@
"options": {
"aot": true,
"preserveSymlinks": true,
"rebaseRootRelativeCssUrls": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",

View file

@ -15,14 +15,20 @@
<ul>
<li><strong class="title">Follow Us</strong></li>
<li class="social">
<a href="https://github.com/akveo/ngx-admin" target="_blank">
<i class="small-social-github"></i>
<a href="https://github.com/akveo/ngx-admin"
aria-label="github"
target="_blank">
<i [innerHTML]="'github-outline' | eva: { width: 30, height: 30, fill: '#f7f8fa' }"></i>
</a>
<a href="https://twitter.com/akveo_inc" target="_blank">
<i class="small-social-twitter"></i>
<a href="https://twitter.com/akveo_inc"
aria-label="twitter"
target="_blank">
<i [innerHTML]="'twitter' | eva: { width: 30, height: 30, fill: '#f7f8fa' }"></i>
</a>
<a href="https://www.facebook.com/akveo" target="_blank">
<i class="small-social-facebook"></i>
<a href="https://www.facebook.com/akveo"
aria-label="facebook"
target="_blank">
<i [innerHTML]="'facebook' | eva: { width: 35, height: 35, fill: '#f7f8fa' }"></i>
</a>
</li>
<li class="copy">

View file

@ -65,11 +65,22 @@
.social {
display: flex;
flex-direction: row;
a {
font-size: 3rem;
display: flex;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
background-color: $social-fg;
border-radius: 0.375rem;
text-decoration: none;
color: $social-fg;
margin-right: 1rem;
margin-right: 1.5rem;
line-height: 0.5;
&:last-child {
margin-right: 0;
}
}
}
}

View file

@ -4,7 +4,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
$helvetica-neue-font-path: 'assets/fonts/helvetica-neue' !default;
$helvetica-neue-font-path: '/ngx-admin/assets/fonts/helvetica-neue' !default;
@font-face {
font-family: 'Helvetica Neue Bold';

View file

@ -1,36 +0,0 @@
$small-social-font-path: '/assets/fonts/small-social' !default;
@font-face {
font-family: 'small-social';
font-display: auto;
src: url('#{$small-social-font-path}/small-social.eot?skntni');
src: url('#{$small-social-font-path}/small-social.eot?skntni#iefix') format('embedded-opentype'),
url('#{$small-social-font-path}/small-social.ttf?skntni') format('truetype'),
url('#{$small-social-font-path}/small-social.woff?skntni') format('woff'),
url('#{$small-social-font-path}/small-social.svg?skntni#small-social') format('svg');
font-weight: normal;
font-style: normal;
}
[class^='small-social-'], [class*=' small-social-'] {
font-family: 'small-social' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.small-social-facebook::before {
content: '\e900';
}
.small-social-github::before {
content: '\e901';
}
.small-social-twitter::before {
content: '\e902';
}

View file

@ -4,7 +4,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
@import 'small-social';
@import 'helvetica-neue';
@import 'themes';

View file

@ -63,7 +63,6 @@ export class NgxAdminLandingPageComponent implements OnDestroy, OnInit {
}),
filter(item => item),
tap((item: any) => {
this.metaDataService.updateTitle(`Nebular - ${item.name}`);
this.metaDataService.updateDescription(item.description);
}),
publishReplay(),

View file

@ -5,10 +5,13 @@
<title>The most popular admin dashboard based on Angular 9+ and Nebular.</title>
<meta name="description" content="Save more than $33,000 using ngx-admin for personal and commercial use. The dashboard is based on Angular 9+ and Bootstrap 4+. Completely FREE and MIT licensed.">
<meta name="keywords" content="ngx-admin LP, admin panel template, admin dashboard, bootstrap templates, angular bootstrap, admin template, free dashboard emplates, angular dashboard, angular templates free, angular template admin, angular template admin free, create a template ngular, angular template builder, angular template download, angular template dashboard, angular ecommerce template, angular emplate free download, angular template github, angular template guide, angular ng-template, angular template in component, angular template theme, angular template ui, angular template website, angular template with bootstrap, angular template 2017, angular template 2018, angular 6 template free, angular 6 template free download, angular 6 template github, angular 6 template dmin, angular 6 template, angular 6 templates free download, open source template, open source responsive admin templates, admin anel template free download, angular admin panel template, dashboard templates, angular admin panel template github backend bundle net core entity framework rest api">
<meta name="keywords" content="ngx admin LP, admin panel template, admin dashboard, bootstrap 4+ templates, angular bootstrap, admin template, free dashboard templates, angular dashboard, angular templates free, angular template admin, angular template admin free, create a template angular, angular template builder, angular template download, angular template dashboard, angular ecommerce template, angular template free download, angular template github, angular template guide, angular ng template, angular template in component, angular template theme, angular template ui, angular template website, angular template with bootstrap, angular template 2020, angular template 2019, angular 9 template free, angular 9 template free download, angular 9 template github, angular 9 template dmin, angular 9 template, angular 9 templates free download, open source template, open source responsive admin templates, admin panel template free download, angular admin panel template, dashboard templates 2020">
<meta property="og:description" content="Save more than $33,000 using ngx-admin for personal and commercial use. The dashboard is based on Angular 9+ and Bootstrap 4+. Completely FREE and MIT licensed.">
<meta property="og:title" content="The most popular admin dashboard based on Angular 9+ and Nebular.">
<meta property="og:image" content="https://i.imgur.com/tQwozJJ.jpg"/>
<meta property="og:video:width" content="190" />
<meta property="og:video:height" content="190" />
<base href="/">

View file

@ -0,0 +1,6 @@
export const structure = [
{
type: 'page',
name: 'Material',
},
];

View file

@ -6,6 +6,7 @@ export const structure = [
{
type: 'page',
name: 'What is ngx-admin?',
description: 'Ngx-admin is Angular 9+ Bootstrap 4+ admin dashboard template. Free and Open Source to bootstrap the development of your product or to learn Angular.',
children: [
{
type: 'block',

9
docs/versions.json Normal file
View file

@ -0,0 +1,9 @@
[
{
"checkoutTarget": "demo",
"name": "demo",
"path": "/ngx-admin/",
"isCurrent": true
}
]

12
gulpfile.js Normal file
View file

@ -0,0 +1,12 @@
'use strict';
/**
* Load the TypeScript compiler and then load the tasks from 'scripts/gulp'.
*/
const path = require('path');
const gulpPath = path.join(__dirname, 'scripts/gulp');
const tsconfigPath = path.join(gulpPath, 'tsconfig.json');
const tsconfig = require(tsconfigPath);
// Register TypeScript.
require('ts-node').register({ project: tsconfigPath });
require(path.join(gulpPath, 'gulpfile'));

3193
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@
"scripts": {
"ng": "ng",
"ngh": "ngh",
"gulp": "gulp",
"conventional-changelog": "conventional-changelog",
"start": "ng serve",
"build": "ng build",
@ -26,9 +27,11 @@
"pree2e": "webdriver-manager update --standalone false --gecko false",
"e2e": "ng e2e",
"docs": "compodoc -p src/tsconfig.app.json -d docs",
"docs:build": "npm run build -- docs --prod --aot --base-href /ngx-admin/",
"docs:dirs": "gulp create-docs-dirs",
"docs:prod": "npm run build -- docs --prod --aot --base-href /ngx-admin/",
"docs:build": "npm-run-all docs:prod docs:dirs",
"docs:serve": "npm start -- docs --port 4100",
"docs:gh-pages": "npm run docs:build && npm run ngh -- --dir ./docs/dist",
"docs:gh-pages": "ts-node -P ./scripts/docs/tsconfig.json ./scripts/docs/build-docs.ts",
"prepush": "npm run lint:ci",
"release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s",
"postinstall": "ngcc --properties es2015 es5 browser module main --first-only --create-ivy-entry-points"
@ -59,8 +62,10 @@
"ckeditor": "4.7.3",
"classlist.js": "1.1.20150312",
"core-js": "2.5.1",
"colors.js": "1.2.4",
"echarts": "^4.0.2",
"eva-icons": "^1.1.3",
"gulp-bump": "2.7.0",
"highlight.js": "^9.18.1",
"intl": "1.2.5",
"ionicons": "2.0.1",
@ -98,9 +103,18 @@
"@types/jasminewd2": "2.0.3",
"@types/leaflet": "1.2.3",
"@types/node": "^12.11.1",
"@types/gulp": "4.0.6",
"angular-cli-ghpages": "^0.6.2",
"codelyzer": "^5.1.2",
"conventional-changelog-cli": "1.3.4",
"doc-prsr": "2.2.0",
"gulp": "4.0.2",
"gulp-rename": "1.4.0",
"gulp-replace": "1.0.0",
"gulp-rollup": "2.13.0",
"gulp-sass": "4.0.2",
"gulp-sourcemaps": "2.6.5",
"gulp-typedoc": "2.2.3",
"husky": "0.13.3",
"jasmine-core": "2.6.4",
"jasmine-spec-reporter": "4.1.1",
@ -115,6 +129,7 @@
"rimraf": "2.6.1",
"style-loader": "^1.1.3",
"stylelint": "7.13.0",
"typedoc": "^0.12.0",
"ts-node": "3.2.2",
"tslint": "^5.7.0",
"tslint-language-service": "^0.9.9",

124
scripts/docs/build-docs.ts Normal file
View file

@ -0,0 +1,124 @@
import { join } from 'path';
import { copy, mkdirp, remove, outputFile, writeJson, readJson } from 'fs-extra';
import { generateGithubSpaScript } from './ghspa-template';
import { runCommand } from './run-command';
import { log } from './log';
import { REPO_URL, OUT_DIR, REPO_OWNER, REPO_NAME } from './config';
const WORK_DIR = join(process.cwd(), '../_DOCS_BUILD_WORK_DIR_');
const MASTER_BRANCH_DIR = join(WORK_DIR, 'MASTER');
const DOCS_VERSIONS_PATH = join(MASTER_BRANCH_DIR, 'docs/versions.json');
export interface Version {
checkoutTarget: string;
name: string;
path: string;
isCurrent?: boolean;
}
(async function () {
log(`Cleaning work dir "${WORK_DIR}"`);
await remove(WORK_DIR);
log(`Cleaning output dir "${OUT_DIR}"`);
await remove(OUT_DIR);
log(`Creating work dir "${WORK_DIR}"`);
await mkdirp(WORK_DIR);
log(`Cloning ${REPO_URL} into ${MASTER_BRANCH_DIR}`);
await runCommand(`git clone -b demo ${REPO_URL} ${MASTER_BRANCH_DIR}`, { cwd: WORK_DIR });
log('Reading versions configuration');
const config: Version[] = await import(DOCS_VERSIONS_PATH);
ensureSingleCurrentVersion(config);
log(`Versions configuration:`);
const jsonConfig = JSON.stringify(config, null, ' ');
log(jsonConfig);
log(`Building docs`);
await buildDocs(config);
log(`Adding versions.json to ${OUT_DIR}`);
await outputFile(join(OUT_DIR, 'versions.json'), jsonConfig);
log(`Deploying to ghpages`);
await deploy(OUT_DIR);
log(`Cleaning up working directory (${WORK_DIR})`);
await remove(WORK_DIR);
}());
function ensureSingleCurrentVersion(versions: Version[]) {
const currentVersion = versions.filter(v => v.isCurrent);
if (currentVersion.length !== 1) {
throw new Error(`Versions config error: Only one current version allowed.`)
}
}
async function buildDocs(versions: Version[]) {
const ghspaScript = generateGithubSpaScript(versions);
return Promise.all(versions.map((version: Version) => {
const versionDistDir = version.isCurrent
? OUT_DIR
: join(OUT_DIR, version.name);
return prepareVersion(version, versionDistDir, ghspaScript);
}));
}
async function prepareVersion(version: Version, distDir: string, ghspaScript: string) {
const projectDir = join(WORK_DIR, `${version.name}`);
await copyToBuildDir(MASTER_BRANCH_DIR, projectDir);
await checkoutVersion(version.checkoutTarget, projectDir);
await runCommand('npm install', { cwd: projectDir });
await addVersionNameToPackageJson(version.name, join(projectDir, 'package.json'));
await buildDocsApp(projectDir, version.path);
await copy(join(projectDir, 'docs/dist'), distDir);
await outputFile(join(distDir, 'assets/ghspa.js'), ghspaScript);
await remove(projectDir);
}
async function copyToBuildDir(from: string, to: string) {
try {
await mkdirp(to);
await copy(from, to);
} catch (e) {
throw new Error(`Error copying from ${from} to ${to}: ${e.message}`);
}
}
async function checkoutVersion(checkoutTarget: string, repoDirectory: string) {
try {
await runCommand(`git checkout ${checkoutTarget}`, { cwd: repoDirectory, showLog: false });
} catch (e) {
throw new Error(`Error checking out ${checkoutTarget}: ${e.message}`);
}
}
async function addVersionNameToPackageJson(versionName: string, packageJsonPath: string) {
const packageJsonObject = await readJson(packageJsonPath);
packageJsonObject.versionName = versionName;
await writeJson(packageJsonPath, packageJsonObject);
}
async function buildDocsApp(projectDir: string, baseHref: string) {
if (!baseHref.endsWith('/')) {
baseHref = baseHref + '/';
}
await runCommand(`npm run build -- docs --prod --base-href ${baseHref}`, { cwd: projectDir });
await runCommand('npm run docs:dirs', { cwd: projectDir });
}
async function deploy(distDir: string) {
await runCommand(
`npx angular-cli-ghpages --dir . --repo=https://GH_TOKEN@github.com/${REPO_OWNER}/${REPO_NAME}.git`,
{ cwd: distDir, showLog: true },
);
}

4
scripts/docs/config.ts Normal file
View file

@ -0,0 +1,4 @@
export const REPO_URL = 'https://github.com/akveo/ngx-admin.git';
export const REPO_OWNER = 'akveo';
export const REPO_NAME = 'ngx-admin';
export const OUT_DIR = 'docs/dist'; // Relative to the directory you run command

View file

@ -0,0 +1,78 @@
import { Version } from './build-docs';
function removeTrailingSlash(path: string): string {
if (path.endsWith('/')) {
return path.slice(0, -1);
}
return path;
}
export function generateGithubSpaScript(versionsToRedirect: Version[]): string {
const paths: string[] = versionsToRedirect.map(v => v.path).map(removeTrailingSlash);
return `
/**
*
* ____ _ ___ _ _ _ _ ___ ___ ____ ____ ____ ____ ____ ___ ____
* | __ | | |__| | | |__] |__] |__| | __ |___ [__ [__ |__] |__|
* |__] | | | | |__| |__] | | | |__] |___ ___] ___] | | |
*
* Easy way to enable Single Page Applications for GitHub Pages
*
* This project was released under MIT license.
*
* @link https://github.com/rafrex/spa-github-pages
* @author Rafael Pedicini <code@rafrex.com>
* @link http://websemantics.ca
* @author Adnan M.Sagar, PhD. <adnan@websemantics.ca>
*
* @param {Object} l, the document current location
* @param {Boolean} projectPages, true by default, https://help.github.com/articles/user-organization-and-project-pages
*
*/
;(function(l) {
var redirectPath;
${JSON.stringify(paths)}.forEach(function (path) {
if (l.pathname.indexOf(path) === 0) {
redirectPath = path;
}
});
if (!redirectPath) {
return;
}
/* redirect all 404 traffic to index.html */
function redirect() {
l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') + redirectPath + '/?' +
(l.pathname ? 'p=' + l.pathname.replace(/&/g, '~and~').replace(redirectPath, '') : '') +
(l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
(l.hash))
}
/* resolve 404 redirects into internal routes */
function resolve() {
if (l.search) {
var q = {};
l.search.slice(1).split('&').forEach(function(v) {
var a = v.split('=');
q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&')
});
if (q.p !== undefined) {
window.history.replaceState(null, null,
redirectPath + (q.p || '') +
(q.q ? ('?' + q.q) : '') +
l.hash
)
}
}
}
/* if current document is 404 page page, redirect to index.html otherwise resolve */
document.title === '404' ? redirect() : resolve()
}(window.location));
`;
}

3
scripts/docs/log.ts Normal file
View file

@ -0,0 +1,3 @@
export function log(message: string) {
console.log(message);
}

View file

@ -0,0 +1,37 @@
import { exec } from 'child_process';
import { promisify } from 'util';
export interface RunCommandOptions {
cwd?: string;
showLog?: boolean;
}
const DEFAULT_OPTIONS: RunCommandOptions = { cwd: process.cwd(), showLog: false };
export async function runCommand(command: string, options?: RunCommandOptions) {
let { cwd, showLog } = Object.assign({}, DEFAULT_OPTIONS, options);
try {
console.log(`Running "${command}" in "${cwd}"`);
const { stdout, stderr } = await promisify(exec)(command, { cwd });
if (showLog && stdout) {
console.log(stdout);
}
if (stderr) {
console.log(`stderr from "${command}" in "${cwd}"`);
console.warn(stderr);
}
} catch ({ message, stdout, stderr }) {
let errorMessage = `Error running "${command}" in "${cwd}": ${message}.`;
if (stdout) {
errorMessage += `\nstdout: ${stdout}`;
console.error(stdout);
}
if (stderr) {
errorMessage += `\nstderr: ${stderr}`;
}
throw new Error(errorMessage);
}
}

View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"rootDir": ".",
"module": "commonjs",
"lib": ["es2018"],
"target": "es2018",
"resolveJsonModule": true
},
"include": [
"build-docs.ts"
]
}

5
scripts/docs/tslint.json Normal file
View file

@ -0,0 +1,5 @@
{
"rules": {
"no-console": false
}
}

1
scripts/gulp/gulpfile.ts Normal file
View file

@ -0,0 +1 @@
import './tasks/docs/docs';

View file

@ -0,0 +1 @@
export const DOCS_DIST = './docs/dist';

View file

@ -0,0 +1,85 @@
import { task, series } from 'gulp';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { isAbsolute, join, resolve, sep } from 'path';
import { structure as DOCS } from '../../../../docs/structure';
import { structure as LANDING } from '../../../../docs/structure-landing';
import { DOCS_DIST } from '../config';
task('create-docs-dirs', (done) => {
const docsStructure = flatten('docs', routesTree(DOCS));
createDirsStructure(docsStructure);
const landingStructure = flatten('', routesTree(LANDING));
createDirsStructure(landingStructure);
done();
});
function routesTree(structure) {
return structure
.filter((page: any) => ['section', 'page', 'tabs'].includes(page.type))
.map((page: any) => {
if (page.type === 'tabs') {
page.children = ['overview', 'api', 'theme', 'examples']
.map(name => ({ name, type: 'page'}));
}
return page;
})
.map((page: any) => {
return {
path: prepareSlag(page.name),
children: page.children ? routesTree(page.children) : [],
}
});
}
function prepareSlag(name) {
return name.replace(/[^a-zA-Z0-9\s]+/g, '')
.replace(/\s/g, '-')
.toLowerCase();
}
function flatten(root, arr) {
let res: any[] = [];
arr.forEach((item: any) => {
const path = `${root}/${item.path}`;
res.push(path);
if (item.children) {
res = res.concat(flatten(path, item.children));
}
});
return res;
}
function createDirsStructure(dirs) {
const index = readFileSync(join(DOCS_DIST, 'index.html'), 'utf8');
dirs.forEach((dir: any) => {
const fullPath = join(DOCS_DIST, dir);
if (!existsSync(fullPath)) {
mkDirByPathSync(fullPath);
}
writeFileSync(join(fullPath, 'index.html'), index);
});
}
function mkDirByPathSync(targetDir, {isRelativeToScript = false} = {}) {
const initDir = isAbsolute(targetDir) ? sep : '';
const baseDir = isRelativeToScript ? __dirname : '.';
targetDir.split(sep).reduce((parentDir, childDir) => {
const curDir = resolve(baseDir, parentDir, childDir);
try {
mkdirSync(curDir);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
}
}
return curDir;
}, initDir);
}

View file

@ -0,0 +1,23 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"noUnusedParameters": true,
"lib": [
"es2017"
],
"module": "commonjs",
"moduleResolution": "node",
"strictNullChecks": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"types": [
"node"
],
"baseUrl": "."
},
"files": [
"gulpfile.ts"
]
}

View file

@ -11,11 +11,15 @@ export class MetadataService {
updateTitle(title: string) {
this.title.setTitle(title);
this.meta.updateTag({ property: 'og:title', content: title });
}
updateDescription(desc: string) {
this.meta.updateTag({ name: 'description', content: desc });
this.meta.updateTag({ property: 'og:description', content: desc });
}
}