Browse Source

use promises to manage async calls

pull/1/head
Cian Butler 4 years ago
parent
commit
62bfefa29d
Signed by: butlerx GPG Key ID: B37CA765BAA89170
7 changed files with 780 additions and 242 deletions
  1. +25
    -65
      .eslintrc.js
  2. +18
    -5
      package.json
  3. +29
    -20
      src/assets.js
  4. +30
    -22
      src/checkEventJson.js
  5. +33
    -27
      src/cli.js
  6. +69
    -58
      src/generate.js
  7. +576
    -45
      yarn.lock

+ 25
- 65
.eslintrc.js View File

@@ -1,4 +1,3 @@
'use strict';
module.exports = {
'env': {
'es6' : true,
@@ -9,73 +8,34 @@ module.exports = {
'flowtype',
],
'extends': [
'eslint:recommended',
'airbnb',
'plugin:flowtype/recommended',
],
'rules' : {
'flowtype/no-types-missing-file-annotation': 0,
'strict': [
2,
'global',
],
'indent': [
'error',
2,
],
'linebreak-style': [
'error',
'unix',
],
'quotes': [
'error',
'single',
],
'semi': [
'error',
'always',
],
'comma-dangle': [
'error',
'always-multiline',
],
'no-console': [
'error', {
'allow': [
'log',
'warn',
'error'
]
}
],
'key-spacing': [
'error',
{
'multiLine': {
'beforeColon': false,
'afterColon' : true,
},
'align': {
'beforeColon': false,
'afterColon' : true,
'on' : 'colon',
'mode' : 'strict',
},
},
],
'no-var': [
'error',
],
'prefer-arrow-callback': [
'error', {
'allowNamedFunctions': true,
},
],
'prefer-const': [
'error',
{
'destructuring' : 'any',
'ignoreReadBeforeAssign': false,
},
],
'linebreak-style' : ['error', 'unix'],
'arrow-parens' : ['error', 'as-needed'],
'no-param-reassign' : ['error', { props: false }],
'func-style' : ['error', 'declaration', { allowArrowFunctions: true }],
'no-use-before-define': ['error', { functions: false }],
'no-shadow' : ['error', {
builtinGlobals: true,
hoist : 'functions',
allow : [
'resolve',
'reject',
'err',
],
}],
'no-console': ['error', {
allow: [
'warn',
'trace',
'log',
'error',
],
}],
'consistent-return': 0,
'key-spacing' : ['error', { multiLine: { beforeColon: false, afterColon: true }, align: { beforeColon: false, afterColon: true, on: 'colon', mode: 'strict' } }],
},
};

+ 18
- 5
package.json View File

@@ -1,6 +1,6 @@
{
"name": "event-page",
"version": "1.1.0",
"version": "1.2.1",
"description": "simple static site generator for events",
"main": "lib/generate.js",
"bin": {
@@ -17,11 +17,18 @@
"license": "MIT",
"scripts": {
"lint": "eslint .",
"fix": "eslint . --fix",
"build": "babel src --source-maps --out-dir lib",
"build:watch": "babel src --watch --source-maps --out-dir lib",
"format": "prettier-eslint --write \"src/**/*.js\"",
"precommit": "lint-staged && eslint .",
"prepublish": "yarn run build"
},
"lint-staged": {
"*.js": [
"prettier-eslint --write",
"git add"
]
},
"dependencies": {
"command-line-args": "^4.0.5",
"fs-extra": "^3.0.1",
@@ -45,14 +52,20 @@
"babel-preset-es2015": "6.24.1",
"babel-preset-flow": "^6.23.0",
"es6-promise": "^4",
"eslint": "3.19.0",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.2",
"eslint-config-standard": "10.2.1",
"eslint-plugin-flowtype": "^2.33.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-import": "^2.6.1",
"eslint-plugin-json": "^1.2.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-node": "^4.2.2",
"eslint-plugin-promise": "3.5.0",
"eslint-plugin-react": "^7.1.0",
"eslint-plugin-standard": "3.0.1",
"flow-bin": "^0.47.0"
"flow-bin": "^0.47.0",
"husky": "^0.14.3",
"lint-staged": "^4.0.1",
"prettier-eslint-cli": "^4.1.1"
}
}

+ 29
- 20
src/assets.js View File

@@ -5,32 +5,41 @@ const sass = require('node-sass');
/**
* Copy static assets from theme to dist
**/
function staticMove (source: string, dest: string, assets: Array<string>): void {
for (const i in assets) {
fs.copy(path.join(source, assets[i]), path.join(dest, assets[i]), err => {
if (err) console.error(err);
function staticMove(source: string, dest: string, assets: Array<string>): Promise {
return new Promise((resolve, reject) => {
assets.forEach(i => {
fs.copy(path.join(source, assets[i]), path.join(dest, assets[i]), err => {
if (err) reject(err);
});
});
}
resolve();
});
}

/**
* Compile scss assets
*/
function scss (source: string, dest: string): void {
sass.render({
file : source,
outFile : dest,
outputStyle: 'compressed',
}, (err, result) => {
if (err) console.error(err);
fs.ensureFile(dest).then(() => {
fs.writeFile(dest, result.css, (err) => {
if (err) console.error(err);
});
})
.catch(err => {
console.error(err);
});
function scss(source: string, dest: string): Promise {
return new Promise((resolve, reject) => {
sass.render(
{
file : source,
outFile : dest,
outputStyle: 'compressed',
},
(err, result) => {
if (err) reject(err);
fs
.ensureFile(dest)
.then(() => {
fs.writeFile(dest, result.css, err => {
if (err) reject(err);
resolve();
});
})
.catch(reject);
},
);
});
}



+ 30
- 22
src/checkEventJson.js View File

@@ -4,31 +4,13 @@ const fm = require('json-matter');
const path = require('path');
const val = require('jsonschema').validate;

/**
* Validate all json files obey the schema
**/
function validate (schemaPath: string, source: string) {
glob(path.join(source, '*.json'), {
ignore: 'node_modules',
}, (err, files) => {
if (err) throw err;
for (const i of files) {
fs.readFile(i, 'utf-8', (err, data) => {
if (err) throw err;
const file = fm.parse(data);
const json = file.attributes;
checkJson(i, json, schemaPath);
});
}
});
}
let schema;

/**
* Check json object against schema
**/
const checkJson = (filename: string, json: ?{}, schemaPath: string): Promise => {
return new Promise((resolve, reject) => {
const schema = require(schemaPath);
const checkJson = (filename: string, json: ?{}): Promise =>
new Promise((resolve, reject) => {
try {
val(json, schema, { throwError: true });
resolve();
@@ -36,6 +18,32 @@ const checkJson = (filename: string, json: ?{}, schemaPath: string): Promise =>
reject(err);
}
});
};

/**
* Validate all json files obey the schema
**/
function validate(schemaPath: string, source: string): Promise {
schema = require(schemaPath); // eslint-disable-line
return new Promise((resolve, reject) => {
glob(
path.join(source, '*.json'),
{
ignore: 'node_modules',
},
(err, files) => {
if (err) reject(err);
files.forEach(i => {
fs.readFile(i, 'utf-8', (err, data) => {
if (err) throw err;
const file = fm.parse(data);
const json = file.attributes;
checkJson(i, json).catch(reject);
});
});
resolve();
},
);
});
}

module.exports = { validate };

+ 33
- 27
src/cli.js View File

@@ -1,47 +1,53 @@
#!/usr/bin/env node
const fs = require('fs-extra');
const commandLineArgs = require('command-line-args');
const generate = require('./generate');

function config (): void {
fs.writeJson('./config.json', {
title : 'Amazing event',
static: ['js', 'fonts', 'images'],
}).then(() => {
console.log('Config created');
}).catch(err => {
console.error(err);
function config(): Promise {
return new Promise((resolve, reject) => {
fs
.writeJson('./config.json', {
title : 'Amazing event',
static: ['js', 'fonts', 'images'],
})
.then(() => resolve('Config created'))
.catch(reject);
});
}

function file (arg: string): void {
fs.ensureFile(arg).then(() => {
console.log(`${arg} created`);
}).catch(err => {
console.error(err);
function file(arg: string): Promise {
return new Promise((resolve, reject) => {
fs.ensureFile(arg).then(() => resolve(`${arg} created`)).catch(reject);
});
}

function folder (arg: string): void {
fs.ensureDir(arg).then(() => {
console.log(`${arg} created`);
}).catch(err => {
console.error(err);
function folder(arg: string): Promise {
return new Promise((resolve, reject) => {
fs.ensureDir(arg).then(() => resolve(`${arg} created`)).catch(reject);
});
}

const optionDefinitions = [
{ name: 'init', alias: 'i', type: Boolean },
{
name : 'init',
alias: 'i',
type : Boolean,
},
];
const options = commandLineArgs(optionDefinitions);

if (options.init) {
config();
folder('theme/js');
folder('theme/fonts');
folder('theme/images');
file('theme/templates/schedule.hbs');
file('theme/css/main.scss');
file('source/index.json');
config().then(console.log).catch(console.error);
Promise.all([
folder('theme/js'),
folder('theme/fonts'),
folder('theme/images'),
file('theme/templates/schedule.hbs'),
file('theme/css/main.scss'),
file('source/index.json'),
])
.then(console.log)
.catch(console.error);
} else {
require('./generate')();
generate().then(console.log).catch(console.error);
}

+ 69
- 58
src/generate.js View File

@@ -10,40 +10,44 @@ const object = require('lodash/fp/object');
const checkJson = require('./checkEventJson');
const assets = require('./assets');

/* eslint-disable */
// Initialise variables
const config = require(path.join(process.cwd(), 'config.json'));
const source = path.join(process.cwd(), config.source || 'source');
let outputDir = path.join(process.cwd(), 'public');
if (config.output) {
outputDir = path.join(process.cwd(), config.output.dir || 'public');
}
if (config.output) outputDir = path.join(process.cwd(), config.output.dir || 'public');

// Load all helper files
glob.sync(path.join(process.cwd(), config.theme || 'theme', config.helper || 'helper', '**', '*.js')).forEach(file => {
Handlebars.registerHelper(require(path.resolve(file)));
});
glob
.sync(path.join(process.cwd(), config.theme || 'theme', config.helper || 'helper', '**', '*.js'))
.forEach(file => Handlebars.registerHelper(require(path.resolve(file))));
/* eslint-enable */

/**
* Render pages with handle bar template
**/
function render (template: string, page: ?{}, url: string): void {
template = `${template}.hbs`;
// Load template and compile
const output = Handlebars.compile(fs.readFileSync(path.join(process.cwd(), config.theme || 'theme', config.template || 'templates', template), 'utf-8'))(page);
// Create output dir if it doesnt exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
}
let dir = outputDir;
// if home page skip else create page dir
if (url !== 'index') {
dir = path.join(dir, url);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
function render(templateFile: string, page: ?{}, url: string): Promise {
return new Promise((resolve, reject) => {
const template = `${templateFile}.hbs`;
// Load template and compile
const output = Handlebars.compile(
fs.readFileSync(
path.join(process.cwd(), config.theme || 'theme', config.template || 'templates', template),
'utf-8',
),
)(page);
// Create output dir if it doesnt exist
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
let dir = outputDir;
// if home page skip else create page dir
if (url !== 'index') {
dir = path.join(dir, url);
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
}
}
fs.writeFile(path.join(dir, 'index.html'), output, 'utf8', err => {
if (err) throw err;
fs.writeFile(path.join(dir, 'index.html'), output, 'utf8', err => {
if (err) reject(err);
resolve();
});
});
}

@@ -51,7 +55,7 @@ function render (template: string, page: ?{}, url: string): void {
* Generate a menu based on the file names in the pages dir
* index.[md|json] is called Home
**/
function generateMenu (): Array<{ title: string, url: string}> {
function generateMenu(): Array<{ title: string, url: string }> {
const menu = [];
menu.push({
title: 'Home',
@@ -68,41 +72,48 @@ function generateMenu (): Array<{ title: string, url: string}> {
return menu;
}

function generate (configArgs: ?{}): void {
object.merge(config, configArgs);
// Validate JSON against schema
const localSchema = '../schema.json';
let schemaPath;
if (config.schema) {
schemaPath = path.join(process.cwd(), config.schema);
} else {
schemaPath = localSchema;
}
checkJson.validate(schemaPath, source).then(() => {
assets.staticMove(path.join(process.cwd(), config.theme || 'theme'), outputDir, config.static);
let css = 'main.css';
if (config.output) css = config.output.css || css;
assets.scss(path.join(process.cwd(), config.theme || 'theme', 'css', config.css || 'main.scss'), path.join(outputDir, 'css', css));
// Generate Menu if not in Config
if (!config.menu) {
config.menu = generateMenu();
}
fs.readdir(source, (err, pages) => {
if (err) throw err;
for (const page of pages) {
const url = path.parse(page).name;
fs.readFile(path.join(source, page), 'utf-8', (err, data) => {
if (err) throw err;
const file = fm.parse(data);
file.site = config;
// render md in to html
file.body = marked(file.__content__);
render(file.template || 'schedule', file, url);
function generate(configArgs: ?{}): Promise {
return new Promise((resolve, reject) => {
object.merge(config, configArgs);
// Validate JSON against schema
const localSchema = '../schema.json';
let schemaPath;
if (config.schema) {
schemaPath = path.join(process.cwd(), config.schema);
} else schemaPath = localSchema;
checkJson
.validate(schemaPath, source)
.then(() => {
assets.staticMove(
path.join(process.cwd(), config.theme || 'theme'),
outputDir,
config.static,
);
let css = 'main.css';
if (config.output) css = config.output.css || css;
assets.scss(
`${process.cwd()}/${config.theme || 'theme'}/css/${config.css || 'main.scss'}`,
`${outputDir}/css/${css}`,
);
// Generate Menu if not in Config
if (!config.menu) config.menu = generateMenu();
fs.readdir(source, (err, pages) => {
if (err) reject(err);
pages.forEach(page => {
const url = path.parse(page).name;
fs.readFile(path.join(source, page), 'utf-8', (err, data) => {
if (err) reject(err);
const file = fm.parse(data);
file.site = config;
// render md in to html
file.body = marked(file.__content__); // eslint-disable-line no-underscore-dangle
render(file.template || 'schedule', file, url);
});
});
resolve('Generated');
});
}
});
}).catch((err) => {
console.error(err);
})
.catch(reject);
});
}



+ 576
- 45
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save