diff --git a/include/config.js b/include/config.js new file mode 100644 index 0000000..e5c7092 --- /dev/null +++ b/include/config.js @@ -0,0 +1,72 @@ +/* eslint no-process-exit: "off" */ +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const crypto = require('crypto'); +const logger = require('hexo-log')(); + +module.exports = hexo => { + if (!process.argv.includes('--icarus-dont-check-config')) { + const SCHEMA_ROOT = path.join(hexo.theme_dir, 'include/schema/'); + const CONFIG_PATH = path.join(hexo.theme_dir, '_config.yml'); + + const yaml = require('../include/util/yaml'); + const { SchemaLoader } = require('../include/util/schema'); + const loader = SchemaLoader.load(require(path.join(SCHEMA_ROOT, 'config.json')), SCHEMA_ROOT); + const schema = loader.getSchema('/config.json'); + logger.info('=== Checking the configuration file ==='); + + // Generate config file if not exist + if (!process.argv.includes('--icarus-dont-generate-config')) { + if (!fs.existsSync(CONFIG_PATH)) { + logger.warn(`${CONFIG_PATH} is not found. We are creating one for you...`); + logger.info('You may add \'--icarus-dont-generate-config\' to prevent creating the configuration file.'); + const defaultValue = schema.getDefaultValue(); + fs.writeFileSync(CONFIG_PATH, defaultValue.toYaml()); + logger.info(`${CONFIG_PATH} created successfully.`); + } + } + + try { + const cfgStr = fs.readFileSync(CONFIG_PATH); + let cfg = yaml.parse(cfgStr); + // Check config version + if (!process.argv.includes('--icarus-dont-upgrade-config')) { + const migrator = new(require('../include/util/migrate'))(path.join(hexo.theme_dir, 'include/migration')); + // Upgrade config + if (migrator.isOudated(cfg.version)) { + logger.info(`Your configuration file is outdated (${cfg.version} < ${migrator.getLatestVersion()}). ` + + 'Trying to upgrade it...'); + // Backup old config + const hash = crypto.createHash('sha256').update(cfgStr).digest('hex'); + const backupPath = CONFIG_PATH + '.' + hash.substring(0, 16); + fs.writeFileSync(backupPath, cfgStr); + logger.info(`Current configurations are written up to ${backupPath}`); + // Migrate config + cfg = migrator.migrate(cfg); + // Save config + fs.writeFileSync(CONFIG_PATH, yaml.stringify(cfg)); + logger.info(`${CONFIG_PATH} upgraded successfully.`); + const defaultValue = schema.getDefaultValue(); + fs.writeFileSync(CONFIG_PATH + '.example', defaultValue.toYaml()); + logger.info(`We also created an example at ${CONFIG_PATH + '.example'} for your reference.`); + } + } + + // Check config file against schemas + const result = schema.validate(cfg); + if (result !== true) { + logger.warn('Configuration file failed one or more checks.'); + logger.warn('Icarus may still run, but you will encounter excepted results.'); + logger.warn('Here is some information for you to correct the configuration file.'); + logger.warn(util.inspect(result)); + } + } catch (e) { + logger.error(e); + logger.error(`Failed to load the configuration file ${CONFIG_PATH}.`); + logger.error('Please add \'--icarus-dont-check-config\' to your Hexo command if you'); + logger.error('wish to skip the config file checking.'); + process.exit(-1); + } + } +}; diff --git a/include/dependency.js b/include/dependency.js new file mode 100644 index 0000000..4e77910 --- /dev/null +++ b/include/dependency.js @@ -0,0 +1,31 @@ +/* eslint no-process-exit: "off" */ +const semver = require('semver'); +const logger = require('hexo-log')(); +const packageInfo = require('../package.json'); +const { yellow, red, green } = require('./util/console'); + +module.exports = hexo => { + function checkDependency(name, reqVer) { + try { + require.resolve(name); + const version = require(name + '/package.json').version; + if (!semver.satisfies(version, reqVer)) { + logger.error(`Package ${yellow(name)}'s version (${yellow(version)}) does not satisfy the required version (${red(reqVer)}).`); + return false; + } + return true; + } catch (e) { + logger.error(`Package ${yellow(name)} is not installed.`); + } + return false; + } + + logger.info('=== Checking package dependencies ==='); + const missingDeps = Object.keys(packageInfo.peerDependencies) + .filter(name => !checkDependency(name, packageInfo.peerDependencies[name])); + if (missingDeps && missingDeps.length) { + logger.error('Please install the missing dependencies your Hexo site root directory:'); + logger.error(green('npm install --save ' + missingDeps.map(name => `${name}@${packageInfo.peerDependencies[name]}`).join(' '))); + process.exit(-1); + } +}; diff --git a/include/filter/locals.js b/include/filter/locals.js index 059e1ba..b2413e5 100644 --- a/include/filter/locals.js +++ b/include/filter/locals.js @@ -48,10 +48,10 @@ module.exports = hexo => { if (page) { if ((page.layout !== 'page' || page.layout !== 'post') && ALTERNATIVE_CONFIG[page.layout]) { // load alternative config if exists - locals.config = Object.assign({}, Object.getPrototypeOf(locals).config, ALTERNATIVE_CONFIG[page.layout]); + locals.config = Object.assign({}, Object.getPrototypeOf(locals).theme || locals.theme, ALTERNATIVE_CONFIG[page.layout]); } else { // site config already merged into theme config in hexo/lib/hexo/index.js#Hexo.prototype._generateLocals() - locals.config = Object.assign({}, Object.getPrototypeOf(locals).theme); + locals.config = Object.assign({}, Object.getPrototypeOf(locals).theme || locals.theme); } // merge page configs if (page.__post === true) { diff --git a/include/register.js b/include/register.js new file mode 100644 index 0000000..39bffd0 --- /dev/null +++ b/include/register.js @@ -0,0 +1,27 @@ +const logger = require('hexo-log')(); + +module.exports = hexo => { + logger.info('=== Patching Hexo ==='); + require('../include/generator/categories')(hexo); + require('../include/generator/category')(hexo); + require('../include/generator/tags')(hexo); + require('../include/generator/insight')(hexo); + require('../include/filter/locals')(hexo); + require('../include/helper/cdn')(hexo); + require('../include/helper/page')(hexo); + require('../include/helper/thumbnail')(hexo); + + const hooks = [ + 'after_render:html', + 'after_post_render' + ]; + const filters = [ + 'hexoMetaGeneratorInject', + 'externalLinkFilter' + ]; + hooks.forEach(hook => { + hexo.extend.filter.list()[hook] + .filter(filter => filters.includes(filter.name)) + .forEach(filter => hexo.extend.filter.unregister(hook, filter)); + }); +}; diff --git a/include/util/console.js b/include/util/console.js new file mode 100644 index 0000000..edf0926 --- /dev/null +++ b/include/util/console.js @@ -0,0 +1,15 @@ +let chalk; +try { + chalk = require('chalk'); +} catch (e) { } + +module.exports = new Proxy({}, { + get(obj, prop) { + if (chalk) { + return chalk[prop]; + } + return function () { + return arguments.length === 1 ? arguments[0] : arguments; + }.bind(obj); + } +}); diff --git a/package.json b/package.json index af3825a..6794ccf 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "ajv": "^6.10.2", "bulma-stylus": "0.8.0", "deepmerge": "^4.2.2", - "hexo": "^4.0.0", + "hexo": "^4.2.0", "hexo-log": "^1.0.0", "hexo-pagination": "^1.0.0", "hexo-renderer-inferno": "^0.1.1", @@ -35,6 +35,7 @@ "inferno": "^7.3.3", "inferno-create-element": "^7.3.3", "js-yaml": "^3.13.1", - "moment": "^2.22.2" + "moment": "^2.22.2", + "semver": ">=5.0.0" } } \ No newline at end of file diff --git a/scripts/index.js b/scripts/index.js index 022fd50..3d72e90 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -1,9 +1,5 @@ -const fs = require('fs'); -const path = require('path'); -const util = require('util'); -const crypto = require('crypto'); +/* global hexo */ const logger = require('hexo-log')(); -const packageInfo = require('../package.json'); /** * Print welcome message @@ -20,122 +16,14 @@ logger.info(`======================================= /** * Check if all dependencies are installed */ -// FIXME: will not check against package version -function checkDependency(name) { - try { - require.resolve(name); - return true; - } catch (e) { - logger.error(`Package ${name} is not installed.`); - } - return false; -} - -logger.info('=== Checking package dependencies ==='); -const missingDeps = Object.keys(packageInfo.peerDependencies) - .map(checkDependency) - .some(installed => !installed); -if (missingDeps) { - logger.error('Please install the missing dependencies from the root directory of your Hexo site.'); - /* eslint no-process-exit: "off" */ - process.exit(-1); -} +require('../include/dependency')(hexo); /** * Configuration file checking and migration */ -if (!process.argv.includes('--icarus-dont-check-config')) { - const SCHEMA_ROOT = path.join(hexo.theme_dir, 'include/schema/'); - const CONFIG_PATH = path.join(hexo.theme_dir, '_config.yml'); - - const yaml = require('../include/util/yaml'); - const { SchemaLoader } = require('../include/util/schema'); - const loader = SchemaLoader.load(require(path.join(SCHEMA_ROOT, 'config.json')), SCHEMA_ROOT); - const schema = loader.getSchema('/config.json'); - logger.info('=== Checking the configuration file ==='); - - // Generate config file if not exist - if (!process.argv.includes('--icarus-dont-generate-config')) { - if (!fs.existsSync(CONFIG_PATH)) { - logger.warn(`${CONFIG_PATH} is not found. We are creating one for you...`); - logger.info('You may add \'--icarus-dont-generate-config\' to prevent creating the configuration file.'); - const defaultValue = schema.getDefaultValue(); - fs.writeFileSync(CONFIG_PATH, defaultValue.toYaml()); - logger.info(`${CONFIG_PATH} created successfully.`); - } - } - - try { - const cfgStr = fs.readFileSync(CONFIG_PATH); - let cfg = yaml.parse(cfgStr); - // Check config version - if (!process.argv.includes('--icarus-dont-upgrade-config')) { - const migrator = new(require('../include/util/migrate'))(path.join(hexo.theme_dir, 'include/migration')); - // Upgrade config - if (migrator.isOudated(cfg.version)) { - logger.info(`Your configuration file is outdated (${cfg.version} < ${migrator.getLatestVersion()}). ` - + 'Trying to upgrade it...'); - // Backup old config - const hash = crypto.createHash('sha256').update(cfgStr).digest('hex'); - const backupPath = CONFIG_PATH + '.' + hash.substring(0, 16); - fs.writeFileSync(backupPath, cfgStr); - logger.info(`Current configurations are written up to ${backupPath}`); - // Migrate config - cfg = migrator.migrate(cfg); - // Save config - fs.writeFileSync(CONFIG_PATH, yaml.stringify(cfg)); - logger.info(`${CONFIG_PATH} upgraded successfully.`); - const defaultValue = schema.getDefaultValue(); - fs.writeFileSync(CONFIG_PATH + '.example', defaultValue.toYaml()); - logger.info(`We also created an example at ${CONFIG_PATH + '.example'} for your reference.`); - } - } - - // Check config file against schemas - const result = schema.validate(cfg); - if (result !== true) { - logger.warn('Configuration file failed one or more checks.'); - logger.warn('Icarus may still run, but you will encounter excepted results.'); - logger.warn('Here is some information for you to correct the configuration file.'); - logger.warn(util.inspect(result)); - } - } catch (e) { - logger.error(e); - logger.error(`Failed to load the configuration file ${CONFIG_PATH}.`); - logger.error('Please add \'--icarus-dont-check-config\' to your Hexo command if you'); - logger.error('wish to skip the config file checking.'); - process.exit(-1); - } -} - +require('../include/config')(hexo); /** - * Register Hexo extensions + * Register Hexo extensions and remove Hexo filters that could cause OOM */ -logger.info('=== Patching Hexo ==='); -/* global hexo */ -require('../include/generator/categories')(hexo); -require('../include/generator/category')(hexo); -require('../include/generator/tags')(hexo); -require('../include/generator/insight')(hexo); -require('../include/filter/locals')(hexo); -require('../include/helper/cdn')(hexo); -require('../include/helper/page')(hexo); -require('../include/helper/thumbnail')(hexo); - -/** - * Remove Hexo filters that could cause OOM - */ -const hooks = [ - 'after_render:html', - 'after_post_render' -]; -const filters = [ - 'hexoMetaGeneratorInject', - 'externalLinkFilter' -]; -hooks.forEach(hook => { - hexo.extend.filter.list()[hook] - .filter(filter => filters.includes(filter.name)) - .forEach(filter => hexo.extend.filter.unregister(hook, filter)); -}); +require('../include/register')(hexo);