diff --git a/.eslintrc.json b/.eslintrc.json index 2cf72c2756..98a0b413b2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,13 @@ -{ - "extends": "hexo", - "root": true -} +{ + "root": true, + "extends": "hexo/ts.js", + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 2020 + }, + "rules": { + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-var-requires": 0, + "node/no-missing-require": 0 + } +} diff --git a/.gitignore b/.gitignore index a62e2da0ac..24b48ae15f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,9 @@ tmp/ .idea/ yarn.lock package-lock.json +pnpm-lock.yaml .nyc_output/ coverage/ .tmp* .vscode +dist/ diff --git a/.lintstagedrc b/.lintstagedrc deleted file mode 100644 index 66886867d5..0000000000 --- a/.lintstagedrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "*.js": "git-exec-and-restage eslint --fix --" -} diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 0000000000..6953073375 --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,4 @@ +{ + "*.js": "eslint --fix", + "*.ts": "eslint --fix" +} diff --git a/lib/box/file.js b/lib/box/file.ts similarity index 64% rename from lib/box/file.js rename to lib/box/file.ts index 87ae2ba5f2..6171e430d6 100644 --- a/lib/box/file.js +++ b/lib/box/file.ts @@ -1,8 +1,15 @@ -'use strict'; - -const { readFile, readFileSync, stat, statSync } = require('hexo-fs'); +import { readFile, readFileSync, stat, statSync } from 'hexo-fs'; class File { + public source: any; + public path: any; + public params: any; + public type: any; + static TYPE_CREATE: 'create'; + static TYPE_UPDATE: 'update'; + static TYPE_SKIP: 'skip'; + static TYPE_DELETE: 'delete'; + constructor({ source, path, params, type }) { this.source = source; this.path = path; @@ -32,4 +39,4 @@ File.TYPE_UPDATE = 'update'; File.TYPE_SKIP = 'skip'; File.TYPE_DELETE = 'delete'; -module.exports = File; +export = File; diff --git a/lib/box/index.js b/lib/box/index.ts similarity index 80% rename from lib/box/index.js rename to lib/box/index.ts index 2975570b8c..a61e08e54e 100644 --- a/lib/box/index.js +++ b/lib/box/index.ts @@ -1,18 +1,35 @@ -'use strict'; - -const { join, sep } = require('path'); -const Promise = require('bluebird'); -const File = require('./file'); -const { Pattern, createSha1Hash } = require('hexo-util'); -const { createReadStream, readdir, stat, watch } = require('hexo-fs'); -const { magenta } = require('picocolors'); -const { EventEmitter } = require('events'); -const { isMatch, makeRe } = require('micromatch'); +import { join, sep } from 'path'; +import BlueBirdPromise from 'bluebird'; +import File from './file'; +import { Pattern, createSha1Hash } from 'hexo-util'; +import { createReadStream, readdir, stat, watch } from 'hexo-fs'; +import { magenta } from 'picocolors'; +import { EventEmitter } from 'events'; +import { isMatch, makeRe } from 'micromatch'; const defaultPattern = new Pattern(() => ({})); +interface Processor { + pattern: Pattern; + process: (file: File) => void; +} + class Box extends EventEmitter { - constructor(ctx, base, options) { + public options: any; + public context: any; + public base: any; + public processors: Processor[]; + public _processingFiles: any; + public watcher: any; + public Cache: any; + // TODO: replace runtime class _File + public File: any; + public ignore: any; + public source: any; + public emit: any; + public ctx: any; + + constructor(ctx, base, options?: object) { super(); this.options = Object.assign({ @@ -40,10 +57,13 @@ class Box extends EventEmitter { this.ignore = targets; this.options.ignored = targets.map(s => toRegExp(ctx, s)).filter(x => x); } + _createFileClass() { const ctx = this.context; class _File extends File { + public box: Box; + render(options) { return ctx.render.render({ path: this.source @@ -100,7 +120,7 @@ class Box extends EventEmitter { })); } - process(callback) { + process(callback?) { const { base, Cache, context: ctx } = this; return stat(base).then(stats => { @@ -121,7 +141,7 @@ class Box extends EventEmitter { _processFile(type, path) { if (this._processingFiles[path]) { - return Promise.resolve(); + return BlueBirdPromise.resolve(); } this._processingFiles[path] = true; @@ -133,7 +153,7 @@ class Box extends EventEmitter { path }); - return Promise.reduce(this.processors, (count, processor) => { + return BlueBirdPromise.reduce(this.processors, (count, processor) => { const params = processor.pattern.match(path); if (!params) return count; @@ -144,7 +164,7 @@ class Box extends EventEmitter { type }); - return Reflect.apply(Promise.method(processor.process), ctx, [file]) + return Reflect.apply(BlueBirdPromise.method(processor.process), ctx, [file]) .thenReturn(count + 1); }, 0).then(count => { if (count) { @@ -162,9 +182,9 @@ class Box extends EventEmitter { }).thenReturn(path); } - watch(callback) { + watch(callback?) { if (this.isWatching()) { - return Promise.reject(new Error('Watcher has already started.')).asCallback(callback); + return BlueBirdPromise.reject(new Error('Watcher has already started.')).asCallback(callback); } const { base } = this; @@ -218,7 +238,7 @@ function getHash(path) { const src = createReadStream(path); const hasher = createSha1Hash(); - const finishedPromise = new Promise((resolve, reject) => { + const finishedPromise = new BlueBirdPromise((resolve, reject) => { src.once('error', reject); src.once('end', resolve); }); @@ -247,9 +267,9 @@ function isIgnoreMatch(path, ignore) { } function readDirWalker(ctx, base, results, ignore, prefix) { - if (isIgnoreMatch(base, ignore)) return Promise.resolve(); + if (isIgnoreMatch(base, ignore)) return BlueBirdPromise.resolve(); - return Promise.map(readdir(base).catch(err => { + return BlueBirdPromise.map(readdir(base).catch(err => { ctx.log.error({ err }, 'Failed to read directory: %s', base); if (err && err.code === 'ENOENT') return []; throw err; @@ -260,16 +280,16 @@ function readDirWalker(ctx, base, results, ignore, prefix) { if (err && err.code === 'ENOENT') return null; throw err; }); - const prefixdPath = `${prefix}${path}`; + const prefixPath = `${prefix}${path}`; if (stats) { if (stats.isDirectory()) { - return readDirWalker(ctx, fullpath, results, ignore, `${prefixdPath}/`); + return readDirWalker(ctx, fullpath, results, ignore, `${prefixPath}/`); } if (!isIgnoreMatch(fullpath, ignore)) { - results.push(prefixdPath); + results.push(prefixPath); } } }); } -module.exports = Box; +export = Box; diff --git a/lib/extend/console.js b/lib/extend/console.ts similarity index 55% rename from lib/extend/console.js rename to lib/extend/console.ts index 3c2765ddac..c4af6322d7 100644 --- a/lib/extend/console.js +++ b/lib/extend/console.ts @@ -1,17 +1,41 @@ -'use strict'; +import Promise from 'bluebird'; +import abbrev from 'abbrev'; -const Promise = require('bluebird'); -const abbrev = require('abbrev'); +type Option = Partial<{ + usage: string; + desc: string; + init: boolean; + arguments: { + name: string; + desc: string; + }[]; + options: { + name: string; + desc: string; + }[]; +}> -/** - * Console plugin option - * @typedef {Object} Option - * @property {String} usage - The usage of a console command - * @property {{name: String, desc: String}[]} arguments - The description of each argument of a console command - * @property {{name: String, desc: String}[]} options - The description of each option of a console command - */ +interface Args { + _: string[]; + [key: string]: string | boolean | string[]; +} +type AnyFn = (args: Args) => any; +interface StoreFunction extends AnyFn { + desc?: string; + options?: Option; +} + +interface Store { + [key: string]: StoreFunction +} +interface Alias { + [key: string]: string +} class Console { + public store: Store; + public alias: Alias; + constructor() { this.store = {}; this.alias = {}; @@ -20,9 +44,9 @@ class Console { /** * Get a console plugin function by name * @param {String} name - The name of the console plugin - * @returns {Function} - The console plugin function + * @returns {StoreFunction} - The console plugin function */ - get(name) { + get(name: string): StoreFunction { name = name.toLowerCase(); return this.store[this.alias[name]]; } @@ -36,9 +60,13 @@ class Console { * @param {String} name - The name of console plugin to be registered * @param {String} desc - More detailed information about a console command * @param {Option} options - The description of each option of a console command - * @param {Function} fn - The console plugin to be registered + * @param {AnyFn} fn - The console plugin to be registered */ - register(name, desc, options, fn) { + register(name: string, fn: AnyFn): void + register(name: string, desc: string, fn: AnyFn): void + register(name: string, options: Option, fn: AnyFn): void + register(name: string, desc: string, options: Option, fn: AnyFn): void + register(name: string, desc: string | Option | AnyFn, options?: Option | AnyFn, fn?: AnyFn) { if (!name) throw new TypeError('name is required'); if (!fn) { @@ -73,13 +101,13 @@ class Console { fn = Promise.method(fn); } - const c = fn; + const c = fn as StoreFunction; this.store[name.toLowerCase()] = c; - c.options = options; - c.desc = desc; + c.options = options as Option; + c.desc = desc as string; this.alias = abbrev(Object.keys(this.store)); } } -module.exports = Console; +export = Console; diff --git a/lib/extend/deployer.js b/lib/extend/deployer.ts similarity index 58% rename from lib/extend/deployer.js rename to lib/extend/deployer.ts index 4653662f08..d42d3d854c 100644 --- a/lib/extend/deployer.js +++ b/lib/extend/deployer.ts @@ -1,8 +1,18 @@ -'use strict'; +import Promise from 'bluebird'; -const Promise = require('bluebird'); +interface StoreFunction { + (deployArg: { + type: string; + [key: string]: any + }) : any; +} +interface Store { + [key: string]: StoreFunction +} class Deployer { + public store: Store; + constructor() { this.store = {}; } @@ -11,11 +21,11 @@ class Deployer { return this.store; } - get(name) { + get(name: string) { return this.store[name]; } - register(name, fn) { + register(name: string, fn: StoreFunction) { if (!name) throw new TypeError('name is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function'); @@ -29,4 +39,4 @@ class Deployer { } } -module.exports = Deployer; +export = Deployer; diff --git a/lib/extend/filter.js b/lib/extend/filter.ts similarity index 63% rename from lib/extend/filter.js rename to lib/extend/filter.ts index 520b22668e..78113d01c2 100644 --- a/lib/extend/filter.js +++ b/lib/extend/filter.ts @@ -1,6 +1,4 @@ -'use strict'; - -const Promise = require('bluebird'); +import Promise from 'bluebird'; const typeAlias = { pre: 'before_post_render', @@ -8,20 +6,42 @@ const typeAlias = { 'after_render:html': '_after_html_render' }; +interface FilterOptions { + context?: any; + args?: any[]; +} + +interface StoreFunction { + (data?: any, ...args: any[]): any; + priority?: number; +} + +interface Store { + [key: string]: StoreFunction[] +} + class Filter { + public store: Store; + constructor() { this.store = {}; } - list(type) { + list(): Store; + list(type: string): StoreFunction[]; + list(type?: string) { if (!type) return this.store; return this.store[type] || []; } - register(type, fn, priority) { + register(fn: StoreFunction): void + register(fn: StoreFunction, priority: number): void + register(type: string, fn: StoreFunction): void + register(type: string, fn: StoreFunction, priority: number): void + register(type: string | StoreFunction, fn?: StoreFunction | number, priority?: number) { if (!priority) { if (typeof type === 'function') { - priority = fn; + priority = fn as number; fn = type; type = 'after_post_render'; } @@ -29,11 +49,11 @@ class Filter { if (typeof fn !== 'function') throw new TypeError('fn must be a function'); - type = typeAlias[type] || type; + type = typeAlias[type as string] || type; priority = priority == null ? 10 : priority; - const store = this.store[type] || []; - this.store[type] = store; + const store = this.store[type as string] || []; + this.store[type as string] = store; fn.priority = priority; store.push(fn); @@ -41,7 +61,7 @@ class Filter { store.sort((a, b) => a.priority - b.priority); } - unregister(type, fn) { + unregister(type: string, fn: StoreFunction) { if (!type) throw new TypeError('type is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function'); @@ -55,7 +75,7 @@ class Filter { if (index !== -1) list.splice(index, 1); } - exec(type, data, options = {}) { + exec(type: string, data: any[], options: FilterOptions = {}) { const filters = this.list(type); if (filters.length === 0) return Promise.resolve(data); @@ -70,7 +90,7 @@ class Filter { })).then(() => args[0]); } - execSync(type, data, options = {}) { + execSync(type: string, data: any[], options: FilterOptions = {}) { const filters = this.list(type); const filtersLen = filters.length; if (filtersLen === 0) return data; @@ -89,4 +109,4 @@ class Filter { } } -module.exports = Filter; +export = Filter; diff --git a/lib/extend/generator.js b/lib/extend/generator.js deleted file mode 100644 index 6a0c087703..0000000000 --- a/lib/extend/generator.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -const Promise = require('bluebird'); - -class Generator { - constructor() { - this.id = 0; - this.store = {}; - } - - list() { - return this.store; - } - - get(name) { - return this.store[name]; - } - - register(name, fn) { - if (!fn) { - if (typeof name === 'function') { - fn = name; - name = `generator-${this.id++}`; - } else { - throw new TypeError('fn must be a function'); - } - } - - if (fn.length > 1) fn = Promise.promisify(fn); - this.store[name] = Promise.method(fn); - } -} - -module.exports = Generator; diff --git a/lib/extend/generator.ts b/lib/extend/generator.ts new file mode 100644 index 0000000000..6360d1eb2c --- /dev/null +++ b/lib/extend/generator.ts @@ -0,0 +1,59 @@ +import Promise from 'bluebird'; + +interface BaseObj { + path: string; + data: any; + layout?: string; +} +type ReturnType = BaseObj | BaseObj[]; +type GeneratorReturnType = ReturnType | Promise; + +interface GeneratorFunction { + (locals: object): GeneratorReturnType; +} + +type StoreFunctionReturn = Promise; + +interface StoreFunction { + (locals: object): StoreFunctionReturn; +} + +interface Store { + [key: string]: StoreFunction +} + +class Generator { + public id: number; + public store: Store; + + constructor() { + this.id = 0; + this.store = {}; + } + + list() { + return this.store; + } + + get(name: string) { + return this.store[name]; + } + + register(fn: GeneratorFunction): void + register(name: string, fn: GeneratorFunction): void + register(name: string | GeneratorFunction, fn?: GeneratorFunction) { + if (!fn) { + if (typeof name === 'function') { // fn + fn = name; + name = `generator-${this.id++}`; + } else { + throw new TypeError('fn must be a function'); + } + } + + if (fn.length > 1) fn = Promise.promisify(fn); + this.store[name as string] = Promise.method(fn); + } +} + +export = Generator; diff --git a/lib/extend/helper.js b/lib/extend/helper.ts similarity index 57% rename from lib/extend/helper.js rename to lib/extend/helper.ts index d8c6156d4c..7903557c97 100644 --- a/lib/extend/helper.js +++ b/lib/extend/helper.ts @@ -1,32 +1,41 @@ -'use strict'; +interface StoreFunction { + (...args: any[]): string; +} + +interface Store { + [key: string]: StoreFunction +} + class Helper { + public store: Store; + constructor() { this.store = {}; } /** - * @returns {Object} - The plugin store + * @returns {Store} - The plugin store */ - list() { + list(): Store { return this.store; } /** * Get helper plugin function by name * @param {String} name - The name of the helper plugin - * @returns {Function} + * @returns {StoreFunction} */ - get(name) { + get(name: string): StoreFunction { return this.store[name]; } /** * Register a helper plugin * @param {String} name - The name of the helper plugin - * @param {Function} fn - The helper plugin function + * @param {StoreFunction} fn - The helper plugin function */ - register(name, fn) { + register(name: string, fn: StoreFunction) { if (!name) throw new TypeError('name is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function'); @@ -34,4 +43,4 @@ class Helper { } } -module.exports = Helper; +export = Helper; diff --git a/lib/extend/index.js b/lib/extend/index.js deleted file mode 100644 index f4ddd1a2e5..0000000000 --- a/lib/extend/index.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -const Console = require('./console'); -const Deployer = require('./deployer'); -const Filter = require('./filter'); -const Generator = require('./generator'); -const Helper = require('./helper'); -const Highlight = require('./syntax_highlight'); -const Injector = require('./injector'); -const Migrator = require('./migrator'); -const Processor = require('./processor'); -const Renderer = require('./renderer'); -const Tag = require('./tag'); - -module.exports = { - Console, Deployer, Filter, Generator, Helper, Highlight, Injector, Migrator, Processor, Renderer, Tag -}; diff --git a/lib/extend/index.ts b/lib/extend/index.ts new file mode 100644 index 0000000000..2c4dac82bc --- /dev/null +++ b/lib/extend/index.ts @@ -0,0 +1,11 @@ +export { default as Console } from './console'; +export { default as Deployer } from './deployer'; +export { default as Filter } from './filter'; +export { default as Generator } from './generator'; +export { default as Helper } from './helper'; +export { default as Highlight } from './syntax_highlight'; +export { default as Injector } from './injector'; +export { default as Migrator } from './migrator'; +export { default as Processor } from './processor'; +export { default as Renderer } from './renderer'; +export { default as Tag } from './tag'; diff --git a/lib/extend/injector.js b/lib/extend/injector.ts similarity index 85% rename from lib/extend/injector.js rename to lib/extend/injector.ts index 74689d3202..fd1d09acdd 100644 --- a/lib/extend/injector.js +++ b/lib/extend/injector.ts @@ -1,8 +1,18 @@ -'use strict'; +import { Cache } from 'hexo-util'; -const { Cache } = require('hexo-util'); +type Entry = 'head_begin' | 'head_end' | 'body_begin' | 'body_end'; + +type Store = { + [key in Entry]: { + [key: string]: Set; + }; +}; class Injector { + public store: Store; + public cache: any; + public page: any; + constructor() { this.store = { head_begin: {}, @@ -18,21 +28,21 @@ class Injector { return this.store; } - get(entry, to = 'default') { + get(entry: Entry, to = 'default') { return Array.from(this.store[entry][to] || []); } - getText(entry, to = 'default') { + getText(entry: Entry, to = 'default') { const arr = this.get(entry, to); if (!arr || !arr.length) return ''; return arr.join(''); } - getSize(entry) { + getSize(entry: Entry) { return this.cache.apply(`${entry}-size`, Object.keys(this.store[entry]).length); } - register(entry, value, to = 'default') { + register(entry: Entry, value: string | (() => string), to = 'default') { if (!entry) throw new TypeError('entry is required'); if (typeof value === 'function') value = value(); @@ -99,4 +109,4 @@ class Injector { } } -module.exports = Injector; +export = Injector; diff --git a/lib/extend/migrator.js b/lib/extend/migrator.ts similarity index 63% rename from lib/extend/migrator.js rename to lib/extend/migrator.ts index 12a25bc5a6..2a149f191a 100644 --- a/lib/extend/migrator.js +++ b/lib/extend/migrator.ts @@ -1,8 +1,15 @@ -'use strict'; +import Promise from 'bluebird'; +interface StoreFunction { + (args: any): any +} -const Promise = require('bluebird'); +interface Store { + [key: string]: StoreFunction +} class Migrator { + public store: Store; + constructor() { this.store = {}; } @@ -11,11 +18,11 @@ class Migrator { return this.store; } - get(name) { + get(name: string) { return this.store[name]; } - register(name, fn) { + register(name: string, fn: StoreFunction) { if (!name) throw new TypeError('name is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function'); @@ -29,4 +36,4 @@ class Migrator { } } -module.exports = Migrator; +export = Migrator; diff --git a/lib/extend/processor.js b/lib/extend/processor.js deleted file mode 100644 index da2efbd7e6..0000000000 --- a/lib/extend/processor.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -const Promise = require('bluebird'); -const { Pattern } = require('hexo-util'); - -class Processor { - constructor() { - this.store = []; - } - - list() { - return this.store; - } - - register(pattern, fn) { - if (!fn) { - if (typeof pattern === 'function') { - fn = pattern; - pattern = /(.*)/; - } else { - throw new TypeError('fn must be a function'); - } - } - - if (fn.length > 1) { - fn = Promise.promisify(fn); - } else { - fn = Promise.method(fn); - } - - this.store.push({ - pattern: new Pattern(pattern), - process: fn - }); - } -} - -module.exports = Processor; diff --git a/lib/extend/processor.ts b/lib/extend/processor.ts new file mode 100644 index 0000000000..0af6998494 --- /dev/null +++ b/lib/extend/processor.ts @@ -0,0 +1,51 @@ +import Promise from 'bluebird'; +import { Pattern } from 'hexo-util'; +import type File from '../box/file'; + +interface StoreFunction { + (file: File): any +} + +type Store = { + pattern: Pattern; + process: StoreFunction + }[]; + +type patternType = Exclude[0], ((str: string) => string)>; +class Processor { + public store: Store; + + constructor() { + this.store = []; + } + + list() { + return this.store; + } + + register(fn: StoreFunction): void; + register(pattern: patternType, fn: StoreFunction): void; + register(pattern: patternType | StoreFunction, fn?: StoreFunction) { + if (!fn) { + if (typeof pattern === 'function') { + fn = pattern; + pattern = /(.*)/; + } else { + throw new TypeError('fn must be a function'); + } + } + + if (fn.length > 1) { + fn = Promise.promisify(fn); + } else { + fn = Promise.method(fn); + } + + this.store.push({ + pattern: new Pattern(pattern as patternType), + process: fn + }); + } +} + +export = Processor; diff --git a/lib/extend/renderer.js b/lib/extend/renderer.js deleted file mode 100644 index 994b1270f9..0000000000 --- a/lib/extend/renderer.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; - -const { extname } = require('path'); -const Promise = require('bluebird'); - -const getExtname = str => { - if (typeof str !== 'string') return ''; - - const ext = extname(str) || str; - return ext.startsWith('.') ? ext.slice(1) : ext; -}; - -class Renderer { - constructor() { - this.store = {}; - this.storeSync = {}; - } - - list(sync) { - return sync ? this.storeSync : this.store; - } - - get(name, sync) { - const store = this[sync ? 'storeSync' : 'store']; - - return store[getExtname(name)] || store[name]; - } - - isRenderable(path) { - return Boolean(this.get(path)); - } - - isRenderableSync(path) { - return Boolean(this.get(path, true)); - } - - getOutput(path) { - const renderer = this.get(path); - return renderer ? renderer.output : ''; - } - - register(name, output, fn, sync) { - if (!name) throw new TypeError('name is required'); - if (!output) throw new TypeError('output is required'); - if (typeof fn !== 'function') throw new TypeError('fn must be a function'); - - name = getExtname(name); - output = getExtname(output); - - if (sync) { - this.storeSync[name] = fn; - this.storeSync[name].output = output; - - this.store[name] = Promise.method(fn); - this.store[name].disableNunjucks = fn.disableNunjucks; - } else { - if (fn.length > 2) fn = Promise.promisify(fn); - this.store[name] = fn; - } - - this.store[name].output = output; - this.store[name].compile = fn.compile; - } -} - -module.exports = Renderer; diff --git a/lib/extend/renderer.ts b/lib/extend/renderer.ts new file mode 100644 index 0000000000..c70e054f18 --- /dev/null +++ b/lib/extend/renderer.ts @@ -0,0 +1,112 @@ +import { extname } from 'path'; +import Promise from 'bluebird'; + +const getExtname = (str: string) => { + if (typeof str !== 'string') return ''; + + const ext = extname(str) || str; + return ext.startsWith('.') ? ext.slice(1) : ext; +}; + +interface StoreSyncFunction { + ( + data: { + path?: string; + text: string; + }, + options: object, + // callback: (err: Error, value: string) => any + ): any; + output?: string; + compile?: (local: object) => string; +} +interface StoreFunction { + ( + data: { + path?: string; + text: string; + }, + options: object, + ): Promise; + ( + data: { + path?: string; + text: string; + }, + options: object, + callback: (err: Error, value: string) => any + ): void; + output?: string; + compile?: (local: object) => string; + disableNunjucks?: boolean; +} + +interface SyncStore { + [key: string]: StoreSyncFunction; +} +interface Store { + [key: string]: StoreFunction; +} + +class Renderer { + public store: Store; + public storeSync: SyncStore; + + constructor() { + this.store = {}; + this.storeSync = {}; + } + + list(sync: boolean) { + return sync ? this.storeSync : this.store; + } + + get(name: string, sync?: boolean) { + const store = this[sync ? 'storeSync' : 'store']; + + return store[getExtname(name)] || store[name]; + } + + isRenderable(path: string) { + return Boolean(this.get(path)); + } + + isRenderableSync(path: string) { + return Boolean(this.get(path, true)); + } + + getOutput(path: string) { + const renderer = this.get(path); + return renderer ? renderer.output : ''; + } + + register(name: string, output: string, fn: StoreFunction): void; + register(name: string, output: string, fn: StoreFunction, sync: false): void; + register(name: string, output: string, fn: StoreSyncFunction, sync: true): void; + register(name: string, output: string, fn: StoreFunction | StoreSyncFunction, sync: boolean): void; + register(name: string, output: string, fn: StoreFunction | StoreSyncFunction, sync?: boolean) { + if (!name) throw new TypeError('name is required'); + if (!output) throw new TypeError('output is required'); + if (typeof fn !== 'function') throw new TypeError('fn must be a function'); + + name = getExtname(name); + output = getExtname(output); + + if (sync) { + this.storeSync[name] = fn; + this.storeSync[name].output = output; + + this.store[name] = Promise.method(fn); + // eslint-disable-next-line no-extra-parens + this.store[name].disableNunjucks = (fn as StoreFunction).disableNunjucks; + } else { + if (fn.length > 2) fn = Promise.promisify(fn); + this.store[name] = fn; + } + + this.store[name].output = output; + this.store[name].compile = fn.compile; + } +} + +export = Renderer; diff --git a/lib/extend/syntax_highlight.js b/lib/extend/syntax_highlight.js deleted file mode 100644 index 56d83ba2a8..0000000000 --- a/lib/extend/syntax_highlight.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -class SyntaxHighlight { - constructor() { - this.store = {}; - } - - register(name, fn) { - if (typeof fn !== 'function') throw new TypeError('fn must be a function'); - - this.store[name] = fn; - } - - query(name) { - return name && this.store[name]; - } - - exec(name, options) { - const fn = this.store[name]; - - if (!fn) throw new TypeError(`syntax highlighter ${name} is not registered`); - const ctx = options.context; - const args = options.args || []; - - return Reflect.apply(fn, ctx, args); - } -} - -module.exports = SyntaxHighlight; diff --git a/lib/extend/syntax_highlight.ts b/lib/extend/syntax_highlight.ts new file mode 100644 index 0000000000..419a950f24 --- /dev/null +++ b/lib/extend/syntax_highlight.ts @@ -0,0 +1,63 @@ +import type Hexo from '../hexo'; + +export interface HighlightOptions { + lang: string | undefined, + caption: string | undefined, + lines_length: number, + + // plugins/filter/before_post_render/backtick_code_block + firstLineNumber?: string | number + + // plugins/tag/code.ts + language_attr?: boolean | undefined; + firstLine?: number; + line_number?: boolean | undefined; + line_threshold?: number | undefined; + mark?: number[]; + wrap?: boolean | undefined; + +} + +interface HighlightExecArgs { + context?: Hexo; + args?: [string, HighlightOptions]; +} + +interface StoreFunction { + (content: string, options: HighlightOptions): string; + priority?: number; +} + +interface Store { + [key: string]: StoreFunction +} + +class SyntaxHighlight { + public store: Store; + + constructor() { + this.store = {}; + } + + register(name: string, fn: StoreFunction) { + if (typeof fn !== 'function') throw new TypeError('fn must be a function'); + + this.store[name] = fn; + } + + query(name: string) { + return name && this.store[name]; + } + + exec(name: string, options: HighlightExecArgs): string { + const fn = this.store[name]; + + if (!fn) throw new TypeError(`syntax highlighter ${name} is not registered`); + const ctx = options.context; + const args = options.args || []; + + return Reflect.apply(fn, ctx, args); + } +} + +export default SyntaxHighlight; diff --git a/lib/extend/tag.js b/lib/extend/tag.ts similarity index 79% rename from lib/extend/tag.js rename to lib/extend/tag.ts index f633781513..f2fcc3d325 100644 --- a/lib/extend/tag.js +++ b/lib/extend/tag.ts @@ -1,15 +1,23 @@ -'use strict'; - -const { stripIndent } = require('hexo-util'); -const { cyan, magenta, red, bold } = require('picocolors'); -const { Environment } = require('nunjucks'); -const Promise = require('bluebird'); +import { stripIndent } from 'hexo-util'; +import { cyan, magenta, red, bold } from 'picocolors'; +import { Environment } from 'nunjucks'; +import Promise from 'bluebird'; const rSwigRawFullBlock = /{% *raw *%}/; const rCodeTag = /]*>[\s\S]+?<\/code>/g; const escapeSwigTag = str => str.replace(/{/g, '{').replace(/}/g, '}'); +interface TagFunction { + (args: any[], content: string): string; +} +interface AsyncTagFunction { + (args: any[], content: string): Promise; +} + class NunjucksTag { - constructor(name, fn) { + public tags: string[]; + public fn: TagFunction | AsyncTagFunction; + + constructor(name: string, fn: TagFunction | AsyncTagFunction) { this.tags = [name]; this.fn = fn; } @@ -49,11 +57,11 @@ class NunjucksTag { return node; } - run(context, args) { + run(context, args, body, callback) { return this._run(context, args, ''); } - _run(context, args, body) { + _run(context, args, body): any { return Reflect.apply(this.fn, context.ctx, [args, body]); } } @@ -77,7 +85,7 @@ class NunjucksBlock extends NunjucksTag { return body; } - run(context, args, body) { + run(context, args, body, callback) { return this._run(context, args, trimBody(body)); } } @@ -153,6 +161,12 @@ const getContext = (lines, errLine, location, type) => { return message; }; +class NunjucksError extends Error { + line?: number; + location?: string; + type?: string; +} + /** * Provide context for Nunjucks error * @param {Error} err Nunjucks error @@ -168,45 +182,57 @@ const formatNunjucksError = (err, input, source = '') => { if (isNaN(errLine)) return err; // trim useless info from Nunjucks Error - const splited = err.message.split('\n'); + const splitted = err.message.split('\n'); - const e = new Error(); + const e = new NunjucksError(); e.name = 'Nunjucks Error'; e.line = errLine; - e.location = splited[0]; - e.type = splited[1].trim(); + e.location = splitted[0]; + e.type = splitted[1].trim(); e.message = getContext(input.split(/\r?\n/), errLine, e.location, e.type).join('\n'); return e; }; +type RegisterOptions = { + async?: boolean; + ends?: boolean; +} + class Tag { + public env: any; + public source: any; + constructor() { this.env = new Environment(null, { autoescape: false }); } - register(name, fn, options) { + register(name: string, fn: TagFunction): void + register(name: string, fn: TagFunction, ends: boolean): void + register(name: string, fn: TagFunction, options: RegisterOptions): void + register(name: string, fn: TagFunction, options?: RegisterOptions | boolean) { if (!name) throw new TypeError('name is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function'); if (options == null || typeof options === 'boolean') { - options = { ends: options }; + options = { ends: options as boolean }; } - let tag; + let tag: NunjucksTag; if (options.async) { + let asyncFn: AsyncTagFunction; if (fn.length > 2) { - fn = Promise.promisify(fn); + asyncFn = Promise.promisify(fn); } else { - fn = Promise.method(fn); + asyncFn = Promise.method(fn); } if (options.ends) { - tag = new NunjucksAsyncBlock(name, fn); + tag = new NunjucksAsyncBlock(name, asyncFn); } else { - tag = new NunjucksAsyncTag(name, fn); + tag = new NunjucksAsyncTag(name, asyncFn); } } else if (options.ends) { tag = new NunjucksBlock(name, fn); @@ -225,7 +251,7 @@ class Tag { if (env.hasExtension(name)) env.removeExtension(name); } - render(str, options = {}, callback) { + render(str, options: { source?: string } = {}, callback) { if (!callback && typeof options === 'function') { callback = options; options = {}; @@ -252,4 +278,4 @@ class Tag { } } -module.exports = Tag; +export = Tag; diff --git a/lib/hexo/default_config.js b/lib/hexo/default_config.ts similarity index 96% rename from lib/hexo/default_config.js rename to lib/hexo/default_config.ts index 3c887a2400..5e01f5781d 100644 --- a/lib/hexo/default_config.js +++ b/lib/hexo/default_config.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = { +export = { // Site title: 'Hexo', subtitle: '', @@ -53,7 +51,8 @@ module.exports = { prismjs: { preprocess: true, line_number: true, - tab_replace: '' + tab_replace: '', + exclude_languages: [] }, // Category & Tag default_category: 'uncategorized', diff --git a/lib/hexo/index.js b/lib/hexo/index.ts similarity index 75% rename from lib/hexo/index.js rename to lib/hexo/index.ts index 8f6fe53344..2d347c7bde 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.ts @@ -1,29 +1,41 @@ -'use strict'; - -const Promise = require('bluebird'); -const { sep, join, dirname } = require('path'); -const tildify = require('tildify'); -const Database = require('warehouse').default; -const { magenta, underline } = require('picocolors'); -const { EventEmitter } = require('events'); -const { readFile } = require('hexo-fs'); -const Module = require('module'); -const { runInThisContext } = require('vm'); -const { version } = require('../../package.json'); -const { logger } = require('hexo-log'); -const { Console, Deployer, Filter, Generator, Helper, Highlight, Injector, Migrator, Processor, Renderer, Tag } = require('../extend'); -const Render = require('./render'); -const registerModels = require('./register_models'); -const Post = require('./post'); -const Scaffold = require('./scaffold'); -const Source = require('./source'); -const Router = require('./router'); -const Theme = require('../theme'); -const Locals = require('./locals'); -const defaultConfig = require('./default_config'); -const loadDatabase = require('./load_database'); -const multiConfigPath = require('./multi_config_path'); -const { deepMerge, full_url_for } = require('hexo-util'); +import Promise from 'bluebird'; +import { sep, join, dirname } from 'path'; +import tildify from 'tildify'; +import Database from 'warehouse'; +import { magenta, underline } from 'picocolors'; +import { EventEmitter } from 'events'; +import { readFile } from 'hexo-fs'; +import Module from 'module'; +import { runInThisContext } from 'vm'; +const {version} = require('../../package.json'); +import logger from 'hexo-log'; + +import { + Console, + Deployer, + Filter, + Generator, + Helper, + Highlight, + Injector, + Migrator, + Processor, + Renderer, + Tag +} from '../extend'; + +import Render from './render'; +import registerModels from './register_models'; +import Post from './post'; +import Scaffold from './scaffold'; +import Source from './source'; +import Router from './router'; +import Theme from '../theme'; +import Locals from './locals'; +import defaultConfig from './default_config'; +import loadDatabase from './load_database'; +import multiConfigPath from './multi_config_path'; +import { deepMerge, full_url_for } from 'hexo-util'; let resolveSync; // = require('resolve'); const libDir = dirname(__dirname); @@ -81,20 +93,100 @@ const createLoadThemeRoute = function(generatorResult, locals, ctx) { }; }; -function debounce(func, wait) { - let timeout; +function debounce(func: () => void, wait: number) { + let timeout: NodeJS.Timeout; return function() { - const context = this; - const args = arguments; clearTimeout(timeout); timeout = setTimeout(() => { - func.apply(context, args); + func.apply(this); }, wait); }; } +interface Args { + debug?: any; + safe?: any; + silent?: any; + _?: any[]; + output?: any; + config?: any; +} + +interface Query { + date?: any; + published?: boolean; +} + +interface Extend { + console: Console, + deployer: Deployer, + filter: Filter, + generator: Generator, + helper: Helper, + highlight: Highlight, + injector: Injector, + migrator: Migrator, + processor: Processor, + renderer: Renderer, + tag: Tag +} + +type DefaultConfigType = typeof defaultConfig; +interface Config extends DefaultConfigType { + [key: string]: any; +} + +// Node.js internal APIs +declare module 'module' { + function _nodeModulePaths(path: string): string[]; + function _resolveFilename(request: string, parent: Module, isMain?: any, options?: any): string; + const _extensions: NodeJS.RequireExtensions, + _cache: any; +} + class Hexo extends EventEmitter { - constructor(base = process.cwd(), args = {}) { + public base_dir: string; + public public_dir: string; + public source_dir: string; + public plugin_dir: string; + public script_dir: string; + public scaffold_dir: string; + public theme_dir: string; + public theme_script_dir: string; + public env: any; + public extend: Extend; + public config: Config; + public log: ReturnType; + public render: Render; + public route: Router; + public post: Post; + public scaffold: Scaffold; + public _dbLoaded: boolean; + public _isGenerating: boolean; + public database: Database; + public config_path: string; + public source: Source; + public theme: Theme; + public locals: Locals; + public version: string; + public _watchBox: () => void; + public page: any; + public path: any; + public url: any; + public layout: any; + public view_dir: any; + public site: any; + public args: any; + public cache: any; + public alias: any; + public data: any; + public lib_dir: string; + public core_dir: string; + static lib_dir: string; + static core_dir: string; + static version: string; + + constructor(base = process.cwd(), args: Args = {}) { super(); this.base_dir = base + sep; @@ -169,7 +261,7 @@ class Hexo extends EventEmitter { this.source = new Source(this); this.theme = new Theme(this); - this.locals = new Locals(this); + this.locals = new Locals(); this._bindLocals(); } @@ -178,7 +270,7 @@ class Hexo extends EventEmitter { const { locals } = this; locals.set('posts', () => { - const query = {}; + const query: Query = {}; if (!this.config.future) { query.date = { $lte: Date.now() }; @@ -192,7 +284,7 @@ class Hexo extends EventEmitter { }); locals.set('pages', () => { - const query = {}; + const query: Query = {}; if (!this.config.future) { query.date = { $lte: Date.now() }; @@ -229,7 +321,7 @@ class Hexo extends EventEmitter { require('../plugins/injector')(this); require('../plugins/processor')(this); require('../plugins/renderer')(this); - require('../plugins/tag')(this); + require('../plugins/tag').default(this); // Load config return Promise.each([ @@ -251,7 +343,8 @@ class Hexo extends EventEmitter { const c = this.extend.console.get(name); - if (c) return Reflect.apply(c, this, [args]).asCallback(callback); + // eslint-disable-next-line no-extra-parens + if (c) return (Reflect.apply(c, this, [args]) as any).asCallback(callback); return Promise.reject(new Error(`Console \`${name}\` has not been registered yet!`)); } @@ -277,7 +370,7 @@ class Hexo extends EventEmitter { } } - loadPlugin(path, callback) { + loadPlugin(path: string, callback: (...args: any[]) => any) { return readFile(path).then(script => { // Based on: https://github.com/joyent/node/blob/v0.10.33/src/node.js#L516 const module = new Module(path); @@ -294,7 +387,7 @@ class Hexo extends EventEmitter { req.extensions = Module._extensions; req.cache = Module._cache; - script = `(function(exports, require, module, __filename, __dirname, hexo){${script}\n});`; + script = `(async function(exports, require, module, __filename, __dirname, hexo){${script}\n});`; const fn = runInThisContext(script, path); @@ -372,6 +465,19 @@ class Hexo extends EventEmitter { const localsObj = this.locals.toObject(); class Locals { + page: { + path: string; + }; + path: string; + url: string; + config: object; + theme: object; + layout: string; + env: any; + view_dir: string; + site: object; + cache?: boolean; + constructor(path, locals) { this.page = { ...locals }; if (this.page.path == null) this.page.path = path; @@ -406,7 +512,7 @@ class Hexo extends EventEmitter { }, []); } - _routerReflesh(runningGenerators, useCache) { + _routerRefresh(runningGenerators, useCache) { const { route } = this; const routeList = route.list(); const Locals = this._generateLocals(); @@ -439,7 +545,7 @@ class Hexo extends EventEmitter { }); } - _generate(options = {}) { + _generate(options: { cache?: boolean } = {}) { if (this._isGenerating) return; const useCache = options.cache; @@ -449,8 +555,8 @@ class Hexo extends EventEmitter { this.emit('generateBefore'); // Run before_generate filters - return this.execFilter('before_generate', this.locals.get('data'), { context: this }) - .then(() => this._routerReflesh(this._runGenerators(), useCache)).then(() => { + return this.execFilter('before_generate', null, { context: this }) + .then(() => this._routerRefresh(this._runGenerators(), useCache)).then(() => { this.emit('generateAfter'); // Run after_generate filters @@ -492,4 +598,11 @@ Hexo.prototype.core_dir = Hexo.core_dir; Hexo.version = version; Hexo.prototype.version = Hexo.version; -module.exports = Hexo; +// define global variable +// this useful for plugin written in typescript +declare global { + // eslint-disable-next-line one-var + const hexo: Hexo; +} + +export = Hexo; diff --git a/lib/hexo/load_config.js b/lib/hexo/load_config.ts similarity index 84% rename from lib/hexo/load_config.js rename to lib/hexo/load_config.ts index 81528e765c..231e285f02 100644 --- a/lib/hexo/load_config.js +++ b/lib/hexo/load_config.ts @@ -1,15 +1,13 @@ -'use strict'; - -const { sep, resolve, join, parse } = require('path'); -const tildify = require('tildify'); -const Theme = require('../theme'); -const Source = require('./source'); -const { exists, readdir } = require('hexo-fs'); -const { magenta } = require('picocolors'); -const { deepMerge } = require('hexo-util'); -const validateConfig = require('./validate_config'); - -module.exports = async ctx => { +import { sep, resolve, join, parse } from 'path'; +import tildify from 'tildify'; +import Theme from '../theme'; +import Source from './source'; +import { exists, readdir } from 'hexo-fs'; +import { magenta } from 'picocolors'; +import { deepMerge } from 'hexo-util'; +import validateConfig from './validate_config'; + +export = async ctx => { if (!ctx.env.init) return; const baseDir = ctx.base_dir; diff --git a/lib/hexo/load_database.js b/lib/hexo/load_database.ts similarity index 65% rename from lib/hexo/load_database.js rename to lib/hexo/load_database.ts index b3d59a036e..c64719c929 100644 --- a/lib/hexo/load_database.js +++ b/lib/hexo/load_database.ts @@ -1,16 +1,14 @@ -'use strict'; +import { exists, unlink } from 'hexo-fs'; +import Promise from 'bluebird'; -const fs = require('hexo-fs'); -const Promise = require('bluebird'); - -module.exports = ctx => { +export = ctx => { if (ctx._dbLoaded) return Promise.resolve(); const db = ctx.database; const { path } = db.options; const { log } = ctx; - return fs.exists(path).then(exist => { + return exists(path).then(exist => { if (!exist) return; log.debug('Loading database.'); @@ -19,6 +17,6 @@ module.exports = ctx => { ctx._dbLoaded = true; }).catch(() => { log.error('Database load failed. Deleting database.'); - return fs.unlink(path); + return unlink(path); }); }; diff --git a/lib/hexo/load_plugins.js b/lib/hexo/load_plugins.ts similarity index 90% rename from lib/hexo/load_plugins.js rename to lib/hexo/load_plugins.ts index 010127c74e..c1dcb29067 100644 --- a/lib/hexo/load_plugins.js +++ b/lib/hexo/load_plugins.ts @@ -1,11 +1,9 @@ -'use strict'; +import { join } from 'path'; +import { exists, readFile, listDir } from 'hexo-fs'; +import Promise from 'bluebird'; +import { magenta } from 'picocolors'; -const { join } = require('path'); -const { exists, readFile, listDir } = require('hexo-fs'); -const Promise = require('bluebird'); -const { magenta } = require('picocolors'); - -module.exports = ctx => { +export = ctx => { if (!ctx.env.init || ctx.env.safe) return; return loadModules(ctx).then(() => loadScripts(ctx)); @@ -20,7 +18,7 @@ function loadModuleList(ctx, basedir) { // Read package.json and find dependencies return readFile(packagePath).then(content => { - const json = JSON.parse(content); + const json = JSON.parse(content as string); const deps = Object.keys(json.dependencies || {}); const devDeps = Object.keys(json.devDependencies || {}); diff --git a/lib/hexo/load_theme_config.js b/lib/hexo/load_theme_config.ts similarity index 81% rename from lib/hexo/load_theme_config.js rename to lib/hexo/load_theme_config.ts index 1abb35d842..57a28adb41 100644 --- a/lib/hexo/load_theme_config.js +++ b/lib/hexo/load_theme_config.ts @@ -1,12 +1,10 @@ -'use strict'; +import { join, parse } from 'path'; +import tildify from 'tildify'; +import { exists, readdir } from 'hexo-fs'; +import { magenta } from 'picocolors'; +import { deepMerge } from 'hexo-util'; -const { join, parse } = require('path'); -const tildify = require('tildify'); -const { exists, readdir } = require('hexo-fs'); -const { magenta } = require('picocolors'); -const { deepMerge } = require('hexo-util'); - -module.exports = ctx => { +export = ctx => { if (!ctx.env.init) return; if (!ctx.config.theme) return; diff --git a/lib/hexo/locals.js b/lib/hexo/locals.ts similarity index 92% rename from lib/hexo/locals.js rename to lib/hexo/locals.ts index dfdc9a772c..468dabe26f 100644 --- a/lib/hexo/locals.js +++ b/lib/hexo/locals.ts @@ -1,8 +1,9 @@ -'use strict'; - -const { Cache } = require('hexo-util'); +import { Cache } from 'hexo-util'; class Locals { + public cache: any; + public getters: any; + constructor() { this.cache = new Cache(); this.getters = {}; @@ -61,4 +62,4 @@ class Locals { } } -module.exports = Locals; +export = Locals; diff --git a/lib/hexo/multi_config_path.js b/lib/hexo/multi_config_path.ts similarity index 78% rename from lib/hexo/multi_config_path.js rename to lib/hexo/multi_config_path.ts index 7d81d67643..8d140d3569 100644 --- a/lib/hexo/multi_config_path.js +++ b/lib/hexo/multi_config_path.ts @@ -1,11 +1,9 @@ -'use strict'; +import { isAbsolute, resolve, join, extname } from 'path'; +import { existsSync, readFileSync, writeFileSync } from 'hexo-fs'; +import yml from 'js-yaml'; +import { deepMerge } from 'hexo-util'; -const { isAbsolute, resolve, join, extname } = require('path'); -const fs = require('hexo-fs'); -const yml = require('js-yaml'); -const { deepMerge } = require('hexo-util'); - -module.exports = ctx => function multiConfigPath(base, configPaths, outputDir) { +export = ctx => function multiConfigPath(base, configPaths, outputDir) { const { log } = ctx; const defaultPath = join(base, '_config.yml'); @@ -22,7 +20,7 @@ module.exports = ctx => function multiConfigPath(base, configPaths, outputDir) { // only one config let configPath = isAbsolute(configPaths) ? configPaths : resolve(base, configPaths); - if (!fs.existsSync(configPath)) { + if (!existsSync(configPath)) { log.w(`Config file ${configPaths} not found, using default.`); configPath = defaultPath; } @@ -38,13 +36,13 @@ module.exports = ctx => function multiConfigPath(base, configPaths, outputDir) { for (let i = 0; i < numPaths; i++) { const configPath = isAbsolute(paths[i]) ? paths[i] : join(base, paths[i]); - if (!fs.existsSync(configPath)) { + if (!existsSync(configPath)) { log.w(`Config file ${paths[i]} not found.`); continue; } // files read synchronously to ensure proper overwrite order - const file = fs.readFileSync(configPath); + const file = readFileSync(configPath); const ext = extname(paths[i]).toLowerCase(); if (ext === '.yml') { @@ -70,7 +68,7 @@ module.exports = ctx => function multiConfigPath(base, configPaths, outputDir) { log.d(`Writing _multiconfig.yml to ${outputPath}`); - fs.writeFileSync(outputPath, yml.dump(combinedConfig)); + writeFileSync(outputPath, yml.dump(combinedConfig)); // write file and return path return outputPath; diff --git a/lib/hexo/post.js b/lib/hexo/post.ts similarity index 90% rename from lib/hexo/post.js rename to lib/hexo/post.ts index 2df362d2fa..748d73fd94 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.ts @@ -1,14 +1,12 @@ -'use strict'; - -const assert = require('assert'); -const moment = require('moment'); -const Promise = require('bluebird'); -const { join, extname, basename } = require('path'); -const { magenta } = require('picocolors'); -const { load } = require('js-yaml'); -const { slugize, escapeRegExp } = require('hexo-util'); -const { copyDir, exists, listDir, mkdirs, readFile, rmdir, unlink, writeFile } = require('hexo-fs'); -const { parse: yfmParse, split: yfmSplit, stringify: yfmStringify } = require('hexo-front-matter'); +import assert from 'assert'; +import moment from 'moment'; +import Promise from 'bluebird'; +import { join, extname, basename } from 'path'; +import { magenta } from 'picocolors'; +import { load } from 'js-yaml'; +import { slugize, escapeRegExp } from 'hexo-util'; +import { copyDir, exists, listDir, mkdirs, readFile, rmdir, unlink, writeFile } from 'hexo-fs'; +import { parse as yfmParse, split as yfmSplit, stringify as yfmStringify } from 'hexo-front-matter'; const preservedKeys = ['title', 'slug', 'path', 'layout', 'date', 'content']; const rHexoPostRenderEscape = /([\s\S]+?)<\/hexoPostRenderCodeBlock>/g; @@ -30,6 +28,9 @@ const isNonWhiteSpaceChar = char => char !== '\r' && char !== ' '; class PostRenderEscape { + public stored: any; + public length: any; + constructor() { this.stored = []; } @@ -217,12 +218,30 @@ const createAssetFolder = (path, assetFolder) => { }); }; +interface Result { + path?: string; + content?: string; +} + +interface Data { + engine?: string; + content?: string; + disableNunjucks?: boolean; + markdown?: object; + source?: string; +} + class Post { + public context: any; + public config: any; + public tag: any; + public separator: any; + constructor(context) { this.context = context; } - create(data, replace, callback) { + create(data, replace, callback?) { if (!callback && typeof replace === 'function') { callback = replace; replace = false; @@ -245,7 +264,7 @@ class Post { ]).spread((path, content) => { const result = { path, content }; - return Promise.all([ + return Promise.all([ // Write content to file writeFile(path, content), // Create asset folder @@ -268,16 +287,16 @@ class Post { _renderScaffold(data) { const { tag } = this.context.extend; - let splited; + let splitted; return this._getScaffold(data.layout).then(scaffold => { - splited = yfmSplit(scaffold); - const jsonMode = splited.separator.startsWith(';'); + splitted = yfmSplit(scaffold); + const jsonMode = splitted.separator.startsWith(';'); const frontMatter = prepareFrontMatter({ ...data }, jsonMode); - return tag.render(splited.data, frontMatter); + return tag.render(splitted.data, frontMatter); }).then(frontMatter => { - const { separator } = splited; + const { separator } = splitted; const jsonMode = separator.startsWith(';'); // Parse front-matter @@ -291,14 +310,14 @@ class Post { let content = ''; // Prepend the separator - if (splited.prefixSeparator) content += `${separator}\n`; + if (splitted.prefixSeparator) content += `${separator}\n`; content += yfmStringify(obj, { mode: jsonMode ? 'json' : '' }); // Concat content - content += splited.content; + content += splitted.content; if (data.content) { content += `\n${data.content}`; @@ -323,7 +342,7 @@ class Post { data.slug = slug; const regex = new RegExp(`^${escapeRegExp(slug)}(?:[^\\/\\\\]+)`); let src = ''; - const result = {}; + const result: Result = {}; data.layout = (data.layout || config.default_layout).toLowerCase(); @@ -361,7 +380,7 @@ class Post { }).thenReturn(result).asCallback(callback); } - render(source, data = {}, callback) { + render(source, data: Data = {}, callback) { const ctx = this.context; const { config } = ctx; const { tag } = ctx.extend; @@ -418,7 +437,7 @@ class Post { data.content = cacheObj.escapeAllSwigTags(data.content); } - const options = data.markdown || {}; + const options: { highlight?: boolean; } = data.markdown || {}; if (!config.syntax_highlighter) options.highlight = null; ctx.log.debug('Rendering post: %s', magenta(source)); @@ -448,4 +467,4 @@ class Post { } } -module.exports = Post; +export = Post; diff --git a/lib/hexo/register_models.js b/lib/hexo/register_models.ts similarity index 70% rename from lib/hexo/register_models.js rename to lib/hexo/register_models.ts index 6245e122a1..864e4e3631 100644 --- a/lib/hexo/register_models.js +++ b/lib/hexo/register_models.ts @@ -1,8 +1,6 @@ -'use strict'; +import * as models from '../models'; -const models = require('../models'); - -module.exports = ctx => { +export = ctx => { const db = ctx.database; const keys = Object.keys(models); diff --git a/lib/hexo/render.js b/lib/hexo/render.ts similarity index 93% rename from lib/hexo/render.js rename to lib/hexo/render.ts index 802d1b38f0..a25d152314 100644 --- a/lib/hexo/render.js +++ b/lib/hexo/render.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { extname } = require('path'); -const Promise = require('bluebird'); -const { readFile, readFileSync } = require('hexo-fs'); +import { extname } from 'path'; +import Promise from 'bluebird'; +import { readFile, readFileSync } from 'hexo-fs'; const getExtname = str => { if (typeof str !== 'string') return ''; @@ -26,6 +24,9 @@ const toString = (result, options) => { }; class Render { + public context: any; + public renderer: any; + constructor(ctx) { this.context = ctx; this.renderer = ctx.extend.renderer; @@ -43,7 +44,7 @@ class Render { return this.renderer.getOutput(path); } - getRenderer(ext, sync) { + getRenderer(ext, sync?) { return this.renderer.get(ext, sync); } @@ -131,4 +132,4 @@ class Render { } } -module.exports = Render; +export = Render; diff --git a/lib/hexo/router.js b/lib/hexo/router.ts similarity index 85% rename from lib/hexo/router.js rename to lib/hexo/router.ts index a1f3a32c8b..f00824d381 100644 --- a/lib/hexo/router.js +++ b/lib/hexo/router.ts @@ -1,12 +1,27 @@ -'use strict'; - -const { EventEmitter } = require('events'); -const Promise = require('bluebird'); -const Stream = require('stream'); +import { EventEmitter } from 'events'; +import Promise from 'bluebird'; +import Stream from 'stream'; const { Readable } = Stream; +interface Data { + data: any; + modified: boolean; +} + +declare module 'stream' { + export default class _Stream extends Stream { + readable: boolean; + } +} + class RouteStream extends Readable { - constructor(data) { + public _data: any; + public _ended: boolean; + public modified: any; + public push: any; + public emit: any; + + constructor(data: Data) { super({ objectMode: true }); this._data = data.data; @@ -71,7 +86,7 @@ class RouteStream extends Readable { } } -const _format = path => { +const _format = (path: string) => { path = path || ''; if (typeof path !== 'string') throw new TypeError('path must be a string!'); @@ -89,6 +104,11 @@ const _format = path => { }; class Router extends EventEmitter { + public routes: { + [key: string]: Data | null; + }; + public emit: any; + constructor() { super(); @@ -100,11 +120,11 @@ class Router extends EventEmitter { return Object.keys(routes).filter(key => routes[key]); } - format(path) { + format(path: string) { return _format(path); } - get(path) { + get(path: string) { if (typeof path !== 'string') throw new TypeError('path must be a string!'); const data = this.routes[this.format(path)]; @@ -124,7 +144,7 @@ class Router extends EventEmitter { if (typeof path !== 'string') throw new TypeError('path must be a string!'); if (data == null) throw new TypeError('data is required!'); - let obj; + let obj: Data; if (typeof data === 'object' && data.data != null) { obj = data; @@ -166,4 +186,4 @@ class Router extends EventEmitter { } } -module.exports = Router; +export = Router; diff --git a/lib/hexo/scaffold.js b/lib/hexo/scaffold.ts similarity index 87% rename from lib/hexo/scaffold.js rename to lib/hexo/scaffold.ts index c1d59f2aa2..18a1a5ff41 100644 --- a/lib/hexo/scaffold.js +++ b/lib/hexo/scaffold.ts @@ -1,9 +1,11 @@ -'use strict'; - -const { extname, join } = require('path'); -const { exists, listDir, readFile, unlink, writeFile } = require('hexo-fs'); +import { extname, join } from 'path'; +import { exists, listDir, readFile, unlink, writeFile } from 'hexo-fs'; class Scaffold { + public context: any; + public scaffoldDir: any; + public defaults: any; + constructor(context) { this.context = context; this.scaffoldDir = context.scaffold_dir; @@ -68,4 +70,4 @@ class Scaffold { } } -module.exports = Scaffold; +export = Scaffold; diff --git a/lib/hexo/source.js b/lib/hexo/source.ts similarity index 66% rename from lib/hexo/source.js rename to lib/hexo/source.ts index 00830b348f..ab4d26bc45 100644 --- a/lib/hexo/source.js +++ b/lib/hexo/source.ts @@ -1,6 +1,4 @@ -'use strict'; - -const Box = require('../box'); +import Box from '../box'; class Source extends Box { constructor(ctx) { @@ -10,4 +8,4 @@ class Source extends Box { } } -module.exports = Source; +export = Source; diff --git a/lib/hexo/update_package.js b/lib/hexo/update_package.ts similarity index 58% rename from lib/hexo/update_package.js rename to lib/hexo/update_package.ts index b0c16fa5ef..03319913e6 100644 --- a/lib/hexo/update_package.js +++ b/lib/hexo/update_package.ts @@ -1,9 +1,7 @@ -'use strict'; +import { join } from 'path'; +import { writeFile, exists, readFile } from 'hexo-fs'; -const { join } = require('path'); -const fs = require('hexo-fs'); - -module.exports = ctx => { +export = ctx => { const pkgPath = join(ctx.base_dir, 'package.json'); return readPkg(pkgPath).then(pkg => { @@ -16,16 +14,16 @@ module.exports = ctx => { pkg.hexo.version = ctx.version; ctx.log.debug('Updating package.json'); - return fs.writeFile(pkgPath, JSON.stringify(pkg, null, ' ')); + return writeFile(pkgPath, JSON.stringify(pkg, null, ' ')); }); }; function readPkg(path) { - return fs.exists(path).then(exist => { + return exists(path).then(exist => { if (!exist) return; - return fs.readFile(path).then(content => { - const pkg = JSON.parse(content); + return readFile(path).then(content => { + const pkg = JSON.parse(content as string); if (typeof pkg.hexo !== 'object') return; return pkg; diff --git a/lib/hexo/validate_config.js b/lib/hexo/validate_config.ts similarity index 94% rename from lib/hexo/validate_config.js rename to lib/hexo/validate_config.ts index 347feea886..8dfcb389e9 100644 --- a/lib/hexo/validate_config.js +++ b/lib/hexo/validate_config.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { config, log } = ctx; log.info('Validating config'); diff --git a/lib/models/asset.js b/lib/models/asset.ts similarity index 65% rename from lib/models/asset.js rename to lib/models/asset.ts index d821ead83f..006391d3b5 100644 --- a/lib/models/asset.js +++ b/lib/models/asset.ts @@ -1,10 +1,8 @@ -'use strict'; +import warehouse from 'warehouse'; +import { join } from 'path'; -const { Schema } = require('warehouse').default; -const { join } = require('path'); - -module.exports = ctx => { - const Asset = new Schema({ +export = ctx => { + const Asset = new warehouse.Schema({ _id: {type: String, required: true}, path: {type: String, required: true}, modified: {type: Boolean, default: true}, diff --git a/lib/models/cache.js b/lib/models/cache.ts similarity index 90% rename from lib/models/cache.js rename to lib/models/cache.ts index 89ef7286a5..d4b43f4722 100644 --- a/lib/models/cache.js +++ b/lib/models/cache.ts @@ -1,10 +1,8 @@ -'use strict'; +import warehouse from 'warehouse'; +import Promise from 'bluebird'; -const { Schema } = require('warehouse').default; -const Promise = require('bluebird'); - -module.exports = ctx => { - const Cache = new Schema({ +export = ctx => { + const Cache = new warehouse.Schema({ _id: {type: String, required: true}, hash: {type: String, default: ''}, modified: {type: Number, default: Date.now() } // UnixTime diff --git a/lib/models/category.js b/lib/models/category.ts similarity index 88% rename from lib/models/category.js rename to lib/models/category.ts index 5c535799a6..740bc67015 100644 --- a/lib/models/category.js +++ b/lib/models/category.ts @@ -1,12 +1,10 @@ -'use strict'; +import warehouse from 'warehouse'; +import { slugize, full_url_for } from 'hexo-util'; -const { Schema } = require('warehouse').default; -const { slugize, full_url_for } = require('hexo-util'); - -module.exports = ctx => { - const Category = new Schema({ +export = ctx => { + const Category = new warehouse.Schema({ name: {type: String, required: true}, - parent: {type: Schema.Types.CUID, ref: 'Category'} + parent: { type: warehouse.Schema.Types.CUID, ref: 'Category'} }); Category.virtual('slug').get(function() { diff --git a/lib/models/data.js b/lib/models/data.js deleted file mode 100644 index 3b3f25487f..0000000000 --- a/lib/models/data.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -const { Schema } = require('warehouse').default; - -module.exports = ctx => { - const Data = new Schema({ - _id: {type: String, required: true}, - data: Object - }); - - return Data; -}; diff --git a/lib/models/data.ts b/lib/models/data.ts new file mode 100644 index 0000000000..0cdefe318a --- /dev/null +++ b/lib/models/data.ts @@ -0,0 +1,10 @@ +import warehouse from 'warehouse'; + +export = ctx => { + const Data = new warehouse.Schema({ + _id: {type: String, required: true}, + data: Object + }); + + return Data; +}; diff --git a/lib/models/index.js b/lib/models/index.js deleted file mode 100644 index 18ae185867..0000000000 --- a/lib/models/index.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const Asset = require('./asset'); -const Cache = require('./cache'); -const Category = require('./category'); -const Data = require('./data'); -const Page = require('./page'); -const Post = require('./post'); -const PostAsset = require('./post_asset'); -const PostCategory = require('./post_category'); -const PostTag = require('./post_tag'); -const Tag = require('./tag'); - -module.exports = { - Asset, Cache, Category, Data, Page, Post, PostAsset, PostCategory, PostTag, Tag -}; diff --git a/lib/models/index.ts b/lib/models/index.ts new file mode 100644 index 0000000000..d76aff60a9 --- /dev/null +++ b/lib/models/index.ts @@ -0,0 +1,10 @@ +export { default as Asset } from './asset'; +export { default as Cache } from './cache'; +export { default as Category } from './category'; +export { default as Data } from './data'; +export { default as Page } from './page'; +export { default as Post } from './post'; +export { default as PostAsset } from './post_asset'; +export { default as PostCategory } from './post_category'; +export { default as PostTag } from './post_tag'; +export { default as Tag } from './tag'; diff --git a/lib/models/page.js b/lib/models/page.ts similarity index 75% rename from lib/models/page.js rename to lib/models/page.ts index 0216ffede5..bbaa606422 100644 --- a/lib/models/page.js +++ b/lib/models/page.ts @@ -1,13 +1,11 @@ -'use strict'; +import warehouse from 'warehouse'; +import { join } from 'path'; +import Moment from './types/moment'; +import moment from 'moment'; +import { full_url_for } from 'hexo-util'; -const { Schema } = require('warehouse').default; -const { join } = require('path'); -const Moment = require('./types/moment'); -const moment = require('moment'); -const { full_url_for } = require('hexo-util'); - -module.exports = ctx => { - const Page = new Schema({ +export = ctx => { + const Page = new warehouse.Schema({ title: {type: String, default: ''}, date: { type: Moment, diff --git a/lib/models/post.js b/lib/models/post.ts similarity index 88% rename from lib/models/post.js rename to lib/models/post.ts index 4cb28421e3..10b274d7ad 100644 --- a/lib/models/post.js +++ b/lib/models/post.ts @@ -1,11 +1,9 @@ -'use strict'; - -const { Schema } = require('warehouse').default; -const moment = require('moment'); -const { extname, join, sep } = require('path'); -const Promise = require('bluebird'); -const Moment = require('./types/moment'); -const { full_url_for } = require('hexo-util'); +import warehouse from 'warehouse'; +import moment from 'moment'; +import { extname, join, sep } from 'path'; +import Promise from 'bluebird'; +import Moment from './types/moment'; +import { full_url_for, Cache } from 'hexo-util'; function pickID(data) { return data._id; @@ -15,8 +13,10 @@ function removeEmptyTag(tags) { return tags.filter(tag => tag != null && tag !== '').map(tag => `${tag}`); } -module.exports = ctx => { - const Post = new Schema({ +const tagsGetterCache = new Cache(); + +export = ctx => { + const Post = new warehouse.Schema({ id: String, title: {type: String, default: ''}, date: { @@ -36,7 +36,6 @@ module.exports = ctx => { source: {type: String, required: true}, slug: {type: String, required: true}, photos: [String], - link: {type: String, default: ''}, raw: {type: String, default: ''}, published: {type: Boolean, default: true}, content: {type: String}, @@ -63,12 +62,14 @@ module.exports = ctx => { }); Post.virtual('tags').get(function() { - const PostTag = ctx.model('PostTag'); - const Tag = ctx.model('Tag'); + return tagsGetterCache.apply(this._id, () => { + const PostTag = ctx.model('PostTag'); + const Tag = ctx.model('Tag'); - const ids = PostTag.find({post_id: this._id}, {lean: true}).map(item => item.tag_id); + const ids = PostTag.find({post_id: this._id}, {lean: true}).map(item => item.tag_id); - return Tag.find({_id: {$in: ids}}); + return Tag.find({_id: {$in: ids}}); + }); }); Post.method('notPublished', function() { @@ -82,6 +83,7 @@ module.exports = ctx => { // If the post is unpublished then the tag needs to be removed, thus the function cannot be returned early here tags = []; } + tagsGetterCache.flush(); tags = removeEmptyTag(tags); const PostTag = ctx.model('PostTag'); @@ -128,7 +130,7 @@ module.exports = ctx => { return Category.find({_id: {$in: ids}}); }); - Post.method('setCategories', function(cats) { + Post.method('setCategories', function(cats: string[]) { if (this.notPublished()) { cats = []; } @@ -147,7 +149,7 @@ module.exports = ctx => { const hasHierarchy = cats.filter(Array.isArray).length > 0; // Add a hierarchy of categories - const addHierarchy = catHierarchy => { + const addHierarchy = (catHierarchy: string | string[]) => { const parentIds = []; if (!Array.isArray(catHierarchy)) catHierarchy = [catHierarchy]; // Don't use "Promise.map". It doesn't run in series. @@ -166,7 +168,7 @@ module.exports = ctx => { } // Insert the category if not exist - const obj = {name: cat}; + const obj: {name: string, parent?: string} = {name: cat}; if (i) obj.parent = parentIds[i - 1]; return Category.insert(obj).catch(err => { diff --git a/lib/models/post_asset.js b/lib/models/post_asset.ts similarity index 76% rename from lib/models/post_asset.js rename to lib/models/post_asset.ts index 9c3c3778f8..72ec5f90f1 100644 --- a/lib/models/post_asset.js +++ b/lib/models/post_asset.ts @@ -1,14 +1,12 @@ -'use strict'; +import warehouse from 'warehouse'; +import { join } from 'path'; -const { Schema } = require('warehouse').default; -const { join } = require('path'); - -module.exports = ctx => { - const PostAsset = new Schema({ +export = ctx => { + const PostAsset = new warehouse.Schema({ _id: {type: String, required: true}, slug: {type: String, required: true}, modified: {type: Boolean, default: true}, - post: {type: Schema.Types.CUID, ref: 'Post'}, + post: {type: warehouse.Schema.Types.CUID, ref: 'Post'}, renderable: {type: Boolean, default: true} }); diff --git a/lib/models/post_category.js b/lib/models/post_category.js deleted file mode 100644 index f7169d0f0d..0000000000 --- a/lib/models/post_category.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -const { Schema } = require('warehouse').default; - -module.exports = ctx => { - const PostCategory = new Schema({ - post_id: {type: Schema.Types.CUID, ref: 'Post'}, - category_id: {type: Schema.Types.CUID, ref: 'Category'} - }); - - return PostCategory; -}; diff --git a/lib/models/post_category.ts b/lib/models/post_category.ts new file mode 100644 index 0000000000..f3c813f8be --- /dev/null +++ b/lib/models/post_category.ts @@ -0,0 +1,10 @@ +import warehouse from 'warehouse'; + +export = ctx => { + const PostCategory = new warehouse.Schema({ + post_id: {type: warehouse.Schema.Types.CUID, ref: 'Post'}, + category_id: {type: warehouse.Schema.Types.CUID, ref: 'Category'} + }); + + return PostCategory; +}; diff --git a/lib/models/post_tag.js b/lib/models/post_tag.js deleted file mode 100644 index 21ce51f287..0000000000 --- a/lib/models/post_tag.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -const { Schema } = require('warehouse').default; - -module.exports = ctx => { - const PostTag = new Schema({ - post_id: {type: Schema.Types.CUID, ref: 'Post'}, - tag_id: {type: Schema.Types.CUID, ref: 'Tag'} - }); - - return PostTag; -}; diff --git a/lib/models/post_tag.ts b/lib/models/post_tag.ts new file mode 100644 index 0000000000..e727ca5e3e --- /dev/null +++ b/lib/models/post_tag.ts @@ -0,0 +1,10 @@ +import warehouse from 'warehouse'; + +export = ctx => { + const PostTag = new warehouse.Schema({ + post_id: {type: warehouse.Schema.Types.CUID, ref: 'Post'}, + tag_id: {type: warehouse.Schema.Types.CUID, ref: 'Tag'} + }); + + return PostTag; +}; diff --git a/lib/models/tag.js b/lib/models/tag.ts similarity index 90% rename from lib/models/tag.js rename to lib/models/tag.ts index 7f6245c861..9c5b10a55e 100644 --- a/lib/models/tag.js +++ b/lib/models/tag.ts @@ -1,11 +1,9 @@ -'use strict'; - -const { Schema } = require('warehouse').default; -const { slugize, full_url_for } = require('hexo-util'); +import warehouse from 'warehouse'; +import { slugize, full_url_for } from 'hexo-util'; const { hasOwnProperty: hasOwn } = Object.prototype; -module.exports = ctx => { - const Tag = new Schema({ +export = ctx => { + const Tag = new warehouse.Schema({ name: {type: String, required: true} }); diff --git a/lib/models/types/moment.js b/lib/models/types/moment.ts similarity index 82% rename from lib/models/types/moment.js rename to lib/models/types/moment.ts index 95dca78b65..59a86e1479 100644 --- a/lib/models/types/moment.js +++ b/lib/models/types/moment.ts @@ -1,9 +1,16 @@ -'use strict'; +import warehouse from 'warehouse'; +import { moment, toMomentLocale } from '../../plugins/helper/date'; -const { SchemaType } = require('warehouse').default; -const { moment, toMomentLocale } = require('../../plugins/helper/date'); +declare module 'moment' { + export default interface Moment extends moment.Moment { + _d: Date; + // eslint-disable-next-line semi + } +} + +class SchemaTypeMoment extends warehouse.SchemaType { + public options: any; -class SchemaTypeMoment extends SchemaType { constructor(name, options = {}) { super(name, options); } @@ -48,7 +55,7 @@ class SchemaTypeMoment extends SchemaType { return 0; } - parse(value, data) { + parse(value) { if (value) return toMoment(value); } @@ -87,4 +94,4 @@ function toMoment(value) { return moment(value); } -module.exports = SchemaTypeMoment; +export = SchemaTypeMoment; diff --git a/lib/plugins/console/clean.js b/lib/plugins/console/clean.ts similarity index 62% rename from lib/plugins/console/clean.js rename to lib/plugins/console/clean.ts index ab6895f165..fb799039d8 100644 --- a/lib/plugins/console/clean.js +++ b/lib/plugins/console/clean.ts @@ -1,7 +1,5 @@ -'use strict'; - -const Promise = require('bluebird'); -const fs = require('hexo-fs'); +import Promise from 'bluebird'; +import { exists, unlink, rmdir } from 'hexo-fs'; function cleanConsole(args) { return Promise.all([ @@ -14,10 +12,10 @@ function cleanConsole(args) { function deleteDatabase(ctx) { const dbPath = ctx.database.options.path; - return fs.exists(dbPath).then(exist => { + return exists(dbPath).then(exist => { if (!exist) return; - return fs.unlink(dbPath).then(() => { + return unlink(dbPath).then(() => { ctx.log.info('Deleted database.'); }); }); @@ -26,13 +24,13 @@ function deleteDatabase(ctx) { function deletePublicDir(ctx) { const publicDir = ctx.public_dir; - return fs.exists(publicDir).then(exist => { + return exists(publicDir).then(exist => { if (!exist) return; - return fs.rmdir(publicDir).then(() => { + return rmdir(publicDir).then(() => { ctx.log.info('Deleted public folder.'); }); }); } -module.exports = cleanConsole; +export = cleanConsole; diff --git a/lib/plugins/console/config.js b/lib/plugins/console/config.ts similarity index 83% rename from lib/plugins/console/config.js rename to lib/plugins/console/config.ts index 3811746475..841c241c23 100644 --- a/lib/plugins/console/config.js +++ b/lib/plugins/console/config.ts @@ -1,9 +1,7 @@ -'use strict'; - -const yaml = require('js-yaml'); -const fs = require('hexo-fs'); -const { extname } = require('path'); -const Promise = require('bluebird'); +import yaml from 'js-yaml'; +import { exists, writeFile } from 'hexo-fs'; +import { extname } from 'path'; +import Promise from 'bluebird'; function configConsole(args) { const key = args._[0]; @@ -23,7 +21,7 @@ function configConsole(args) { const configPath = this.config_path; const ext = extname(configPath); - return fs.exists(configPath).then(exist => { + return exists(configPath).then(exist => { if (!exist) return {}; return this.render.render({path: configPath}); }).then(config => { @@ -33,7 +31,7 @@ function configConsole(args) { const result = ext === '.json' ? JSON.stringify(config) : yaml.dump(config); - return fs.writeFile(configPath, result); + return writeFile(configPath, result); }); } @@ -83,4 +81,4 @@ function castValue(value) { return value; } -module.exports = configConsole; +export = configConsole; diff --git a/lib/plugins/console/deploy.js b/lib/plugins/console/deploy.ts similarity index 83% rename from lib/plugins/console/deploy.js rename to lib/plugins/console/deploy.ts index bd35db8a23..6da32c2e76 100644 --- a/lib/plugins/console/deploy.js +++ b/lib/plugins/console/deploy.ts @@ -1,7 +1,5 @@ -'use strict'; - -const { exists } = require('hexo-fs'); -const { underline, magenta } = require('picocolors'); +import { exists } from 'hexo-fs'; +import { underline, magenta } from 'picocolors'; function deployConsole(args) { let config = this.config.deploy; @@ -50,7 +48,8 @@ function deployConsole(args) { this.log.info('Deploying: %s', magenta(type)); - return Reflect.apply(deployers[type], this, [{ ...item, ...args }]).then(() => { + // eslint-disable-next-line no-extra-parens + return (Reflect.apply(deployers[type], this, [{ ...item, ...args }]) as any).then(() => { this.log.info('Deploy done: %s', magenta(type)); }); }).then(() => { @@ -58,4 +57,4 @@ function deployConsole(args) { }); } -module.exports = deployConsole; +export = deployConsole; diff --git a/lib/plugins/console/generate.js b/lib/plugins/console/generate.ts similarity index 89% rename from lib/plugins/console/generate.js rename to lib/plugins/console/generate.ts index 261ba40132..ccae2b3d94 100644 --- a/lib/plugins/console/generate.js +++ b/lib/plugins/console/generate.ts @@ -1,15 +1,25 @@ -'use strict'; - -const { exists, writeFile, unlink, stat, mkdirs } = require('hexo-fs'); -const { join } = require('path'); -const Promise = require('bluebird'); -const prettyHrtime = require('pretty-hrtime'); -const { cyan, magenta } = require('picocolors'); -const tildify = require('tildify'); -const { PassThrough } = require('stream'); -const { createSha1Hash } = require('hexo-util'); +import { exists, writeFile, unlink, stat, mkdirs } from 'hexo-fs'; +import { join } from 'path'; +import Promise from 'bluebird'; +import prettyHrtime from 'pretty-hrtime'; +import { cyan, magenta } from 'picocolors'; +import tildify from 'tildify'; +import { PassThrough } from 'stream'; +import { createSha1Hash } from 'hexo-util'; class Generater { + public context: any; + public force: any; + public bail: any; + public concurrency: any; + public watch: any; + public deploy: any; + public generatingFiles: any; + public start: any; + public args: any; + public route: any; + public log: any; + constructor(ctx, args) { this.context = ctx; this.force = args.f || args.force; @@ -48,7 +58,7 @@ class Generater { generatingFiles.delete(path); }); } - writeFile(path, force) { + writeFile(path, force?) { const { route, log } = this.context; const publicDir = this.context.public_dir; const Cache = this.context.model('Cache'); @@ -196,4 +206,4 @@ function generateConsole(args = {}) { }); } -module.exports = generateConsole; +export = generateConsole; diff --git a/lib/plugins/console/index.js b/lib/plugins/console/index.ts similarity index 97% rename from lib/plugins/console/index.js rename to lib/plugins/console/index.ts index 3a34b7b0fb..dc4b6e5f40 100644 --- a/lib/plugins/console/index.js +++ b/lib/plugins/console/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = function(ctx) { +export = function(ctx: Hexo) { const { console } = ctx.extend; console.register('clean', 'Remove generated files and cache.', require('./clean')); @@ -77,4 +77,4 @@ module.exports = function(ctx) { {name: '--pretty', desc: 'Prettify JSON output'} ] }, require('./render')); -}; +} diff --git a/lib/plugins/console/list/category.js b/lib/plugins/console/list/category.ts similarity index 70% rename from lib/plugins/console/list/category.js rename to lib/plugins/console/list/category.ts index 8550ec6c1d..0044ec3a0e 100644 --- a/lib/plugins/console/list/category.js +++ b/lib/plugins/console/list/category.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { underline } = require('picocolors'); -const table = require('text-table'); -const { stringLength } = require('./common'); +import { underline } from 'picocolors'; +import table from 'text-table'; +import { stringLength } from './common'; function listCategory() { const categories = this.model('Category'); @@ -23,4 +21,4 @@ function listCategory() { if (data.length === 1) console.log('No categories.'); } -module.exports = listCategory; +export = listCategory; diff --git a/lib/plugins/console/list/common.js b/lib/plugins/console/list/common.ts similarity index 71% rename from lib/plugins/console/list/common.js rename to lib/plugins/console/list/common.ts index ee0b12d039..5adcb8022c 100644 --- a/lib/plugins/console/list/common.js +++ b/lib/plugins/console/list/common.ts @@ -1,8 +1,6 @@ -'use strict'; +import strip from 'strip-ansi'; -const strip = require('strip-ansi'); - -exports.stringLength = str => { +export function stringLength(str) { str = strip(str); const len = str.length; @@ -16,4 +14,4 @@ exports.stringLength = str => { } return result; -}; +} diff --git a/lib/plugins/console/list/index.js b/lib/plugins/console/list/index.ts similarity index 61% rename from lib/plugins/console/list/index.js rename to lib/plugins/console/list/index.ts index debab88983..7c3f084138 100644 --- a/lib/plugins/console/list/index.js +++ b/lib/plugins/console/list/index.ts @@ -1,11 +1,9 @@ -'use strict'; - -const abbrev = require('abbrev'); -const page = require('./page'); -const post = require('./post'); -const route = require('./route'); -const tag = require('./tag'); -const category = require('./category'); +import abbrev from 'abbrev'; +import page from './page'; +import post from './post'; +import route from './route'; +import tag from './tag'; +import category from './category'; const store = { page, post, route, tag, category @@ -24,4 +22,4 @@ function listConsole(args) { return this.load().then(() => Reflect.apply(store[alias[type]], this, [args])); } -module.exports = listConsole; +export = listConsole; diff --git a/lib/plugins/console/list/page.js b/lib/plugins/console/list/page.ts similarity index 71% rename from lib/plugins/console/list/page.js rename to lib/plugins/console/list/page.ts index 7ea880ed54..d132231450 100644 --- a/lib/plugins/console/list/page.js +++ b/lib/plugins/console/list/page.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { magenta, underline, gray } = require('picocolors'); -const table = require('text-table'); -const { stringLength } = require('./common'); +import { magenta, underline, gray } from 'picocolors'; +import table from 'text-table'; +import { stringLength } from './common'; function listPage() { const Page = this.model('Page'); @@ -25,4 +23,4 @@ function listPage() { if (data.length === 1) console.log('No pages.'); } -module.exports = listPage; +export = listPage; diff --git a/lib/plugins/console/list/post.js b/lib/plugins/console/list/post.ts similarity index 80% rename from lib/plugins/console/list/post.js rename to lib/plugins/console/list/post.ts index e09a975b5e..e4dca1a578 100644 --- a/lib/plugins/console/list/post.js +++ b/lib/plugins/console/list/post.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { gray, magenta, underline } = require('picocolors'); -const table = require('text-table'); -const { stringLength } = require('./common'); +import { gray, magenta, underline } from 'picocolors'; +import table from 'text-table'; +import { stringLength } from './common'; function mapName(item) { return item.name; @@ -38,4 +36,4 @@ function listPost() { if (data.length === 1) console.log('No posts.'); } -module.exports = listPost; +export = listPost; diff --git a/lib/plugins/console/list/route.js b/lib/plugins/console/list/route.ts similarity index 92% rename from lib/plugins/console/list/route.js rename to lib/plugins/console/list/route.ts index d6a3cdbb8e..a29e4e4c54 100644 --- a/lib/plugins/console/list/route.js +++ b/lib/plugins/console/list/route.ts @@ -1,6 +1,4 @@ -'use strict'; - -const archy = require('archy'); +import archy from 'archy'; function listRoute() { const routes = this.route.list().sort(); @@ -50,4 +48,4 @@ function buildNodes(tree) { return nodes; } -module.exports = listRoute; +export = listRoute; diff --git a/lib/plugins/console/list/tag.js b/lib/plugins/console/list/tag.ts similarity index 70% rename from lib/plugins/console/list/tag.js rename to lib/plugins/console/list/tag.ts index 6f5dab7c69..09c0a148df 100644 --- a/lib/plugins/console/list/tag.js +++ b/lib/plugins/console/list/tag.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { magenta, underline } = require('picocolors'); -const table = require('text-table'); -const { stringLength } = require('./common'); +import { magenta, underline } from 'picocolors'; +import table from 'text-table'; +import { stringLength } from './common'; function listTag() { const Tag = this.model('Tag'); @@ -23,4 +21,4 @@ function listTag() { if (data.length === 1) console.log('No tags.'); } -module.exports = listTag; +export = listTag; diff --git a/lib/plugins/console/migrate.js b/lib/plugins/console/migrate.ts similarity index 86% rename from lib/plugins/console/migrate.js rename to lib/plugins/console/migrate.ts index 707b14ba55..df341f6ed4 100644 --- a/lib/plugins/console/migrate.js +++ b/lib/plugins/console/migrate.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { underline, magenta } = require('picocolors'); +import { underline, magenta } from 'picocolors'; function migrateConsole(args) { // Display help message if user didn't input any arguments @@ -26,4 +24,4 @@ function migrateConsole(args) { return Reflect.apply(migrators[type], this, [args]); } -module.exports = migrateConsole; +export = migrateConsole; diff --git a/lib/plugins/console/new.js b/lib/plugins/console/new.ts similarity index 86% rename from lib/plugins/console/new.js rename to lib/plugins/console/new.ts index a73663e27f..becbe1c625 100644 --- a/lib/plugins/console/new.js +++ b/lib/plugins/console/new.ts @@ -1,8 +1,6 @@ -'use strict'; - -const tildify = require('tildify'); -const { magenta } = require('picocolors'); -const { basename } = require('path'); +import tildify from 'tildify'; +import { magenta } from 'picocolors'; +import { basename } from 'path'; const reservedKeys = { _: true, @@ -53,4 +51,4 @@ function newConsole(args) { }); } -module.exports = newConsole; +export = newConsole; diff --git a/lib/plugins/console/publish.js b/lib/plugins/console/publish.ts similarity index 76% rename from lib/plugins/console/publish.js rename to lib/plugins/console/publish.ts index 2192e196a1..f73d87ec55 100644 --- a/lib/plugins/console/publish.js +++ b/lib/plugins/console/publish.ts @@ -1,7 +1,5 @@ -'use strict'; - -const tildify = require('tildify'); -const { magenta } = require('picocolors'); +import tildify from 'tildify'; +import { magenta } from 'picocolors'; function publishConsole(args) { // Display help message if user didn't input any arguments @@ -17,4 +15,4 @@ function publishConsole(args) { }); } -module.exports = publishConsole; +export = publishConsole; diff --git a/lib/plugins/console/render.js b/lib/plugins/console/render.ts similarity index 75% rename from lib/plugins/console/render.js rename to lib/plugins/console/render.ts index 57028ba0d1..01cb57c581 100644 --- a/lib/plugins/console/render.js +++ b/lib/plugins/console/render.ts @@ -1,10 +1,8 @@ -'use strict'; - -const { resolve } = require('path'); -const tildify = require('tildify'); -const prettyHrtime = require('pretty-hrtime'); -const fs = require('hexo-fs'); -const { cyan, magenta } = require('picocolors'); +import { resolve } from 'path'; +import tildify from 'tildify'; +import prettyHrtime from 'pretty-hrtime'; +import { writeFile } from 'hexo-fs'; +import { cyan, magenta } from 'picocolors'; function renderConsole(args) { // Display help message if user didn't input any arguments @@ -36,8 +34,8 @@ function renderConsole(args) { const interval = prettyHrtime(process.hrtime(start)); log.info('Rendered in %s: %s -> %s', cyan(interval), magenta(tildify(src)), magenta(tildify(dest))); - return fs.writeFile(dest, result); + return writeFile(dest, result); }); } -module.exports = renderConsole; +export = renderConsole; diff --git a/lib/plugins/filter/after_post_render/excerpt.js b/lib/plugins/filter/after_post_render/excerpt.ts similarity index 91% rename from lib/plugins/filter/after_post_render/excerpt.js rename to lib/plugins/filter/after_post_render/excerpt.ts index d5705c09be..ef1746a352 100644 --- a/lib/plugins/filter/after_post_render/excerpt.js +++ b/lib/plugins/filter/after_post_render/excerpt.ts @@ -1,5 +1,3 @@ -'use strict'; - const rExcerpt = //i; function excerptFilter(data) { @@ -20,4 +18,4 @@ function excerptFilter(data) { } } -module.exports = excerptFilter; +export = excerptFilter; diff --git a/lib/plugins/filter/after_post_render/external_link.js b/lib/plugins/filter/after_post_render/external_link.ts similarity index 90% rename from lib/plugins/filter/after_post_render/external_link.js rename to lib/plugins/filter/after_post_render/external_link.ts index cd7bec136c..0b116de8b4 100644 --- a/lib/plugins/filter/after_post_render/external_link.js +++ b/lib/plugins/filter/after_post_render/external_link.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { isExternalLink } = require('hexo-util'); +import { isExternalLink } from 'hexo-util'; let EXTERNAL_LINK_POST_ENABLED = true; const rATag = /]+?\s+?)href=["']((?:https?:|\/\/)[^<>"']+)["'][^<>]*>/gi; const rTargetAttr = /target=/i; @@ -31,4 +29,4 @@ function externalLinkFilter(data) { }); } -module.exports = externalLinkFilter; +export = externalLinkFilter; diff --git a/lib/plugins/filter/after_post_render/index.js b/lib/plugins/filter/after_post_render/index.ts similarity index 80% rename from lib/plugins/filter/after_post_render/index.js rename to lib/plugins/filter/after_post_render/index.ts index fbc7a7822c..87bb8b433c 100644 --- a/lib/plugins/filter/after_post_render/index.js +++ b/lib/plugins/filter/after_post_render/index.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { filter } = ctx.extend; filter.register('after_post_render', require('./external_link')); diff --git a/lib/plugins/filter/after_render/external_link.js b/lib/plugins/filter/after_render/external_link.ts similarity index 90% rename from lib/plugins/filter/after_render/external_link.js rename to lib/plugins/filter/after_render/external_link.ts index 40ff7b6b65..a5d7218133 100644 --- a/lib/plugins/filter/after_render/external_link.js +++ b/lib/plugins/filter/after_render/external_link.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { isExternalLink } = require('hexo-util'); +import { isExternalLink } from 'hexo-util'; let EXTERNAL_LINK_SITE_ENABLED = true; const rATag = /]+?\s+?)href=["']((?:https?:|\/\/)[^<>"']+)["'][^<>]*>/gi; @@ -32,4 +30,4 @@ function externalLinkFilter(data) { }); } -module.exports = externalLinkFilter; +export = externalLinkFilter; diff --git a/lib/plugins/filter/after_render/index.js b/lib/plugins/filter/after_render/index.ts similarity index 80% rename from lib/plugins/filter/after_render/index.js rename to lib/plugins/filter/after_render/index.ts index 819e0490ce..509e95a0f8 100644 --- a/lib/plugins/filter/after_render/index.js +++ b/lib/plugins/filter/after_render/index.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { filter } = ctx.extend; filter.register('after_render:html', require('./external_link')); diff --git a/lib/plugins/filter/after_render/meta_generator.js b/lib/plugins/filter/after_render/meta_generator.ts similarity index 88% rename from lib/plugins/filter/after_render/meta_generator.js rename to lib/plugins/filter/after_render/meta_generator.ts index d5e4f2c2f3..4c04daee3a 100644 --- a/lib/plugins/filter/after_render/meta_generator.js +++ b/lib/plugins/filter/after_render/meta_generator.ts @@ -1,5 +1,3 @@ -'use strict'; - let NEED_INJECT = true; let META_GENERATOR_TAG; @@ -17,4 +15,4 @@ function hexoMetaGeneratorInject(data) { return data.replace('', `${META_GENERATOR_TAG}`); } -module.exports = hexoMetaGeneratorInject; +export = hexoMetaGeneratorInject; diff --git a/lib/plugins/filter/before_exit/index.js b/lib/plugins/filter/before_exit/index.ts similarity index 70% rename from lib/plugins/filter/before_exit/index.js rename to lib/plugins/filter/before_exit/index.ts index 374eb8a225..ba3312749e 100644 --- a/lib/plugins/filter/before_exit/index.js +++ b/lib/plugins/filter/before_exit/index.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { filter } = ctx.extend; filter.register('before_exit', require('./save_database')); diff --git a/lib/plugins/filter/before_exit/save_database.js b/lib/plugins/filter/before_exit/save_database.ts similarity index 76% rename from lib/plugins/filter/before_exit/save_database.js rename to lib/plugins/filter/before_exit/save_database.ts index a5388e399e..9aadce2ee8 100644 --- a/lib/plugins/filter/before_exit/save_database.js +++ b/lib/plugins/filter/before_exit/save_database.ts @@ -1,5 +1,3 @@ -'use strict'; - function saveDatabaseFilter() { if (!this.env.init || !this._dbLoaded) return; @@ -8,4 +6,4 @@ function saveDatabaseFilter() { }); } -module.exports = saveDatabaseFilter; +export = saveDatabaseFilter; diff --git a/lib/plugins/filter/before_generate/index.js b/lib/plugins/filter/before_generate/index.ts similarity index 71% rename from lib/plugins/filter/before_generate/index.js rename to lib/plugins/filter/before_generate/index.ts index 960e469e3c..288bb1651a 100644 --- a/lib/plugins/filter/before_generate/index.js +++ b/lib/plugins/filter/before_generate/index.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { filter } = ctx.extend; filter.register('before_generate', require('./render_post')); diff --git a/lib/plugins/filter/before_generate/render_post.js b/lib/plugins/filter/before_generate/render_post.ts similarity index 64% rename from lib/plugins/filter/before_generate/render_post.js rename to lib/plugins/filter/before_generate/render_post.ts index 100450004a..539b5dadda 100644 --- a/lib/plugins/filter/before_generate/render_post.js +++ b/lib/plugins/filter/before_generate/render_post.ts @@ -1,14 +1,11 @@ -'use strict'; +import Promise from 'bluebird'; -const Promise = require('bluebird'); - -function renderPostFilter(data) { +function renderPostFilter() { const renderPosts = model => { const posts = model.toArray().filter(post => post.content == null); - return Promise.map(posts, post => { + return Promise.map(posts, (post: any) => { post.content = post._content; - post.site = {data}; return this.post.render(post.full_source, post).then(() => post.save()); }); @@ -20,4 +17,4 @@ function renderPostFilter(data) { ]); } -module.exports = renderPostFilter; +export = renderPostFilter; diff --git a/lib/plugins/filter/before_post_render/backtick_code_block.js b/lib/plugins/filter/before_post_render/backtick_code_block.ts similarity index 93% rename from lib/plugins/filter/before_post_render/backtick_code_block.js rename to lib/plugins/filter/before_post_render/backtick_code_block.ts index 32f3590e27..b9df3e60f2 100644 --- a/lib/plugins/filter/before_post_render/backtick_code_block.js +++ b/lib/plugins/filter/before_post_render/backtick_code_block.ts @@ -1,12 +1,17 @@ -'use strict'; - const rBacktick = /^((?:[^\S\r\n]*>){0,3}[^\S\r\n]*)(`{3,}|~{3,})[^\S\r\n]*((?:.*?[^`\s])?)[^\S\r\n]*\n((?:[\s\S]*?\n)?)(?:(?:[^\S\r\n]*>){0,3}[^\S\r\n]*)\2[^\S\r\n]?(\n+|$)/gm; const rAllOptions = /([^\s]+)\s+(.+?)\s+(https?:\/\/\S+|\/\S+)\s*(.+)?/; const rLangCaption = /([^\s]+)\s*(.+)?/; const escapeSwigTag = str => str.replace(/{/g, '{').replace(/}/g, '}'); -module.exports = ctx => { +interface Options { + lang: string, + caption: string, + lines_length: number, + firstLineNumber?: string | number +} + +export = ctx => { return function backtickCodeBlock(data) { const dataContent = data.content; @@ -46,7 +51,7 @@ module.exports = ctx => { content = content.replace(regexp, ''); } - const options = { + const options: Options = { lang, caption, lines_length: content.split('\n').length diff --git a/lib/plugins/filter/before_post_render/index.js b/lib/plugins/filter/before_post_render/index.ts similarity index 81% rename from lib/plugins/filter/before_post_render/index.js rename to lib/plugins/filter/before_post_render/index.ts index bcff7246b4..07bde83da3 100644 --- a/lib/plugins/filter/before_post_render/index.js +++ b/lib/plugins/filter/before_post_render/index.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { filter } = ctx.extend; filter.register('before_post_render', require('./backtick_code_block')(ctx)); diff --git a/lib/plugins/filter/before_post_render/titlecase.js b/lib/plugins/filter/before_post_render/titlecase.ts similarity index 83% rename from lib/plugins/filter/before_post_render/titlecase.js rename to lib/plugins/filter/before_post_render/titlecase.ts index 0fad5bc4e9..24e596ace8 100644 --- a/lib/plugins/filter/before_post_render/titlecase.js +++ b/lib/plugins/filter/before_post_render/titlecase.ts @@ -1,5 +1,3 @@ -'use strict'; - let titlecase; function titlecaseFilter(data) { @@ -9,4 +7,4 @@ function titlecaseFilter(data) { data.title = titlecase(data.title); } -module.exports = titlecaseFilter; +export = titlecaseFilter; diff --git a/lib/plugins/filter/index.js b/lib/plugins/filter/index.ts similarity index 86% rename from lib/plugins/filter/index.js rename to lib/plugins/filter/index.ts index f349b99b29..26aed0bf0d 100644 --- a/lib/plugins/filter/index.js +++ b/lib/plugins/filter/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +export = (ctx: Hexo) => { const { filter } = ctx.extend; require('./after_render')(ctx); diff --git a/lib/plugins/filter/new_post_path.js b/lib/plugins/filter/new_post_path.ts similarity index 81% rename from lib/plugins/filter/new_post_path.js rename to lib/plugins/filter/new_post_path.ts index 706c8a2037..c06b946230 100644 --- a/lib/plugins/filter/new_post_path.js +++ b/lib/plugins/filter/new_post_path.ts @@ -1,10 +1,8 @@ -'use strict'; - -const { join, extname } = require('path'); -const moment = require('moment'); -const Promise = require('bluebird'); -const { createSha1Hash, Permalink } = require('hexo-util'); -const fs = require('hexo-fs'); +import { join, extname } from 'path'; +import moment from 'moment'; +import Promise from 'bluebird'; +import { createSha1Hash, Permalink } from 'hexo-util'; +import { ensurePath } from 'hexo-fs'; let permalink; const reservedKeys = { @@ -17,7 +15,14 @@ const reservedKeys = { hash: true }; -function newPostPathFilter(data = {}, replace) { +interface Data { + path?: string; + layout?: string; + slug?: string; + date?: Date; +} + +function newPostPathFilter(data: Data = {}, replace) { const sourceDir = this.source_dir; const draftDir = join(sourceDir, '_drafts'); const postDir = join(sourceDir, '_posts'); @@ -27,7 +32,7 @@ function newPostPathFilter(data = {}, replace) { const { path, layout, slug } = data; if (!permalink || permalink.rule !== newPostName) { - permalink = new Permalink(newPostName); + permalink = new Permalink(newPostName, {}); } let target = ''; @@ -94,7 +99,7 @@ function newPostPathFilter(data = {}, replace) { return Promise.resolve(target); } - return fs.ensurePath(target); + return ensurePath(target); } -module.exports = newPostPathFilter; +export = newPostPathFilter; diff --git a/lib/plugins/filter/post_permalink.js b/lib/plugins/filter/post_permalink.ts similarity index 85% rename from lib/plugins/filter/post_permalink.js rename to lib/plugins/filter/post_permalink.ts index 1087c5ef37..c715e67b5d 100644 --- a/lib/plugins/filter/post_permalink.js +++ b/lib/plugins/filter/post_permalink.ts @@ -1,7 +1,5 @@ -'use strict'; - -const { createSha1Hash, Permalink, slugize } = require('hexo-util'); -const { basename } = require('path'); +import { createSha1Hash, Permalink, slugize } from 'hexo-util'; +import { basename } from 'path'; let permalink; function postPermalinkFilter(data) { @@ -29,19 +27,18 @@ function postPermalinkFilter(data) { second: date.format('ss'), i_month: date.format('M'), i_day: date.format('D'), - hash + hash, + category: config.default_category }; if (!permalink || permalink.rule !== config.permalink) { - permalink = new Permalink(config.permalink); + permalink = new Permalink(config.permalink, {}); } const { categories } = data; if (categories.length) { meta.category = categories.last().slug; - } else { - meta.category = config.default_category; } const keys = Object.keys(data); @@ -67,4 +64,4 @@ function postPermalinkFilter(data) { return permalink.stringify(meta); } -module.exports = postPermalinkFilter; +export = postPermalinkFilter; diff --git a/lib/plugins/filter/template_locals/i18n.js b/lib/plugins/filter/template_locals/i18n.ts similarity index 90% rename from lib/plugins/filter/template_locals/i18n.js rename to lib/plugins/filter/template_locals/i18n.ts index 9bdddb1fb6..73367d8df0 100644 --- a/lib/plugins/filter/template_locals/i18n.js +++ b/lib/plugins/filter/template_locals/i18n.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { Pattern } = require('hexo-util'); +import { Pattern } from 'hexo-util'; function i18nLocalsFilter(locals) { const { i18n } = this.theme; @@ -33,4 +31,4 @@ function i18nLocalsFilter(locals) { locals._p = i18n._p(languages); } -module.exports = i18nLocalsFilter; +export = i18nLocalsFilter; diff --git a/lib/plugins/filter/template_locals/index.js b/lib/plugins/filter/template_locals/index.ts similarity index 69% rename from lib/plugins/filter/template_locals/index.js rename to lib/plugins/filter/template_locals/index.ts index 53ce0da6d3..663cb8ab3f 100644 --- a/lib/plugins/filter/template_locals/index.js +++ b/lib/plugins/filter/template_locals/index.ts @@ -1,6 +1,4 @@ -'use strict'; - -module.exports = ctx => { +export = ctx => { const { filter } = ctx.extend; filter.register('template_locals', require('./i18n')); diff --git a/lib/plugins/generator/asset.js b/lib/plugins/generator/asset.ts similarity index 53% rename from lib/plugins/generator/asset.js rename to lib/plugins/generator/asset.ts index 3fda4db77d..722807c52e 100644 --- a/lib/plugins/generator/asset.js +++ b/lib/plugins/generator/asset.ts @@ -1,20 +1,31 @@ -'use strict'; - -const fs = require('hexo-fs'); -const Promise = require('bluebird'); -const { extname } = require('path'); -const { magenta } = require('picocolors'); +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { exists, createReadStream } from 'hexo-fs'; +import Promise from 'bluebird'; +import { extname } from 'path'; +import { magenta } from 'picocolors'; +import type warehouse from 'warehouse'; + +interface Data { + modified: boolean; + data?: () => any; +} const process = (name, ctx) => { - return Promise.filter(ctx.model(name).toArray(), asset => fs.exists(asset.source).tap(exist => { + // @ts-expect-error + return Promise.filter(ctx.model(name).toArray(), (asset: warehouse['Schema']) => exists(asset.source).tap(exist => { + // @ts-expect-error if (!exist) return asset.remove(); - })).map(asset => { + })).map((asset: warehouse['Schema']) => { + // @ts-expect-error const { source } = asset; + // @ts-expect-error let { path } = asset; - const data = { + const data: Data = { + // @ts-expect-error modified: asset.modified }; + // @ts-expect-error if (asset.renderable && ctx.render.isRenderable(path)) { // Replace extension name if the asset is renderable const filename = path.substring(0, path.length - extname(path).length); @@ -28,7 +39,7 @@ const process = (name, ctx) => { ctx.log.error({err}, 'Asset render failed: %s', magenta(path)); }); } else { - data.data = () => fs.createReadStream(source); + data.data = () => createReadStream(source); } return { path, data }; @@ -42,4 +53,4 @@ function assetGenerator() { ]).then(data => [].concat(...data)); } -module.exports = assetGenerator; +export = assetGenerator; diff --git a/lib/plugins/generator/index.js b/lib/plugins/generator/index.ts similarity index 75% rename from lib/plugins/generator/index.js rename to lib/plugins/generator/index.ts index da39f2680f..9c65363065 100644 --- a/lib/plugins/generator/index.js +++ b/lib/plugins/generator/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +export = (ctx: Hexo) => { const { generator } = ctx.extend; generator.register('asset', require('./asset')); diff --git a/lib/plugins/generator/page.js b/lib/plugins/generator/page.ts similarity index 90% rename from lib/plugins/generator/page.js rename to lib/plugins/generator/page.ts index a80d42254c..8dbbcd817c 100644 --- a/lib/plugins/generator/page.js +++ b/lib/plugins/generator/page.ts @@ -1,5 +1,3 @@ -'use strict'; - function pageGenerator(locals) { return locals.pages.map(page => { const { path, layout } = page; @@ -24,4 +22,4 @@ function pageGenerator(locals) { }); } -module.exports = pageGenerator; +export = pageGenerator; diff --git a/lib/plugins/generator/post.js b/lib/plugins/generator/post.ts similarity index 92% rename from lib/plugins/generator/post.js rename to lib/plugins/generator/post.ts index 0d32332329..4deec563dd 100644 --- a/lib/plugins/generator/post.js +++ b/lib/plugins/generator/post.ts @@ -1,5 +1,3 @@ -'use strict'; - function postGenerator(locals) { const posts = locals.posts.sort('-date').toArray(); const { length } = posts; @@ -30,4 +28,4 @@ function postGenerator(locals) { }); } -module.exports = postGenerator; +export = postGenerator; diff --git a/lib/plugins/helper/css.js b/lib/plugins/helper/css.ts similarity index 70% rename from lib/plugins/helper/css.js rename to lib/plugins/helper/css.ts index 0e728bd1b6..74ca0ba357 100644 --- a/lib/plugins/helper/css.js +++ b/lib/plugins/helper/css.ts @@ -1,11 +1,12 @@ -'use strict'; - -const { htmlTag, url_for } = require('hexo-util'); -const { default: moize } = require('moize'); +import { htmlTag, url_for } from 'hexo-util'; +import moize from 'moize'; +let relative_link = true; function cssHelper(...args) { let result = '\n'; + relative_link = this.config.relative_link; + args.flat(Infinity).forEach(item => { if (typeof item === 'string' || item instanceof String) { let path = item; @@ -23,7 +24,10 @@ function cssHelper(...args) { return result; } -module.exports = moize(cssHelper, { +export = moize(cssHelper, { maxSize: 10, - isDeepEqual: true + isDeepEqual: true, + updateCacheForKey() { + return relative_link; + } }); diff --git a/lib/plugins/helper/date.js b/lib/plugins/helper/date.ts similarity index 82% rename from lib/plugins/helper/date.js rename to lib/plugins/helper/date.ts index 8235e5fff4..b347770dc8 100644 --- a/lib/plugins/helper/date.js +++ b/lib/plugins/helper/date.ts @@ -1,8 +1,6 @@ -'use strict'; - -const moment = require('moment-timezone'); +import moment from 'moment-timezone'; const { isMoment } = moment; -const { default: moize } = require('moize'); +import moize from 'moize'; const isDate = value => typeof value === 'object' && value instanceof Date && !isNaN(value.getTime()); @@ -10,7 +8,7 @@ const isDate = value => function getMoment(date, lang, timezone) { if (date == null) date = moment(); if (!isMoment(date)) date = moment(isDate(date) ? date : new Date(date)); - lang = toMomentLocale(lang); + lang = _toMomentLocale(lang); if (lang) date = date.locale(lang); if (timezone) date = date.tz(timezone); @@ -74,7 +72,7 @@ function getLanguage(ctx) { * * Moment defined locales: https://github.com/moment/moment/tree/master/locale */ -function toMomentLocale(lang) { +function _toMomentLocale(lang) { if (lang === undefined) { return undefined; } @@ -87,11 +85,11 @@ function toMomentLocale(lang) { return lang.toLowerCase().replace('_', '-'); } -exports.date = dateHelper; -exports.date_xml = toISOString; -exports.time = timeHelper; -exports.full_date = fullDateHelper; -exports.relative_date = relativeDateHelper; -exports.time_tag = timeTagHelper; -exports.moment = moment; -exports.toMomentLocale = moize.shallow(toMomentLocale); +export {dateHelper as date}; +export {toISOString as date_xml}; +export {timeHelper as time}; +export {fullDateHelper as full_date}; +export {relativeDateHelper as relative_date}; +export {timeTagHelper as time_tag}; +export {moment}; +export const toMomentLocale = moize.shallow(_toMomentLocale); diff --git a/lib/plugins/helper/debug.js b/lib/plugins/helper/debug.ts similarity index 69% rename from lib/plugins/helper/debug.js rename to lib/plugins/helper/debug.ts index 22b7935385..ba4a96983f 100644 --- a/lib/plugins/helper/debug.js +++ b/lib/plugins/helper/debug.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { inspect } = require('util'); +import { inspect } from 'util'; // this format object as string, resolves circular reference function inspectObject(object, options) { @@ -12,5 +10,5 @@ function log(...args) { return Reflect.apply(console.log, null, args); } -exports.inspectObject = inspectObject; -exports.log = log; +export {inspectObject}; +export {log}; diff --git a/lib/plugins/helper/favicon_tag.js b/lib/plugins/helper/favicon_tag.ts similarity index 54% rename from lib/plugins/helper/favicon_tag.js rename to lib/plugins/helper/favicon_tag.ts index e2b756bcc8..579c04379a 100644 --- a/lib/plugins/helper/favicon_tag.js +++ b/lib/plugins/helper/favicon_tag.ts @@ -1,9 +1,7 @@ -'use strict'; - -const { url_for } = require('hexo-util'); +import { url_for } from 'hexo-util'; function faviconTagHelper(path) { return ``; } -module.exports = faviconTagHelper; +export = faviconTagHelper; diff --git a/lib/plugins/helper/feed_tag.js b/lib/plugins/helper/feed_tag.ts similarity index 85% rename from lib/plugins/helper/feed_tag.js rename to lib/plugins/helper/feed_tag.ts index a72af70f12..8fccfc7738 100644 --- a/lib/plugins/helper/feed_tag.js +++ b/lib/plugins/helper/feed_tag.ts @@ -1,14 +1,17 @@ -'use strict'; - -const { url_for } = require('hexo-util'); -const { default: moize } = require('moize'); +import { url_for } from 'hexo-util'; +import moize from 'moize'; const feedFn = (str = '') => { if (str) return str.replace(/2$/, ''); return str; }; -function makeFeedTag(path, options = {}, configFeed, configTitle) { +interface Options { + title?: string; + type?: string; +} + +function makeFeedTag(path, options: Options = {}, configFeed?, configTitle?) { const title = options.title || configTitle; if (path) { @@ -48,4 +51,4 @@ function feedTagHelper(path, options = {}) { return moize.deep(makeFeedTag.bind(this))(path, options, config.feed, config.title); } -module.exports = feedTagHelper; +export = feedTagHelper; diff --git a/lib/plugins/helper/format.js b/lib/plugins/helper/format.js deleted file mode 100644 index e80f54b50f..0000000000 --- a/lib/plugins/helper/format.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const { stripHTML, wordWrap, truncate, escapeHTML } = require('hexo-util'); -const titlecase = require('titlecase'); - -exports.strip_html = stripHTML; -exports.stripHTML = stripHTML; - -exports.trim = str => str.trim(); - -exports.titlecase = titlecase; - -exports.word_wrap = wordWrap; -exports.wordWrap = wordWrap; - -exports.truncate = truncate; - -exports.escape_html = escapeHTML; -exports.escapeHTML = escapeHTML; diff --git a/lib/plugins/helper/format.ts b/lib/plugins/helper/format.ts new file mode 100644 index 0000000000..c9372b507e --- /dev/null +++ b/lib/plugins/helper/format.ts @@ -0,0 +1,15 @@ +import { stripHTML, wordWrap, truncate, escapeHTML } from 'hexo-util'; +import titlecase from 'titlecase'; +export {stripHTML as strip_html}; +export {stripHTML}; + +export function trim(str) { + return str.trim(); +} + +export {titlecase}; +export {wordWrap as word_wrap}; +export {wordWrap}; +export {truncate}; +export {escapeHTML as escape_html}; +export {escapeHTML}; diff --git a/lib/plugins/helper/fragment_cache.js b/lib/plugins/helper/fragment_cache.ts similarity index 78% rename from lib/plugins/helper/fragment_cache.js rename to lib/plugins/helper/fragment_cache.ts index 19e4f675a0..1c38229e0c 100644 --- a/lib/plugins/helper/fragment_cache.js +++ b/lib/plugins/helper/fragment_cache.ts @@ -1,7 +1,7 @@ -'use strict'; -const { Cache } = require('hexo-util'); -module.exports = ctx => { +import { Cache } from 'hexo-util'; + +export = ctx => { const cache = new Cache(); // reset cache for watch mode diff --git a/lib/plugins/helper/full_url_for.js b/lib/plugins/helper/full_url_for.js deleted file mode 100755 index 997db74ab6..0000000000 --- a/lib/plugins/helper/full_url_for.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -const { full_url_for } = require('hexo-util'); - -module.exports = function(path) { - return full_url_for.call(this, path); -}; diff --git a/lib/plugins/helper/full_url_for.ts b/lib/plugins/helper/full_url_for.ts new file mode 100644 index 0000000000..4e77338082 --- /dev/null +++ b/lib/plugins/helper/full_url_for.ts @@ -0,0 +1,6 @@ + +import { full_url_for } from 'hexo-util'; + +export = function(path) { + return full_url_for.call(this, path); +} diff --git a/lib/plugins/helper/gravatar.js b/lib/plugins/helper/gravatar.js deleted file mode 100644 index 888a5eef30..0000000000 --- a/lib/plugins/helper/gravatar.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -const { gravatar } = require('hexo-util'); - -module.exports = gravatar; diff --git a/lib/plugins/helper/gravatar.ts b/lib/plugins/helper/gravatar.ts new file mode 100644 index 0000000000..5500335216 --- /dev/null +++ b/lib/plugins/helper/gravatar.ts @@ -0,0 +1,2 @@ +import { gravatar } from 'hexo-util'; +export = gravatar; diff --git a/lib/plugins/helper/image_tag.js b/lib/plugins/helper/image_tag.js deleted file mode 100644 index 5ca27c1f5c..0000000000 --- a/lib/plugins/helper/image_tag.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -const { htmlTag, url_for } = require('hexo-util'); - -function imageTagHelper(path, options = {}) { - const attrs = Object.assign({ - src: url_for.call(this, path) - }, options); - - if (attrs.class && Array.isArray(attrs.class)) { - attrs.class = attrs.class.join(' '); - } - - return htmlTag('img', attrs); -} - -module.exports = imageTagHelper; diff --git a/lib/plugins/helper/image_tag.ts b/lib/plugins/helper/image_tag.ts new file mode 100644 index 0000000000..402c6e3714 --- /dev/null +++ b/lib/plugins/helper/image_tag.ts @@ -0,0 +1,26 @@ +import { htmlTag, url_for } from 'hexo-util'; + +interface Options { + src?: string; + class?: string | string[]; +} + +interface Attrs { + src?: string; + class?: string; + [key: string]: string | undefined; +} + +function imageTagHelper(path: string, options: Options = {}) { + const attrs = Object.assign({ + src: url_for.call(this, path) as string + }, options); + + if (attrs.class && Array.isArray(attrs.class)) { + attrs.class = attrs.class.join(' '); + } + + return htmlTag('img', attrs as Attrs); +} + +export = imageTagHelper; diff --git a/lib/plugins/helper/index.js b/lib/plugins/helper/index.ts similarity index 97% rename from lib/plugins/helper/index.js rename to lib/plugins/helper/index.ts index fc86aa6074..8a90bc1132 100644 --- a/lib/plugins/helper/index.js +++ b/lib/plugins/helper/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +export = (ctx: Hexo) => { const { helper } = ctx.extend; const date = require('./date'); diff --git a/lib/plugins/helper/is.js b/lib/plugins/helper/is.ts similarity index 81% rename from lib/plugins/helper/is.js rename to lib/plugins/helper/is.ts index 9f74f3b6d6..1f843cf3ff 100644 --- a/lib/plugins/helper/is.js +++ b/lib/plugins/helper/is.ts @@ -1,5 +1,3 @@ -'use strict'; - function isCurrentHelper(path = '/', strict) { const currentPath = this.path.replace(/^[^/].*/, '/$&'); @@ -81,13 +79,13 @@ function isTagHelper(tag) { return Boolean(this.page.tag); } -exports.current = isCurrentHelper; -exports.home = isHomeHelper; -exports.home_first_page = isHomeFirstPageHelper; -exports.post = isPostHelper; -exports.page = isPageHelper; -exports.archive = isArchiveHelper; -exports.year = isYearHelper; -exports.month = isMonthHelper; -exports.category = isCategoryHelper; -exports.tag = isTagHelper; +export {isCurrentHelper as current}; +export {isHomeHelper as home}; +export {isHomeFirstPageHelper as home_first_page}; +export {isPostHelper as post}; +export {isPageHelper as page}; +export {isArchiveHelper as archive}; +export {isYearHelper as year}; +export {isMonthHelper as month}; +export {isCategoryHelper as category}; +export {isTagHelper as tag}; diff --git a/lib/plugins/helper/js.js b/lib/plugins/helper/js.ts similarity index 69% rename from lib/plugins/helper/js.js rename to lib/plugins/helper/js.ts index 1ab2ebcff4..24ca0fca3d 100644 --- a/lib/plugins/helper/js.js +++ b/lib/plugins/helper/js.ts @@ -1,11 +1,12 @@ -'use strict'; - -const { htmlTag, url_for } = require('hexo-util'); -const { default: moize } = require('moize'); +import { htmlTag, url_for } from 'hexo-util'; +import moize from 'moize'; +let relative_link = true; function jsHelper(...args) { let result = '\n'; + relative_link = this.config.relative_link; + args.flat(Infinity).forEach(item => { if (typeof item === 'string' || item instanceof String) { let path = item; @@ -23,7 +24,10 @@ function jsHelper(...args) { return result; } -module.exports = moize(jsHelper, { +export = moize(jsHelper, { maxSize: 10, - isDeepEqual: true + isDeepEqual: true, + updateCacheForKey() { + return relative_link; + } }); diff --git a/lib/plugins/helper/link_to.js b/lib/plugins/helper/link_to.js deleted file mode 100644 index 09daed2176..0000000000 --- a/lib/plugins/helper/link_to.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -const { htmlTag, url_for } = require('hexo-util'); - -function linkToHelper(path, text, options = {}) { - if (typeof options === 'boolean') options = {external: options}; - - if (!text) text = path.replace(/^https?:\/\/|\/$/g, ''); - - const attrs = Object.assign({ - href: url_for.call(this, path), - title: text - }, options); - - if (attrs.external) { - attrs.target = '_blank'; - attrs.rel = 'noopener'; - attrs.external = null; - } - - if (attrs.class && Array.isArray(attrs.class)) { - attrs.class = attrs.class.join(' '); - } - - return htmlTag('a', attrs, text); -} - -module.exports = linkToHelper; diff --git a/lib/plugins/helper/link_to.ts b/lib/plugins/helper/link_to.ts new file mode 100644 index 0000000000..cb0792d172 --- /dev/null +++ b/lib/plugins/helper/link_to.ts @@ -0,0 +1,45 @@ +import { htmlTag, url_for } from 'hexo-util'; + +interface Options { + href?: string; + title?: string; + external?: boolean | null; + class?: string | string[]; + target?: string; + rel?: string; +} + +interface Attrs { + href: string; + title: string; + external?: boolean | null; + class?: string; + target?: string; + rel?: string; + [key: string]: string | boolean | null | undefined; +} + +function linkToHelper(path: string, text: string, options: Options | boolean = {}) { + if (typeof options === 'boolean') options = {external: options}; + + if (!text) text = path.replace(/^https?:\/\/|\/$/g, ''); + + const attrs = Object.assign({ + href: url_for.call(this, path) as string, + title: text + }, options); + + if (attrs.external) { + attrs.target = '_blank'; + attrs.rel = 'noopener'; + attrs.external = null; + } + + if (attrs.class && Array.isArray(attrs.class)) { + attrs.class = attrs.class.join(' '); + } + + return htmlTag('a', attrs as Attrs, text); +} + +export = linkToHelper; diff --git a/lib/plugins/helper/list_archives.js b/lib/plugins/helper/list_archives.ts similarity index 84% rename from lib/plugins/helper/list_archives.js rename to lib/plugins/helper/list_archives.ts index 89b2b69414..2572a25cb7 100644 --- a/lib/plugins/helper/list_archives.js +++ b/lib/plugins/helper/list_archives.ts @@ -1,9 +1,25 @@ -'use strict'; +import { toMomentLocale } from './date'; +import { url_for } from 'hexo-util'; + +interface Options { + format?: string; + type?: string; + style?: string; + transform?: (name: string) => string; + separator?: string; + show_count?: boolean; + class?: string; + order?: number; +} -const { toMomentLocale } = require('./date'); -const { url_for } = require('hexo-util'); +interface Data { + name: string; + year: number; + month: number; + count: number; +} -function listArchivesHelper(options = {}) { +function listArchivesHelper(options: Options = {}) { const { config } = this; const archiveDir = config.archive_dir; const { timezone } = config; @@ -27,7 +43,7 @@ function listArchivesHelper(options = {}) { const posts = this.site.posts.sort('date', order); if (!posts.length) return result; - const data = []; + const data: Data[] = []; let length = 0; posts.forEach(post => { @@ -105,4 +121,4 @@ function listArchivesHelper(options = {}) { return result; } -module.exports = listArchivesHelper; +export = listArchivesHelper; diff --git a/lib/plugins/helper/list_categories.js b/lib/plugins/helper/list_categories.ts similarity index 93% rename from lib/plugins/helper/list_categories.js rename to lib/plugins/helper/list_categories.ts index 51455f2294..ff248d6bf3 100644 --- a/lib/plugins/helper/list_categories.js +++ b/lib/plugins/helper/list_categories.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { url_for } = require('hexo-util'); +import { url_for } from 'hexo-util'; function listCategoriesHelper(categories, options) { if (!options && (!categories || !Object.prototype.hasOwnProperty.call(categories, 'length'))) { @@ -21,7 +19,7 @@ function listCategoriesHelper(categories, options) { const childrenIndicator = Object.prototype.hasOwnProperty.call(options, 'children_indicator') ? options.children_indicator : false; const prepareQuery = parent => { - const query = {}; + const query: { parent?: any } = {}; if (parent) { query.parent = parent; @@ -32,7 +30,7 @@ function listCategoriesHelper(categories, options) { return categories.find(query).sort(orderby, order); }; - const hierarchicalList = (level, parent) => { + const hierarchicalList = (level: number, parent?: any) => { let result = ''; prepareQuery(parent).forEach((cat, i) => { @@ -77,7 +75,7 @@ function listCategoriesHelper(categories, options) { return result; }; - const flatList = (level, parent) => { + const flatList = (level: number, parent?: any) => { let result = ''; prepareQuery(parent).forEach((cat, i) => { @@ -107,4 +105,4 @@ function listCategoriesHelper(categories, options) { return flatList(0); } -module.exports = listCategoriesHelper; +export = listCategoriesHelper; diff --git a/lib/plugins/helper/list_posts.js b/lib/plugins/helper/list_posts.ts similarity index 93% rename from lib/plugins/helper/list_posts.js rename to lib/plugins/helper/list_posts.ts index 156ebff2fc..a3d9b9b5ca 100644 --- a/lib/plugins/helper/list_posts.js +++ b/lib/plugins/helper/list_posts.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { url_for } = require('hexo-util'); +import { url_for } from 'hexo-util'; function listPostsHelper(posts, options) { if (!options && (!posts || !Object.prototype.hasOwnProperty.call(posts, 'length'))) { @@ -55,4 +53,4 @@ function listPostsHelper(posts, options) { return result; } -module.exports = listPostsHelper; +export = listPostsHelper; diff --git a/lib/plugins/helper/list_tags.js b/lib/plugins/helper/list_tags.ts similarity index 94% rename from lib/plugins/helper/list_tags.js rename to lib/plugins/helper/list_tags.ts index c75626f720..b7b0d0b523 100644 --- a/lib/plugins/helper/list_tags.js +++ b/lib/plugins/helper/list_tags.ts @@ -1,9 +1,7 @@ -'use strict'; +import { url_for, escapeHTML } from 'hexo-util'; +import moize from 'moize'; -const { url_for, escapeHTML } = require('hexo-util'); -const { default: moize } = require('moize'); - -function listTagsHelper(tags, options) { +function listTagsHelper(tags, options?) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { options = tags; tags = this.site.tags; @@ -107,4 +105,4 @@ function listTagsHelperFactory(tags, options) { }).call(this, tags, options); } -module.exports = listTagsHelperFactory; +export = listTagsHelperFactory; diff --git a/lib/plugins/helper/mail_to.js b/lib/plugins/helper/mail_to.ts similarity index 60% rename from lib/plugins/helper/mail_to.js rename to lib/plugins/helper/mail_to.ts index 9752f0055d..90f9ef88c9 100644 --- a/lib/plugins/helper/mail_to.js +++ b/lib/plugins/helper/mail_to.ts @@ -1,9 +1,20 @@ -'use strict'; +import { htmlTag } from 'hexo-util'; +import moize from 'moize'; -const { htmlTag } = require('hexo-util'); -const { default: moize } = require('moize'); +interface Options { + href?: string; + title?: string; + class?: string | string[]; +} + +interface Attrs { + href: string; + title: string; + class?: string; + [key: string]: string | boolean | null | undefined; +} -function mailToHelper(path, text, options = {}) { +function mailToHelper(path, text, options: Options = {}) { if (Array.isArray(path)) path = path.join(','); if (!text) text = path; @@ -30,10 +41,10 @@ function mailToHelper(path, text, options = {}) { const querystring = new URLSearchParams(data).toString(); if (querystring) attrs.href += `?${querystring}`; - return htmlTag('a', attrs, text); + return htmlTag('a', attrs as Attrs, text); } -module.exports = moize(mailToHelper, { +export = moize(mailToHelper, { maxSize: 10, isDeepEqual: true }); diff --git a/lib/plugins/helper/markdown.js b/lib/plugins/helper/markdown.ts similarity index 65% rename from lib/plugins/helper/markdown.js rename to lib/plugins/helper/markdown.ts index 7df352843d..956425f072 100644 --- a/lib/plugins/helper/markdown.js +++ b/lib/plugins/helper/markdown.ts @@ -1,7 +1,5 @@ -'use strict'; - function markdownHelper(text, options) { return this.render(text, 'markdown', options); } -module.exports = markdownHelper; +export = markdownHelper; diff --git a/lib/plugins/helper/meta_generator.js b/lib/plugins/helper/meta_generator.ts old mode 100755 new mode 100644 similarity index 66% rename from lib/plugins/helper/meta_generator.js rename to lib/plugins/helper/meta_generator.ts index 20b7613939..4b0036c558 --- a/lib/plugins/helper/meta_generator.js +++ b/lib/plugins/helper/meta_generator.ts @@ -1,7 +1,5 @@ -'use strict'; - function metaGeneratorHelper() { return ``; } -module.exports = metaGeneratorHelper; +export = metaGeneratorHelper; diff --git a/lib/plugins/helper/number_format.js b/lib/plugins/helper/number_format.ts similarity index 81% rename from lib/plugins/helper/number_format.js rename to lib/plugins/helper/number_format.ts index c6c2c0985d..350e1b1129 100644 --- a/lib/plugins/helper/number_format.js +++ b/lib/plugins/helper/number_format.ts @@ -1,15 +1,19 @@ -'use strict'; +interface Options { + delimiter?: string; + separator?: string; + precision?: number; +} -function numberFormatHelper(num, options = {}) { +function numberFormatHelper(num: number, options: Options = {}) { const split = num.toString().split('.'); - let before = split.shift(); + let before = split.shift() as string; let after = split.length ? split[0] : ''; const delimiter = options.delimiter || ','; const separator = options.separator || '.'; const { precision } = options; if (delimiter) { - const beforeArr = []; + const beforeArr: string[] = []; const beforeLength = before.length; const beforeFirst = beforeLength % 3; @@ -46,4 +50,4 @@ function numberFormatHelper(num, options = {}) { return before + (after ? separator + after : ''); } -module.exports = numberFormatHelper; +export = numberFormatHelper; diff --git a/lib/plugins/helper/open_graph.js b/lib/plugins/helper/open_graph.ts similarity index 85% rename from lib/plugins/helper/open_graph.js rename to lib/plugins/helper/open_graph.ts index e9c3eb0dbe..9209eeda63 100644 --- a/lib/plugins/helper/open_graph.js +++ b/lib/plugins/helper/open_graph.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { isMoment, isDate } = require('moment'); -const { encodeURL, prettyUrls, stripHTML, escapeHTML } = require('hexo-util'); -const { default: moize } = require('moize'); +import { isMoment, isDate } from 'moment'; +import { encodeURL, prettyUrls, stripHTML, escapeHTML } from 'hexo-util'; +import moize from 'moize'; const localeMap = { 'en': 'en_US', @@ -36,7 +34,7 @@ const localeToTerritory = moize.shallow(str => { } }); -const meta = (name, content, escape) => { +const meta = (name, content, escape?: boolean) => { if (escape !== false && typeof content === 'string') { content = escapeHTML(content); } @@ -45,7 +43,7 @@ const meta = (name, content, escape) => { return `\n`; }; -const og = (name, content, escape) => { +const og = (name, content?: string, escape?: boolean) => { if (escape !== false && typeof content === 'string') { content = escapeHTML(content); } @@ -54,7 +52,27 @@ const og = (name, content, escape) => { return `\n`; }; -function openGraphHelper(options = {}) { +interface Options { + image?: string; + images?: string[]; + description?: string; + title?: string; + type?: string; + url?: string; + site_name?: string; + twitter_card?: string; + date?: boolean; + updated?: boolean; + language?: string; + author?: string; + twitter_image?: string; + twitter_id?: string; + twitter_site?: string; + fb_admins?: string; + fb_app_id?: string; +} + +function openGraphHelper(options: Options = {}) { const { config, page } = this; const { content } = page; let images = options.image || options.images || page.photos || []; @@ -151,7 +169,7 @@ function openGraphHelper(options = {}) { result += meta('twitter:card', twitterCard); if (options.twitter_image) { - let twitter_image = options.twitter_image; + let twitter_image: string | URL = options.twitter_image; twitter_image = new URL(twitter_image, url || config.url); result += meta('twitter:image', twitter_image, false); } else if (images.length) { @@ -180,4 +198,4 @@ function openGraphHelper(options = {}) { return result.trim(); } -module.exports = openGraphHelper; +export = openGraphHelper; diff --git a/lib/plugins/helper/paginator.js b/lib/plugins/helper/paginator.ts similarity index 85% rename from lib/plugins/helper/paginator.js rename to lib/plugins/helper/paginator.ts index 662c61022c..8d37c80739 100644 --- a/lib/plugins/helper/paginator.js +++ b/lib/plugins/helper/paginator.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { htmlTag, url_for } = require('hexo-util'); +import { htmlTag, url_for } from 'hexo-util'; const createLink = (options, ctx) => { const { base, format } = options; @@ -36,7 +34,7 @@ const showAll = (tags, options, ctx) => { } }; -const pagenasionPartShow = (tags, options, ctx) => { +const paginationPartShow = (tags, options, ctx) => { const { current, total, @@ -88,7 +86,28 @@ const pagenasionPartShow = (tags, options, ctx) => { } }; -function paginatorHelper(options = {}) { +interface Options { + base?: string; + current?: number; + format?: string; + total?: number; + end_size?: number; + mid_size?: number; + space?: string; + next_text?: string; + prev_text?: string; + prev_next?: boolean; + escape?: boolean; + page_class?: string; + current_class?: string; + space_class?: string; + prev_class?: string; + next_class?: string; + force_prev_next?: boolean; + show_all?: boolean; +} + +function paginatorHelper(options: Options = {}) { options = Object.assign({ base: this.page.base || '', current: this.page.current || 0, @@ -137,7 +156,7 @@ function paginatorHelper(options = {}) { if (options.show_all) { showAll(tags, options, this); } else { - pagenasionPartShow(tags, options, this); + paginationPartShow(tags, options, this); } // Display the link to the next page @@ -150,4 +169,4 @@ function paginatorHelper(options = {}) { return tags.join(''); } -module.exports = paginatorHelper; +export = paginatorHelper; diff --git a/lib/plugins/helper/partial.js b/lib/plugins/helper/partial.ts similarity index 82% rename from lib/plugins/helper/partial.js rename to lib/plugins/helper/partial.ts index bc3994189c..d548a58ba9 100644 --- a/lib/plugins/helper/partial.js +++ b/lib/plugins/helper/partial.ts @@ -1,8 +1,11 @@ -'use strict'; +import { dirname, join } from 'path'; -const { dirname, join } = require('path'); +interface Options { + cache?: boolean | string; + only?: boolean; +} -module.exports = ctx => function partial(name, locals, options = {}) { +export = ctx => function partial(name, locals, options: Options = {}) { if (typeof name !== 'string') throw new TypeError('name must be a string!'); const { cache } = options; diff --git a/lib/plugins/helper/relative_url.js b/lib/plugins/helper/relative_url.js deleted file mode 100644 index db1fba37fc..0000000000 --- a/lib/plugins/helper/relative_url.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -const { relative_url } = require('hexo-util'); - -module.exports = function(from, to) { - return relative_url(from, to); -}; diff --git a/lib/plugins/helper/relative_url.ts b/lib/plugins/helper/relative_url.ts new file mode 100644 index 0000000000..631a9946fd --- /dev/null +++ b/lib/plugins/helper/relative_url.ts @@ -0,0 +1,5 @@ +import { relative_url } from 'hexo-util'; + +export = function(from, to) { + return relative_url(from, to); +} diff --git a/lib/plugins/helper/render.js b/lib/plugins/helper/render.js deleted file mode 100644 index f71bdb2917..0000000000 --- a/lib/plugins/helper/render.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -module.exports = ctx => function render(text, engine, options) { - return ctx.render.renderSync({ - text, - engine - }, options); -}; diff --git a/lib/plugins/helper/render.ts b/lib/plugins/helper/render.ts new file mode 100644 index 0000000000..9f8473efb3 --- /dev/null +++ b/lib/plugins/helper/render.ts @@ -0,0 +1,6 @@ +export = ctx => function render(text, engine, options) { + return ctx.render.renderSync({ + text, + engine + }, options); +}; diff --git a/lib/plugins/helper/search_form.js b/lib/plugins/helper/search_form.ts similarity index 72% rename from lib/plugins/helper/search_form.js rename to lib/plugins/helper/search_form.ts index f90a99b6d7..6414615e82 100644 --- a/lib/plugins/helper/search_form.js +++ b/lib/plugins/helper/search_form.ts @@ -1,8 +1,12 @@ -'use strict'; +import moize from 'moize'; -const { default: moize } = require('moize'); +interface Options { + class?: string; + text?: string; + button?: string | boolean; +} -function searchFormHelper(options = {}) { +function searchFormHelper(options: Options = {}) { const { config } = this; const className = options.class || 'search-form'; const { text = 'Search', button } = options; @@ -10,4 +14,4 @@ function searchFormHelper(options = {}) { return `
${button ? `` : ''}
`; } -module.exports = moize.deep(searchFormHelper); +export = moize.deep(searchFormHelper); diff --git a/lib/plugins/helper/tagcloud.js b/lib/plugins/helper/tagcloud.ts similarity index 93% rename from lib/plugins/helper/tagcloud.js rename to lib/plugins/helper/tagcloud.ts index c6ee5f718d..0b9a58d0b0 100644 --- a/lib/plugins/helper/tagcloud.js +++ b/lib/plugins/helper/tagcloud.ts @@ -1,9 +1,7 @@ -'use strict'; +import { Color, url_for } from 'hexo-util'; +import moize from 'moize'; -const { Color, url_for } = require('hexo-util'); -const { default: moize } = require('moize'); - -function tagcloudHelper(tags, options) { +function tagcloudHelper(tags, options?) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { options = tags; tags = this.site.tags; @@ -94,4 +92,4 @@ function tagcloudHelperFactory(tags, options) { }).call(this, tags, options); } -module.exports = tagcloudHelperFactory; +export = tagcloudHelperFactory; diff --git a/lib/plugins/helper/toc.js b/lib/plugins/helper/toc.ts similarity index 86% rename from lib/plugins/helper/toc.js rename to lib/plugins/helper/toc.ts index e055ffd6f2..4abec4d30b 100644 --- a/lib/plugins/helper/toc.js +++ b/lib/plugins/helper/toc.ts @@ -1,8 +1,19 @@ -'use strict'; - -const { tocObj, escapeHTML, encodeURL } = require('hexo-util'); +import { tocObj, escapeHTML, encodeURL } from 'hexo-util'; + +interface Options { + min_depth?: number; + max_depth?: number; + class?: string; + class_item?: string; + class_link?: string; + class_text?: string; + class_child?: string; + class_number?: string; + class_level?: string; + list_number?: boolean; +} -function tocHelper(str, options = {}) { +function tocHelper(str, options: Options = {}) { options = Object.assign({ min_depth: 1, max_depth: 6, @@ -91,4 +102,4 @@ function tocHelper(str, options = {}) { return result; } -module.exports = tocHelper; +export = tocHelper; diff --git a/lib/plugins/helper/url_for.js b/lib/plugins/helper/url_for.js deleted file mode 100644 index a31379a29b..0000000000 --- a/lib/plugins/helper/url_for.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -const { url_for } = require('hexo-util'); - -module.exports = function(path, options) { - return url_for.call(this, path, options); -}; diff --git a/lib/plugins/helper/url_for.ts b/lib/plugins/helper/url_for.ts new file mode 100644 index 0000000000..49e7a5f124 --- /dev/null +++ b/lib/plugins/helper/url_for.ts @@ -0,0 +1,5 @@ +import { url_for } from 'hexo-util'; + +export = function(path, options) { + return url_for.call(this, path, options); +} diff --git a/lib/plugins/highlight/highlight.js b/lib/plugins/highlight/highlight.ts similarity index 98% rename from lib/plugins/highlight/highlight.js rename to lib/plugins/highlight/highlight.ts index c90ccd7994..787017fcf7 100644 --- a/lib/plugins/highlight/highlight.js +++ b/lib/plugins/highlight/highlight.ts @@ -1,5 +1,3 @@ -'use strict'; - // Lazy require highlight.js let highlight; diff --git a/lib/plugins/highlight/index.js b/lib/plugins/highlight/index.ts similarity index 69% rename from lib/plugins/highlight/index.js rename to lib/plugins/highlight/index.ts index 9dc22fd7d4..14da505e7f 100644 --- a/lib/plugins/highlight/index.js +++ b/lib/plugins/highlight/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +module.exports = (ctx: Hexo) => { const { highlight } = ctx.extend; highlight.register('highlight.js', require('./highlight')); diff --git a/lib/plugins/highlight/prism.js b/lib/plugins/highlight/prism.ts similarity index 74% rename from lib/plugins/highlight/prism.js rename to lib/plugins/highlight/prism.ts index 7d46461c96..33304068d4 100644 --- a/lib/plugins/highlight/prism.js +++ b/lib/plugins/highlight/prism.ts @@ -1,5 +1,3 @@ -'use strict'; - // Lazy require prismjs let prismHighlight; @@ -22,5 +20,9 @@ module.exports = function(code, options) { if (!prismHighlight) prismHighlight = require('hexo-util').prismHighlight; + if (Array.isArray(prismjsCfg.exclude_languages) && prismjsCfg.exclude_languages.includes(prismjsOptions.lang)) { + // Only wrap with
+ return `
${require('hexo-util').escapeHTML(code)}
`; + } return prismHighlight(code, prismjsOptions); }; diff --git a/lib/plugins/injector/index.js b/lib/plugins/injector/index.ts similarity index 57% rename from lib/plugins/injector/index.js rename to lib/plugins/injector/index.ts index be1a9aaca9..6736417bc8 100644 --- a/lib/plugins/injector/index.js +++ b/lib/plugins/injector/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +export = (ctx: Hexo) => { // eslint-disable-next-line no-unused-vars const { injector } = ctx.extend; }; diff --git a/lib/plugins/processor/asset.js b/lib/plugins/processor/asset.ts similarity index 88% rename from lib/plugins/processor/asset.js rename to lib/plugins/processor/asset.ts index 113dab4e8d..9693bbaeb5 100644 --- a/lib/plugins/processor/asset.js +++ b/lib/plugins/processor/asset.ts @@ -1,13 +1,11 @@ -'use strict'; - -const { timezone, toDate, isExcludedFile, isMatch } = require('./common'); -const Promise = require('bluebird'); -const { parse: yfm } = require('hexo-front-matter'); -const { extname, relative } = require('path'); -const { Pattern } = require('hexo-util'); -const { magenta } = require('picocolors'); - -module.exports = ctx => { +import { timezone, toDate, isExcludedFile, isMatch } from './common'; +import Promise from 'bluebird'; +import { parse as yfm } from 'hexo-front-matter'; +import { extname, relative } from 'path'; +import { Pattern } from 'hexo-util'; +import { magenta } from 'picocolors'; + +export = ctx => { return { pattern: new Pattern(path => { if (isExcludedFile(path, ctx.config)) return; diff --git a/lib/plugins/processor/common.js b/lib/plugins/processor/common.ts similarity index 71% rename from lib/plugins/processor/common.js rename to lib/plugins/processor/common.ts index e29c05ecb4..c981536598 100644 --- a/lib/plugins/processor/common.js +++ b/lib/plugins/processor/common.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { Pattern } = require('hexo-util'); -const moment = require('moment-timezone'); -const micromatch = require('micromatch'); +import { Pattern } from 'hexo-util'; +import moment from 'moment-timezone'; +import micromatch from 'micromatch'; const DURATION_MINUTE = 1000 * 60; @@ -27,16 +25,16 @@ function isExcludedFile(path, config) { return false; } -exports.ignoreTmpAndHiddenFile = new Pattern(path => { +export const ignoreTmpAndHiddenFile = new Pattern(path => { if (isTmpFile(path) || isHiddenFile(path)) return false; return true; }); -exports.isTmpFile = isTmpFile; -exports.isHiddenFile = isHiddenFile; -exports.isExcludedFile = isExcludedFile; +export {isTmpFile}; +export {isHiddenFile}; +export {isExcludedFile}; -exports.toDate = date => { +export function toDate(date) { if (!date || moment.isMoment(date)) return date; if (!(date instanceof Date)) { @@ -46,9 +44,9 @@ exports.toDate = date => { if (isNaN(date.getTime())) return; return date; -}; +} -exports.timezone = (date, timezone) => { +export function timezone(date, timezone) { if (moment.isMoment(date)) date = date.toDate(); const offset = date.getTimezoneOffset(); @@ -57,6 +55,6 @@ exports.timezone = (date, timezone) => { const diff = (offset - target) * DURATION_MINUTE; return new Date(ms - diff); -}; +} -exports.isMatch = isMatch; +export {isMatch}; diff --git a/lib/plugins/processor/data.js b/lib/plugins/processor/data.ts similarity index 83% rename from lib/plugins/processor/data.js rename to lib/plugins/processor/data.ts index e6878f24b5..7757e7c99c 100644 --- a/lib/plugins/processor/data.js +++ b/lib/plugins/processor/data.ts @@ -1,9 +1,7 @@ -'use strict'; +import { Pattern } from 'hexo-util'; +import { extname } from 'path'; -const { Pattern } = require('hexo-util'); -const { extname } = require('path'); - -module.exports = ctx => ({ +export = ctx => ({ pattern: new Pattern('_data/*path'), process: function dataProcessor(file) { diff --git a/lib/plugins/processor/index.js b/lib/plugins/processor/index.ts similarity index 67% rename from lib/plugins/processor/index.js rename to lib/plugins/processor/index.ts index caa18dbaca..e7ae2c580c 100644 --- a/lib/plugins/processor/index.js +++ b/lib/plugins/processor/index.ts @@ -1,9 +1,9 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +export = (ctx: Hexo) => { const { processor } = ctx.extend; - function register(name) { + function register(name: string) { const obj = require(`./${name}`)(ctx); processor.register(obj.pattern, obj.process); } diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.ts similarity index 91% rename from lib/plugins/processor/post.js rename to lib/plugins/processor/post.ts index 4e61712800..9c61a561b8 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.ts @@ -1,12 +1,10 @@ -'use strict'; - -const { toDate, timezone, isExcludedFile, isTmpFile, isHiddenFile, isMatch } = require('./common'); -const Promise = require('bluebird'); -const { parse: yfm } = require('hexo-front-matter'); -const { extname, join } = require('path'); -const { stat, listDir } = require('hexo-fs'); -const { slugize, Pattern, Permalink } = require('hexo-util'); -const { magenta } = require('picocolors'); +import { toDate, timezone, isExcludedFile, isTmpFile, isHiddenFile, isMatch } from './common'; +import Promise from 'bluebird'; +import { parse as yfm } from 'hexo-front-matter'; +import { extname, join } from 'path'; +import { stat, listDir } from 'hexo-fs'; +import { slugize, Pattern, Permalink } from 'hexo-util'; +import { magenta } from 'picocolors'; const postDir = '_posts/'; const draftDir = '_drafts/'; @@ -22,7 +20,7 @@ const preservedKeys = { hash: true }; -module.exports = ctx => { +export = ctx => { return { pattern: new Pattern(path => { if (isTmpFile(path)) return; @@ -161,10 +159,6 @@ function processPost(ctx, file) { data.photos = [data.photos]; } - if (data.link && !data.title) { - data.title = data.link.replace(/^https?:\/\/|\/$/g, ''); - } - if (data.permalink) { data.__permalink = data.permalink; data.permalink = undefined; @@ -223,7 +217,9 @@ function scanAssetDir(ctx, post) { const assetDir = post.asset_dir; const baseDir = ctx.base_dir; + const sourceDir = ctx.config.source_dir; const baseDirLength = baseDir.length; + const sourceDirLength = sourceDir.length; const PostAsset = ctx.model('PostAsset'); return stat(assetDir).then(stats => { @@ -235,6 +231,7 @@ function scanAssetDir(ctx, post) { throw err; }).filter(item => !isExcludedFile(item, ctx.config)).map(item => { const id = join(assetDir, item).substring(baseDirLength).replace(/\\/g, '/'); + const renderablePath = id.substring(sourceDirLength + 1); const asset = PostAsset.findById(id); if (shouldSkipAsset(ctx, post, asset)) return undefined; @@ -243,7 +240,8 @@ function scanAssetDir(ctx, post) { _id: id, post: post._id, slug: item, - modified: true + modified: true, + renderable: ctx.render.isRenderable(renderablePath) && !isMatch(renderablePath, ctx.config.skip_render) }); }); } diff --git a/lib/plugins/renderer/index.js b/lib/plugins/renderer/index.ts similarity index 90% rename from lib/plugins/renderer/index.js rename to lib/plugins/renderer/index.ts index d9f03fe718..c8529c5a57 100644 --- a/lib/plugins/renderer/index.js +++ b/lib/plugins/renderer/index.ts @@ -1,6 +1,6 @@ -'use strict'; +import type Hexo from '../../hexo'; -module.exports = ctx => { +export = (ctx: Hexo) => { const { renderer } = ctx.extend; const plain = require('./plain'); diff --git a/lib/plugins/renderer/json.js b/lib/plugins/renderer/json.ts similarity index 58% rename from lib/plugins/renderer/json.js rename to lib/plugins/renderer/json.ts index 8dbe42cacc..7c87cf6ecb 100644 --- a/lib/plugins/renderer/json.js +++ b/lib/plugins/renderer/json.ts @@ -1,7 +1,5 @@ -'use strict'; - function jsonRenderer(data) { return JSON.parse(data.text); } -module.exports = jsonRenderer; +export = jsonRenderer; diff --git a/lib/plugins/renderer/nunjucks.js b/lib/plugins/renderer/nunjucks.ts similarity index 90% rename from lib/plugins/renderer/nunjucks.js rename to lib/plugins/renderer/nunjucks.ts index b6760e0e00..1ba4ba0205 100644 --- a/lib/plugins/renderer/nunjucks.js +++ b/lib/plugins/renderer/nunjucks.ts @@ -1,9 +1,6 @@ - -'use strict'; - -const nunjucks = require('nunjucks'); -const { readFileSync } = require('hexo-fs'); -const { dirname } = require('path'); +import nunjucks from 'nunjucks'; +import { readFileSync } from 'hexo-fs'; +import { dirname } from 'path'; function toArray(value) { if (Array.isArray(value)) { @@ -67,4 +64,4 @@ njkRenderer.compile = data => { return locals => njkCompile(data).render(locals); }; -module.exports = njkRenderer; +export = njkRenderer; diff --git a/lib/plugins/renderer/plain.js b/lib/plugins/renderer/plain.ts similarity index 53% rename from lib/plugins/renderer/plain.js rename to lib/plugins/renderer/plain.ts index 2961bd8e7a..e1aa47f2df 100644 --- a/lib/plugins/renderer/plain.js +++ b/lib/plugins/renderer/plain.ts @@ -1,7 +1,5 @@ -'use strict'; - function plainRenderer(data) { return data.text; } -module.exports = plainRenderer; +export = plainRenderer; diff --git a/lib/plugins/renderer/yaml.js b/lib/plugins/renderer/yaml.ts similarity index 56% rename from lib/plugins/renderer/yaml.js rename to lib/plugins/renderer/yaml.ts index ad9a8a6c02..a8f0af4a34 100644 --- a/lib/plugins/renderer/yaml.js +++ b/lib/plugins/renderer/yaml.ts @@ -1,8 +1,6 @@ -'use strict'; - -const yaml = require('js-yaml'); -const { escape } = require('hexo-front-matter'); -const log = require('hexo-log').default(); +import yaml from 'js-yaml'; +import { escape } from 'hexo-front-matter'; +import logger from 'hexo-log'; let schema = {}; // FIXME: workaround for https://github.com/hexojs/hexo/issues/4917 @@ -10,7 +8,7 @@ try { schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all); } catch (e) { if (e instanceof yaml.YAMLException) { - log.warn('YAMLException: please see https://github.com/hexojs/hexo/issues/4917'); + logger().warn('YAMLException: please see https://github.com/hexojs/hexo/issues/4917'); } else { throw e; } @@ -20,4 +18,4 @@ function yamlHelper(data) { return yaml.load(escape(data.text), { schema }); } -module.exports = yamlHelper; +export = yamlHelper; diff --git a/lib/plugins/tag/asset_img.js b/lib/plugins/tag/asset_img.ts similarity index 81% rename from lib/plugins/tag/asset_img.js rename to lib/plugins/tag/asset_img.ts index 5189c28a27..e608b499ff 100644 --- a/lib/plugins/tag/asset_img.js +++ b/lib/plugins/tag/asset_img.ts @@ -1,7 +1,5 @@ -'use strict'; - -const img = require('./img'); -const { encodeURL } = require('hexo-util'); +import img from './img'; +import { encodeURL } from 'hexo-util'; /** * Asset image tag @@ -9,7 +7,7 @@ const { encodeURL } = require('hexo-util'); * Syntax: * {% asset_img [class names] slug [width] [height] [title text [alt text]]%} */ -module.exports = ctx => { +export = ctx => { const PostAsset = ctx.model('PostAsset'); return function assetImgTag(args) { diff --git a/lib/plugins/tag/asset_link.js b/lib/plugins/tag/asset_link.ts similarity index 88% rename from lib/plugins/tag/asset_link.js rename to lib/plugins/tag/asset_link.ts index 8f3225e297..eb07087625 100644 --- a/lib/plugins/tag/asset_link.js +++ b/lib/plugins/tag/asset_link.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { encodeURL, escapeHTML } = require('hexo-util'); +import { encodeURL, escapeHTML } from 'hexo-util'; /** * Asset link tag @@ -8,7 +6,7 @@ const { encodeURL, escapeHTML } = require('hexo-util'); * Syntax: * {% asset_link slug [title] [escape] %} */ -module.exports = ctx => { +export = ctx => { const PostAsset = ctx.model('PostAsset'); return function assetLinkTag(args) { diff --git a/lib/plugins/tag/asset_path.js b/lib/plugins/tag/asset_path.ts similarity index 82% rename from lib/plugins/tag/asset_path.js rename to lib/plugins/tag/asset_path.ts index d988176d30..7242e3ce73 100644 --- a/lib/plugins/tag/asset_path.js +++ b/lib/plugins/tag/asset_path.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { encodeURL } = require('hexo-util'); +import { encodeURL } from 'hexo-util'; /** * Asset path tag @@ -8,7 +6,7 @@ const { encodeURL } = require('hexo-util'); * Syntax: * {% asset_path slug %} */ -module.exports = ctx => { +export = ctx => { const PostAsset = ctx.model('PostAsset'); return function assetPathTag(args) { diff --git a/lib/plugins/tag/blockquote.js b/lib/plugins/tag/blockquote.ts similarity index 93% rename from lib/plugins/tag/blockquote.js rename to lib/plugins/tag/blockquote.ts index 847727da92..1f99e67c1a 100644 --- a/lib/plugins/tag/blockquote.js +++ b/lib/plugins/tag/blockquote.ts @@ -1,8 +1,6 @@ -'use strict'; - // Based on: https://raw.github.com/imathis/octopress/master/plugins/blockquote.rb -const titlecase = require('titlecase'); +import titlecase from 'titlecase'; const rFullCiteWithTitle = /(\S.*)\s+(https?:\/\/\S+)\s+(.+)/i; const rFullCite = /(\S.*)\s+(https?:\/\/\S+)/i; @@ -57,7 +55,7 @@ const parseFooter = (args, ctx) => { * {% endblockquote %} */ -module.exports = ctx => function blockquoteTag(args, content) { +export = ctx => function blockquoteTag(args, content) { const footer = parseFooter(args, ctx); let result = '
'; diff --git a/lib/plugins/tag/code.js b/lib/plugins/tag/code.ts similarity index 92% rename from lib/plugins/tag/code.js rename to lib/plugins/tag/code.ts index d4620dcccc..266bda70e2 100644 --- a/lib/plugins/tag/code.js +++ b/lib/plugins/tag/code.ts @@ -1,13 +1,23 @@ -'use strict'; - // Based on: https://raw.github.com/imathis/octopress/master/plugins/code_block.rb -const { escapeHTML } = require('hexo-util'); +import { escapeHTML } from 'hexo-util'; const rCaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/\S+)\s+(.+)/i; const rCaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/\S+)/i; const rCaption = /\S[\S\s]*/; +interface Options { + lang: string; + language_attr: boolean; + firstLine: number; + caption: string; + line_number: boolean; + line_threshold: number; + mark: number[]; + wrap: boolean; + lines_length?: number; +} + /** * Code block tag * Syntax: @@ -27,7 +37,7 @@ const rCaption = /\S[\S\s]*/; * @returns {String} Code snippet with code highlighting */ -function parseArgs(args) { +function parseArgs(args): Options { const _else = []; const len = args.length; let lang, language_attr, @@ -116,7 +126,7 @@ function parseArgs(args) { }; } -module.exports = ctx => function codeTag(args, content) { +export = ctx => function codeTag(args, content) { // If neither highlight.js nor prism.js is enabled, return escaped code directly if (!ctx.extend.highlight.query(ctx.config.syntax_highlighter)) { diff --git a/lib/plugins/tag/full_url_for.ts b/lib/plugins/tag/full_url_for.ts new file mode 100644 index 0000000000..6dd2ff76b8 --- /dev/null +++ b/lib/plugins/tag/full_url_for.ts @@ -0,0 +1,17 @@ +import { full_url_for, htmlTag } from 'hexo-util'; + +/** + * Full url for tag + * + * Syntax: + * {% full_url_for text path %} + */ +export = ctx => { + return function fullUrlForTag([text, path]) { + const url = full_url_for.call(ctx, path); + const attrs = { + href: url + }; + return htmlTag('a', attrs, text); + }; +}; diff --git a/lib/plugins/tag/iframe.js b/lib/plugins/tag/iframe.ts similarity index 83% rename from lib/plugins/tag/iframe.js rename to lib/plugins/tag/iframe.ts index ec8b7875f9..f1e6b2663d 100644 --- a/lib/plugins/tag/iframe.js +++ b/lib/plugins/tag/iframe.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { htmlTag } = require('hexo-util'); +import { htmlTag } from 'hexo-util'; /** * Iframe tag @@ -26,4 +24,4 @@ function iframeTag(args) { return htmlTag('iframe', attrs, ''); } -module.exports = iframeTag; +export = iframeTag; diff --git a/lib/plugins/tag/img.js b/lib/plugins/tag/img.ts similarity index 94% rename from lib/plugins/tag/img.js rename to lib/plugins/tag/img.ts index e474d27b32..22e4970020 100644 --- a/lib/plugins/tag/img.js +++ b/lib/plugins/tag/img.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { htmlTag, url_for } = require('hexo-util'); +import { htmlTag, url_for } from 'hexo-util'; const rUrl = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\w]*))?)/; const rMetaDoubleQuote = /"?([^"]+)?"?/; @@ -12,7 +10,7 @@ const rMetaSingleQuote = /'?([^']+)?'?/; * Syntax: * {% img [class names] /path/to/image [width] [height] [title text [alt text]] %} */ -module.exports = ctx => { +export = ctx => { return function imgTag(args) { const classes = []; diff --git a/lib/plugins/tag/include_code.js b/lib/plugins/tag/include_code.ts similarity index 89% rename from lib/plugins/tag/include_code.js rename to lib/plugins/tag/include_code.ts index 8d9d1a9a95..8457c75294 100644 --- a/lib/plugins/tag/include_code.js +++ b/lib/plugins/tag/include_code.ts @@ -1,7 +1,5 @@ -'use strict'; - -const { exists, readFile } = require('hexo-fs'); -const { basename, extname, join, posix } = require('path'); +import { exists, readFile } from 'hexo-fs'; +import { basename, extname, join, posix } from 'path'; const rCaptionTitleFile = /(.*)?(?:\s+|^)(\/*\S+)/; const rLang = /\s*lang:(\w+)/i; @@ -15,7 +13,7 @@ const rTo = /\s*to:(\d+)/i; * {% include_code [title] [lang:language] path/to/file %} */ -module.exports = ctx => function includeCodeTag(args) { +export = ctx => function includeCodeTag(args) { let codeDir = ctx.config.code_dir; let arg = args.join(' '); @@ -56,7 +54,7 @@ module.exports = ctx => function includeCodeTag(args) { return exists(src).then(exist => { if (exist) return readFile(src); - }).then(code => { + }).then((code: string) => { if (!code) return; const lines = code.split('\n'); diff --git a/lib/plugins/tag/index.js b/lib/plugins/tag/index.ts similarity index 86% rename from lib/plugins/tag/index.js rename to lib/plugins/tag/index.ts index 4b6aa2531f..b2306d36e4 100644 --- a/lib/plugins/tag/index.js +++ b/lib/plugins/tag/index.ts @@ -1,8 +1,7 @@ -'use strict'; +import moize from 'moize'; +import type Hexo from '../../hexo'; -const { default: moize } = require('moize'); - -module.exports = ctx => { +export default (ctx: Hexo) => { const { tag } = ctx.extend; const blockquote = require('./blockquote')(ctx); @@ -45,12 +44,15 @@ module.exports = ctx => { tag.register('asset_image', assetImg); tag.register('pullquote', require('./pullquote')(ctx), true); + + tag.register('url_for', require('./url_for')(ctx)); + tag.register('full_url_for', require('./full_url_for')(ctx)); }; // Use WeakMap to track different ctx (in case there is any) const moized = new WeakMap(); -module.exports.postFindOneFactory = function postFindOneFactory(ctx) { +export function postFindOneFactory(ctx) { if (moized.has(ctx)) { return moized.get(ctx); } @@ -62,7 +64,7 @@ module.exports.postFindOneFactory = function postFindOneFactory(ctx) { moized.set(ctx, moizedPostFindOne); return moizedPostFindOne; -}; +} function createPostFindOne(ctx) { const Post = ctx.model('Post'); diff --git a/lib/plugins/tag/link.js b/lib/plugins/tag/link.ts similarity index 92% rename from lib/plugins/tag/link.js rename to lib/plugins/tag/link.ts index 744f874f88..0df202b574 100644 --- a/lib/plugins/tag/link.js +++ b/lib/plugins/tag/link.ts @@ -1,6 +1,4 @@ -'use strict'; - -const { htmlTag } = require('hexo-util'); +import { htmlTag } from 'hexo-util'; const rUrl = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\w]*))?)/; @@ -56,4 +54,4 @@ function linkTag(args, content) { return htmlTag('a', attrs, text.join(' ')); } -module.exports = linkTag; +export = linkTag; diff --git a/lib/plugins/tag/post_link.js b/lib/plugins/tag/post_link.ts similarity index 78% rename from lib/plugins/tag/post_link.js rename to lib/plugins/tag/post_link.ts index 838b046560..782714a1d2 100644 --- a/lib/plugins/tag/post_link.js +++ b/lib/plugins/tag/post_link.ts @@ -1,7 +1,5 @@ -'use strict'; - -const { encodeURL, escapeHTML } = require('hexo-util'); -const { postFindOneFactory } = require('./'); +import { encodeURL, escapeHTML } from 'hexo-util'; +import { postFindOneFactory } from './'; /** * Post link tag @@ -9,7 +7,7 @@ const { postFindOneFactory } = require('./'); * Syntax: * {% post_link slug | title [title] [escape] %} */ -module.exports = ctx => { +export = ctx => { return function postLinkTag(args) { const slug = args.shift(); if (!slug) { @@ -29,9 +27,9 @@ module.exports = ctx => { throw new Error(`Post not found: post_link ${slug}.`); } - let title = args.length ? args.join(' ') : post.title; + let title = args.length ? args.join(' ') : post.title || post.slug; // Let attribute be the true post title so it appears in tooltip. - const attrTitle = escapeHTML(post.title); + const attrTitle = escapeHTML(post.title || post.slug); if (escape === 'true') title = escapeHTML(title); const link = encodeURL(new URL(post.path, ctx.config.url).pathname); diff --git a/lib/plugins/tag/post_path.js b/lib/plugins/tag/post_path.ts similarity index 75% rename from lib/plugins/tag/post_path.js rename to lib/plugins/tag/post_path.ts index 679f65039f..e9aec212f2 100644 --- a/lib/plugins/tag/post_path.js +++ b/lib/plugins/tag/post_path.ts @@ -1,7 +1,5 @@ -'use strict'; - -const { encodeURL } = require('hexo-util'); -const { postFindOneFactory } = require('./'); +import { encodeURL } from 'hexo-util'; +import { postFindOneFactory } from './'; /** * Post path tag @@ -9,7 +7,7 @@ const { postFindOneFactory } = require('./'); * Syntax: * {% post_path slug | title %} */ -module.exports = ctx => { +export = ctx => { return function postPathTag(args) { const slug = args.shift(); if (!slug) return; diff --git a/lib/plugins/tag/pullquote.js b/lib/plugins/tag/pullquote.ts similarity index 78% rename from lib/plugins/tag/pullquote.js rename to lib/plugins/tag/pullquote.ts index 8a2f945d4a..1235a26d9e 100644 --- a/lib/plugins/tag/pullquote.js +++ b/lib/plugins/tag/pullquote.ts @@ -1,5 +1,3 @@ -'use strict'; - /** * Pullquote tag * @@ -8,7 +6,7 @@ * Quote string * {% endpullquote %} */ -module.exports = ctx => function pullquoteTag(args, content) { +export = ctx => function pullquoteTag(args, content) { args.unshift('pullquote'); const result = ctx.render.renderSync({text: content, engine: 'markdown'}); diff --git a/lib/plugins/tag/url_for.ts b/lib/plugins/tag/url_for.ts new file mode 100644 index 0000000000..6fb3750886 --- /dev/null +++ b/lib/plugins/tag/url_for.ts @@ -0,0 +1,17 @@ +import { url_for, htmlTag } from 'hexo-util'; + +/** + * Url for tag + * + * Syntax: + * {% url_for text path [relative] %} + */ +export = ctx => { + return function urlForTag([text, path, relative]) { + const url = url_for.call(ctx, path, relative ? { relative: relative !== 'false' } : undefined); + const attrs = { + href: url + }; + return htmlTag('a', attrs, text); + }; +}; diff --git a/lib/theme/index.js b/lib/theme/index.ts similarity index 71% rename from lib/theme/index.js rename to lib/theme/index.ts index 95f21aed4d..d37cd281b5 100644 --- a/lib/theme/index.js +++ b/lib/theme/index.ts @@ -1,12 +1,19 @@ -'use strict'; - -const { extname } = require('path'); -const Box = require('../box'); -const View = require('./view'); -const I18n = require('hexo-i18n'); +import { extname } from 'path'; +import Box from '../box'; +import View from './view'; +import I18n from 'hexo-i18n'; +import { config } from './processors/config'; +import { i18n } from './processors/i18n'; +import { source } from './processors/source'; +import { view } from './processors/view'; class Theme extends Box { - constructor(ctx, options) { + public config: any; + public views: any; + public i18n: any; + public View: any; + + constructor(ctx, options?) { super(ctx, ctx.theme_dir, options); this.config = {}; @@ -14,13 +21,13 @@ class Theme extends Box { this.views = {}; this.processors = [ - require('./processors/config'), - require('./processors/i18n'), - require('./processors/source'), - require('./processors/view') + config, + i18n, + source, + view ]; - let languages = ctx.config.language; + let languages: string | string[] = ctx.config.language; if (!Array.isArray(languages)) languages = [languages]; @@ -76,4 +83,4 @@ class Theme extends Box { } } -module.exports = Theme; +export = Theme; diff --git a/lib/theme/processors/config.js b/lib/theme/processors/config.ts similarity index 64% rename from lib/theme/processors/config.js rename to lib/theme/processors/config.ts index 53ce70ff09..64f65e2c28 100644 --- a/lib/theme/processors/config.js +++ b/lib/theme/processors/config.ts @@ -1,8 +1,6 @@ -'use strict'; +import { Pattern } from 'hexo-util'; -const { Pattern } = require('hexo-util'); - -exports.process = function(file) { +function process(file) { if (file.type === 'delete') { file.box.config = {}; return; @@ -15,6 +13,11 @@ exports.process = function(file) { this.log.error('Theme config load failed.'); throw err; }); -}; +} -exports.pattern = new Pattern(/^_config\.\w+$/); +const pattern = new Pattern(/^_config\.\w+$/); + +export const config = { + pattern, + process +}; diff --git a/lib/theme/processors/i18n.js b/lib/theme/processors/i18n.ts similarity index 64% rename from lib/theme/processors/i18n.js rename to lib/theme/processors/i18n.ts index 1fe8983671..ce955eb8a4 100644 --- a/lib/theme/processors/i18n.js +++ b/lib/theme/processors/i18n.ts @@ -1,9 +1,7 @@ -'use strict'; +import { Pattern } from 'hexo-util'; +import { extname } from 'path'; -const { Pattern } = require('hexo-util'); -const { extname } = require('path'); - -exports.process = file => { +function process(file) { const { path } = file.params; const ext = extname(path); const name = path.substring(0, path.length - ext.length); @@ -18,6 +16,11 @@ exports.process = file => { if (typeof data !== 'object') return; i18n.set(name, data); }); -}; +} -exports.pattern = new Pattern('languages/*path'); +const pattern = new Pattern('languages/*path'); + +export const i18n = { + pattern, + process +}; diff --git a/lib/theme/processors/source.js b/lib/theme/processors/source.ts similarity index 73% rename from lib/theme/processors/source.js rename to lib/theme/processors/source.ts index 826b6a109e..40915fae47 100644 --- a/lib/theme/processors/source.js +++ b/lib/theme/processors/source.ts @@ -1,9 +1,7 @@ -'use strict'; +import { Pattern } from 'hexo-util'; +import * as common from '../../plugins/processor/common'; -const { Pattern } = require('hexo-util'); -const common = require('../../plugins/processor/common'); - -exports.process = function(file) { +function process(file) { const Asset = this.model('Asset'); const id = file.source.substring(this.base_dir.length).replace(/\\/g, '/'); const { path } = file.params; @@ -22,9 +20,9 @@ exports.process = function(file) { path, modified: file.type !== 'skip' }); -}; +} -exports.pattern = new Pattern(path => { +const pattern = new Pattern(path => { if (!path.startsWith('source/')) return false; path = path.substring(7); @@ -32,3 +30,8 @@ exports.pattern = new Pattern(path => { return {path}; }); + +export const source = { + pattern, + process +}; diff --git a/lib/theme/processors/view.js b/lib/theme/processors/view.ts similarity index 56% rename from lib/theme/processors/view.js rename to lib/theme/processors/view.ts index b7cdd4a5bb..c4011564e2 100644 --- a/lib/theme/processors/view.js +++ b/lib/theme/processors/view.ts @@ -1,8 +1,6 @@ -'use strict'; +import { Pattern } from 'hexo-util'; -const { Pattern } = require('hexo-util'); - -exports.process = file => { +function process(file) { const { path } = file.params; if (file.type === 'delete') { @@ -13,6 +11,11 @@ exports.process = file => { return file.read().then(result => { file.box.setView(path, result); }); -}; +} -exports.pattern = new Pattern('layout/*path'); +const pattern = new Pattern('layout/*path'); + +export const view = { + pattern, + process +}; diff --git a/lib/theme/view.js b/lib/theme/view.ts similarity index 83% rename from lib/theme/view.js rename to lib/theme/view.ts index 6c0439bc48..f2b2234d4f 100644 --- a/lib/theme/view.js +++ b/lib/theme/view.ts @@ -1,8 +1,6 @@ -'use strict'; - -const { dirname, extname, join } = require('path'); -const { parse: yfm } = require('hexo-front-matter'); -const Promise = require('bluebird'); +import { dirname, extname, join } from 'path'; +import { parse as yfm } from 'hexo-front-matter'; +import Promise from 'bluebird'; const assignIn = (target, ...sources) => { const length = sources.length; @@ -18,7 +16,22 @@ const assignIn = (target, ...sources) => { return target; }; +class Options { + layout?: any; +} + class View { + public path: any; + public source: any; + public _theme: any; + public data: any; + public _compiled: any; + public _compiledSync: any; + public _helper: any; + public _render: any; + public layout: any; + public _content: any; + constructor(path, data) { this.path = path; this.source = join(this._theme.base, 'layout', path); @@ -27,13 +40,15 @@ class View { this._precompile(); } - render(options = {}, callback) { + // eslint-disable-next-line @typescript-eslint/ban-types + render(options: Options | Function = {}, callback) { if (!callback && typeof options === 'function') { callback = options; options = {}; } const { data } = this; - const { layout = options.layout } = data; + // eslint-disable-next-line no-extra-parens + const { layout = (options as Options).layout } = data; const locals = this._buildLocals(options); return this._compiled(this._bindHelpers(locals)).then(result => { @@ -52,7 +67,7 @@ class View { }).asCallback(callback); } - renderSync(options = {}) { + renderSync(options: Options = {}) { const { data } = this; const { layout = options.layout } = data; const locals = this._buildLocals(options); @@ -143,4 +158,4 @@ class View { } } -module.exports = View; +export = View; diff --git a/package.json b/package.json index 22194c1ad8..a9681ab722 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,26 @@ { "name": "hexo", - "version": "6.3.0", + "version": "7.0.0-rc2", "description": "A fast, simple & powerful blog framework, powered by Node.js.", - "main": "lib/hexo", + "main": "dist/hexo", "bin": { "hexo": "./bin/hexo" }, "scripts": { - "eslint": "eslint .", - "test": "mocha test/index.js", + "prepublish ": "npm run clean && npm run build", + "build": "tsc -b", + "clean": "tsc -b --clean", + "eslint": "eslint lib test", + "pretest": "npm run clean && npm run build", + "test": "mocha test/index.js --require ts-node/register", "test-cov": "c8 --reporter=lcovonly npm test -- --no-parallel", "prepare": "husky install" }, - "directories": { - "lib": "./lib", - "bin": "./bin" - }, "files": [ - "lib/", + "dist/", "bin/" ], + "types": "./dist/hexo/index.d.ts", "repository": "hexojs/hexo", "homepage": "https://hexo.io/", "funding": { @@ -43,15 +44,15 @@ "archy": "^1.0.0", "bluebird": "^3.7.2", "hexo-cli": "^4.3.0", - "hexo-front-matter": "^4.0.0", - "hexo-fs": "^4.0.0", + "hexo-front-matter": "4.0.0", + "hexo-fs": "^4.1.1", "hexo-i18n": "^2.0.0", "hexo-log": "^4.0.1", "hexo-util": "^3.0.1", "js-yaml": "^4.1.0", "js-yaml-js-types": "^1.0.0", "micromatch": "^4.0.4", - "moize": "^6.1.0", + "moize": "^6.1.6", "moment": "^2.29.1", "moment-timezone": "^0.5.34", "nunjucks": "^3.2.3", @@ -65,19 +66,25 @@ "warehouse": "^5.0.0" }, "devDependencies": { - "@easyops/git-exec-and-restage": "^1.0.4", "0x": "^5.1.2", + "@types/bluebird": "^3.5.37", + "@types/node": "^18.11.8", + "@types/nunjucks": "^3.2.2", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", "c8": "^8.0.0", "chai": "^4.3.6", "cheerio": "0.22.0", "decache": "^4.6.1", - "eslint": "^8.8.0", + "eslint": "^8.48.0", "eslint-config-hexo": "^5.0.0", "hexo-renderer-marked": "^6.0.0", "husky": "^8.0.1", - "lint-staged": "^13.0.3", + "lint-staged": "^14.0.1", "mocha": "^10.0.0", - "sinon": "^15.0.0" + "sinon": "^15.0.0", + "ts-node": "^10.9.1", + "typescript": "^4.8.4" }, "engines": { "node": ">=14" diff --git a/test/.eslintrc b/test/.eslintrc index b05fbe4df3..6f2a1521e6 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -2,5 +2,11 @@ "extends": "hexo/test", "globals": { "should": true + }, + "rules": { + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/no-empty-function": 0, + "@typescript-eslint/no-unused-vars": 0, + "node/no-missing-require": 0 } } diff --git a/test/benchmark.js b/test/benchmark.js index f22a830605..e377b140b6 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -5,7 +5,7 @@ const { spawn } = require('child_process'); const { spawn: spawnAsync } = require('hexo-util'); const { rmdir, exists } = require('hexo-fs'); const { appendFileSync: appendFile } = require('fs'); -const { join, resolve } = require('path'); +const { resolve } = require('path'); const log = require('hexo-log').default(); const { red } = require('picocolors'); const hooks = [ @@ -22,9 +22,7 @@ const isWin32 = require('os').platform() === 'win32'; const npmScript = isWin32 ? 'npm.cmd' : 'npm'; const testDir = resolve('.tmp-hexo-theme-unit-test'); -const zeroEksDir = process.env.TRAVIS_BUILD_DIR - ? join(process.env.TRAVIS_BUILD_DIR, '0x') - : resolve(testDir, '0x'); +const zeroEksDir = resolve(testDir, '0x'); const hexoBin = resolve(testDir, 'node_modules/.bin/hexo'); const isGitHubActions = process.env.GITHUB_ACTIONS; @@ -176,6 +174,9 @@ async function init() { if (await exists(resolve(testDir, 'node_modules'))) await rmdir(resolve(testDir, 'node_modules')); await spawnAsync(npmScript, ['install', '--silent'], { cwd: testDir }); + log.info('Build hexo'); + await spawnAsync(npmScript, ['run', 'build']); + log.info('Replacing hexo'); await rmdir(resolve(testDir, 'node_modules', 'hexo')); diff --git a/test/scripts/box/box.js b/test/scripts/box/box.js index 4c9d26cf21..71548cceeb 100644 --- a/test/scripts/box/box.js +++ b/test/scripts/box/box.js @@ -7,9 +7,9 @@ const { hash, Pattern } = require('hexo-util'); const { spy, match, assert: sinonAssert } = require('sinon'); describe('Box', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const baseDir = join(__dirname, 'box_tmp'); - const Box = require('../../../lib/box'); + const Box = require('../../../dist/box'); const newBox = (path, config) => { const hexo = new Hexo(baseDir, { silent: true }); @@ -262,6 +262,16 @@ describe('Box', () => { await rmdir(box.base); }); + it('process() - error ignore - 1', async () => { + const box = newBox('test', { ignore: [null] }); + box.options.ignored.should.eql([]); + }); + + it('process() - error ignore - 2', async () => { + const box = newBox('test', { ignore: [111] }); + box.options.ignored.should.eql([]); + }); + it('process() - skip files if they match a glob epression in ignore', async () => { const box = newBox('test', { ignore: '**/ignore_me' }); const data = {}; diff --git a/test/scripts/box/file.js b/test/scripts/box/file.js index cf4626ebe5..e019d913cc 100644 --- a/test/scripts/box/file.js +++ b/test/scripts/box/file.js @@ -5,9 +5,9 @@ const { rmdir, stat, statSync, writeFile } = require('hexo-fs'); const { load } = require('js-yaml'); describe('File', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const Box = require('../../../lib/box'); + const Box = require('../../../dist/box'); const box = new Box(hexo, join(hexo.base_dir, 'file_test')); const { File } = box; diff --git a/test/scripts/console/clean.js b/test/scripts/console/clean.js index 8f82672890..f0efb0dcac 100644 --- a/test/scripts/console/clean.js +++ b/test/scripts/console/clean.js @@ -3,12 +3,12 @@ const { exists, mkdirs, unlink, writeFile } = require('hexo-fs'); describe('clean', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); let hexo, clean; beforeEach(() => { hexo = new Hexo(__dirname, {silent: true}); - clean = require('../../../lib/plugins/console/clean').bind(hexo); + clean = require('../../../dist/plugins/console/clean').bind(hexo); }); it('delete database', async () => { diff --git a/test/scripts/console/config.js b/test/scripts/console/config.js index 747ab6f51e..9ed935e19b 100644 --- a/test/scripts/console/config.js +++ b/test/scripts/console/config.js @@ -6,9 +6,9 @@ const { load } = require('js-yaml'); const { stub, assert: sinonAssert } = require('sinon'); describe('config', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'config_test'), {silent: true}); - const config = require('../../../lib/plugins/console/config').bind(hexo); + const config = require('../../../dist/plugins/console/config').bind(hexo); before(async () => { await mkdirs(hexo.base_dir); diff --git a/test/scripts/console/deploy.js b/test/scripts/console/deploy.js index 457e4c3305..86e0c16675 100644 --- a/test/scripts/console/deploy.js +++ b/test/scripts/console/deploy.js @@ -5,9 +5,9 @@ const { join } = require('path'); const { spy, stub, assert: sinonAssert } = require('sinon'); describe('deploy', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'deploy_test'), { silent: true }); - const deploy = require('../../../lib/plugins/console/deploy').bind(hexo); + const deploy = require('../../../dist/plugins/console/deploy').bind(hexo); before(async () => { await mkdirs(hexo.public_dir); @@ -97,7 +97,7 @@ describe('deploy', () => { const hexo = new Hexo(join(__dirname, 'deploy_test')); hexo.log.error = logSpy; - const deploy = require('../../../lib/plugins/console/deploy').bind(hexo); + const deploy = require('../../../dist/plugins/console/deploy').bind(hexo); hexo.extend.deployer.register('baz', () => { }); hexo.config.deploy = { diff --git a/test/scripts/console/generate.js b/test/scripts/console/generate.js index 68c8efaaaa..d4b39ef337 100644 --- a/test/scripts/console/generate.js +++ b/test/scripts/console/generate.js @@ -6,8 +6,8 @@ const Promise = require('bluebird'); const { spy } = require('sinon'); describe('generate', () => { - const Hexo = require('../../../lib/hexo'); - const generateConsole = require('../../../lib/plugins/console/generate'); + const Hexo = require('../../../dist/hexo'); + const generateConsole = require('../../../dist/plugins/console/generate'); let hexo, generate; beforeEach(async () => { @@ -55,6 +55,23 @@ describe('generate', () => { it('default', () => testGenerate()); + it('public_dir is not a directory', async () => { + await Promise.all([ + // Add some source files + writeFile(join(hexo.source_dir, 'test.txt'), 'test'), + // Add some files to public folder + writeFile(join(hexo.public_dir, 'foo.txt'), 'foo') + ]); + const old = hexo.public_dir; + hexo.public_dir = join(hexo.public_dir, 'foo.txt'); + try { + await generate(); + } catch (e) { + e.message.split(' ').slice(1).join(' ').should.eql('is not a directory'); + } + hexo.public_dir = old; + }); + it('write file if not exist', async () => { const src = join(hexo.source_dir, 'test.txt'); const dest = join(hexo.public_dir, 'test.txt'); @@ -290,8 +307,8 @@ describe('generate', () => { // #3975 workaround for Windows describe('generate - watch (delete)', () => { - const Hexo = require('../../../lib/hexo'); - const generateConsole = require('../../../lib/plugins/console/generate'); + const Hexo = require('../../../dist/hexo'); + const generateConsole = require('../../../dist/plugins/console/generate'); const hexo = new Hexo(join(__dirname, 'generate_test'), {silent: true}); const generate = generateConsole.bind(hexo); diff --git a/test/scripts/console/list.js b/test/scripts/console/list.js index 61c3ca2fa2..124274888d 100644 --- a/test/scripts/console/list.js +++ b/test/scripts/console/list.js @@ -1,15 +1,16 @@ 'use strict'; -const { spy } = require('sinon'); +const { spy, stub, assert: sinonAssert } = require('sinon'); +const Promise = require('bluebird'); describe('Console list', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); it('no args', () => { hexo.call = spy(); - const list = require('../../../lib/plugins/console/list').bind(hexo); + const list = require('../../../dist/plugins/console/list').bind(hexo); list({ _: [''] }); @@ -18,10 +19,26 @@ describe('Console list', () => { hexo.call.args[0][1]._[0].should.eql('list'); }); + it('has args', async () => { + const logStub = stub(console, 'log'); + + hexo.load = () => Promise.resolve(); + + const list = require('../../../dist/plugins/console/list').bind(hexo); + + await list({ _: ['page'] }); + + sinonAssert.calledWithMatch(logStub, 'Date'); + sinonAssert.calledWithMatch(logStub, 'Title'); + sinonAssert.calledWithMatch(logStub, 'Path'); + sinonAssert.calledWithMatch(logStub, 'No pages.'); + logStub.restore(); + }); + it('list type not found', () => { hexo.call = spy(); - const list = require('../../../lib/plugins/console/list').bind(hexo); + const list = require('../../../dist/plugins/console/list').bind(hexo); list({ _: ['test'] }); diff --git a/test/scripts/console/list_categories.js b/test/scripts/console/list_categories.js index e46324a396..5a67aa93be 100644 --- a/test/scripts/console/list_categories.js +++ b/test/scripts/console/list_categories.js @@ -4,11 +4,11 @@ const Promise = require('bluebird'); const { stub, assert: sinonAssert } = require('sinon'); describe('Console list', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); - const listCategories = require('../../../lib/plugins/console/list/category').bind(hexo); + const listCategories = require('../../../dist/plugins/console/list/category').bind(hexo); let logStub; diff --git a/test/scripts/console/list_page.js b/test/scripts/console/list_page.js index d0bb19c4e1..dc8fd444ba 100644 --- a/test/scripts/console/list_page.js +++ b/test/scripts/console/list_page.js @@ -3,10 +3,10 @@ const { stub, assert: sinonAssert } = require('sinon'); describe('Console list', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Page = hexo.model('Page'); - const listPages = require('../../../lib/plugins/console/list/page').bind(hexo); + const listPages = require('../../../dist/plugins/console/list/page').bind(hexo); hexo.config.permalink = ':title/'; @@ -39,4 +39,18 @@ describe('Console list', () => { sinonAssert.calledWithMatch(logStub, 'Hello World'); sinonAssert.calledWithMatch(logStub, 'foo'); }); + + it('page with unicode', async () => { + await Page.insert({ + source: 'foo', + title: '\u0100', + path: 'bar' + }); + listPages(); + sinonAssert.calledWithMatch(logStub, 'Date'); + sinonAssert.calledWithMatch(logStub, 'Title'); + sinonAssert.calledWithMatch(logStub, 'Path'); + sinonAssert.calledWithMatch(logStub, '\u0100'); + sinonAssert.calledWithMatch(logStub, 'foo'); + }); }); diff --git a/test/scripts/console/list_post.js b/test/scripts/console/list_post.js index a57e5d538a..64ab652d3b 100644 --- a/test/scripts/console/list_post.js +++ b/test/scripts/console/list_post.js @@ -1,13 +1,14 @@ 'use strict'; +const Promise = require('bluebird'); const { stub, assert: sinonAssert } = require('sinon'); describe('Console list', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); - const listPosts = require('../../../lib/plugins/console/list/post').bind(hexo); + const listPosts = require('../../../dist/plugins/console/list/post').bind(hexo); let logStub; @@ -34,8 +35,15 @@ describe('Console list', () => { {source: 'baz', slug: 'baz', title: 'Dude', date: 1e8 - 1} ]; + const tags = [ + ['foo'], + ['baz'], + ['baz'] + ]; + await hexo.init(); - await Post.insert(posts); + const output = await Post.insert(posts); + await Promise.each(tags, (tags, i) => output[i].setTags(tags)); await hexo.locals.invalidate(); listPosts(); @@ -48,6 +56,7 @@ describe('Console list', () => { sinonAssert.calledWithMatch(logStub, posts[i].source); sinonAssert.calledWithMatch(logStub, posts[i].slug); sinonAssert.calledWithMatch(logStub, posts[i].title); + sinonAssert.calledWithMatch(logStub, tags[i][0]); } }); }); diff --git a/test/scripts/console/list_route.js b/test/scripts/console/list_route.js index ae3892884b..3169544ad4 100644 --- a/test/scripts/console/list_route.js +++ b/test/scripts/console/list_route.js @@ -3,10 +3,10 @@ const { stub, assert: sinonAssert } = require('sinon'); describe('Console list', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const listRoutes = require('../../../lib/plugins/console/list/route').bind(hexo); + const listRoutes = require('../../../dist/plugins/console/list/route').bind(hexo); const { route } = hexo; let logStub; @@ -27,5 +27,15 @@ describe('Console list', () => { listRoutes(); sinonAssert.calledWithMatch(logStub, 'Total: 1'); + route.remove('test'); + }); + + it('route with nodes', async () => { + route.set('test0/test1', 'foo'); + + listRoutes(); + sinonAssert.calledWithMatch(logStub, 'Total: 1'); + sinonAssert.calledWithMatch(logStub, '└─┬ test0'); + sinonAssert.calledWithMatch(logStub, ' └── test1'); }); }); diff --git a/test/scripts/console/list_tags.js b/test/scripts/console/list_tags.js index 7dd39532cf..01073f7b14 100644 --- a/test/scripts/console/list_tags.js +++ b/test/scripts/console/list_tags.js @@ -4,11 +4,11 @@ const Promise = require('bluebird'); const { stub, assert: sinonAssert } = require('sinon'); describe('Console list', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); - const listTags = require('../../../lib/plugins/console/list/tag').bind(hexo); + const listTags = require('../../../dist/plugins/console/list/tag').bind(hexo); hexo.config.permalink = ':title/'; diff --git a/test/scripts/console/migrate.js b/test/scripts/console/migrate.js index 2de2f77361..a277197d4e 100644 --- a/test/scripts/console/migrate.js +++ b/test/scripts/console/migrate.js @@ -3,9 +3,9 @@ const { spy, assert: sinonAssert, stub } = require('sinon'); describe('migrate', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname, { silent: true }); - const migrate = require('../../../lib/plugins/console/migrate').bind(hexo); + const migrate = require('../../../dist/plugins/console/migrate').bind(hexo); it('default', async () => { const migrator = spy(); @@ -21,7 +21,7 @@ describe('migrate', () => { it('no args', async () => { const hexo = new Hexo(__dirname, { silent: true }); hexo.call = spy(); - const migrate = require('../../../lib/plugins/console/migrate').bind(hexo); + const migrate = require('../../../dist/plugins/console/migrate').bind(hexo); await migrate({ _: [] }); hexo.call.calledOnce.should.be.true; diff --git a/test/scripts/console/new.js b/test/scripts/console/new.js index 8e64429ae0..4fd68eaf62 100644 --- a/test/scripts/console/new.js +++ b/test/scripts/console/new.js @@ -5,11 +5,12 @@ const moment = require('moment'); const { join } = require('path'); const Promise = require('bluebird'); const { useFakeTimers } = require('sinon'); +const { spy } = require('sinon'); describe('new', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'new_test'), {silent: true}); - const n = require('../../../lib/plugins/console/new').bind(hexo); + const n = require('../../../dist/plugins/console/new').bind(hexo); const post = hexo.post; const now = Date.now(); let clock; @@ -39,6 +40,16 @@ describe('new', () => { return rmdir(hexo.base_dir); }); + it('no args', async () => { + hexo.call = spy(); + await n({ + _: [] + }); + hexo.call.calledOnce.should.be.true; + hexo.call.args[0][0].should.eql('help'); + hexo.call.args[0][1]._[0].should.eql('new'); + }); + it('title', async () => { const date = moment(now); const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); @@ -157,6 +168,26 @@ describe('new', () => { await unlink(path); }); + it('without _', async () => { + const date = moment(now); + const path = join(hexo.source_dir, '_posts', 'bar.md'); + const body = [ + 'title: bar', + 'date: ' + date.format('YYYY-MM-DD HH:mm:ss'), + 'tags:', + '---' + ].join('\n') + '\n'; + + await n({ + _: [], + path: 'bar' + }); + const content = await readFile(path); + content.should.eql(body); + + await unlink(path); + }); + it('rename if target existed', async () => { const path = join(hexo.source_dir, '_posts', 'Hello-World-1.md'); diff --git a/test/scripts/console/publish.js b/test/scripts/console/publish.js index e96738f9eb..44069af45e 100644 --- a/test/scripts/console/publish.js +++ b/test/scripts/console/publish.js @@ -7,9 +7,9 @@ const Promise = require('bluebird'); const { useFakeTimers, spy } = require('sinon'); describe('publish', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'publish_test'), {silent: true}); - const publish = require('../../../lib/plugins/console/publish').bind(hexo); + const publish = require('../../../dist/plugins/console/publish').bind(hexo); const post = hexo.post; const now = Date.now(); let clock; @@ -73,7 +73,7 @@ describe('publish', () => { it('no args', async () => { const hexo = new Hexo(join(__dirname, 'publish_test'), {silent: true}); hexo.call = spy(); - const publish = require('../../../lib/plugins/console/publish').bind(hexo); + const publish = require('../../../dist/plugins/console/publish').bind(hexo); await publish({_: []}); diff --git a/test/scripts/console/render.js b/test/scripts/console/render.js index d30217ba1a..1a611766c8 100644 --- a/test/scripts/console/render.js +++ b/test/scripts/console/render.js @@ -6,9 +6,9 @@ const Promise = require('bluebird'); const { spy } = require('sinon'); describe('render', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'render_test'), {silent: true}); - const render = require('../../../lib/plugins/console/render').bind(hexo); + const render = require('../../../dist/plugins/console/render').bind(hexo); before(async () => { await mkdirs(hexo.base_dir); @@ -26,7 +26,7 @@ describe('render', () => { it('no args', async () => { const hexo = new Hexo(join(__dirname, 'render_test'), {silent: true}); hexo.call = spy(); - const render = require('../../../lib/plugins/console/render').bind(hexo); + const render = require('../../../dist/plugins/console/render').bind(hexo); await render({_: []}); diff --git a/test/scripts/extend/console.js b/test/scripts/extend/console.js index 2d3ba48765..479937d687 100644 --- a/test/scripts/extend/console.js +++ b/test/scripts/extend/console.js @@ -1,7 +1,7 @@ 'use strict'; describe('Console', () => { - const Console = require('../../../lib/extend/console'); + const Console = require('../../../dist/extend/console'); it('register()', () => { const c = new Console(); diff --git a/test/scripts/extend/deployer.js b/test/scripts/extend/deployer.js index ddc73193c9..7318cc9777 100644 --- a/test/scripts/extend/deployer.js +++ b/test/scripts/extend/deployer.js @@ -1,7 +1,7 @@ 'use strict'; describe('Deployer', () => { - const Deployer = require('../../../lib/extend/deployer'); + const Deployer = require('../../../dist/extend/deployer'); it('register()', () => { const d = new Deployer(); diff --git a/test/scripts/extend/filter.js b/test/scripts/extend/filter.js index 3f03ee74b6..6010ce5bc6 100644 --- a/test/scripts/extend/filter.js +++ b/test/scripts/extend/filter.js @@ -3,7 +3,7 @@ const { spy } = require('sinon'); describe('Filter', () => { - const Filter = require('../../../lib/extend/filter'); + const Filter = require('../../../dist/extend/filter'); it('register()', () => { const f = new Filter(); diff --git a/test/scripts/extend/generator.js b/test/scripts/extend/generator.js index 154883c579..d90b295fd1 100644 --- a/test/scripts/extend/generator.js +++ b/test/scripts/extend/generator.js @@ -1,7 +1,7 @@ 'use strict'; describe('Generator', () => { - const Generator = require('../../../lib/extend/generator'); + const Generator = require('../../../dist/extend/generator'); it('register()', () => { const g = new Generator(); diff --git a/test/scripts/extend/helper.js b/test/scripts/extend/helper.js index fd5078313d..30054f1a4b 100644 --- a/test/scripts/extend/helper.js +++ b/test/scripts/extend/helper.js @@ -1,7 +1,7 @@ 'use strict'; describe('Helper', () => { - const Helper = require('../../../lib/extend/helper'); + const Helper = require('../../../dist/extend/helper'); it('register()', () => { const h = new Helper(); diff --git a/test/scripts/extend/injector.js b/test/scripts/extend/injector.js index 08ffc8110c..d82c035607 100644 --- a/test/scripts/extend/injector.js +++ b/test/scripts/extend/injector.js @@ -13,7 +13,7 @@ describe('Injector', () => { '' ].join(''); - const Injector = require('../../../lib/extend/injector'); + const Injector = require('../../../dist/extend/injector'); it('register() - entry is required', () => { const i = new Injector(); diff --git a/test/scripts/extend/migrator.js b/test/scripts/extend/migrator.js index 8668c3e94e..fe045b3c28 100644 --- a/test/scripts/extend/migrator.js +++ b/test/scripts/extend/migrator.js @@ -1,7 +1,7 @@ 'use strict'; describe('Migrator', () => { - const Migrator = require('../../../lib/extend/migrator'); + const Migrator = require('../../../dist/extend/migrator'); it('register()', () => { const d = new Migrator(); diff --git a/test/scripts/extend/processor.js b/test/scripts/extend/processor.js index 00894de7f0..1d9cb12407 100644 --- a/test/scripts/extend/processor.js +++ b/test/scripts/extend/processor.js @@ -1,7 +1,7 @@ 'use strict'; describe('Processor', () => { - const Processor = require('../../../lib/extend/processor'); + const Processor = require('../../../dist/extend/processor'); it('register()', () => { const p = new Processor(); @@ -16,6 +16,11 @@ describe('Processor', () => { p.list()[1].should.exist; + // more than one arg + p.register((a, b) => {}); + + p.list()[1].should.exist; + // no fn should.throw(() => p.register(), TypeError, 'fn must be a function'); }); diff --git a/test/scripts/extend/renderer.js b/test/scripts/extend/renderer.js index be09798410..44327d0cb7 100644 --- a/test/scripts/extend/renderer.js +++ b/test/scripts/extend/renderer.js @@ -1,7 +1,7 @@ 'use strict'; describe('Renderer', () => { - const Renderer = require('../../../lib/extend/renderer'); + const Renderer = require('../../../dist/extend/renderer'); it('register()', () => { const r = new Renderer(); diff --git a/test/scripts/extend/tag.js b/test/scripts/extend/tag.js index a89f6f7c8b..8f10573af1 100644 --- a/test/scripts/extend/tag.js +++ b/test/scripts/extend/tag.js @@ -1,9 +1,7 @@ 'use strict'; -const { spy } = require('sinon'); - describe('Tag', () => { - const Tag = require('../../../lib/extend/tag'); + const Tag = require('../../../dist/extend/tag'); const tag = new Tag(); it('register()', async () => { @@ -164,13 +162,17 @@ describe('Tag', () => { it('render() - callback', () => { const tag = new Tag(); - const callback = spy(); + // spy() is not a function + let spy = false; + const callback = () => { + spy = true; + }; tag.register('test', () => 'foo'); - return tag.render('{% test %}', callback()).then(result => { + return tag.render('{% test %}', callback).then(result => { result.should.eql('foo'); - callback.calledOnce.should.be.true; + spy.should.eql(true); }); }); }); diff --git a/test/scripts/extend/tag_errors.js b/test/scripts/extend/tag_errors.js index 6a674430b8..3184d8a09d 100644 --- a/test/scripts/extend/tag_errors.js +++ b/test/scripts/extend/tag_errors.js @@ -1,7 +1,7 @@ 'use strict'; describe('Tag Errors', () => { - const Tag = require('../../../lib/extend/tag'); + const Tag = require('../../../dist/extend/tag'); const assertNunjucksError = (err, line, type) => { err.should.have.property('name', 'Nunjucks Error'); diff --git a/test/scripts/filters/backtick_code_block.js b/test/scripts/filters/backtick_code_block.js index 4b0d75e31b..5c75bbccdb 100644 --- a/test/scripts/filters/backtick_code_block.js +++ b/test/scripts/filters/backtick_code_block.js @@ -1,13 +1,13 @@ 'use strict'; const util = require('hexo-util'); -const defaultConfig = require('../../../lib/hexo/default_config'); +const defaultConfig = require('../../../dist/hexo/default_config'); describe('Backtick code block', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - require('../../../lib/plugins/highlight/')(hexo); - const codeBlock = require('../../../lib/plugins/filter/before_post_render/backtick_code_block')(hexo); + require('../../../dist/plugins/highlight/')(hexo); + const codeBlock = require('../../../dist/plugins/filter/before_post_render/backtick_code_block')(hexo); const code = [ 'if (tired && night) {', @@ -635,5 +635,20 @@ describe('Backtick code block', () => { codeBlock(data); data.content.should.eql('' + expected + ''); }); + + it('prism only wrap with pre and code', () => { + hexo.config.prismjs.exclude_languages = ['js']; + const data = { + content: [ + '``` js', + code, + '```' + ].join('\n') + }; + const escapeSwigTag = str => str.replace(/{/g, '{').replace(/}/g, '}'); + const expected = `
${escapeSwigTag(util.escapeHTML(code))}
`; + codeBlock(data); + data.content.should.eql('' + expected + ''); + }); }); }); diff --git a/test/scripts/filters/excerpt.js b/test/scripts/filters/excerpt.js index eb485221f5..c45bd7f757 100644 --- a/test/scripts/filters/excerpt.js +++ b/test/scripts/filters/excerpt.js @@ -1,9 +1,9 @@ 'use strict'; describe('Excerpt', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const excerpt = require('../../../lib/plugins/filter/after_post_render/excerpt').bind(hexo); + const excerpt = require('../../../dist/plugins/filter/after_post_render/excerpt').bind(hexo); it('without ', () => { const content = [ diff --git a/test/scripts/filters/external_link.js b/test/scripts/filters/external_link.js index 46b4ade372..dbec1dbca5 100644 --- a/test/scripts/filters/external_link.js +++ b/test/scripts/filters/external_link.js @@ -3,13 +3,13 @@ const decache = require('decache'); describe('External link', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); let externalLink; beforeEach(() => { - decache('../../../lib/plugins/filter/after_render/external_link'); - externalLink = require('../../../lib/plugins/filter/after_render/external_link').bind(hexo); + decache('../../../dist/plugins/filter/after_render/external_link'); + externalLink = require('../../../dist/plugins/filter/after_render/external_link').bind(hexo); }); hexo.config = { @@ -137,14 +137,14 @@ describe('External link', () => { }); describe('External link - post', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); let externalLink; beforeEach(() => { - decache('../../../lib/plugins/filter/after_post_render/external_link'); - externalLink = require('../../../lib/plugins/filter/after_post_render/external_link').bind(hexo); + decache('../../../dist/plugins/filter/after_post_render/external_link'); + externalLink = require('../../../dist/plugins/filter/after_post_render/external_link').bind(hexo); }); hexo.config = { diff --git a/test/scripts/filters/i18n_locals.js b/test/scripts/filters/i18n_locals.js index 75fe3ed9f0..6ac2f920dc 100644 --- a/test/scripts/filters/i18n_locals.js +++ b/test/scripts/filters/i18n_locals.js @@ -1,9 +1,9 @@ 'use strict'; describe('i18n locals', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const i18nFilter = require('../../../lib/plugins/filter/template_locals/i18n').bind(hexo); + const i18nFilter = require('../../../dist/plugins/filter/template_locals/i18n').bind(hexo); const theme = hexo.theme; const i18n = theme.i18n; diff --git a/test/scripts/filters/meta_generator.js b/test/scripts/filters/meta_generator.js index 7d6078cedf..f87bcc000b 100644 --- a/test/scripts/filters/meta_generator.js +++ b/test/scripts/filters/meta_generator.js @@ -3,14 +3,14 @@ const decache = require('decache'); describe('Meta Generator', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); let metaGenerator; const cheerio = require('cheerio'); beforeEach(() => { - decache('../../../lib/plugins/filter/after_render/meta_generator'); - metaGenerator = require('../../../lib/plugins/filter/after_render/meta_generator').bind(hexo); + decache('../../../dist/plugins/filter/after_render/meta_generator'); + metaGenerator = require('../../../dist/plugins/filter/after_render/meta_generator').bind(hexo); }); it('default', () => { diff --git a/test/scripts/filters/new_post_path.js b/test/scripts/filters/new_post_path.js index c5e2f528e9..006c973538 100644 --- a/test/scripts/filters/new_post_path.js +++ b/test/scripts/filters/new_post_path.js @@ -6,9 +6,9 @@ const { createSha1Hash } = require('hexo-util'); const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); describe('new_post_path', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'new_post_path_test')); - const newPostPath = require('../../../lib/plugins/filter/new_post_path').bind(hexo); + const newPostPath = require('../../../dist/plugins/filter/new_post_path').bind(hexo); const sourceDir = hexo.source_dir; const draftDir = join(sourceDir, '_drafts'); const postDir = join(sourceDir, '_posts'); diff --git a/test/scripts/filters/post_permalink.js b/test/scripts/filters/post_permalink.js index 0098edf065..e01f22a1be 100644 --- a/test/scripts/filters/post_permalink.js +++ b/test/scripts/filters/post_permalink.js @@ -3,9 +3,9 @@ const moment = require('moment'); describe('post_permalink', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const postPermalink = require('../../../lib/plugins/filter/post_permalink').bind(hexo); + const postPermalink = require('../../../dist/plugins/filter/post_permalink').bind(hexo); const Post = hexo.model('Post'); let post; diff --git a/test/scripts/filters/render_post.js b/test/scripts/filters/render_post.js index b74074c831..e5540cc7e4 100644 --- a/test/scripts/filters/render_post.js +++ b/test/scripts/filters/render_post.js @@ -3,11 +3,11 @@ const { content, expected } = require('../../fixtures/post_render'); describe('Render post', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Post = hexo.model('Post'); const Page = hexo.model('Page'); - const renderPost = require('../../../lib/plugins/filter/before_generate/render_post').bind(hexo); + const renderPost = require('../../../dist/plugins/filter/before_generate/render_post').bind(hexo); before(async () => { await hexo.init(); @@ -46,20 +46,4 @@ describe('Render post', () => { page.remove(); }); - it('use data variables', async () => { - let page = await Page.insert({ - source: 'foo.md', - path: 'foo.html', - _content: '

Hello {{site.data.foo.name}}

' - }); - - const id = page._id; - await renderPost({foo: {name: 'Hexo'}}); - - page = Page.findById(id); - page.content.trim().should.eql('

Hello Hexo

'); - - page.remove(); - }); - }); diff --git a/test/scripts/filters/save_database.js b/test/scripts/filters/save_database.js index d6a38368c4..f479cdc411 100644 --- a/test/scripts/filters/save_database.js +++ b/test/scripts/filters/save_database.js @@ -4,9 +4,9 @@ const { exists, unlink } = require('hexo-fs'); const Promise = require('bluebird'); describe('Save database', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const saveDatabase = Promise.method(require('../../../lib/plugins/filter/before_exit/save_database')).bind(hexo); + const saveDatabase = Promise.method(require('../../../dist/plugins/filter/before_exit/save_database')).bind(hexo); const dbPath = hexo.database.options.path; it('default', async () => { diff --git a/test/scripts/filters/titlecase.js b/test/scripts/filters/titlecase.js index 126b2a156e..10b979c326 100644 --- a/test/scripts/filters/titlecase.js +++ b/test/scripts/filters/titlecase.js @@ -1,9 +1,9 @@ 'use strict'; describe('Titlecase', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const titlecase = require('../../../lib/plugins/filter/before_post_render/titlecase').bind(hexo); + const titlecase = require('../../../dist/plugins/filter/before_post_render/titlecase').bind(hexo); it('disabled', () => { const title = 'Today is a good day'; diff --git a/test/scripts/generators/asset.js b/test/scripts/generators/asset.js index 43ad30ce67..3e898cc671 100644 --- a/test/scripts/generators/asset.js +++ b/test/scripts/generators/asset.js @@ -3,11 +3,12 @@ const { join } = require('path'); const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); const testUtil = require('../../util'); +const { spy } = require('sinon'); describe('asset', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'asset_test'), {silent: true}); - const generator = require('../../../lib/plugins/generator/asset').bind(hexo); + const generator = require('../../../dist/plugins/generator/asset').bind(hexo); const Asset = hexo.model('Asset'); const checkStream = async (stream, expected) => { @@ -44,6 +45,31 @@ describe('asset', () => { ]); }); + it('renderable - error', async () => { + const logSpy = spy(); + hexo.log.error = logSpy; + const path = 'test.yml'; + const source = join(hexo.base_dir, path); + const content = 'foo: :'; + + await Promise.all([ + Asset.insert({_id: path, path}), + writeFile(source, content) + ]); + const data = await generator(hexo.locals); + data[0].path.should.eql('test.json'); + data[0].data.modified.should.be.true; + await data[0].data.data(); + logSpy.called.should.be.true; + + logSpy.args[0][1].should.contains('Asset render failed: %s'); + logSpy.args[0][2].should.contains('test.json'); + await Promise.all([ + Asset.removeById(path), + unlink(source) + ]); + }); + it('not renderable', async () => { const path = 'test.txt'; const source = join(hexo.base_dir, path); diff --git a/test/scripts/generators/page.js b/test/scripts/generators/page.js index 2371566fad..cf0290c640 100644 --- a/test/scripts/generators/page.js +++ b/test/scripts/generators/page.js @@ -3,10 +3,10 @@ const Promise = require('bluebird'); describe('page', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname, {silent: true}); const Page = hexo.model('Page'); - const generator = Promise.method(require('../../../lib/plugins/generator/page').bind(hexo)); + const generator = Promise.method(require('../../../dist/plugins/generator/page').bind(hexo)); const locals = () => { hexo.locals.invalidate(); diff --git a/test/scripts/generators/post.js b/test/scripts/generators/post.js index 7132678015..ace0d2e859 100644 --- a/test/scripts/generators/post.js +++ b/test/scripts/generators/post.js @@ -3,10 +3,10 @@ const Promise = require('bluebird'); describe('post', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname, {silent: true}); const Post = hexo.model('Post'); - const generator = Promise.method(require('../../../lib/plugins/generator/post').bind(hexo)); + const generator = Promise.method(require('../../../dist/plugins/generator/post').bind(hexo)); hexo.config.permalink = ':title/'; diff --git a/test/scripts/helpers/css.js b/test/scripts/helpers/css.js index 4922a1f265..6241ff93fd 100644 --- a/test/scripts/helpers/css.js +++ b/test/scripts/helpers/css.js @@ -3,14 +3,14 @@ const cheerio = require('cheerio'); describe('css', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { config: hexo.config }; - const css = require('../../../lib/plugins/helper/css').bind(ctx); + const css = require('../../../dist/plugins/helper/css').bind(ctx); function assertResult(result, expected) { const $ = cheerio.load(result); @@ -73,4 +73,17 @@ describe('css', () => { assertResult(css([{href: '/aaa.css', bbb: 'ccc'}, {href: '/ddd.css', eee: 'fff'}]), [{href: '/aaa.css', bbb: 'ccc'}, {href: '/ddd.css', eee: 'fff'}]); }); + + it('relative link', () => { + ctx.config.relative_link = true; + ctx.config.root = '/'; + + ctx.path = ''; + assertResult(css('style'), 'style.css'); + + ctx.path = 'foo/bar/'; + assertResult(css('style'), '../../style.css'); + + ctx.config.relative_link = false; + }); }); diff --git a/test/scripts/helpers/date.js b/test/scripts/helpers/date.js index 1b33a8528b..80e5cfa510 100644 --- a/test/scripts/helpers/date.js +++ b/test/scripts/helpers/date.js @@ -4,9 +4,9 @@ const moment = require('moment-timezone'); const { useFakeTimers } = require('sinon'); describe('date', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const dateHelper = require('../../../lib/plugins/helper/date'); + const dateHelper = require('../../../dist/plugins/helper/date'); let clock; before(() => { diff --git a/test/scripts/helpers/debug.js b/test/scripts/helpers/debug.js index 409616f3bd..200dc33705 100644 --- a/test/scripts/helpers/debug.js +++ b/test/scripts/helpers/debug.js @@ -3,7 +3,7 @@ const { stub } = require('sinon'); describe('debug', () => { - const debug = require('../../../lib/plugins/helper/debug'); + const debug = require('../../../dist/plugins/helper/debug'); const { inspect } = require('util'); it('inspect simple object', () => { diff --git a/test/scripts/helpers/escape_html.js b/test/scripts/helpers/escape_html.js index 076a27285b..802a85b8b8 100644 --- a/test/scripts/helpers/escape_html.js +++ b/test/scripts/helpers/escape_html.js @@ -1,7 +1,7 @@ 'use strict'; describe('escape_html', () => { - const { escapeHTML } = require('../../../lib/plugins/helper/format'); + const { escapeHTML } = require('../../../dist/plugins/helper/format'); it('default', () => { escapeHTML('

Hello "world".

').should.eql('<p class="foo">Hello "world".</p>'); diff --git a/test/scripts/helpers/favicon_tag.js b/test/scripts/helpers/favicon_tag.js index 81c2dd7e11..2057042510 100644 --- a/test/scripts/helpers/favicon_tag.js +++ b/test/scripts/helpers/favicon_tag.js @@ -1,14 +1,14 @@ 'use strict'; describe('favicon_tag', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { config: hexo.config }; - const favicon = require('../../../lib/plugins/helper/favicon_tag').bind(ctx); + const favicon = require('../../../dist/plugins/helper/favicon_tag').bind(ctx); it('path', () => { favicon('favicon.ico').should.eql(''); diff --git a/test/scripts/helpers/feed_tag.js b/test/scripts/helpers/feed_tag.js index 354752c856..9dd96328f5 100644 --- a/test/scripts/helpers/feed_tag.js +++ b/test/scripts/helpers/feed_tag.js @@ -12,7 +12,7 @@ describe('feed_tag', () => { beforeEach(() => { ctx.config.feed = {}; }); - const feed = require('../../../lib/plugins/helper/feed_tag').bind(ctx); + const feed = require('../../../dist/plugins/helper/feed_tag').bind(ctx); it('path - atom', () => { feed('atom.xml').should.eql(''); diff --git a/test/scripts/helpers/fragment_cache.js b/test/scripts/helpers/fragment_cache.js index 92ff914fe6..f9e391a492 100644 --- a/test/scripts/helpers/fragment_cache.js +++ b/test/scripts/helpers/fragment_cache.js @@ -1,9 +1,9 @@ 'use strict'; describe('fragment_cache', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const fragment_cache = require('../../../lib/plugins/helper/fragment_cache')(hexo); + const fragment_cache = require('../../../dist/plugins/helper/fragment_cache')(hexo); fragment_cache.call({cache: true}, 'foo', () => 123); diff --git a/test/scripts/helpers/full_url_for.js b/test/scripts/helpers/full_url_for.js index 35dc936062..313de00273 100755 --- a/test/scripts/helpers/full_url_for.js +++ b/test/scripts/helpers/full_url_for.js @@ -5,7 +5,7 @@ describe('full_url_for', () => { config: { url: 'https://example.com' } }; - const fullUrlFor = require('../../../lib/plugins/helper/full_url_for').bind(ctx); + const fullUrlFor = require('../../../dist/plugins/helper/full_url_for').bind(ctx); it('no path input', () => { fullUrlFor().should.eql(ctx.config.url + '/'); diff --git a/test/scripts/helpers/gravatar.js b/test/scripts/helpers/gravatar.js index 8b577e97af..7fd79bfdba 100644 --- a/test/scripts/helpers/gravatar.js +++ b/test/scripts/helpers/gravatar.js @@ -3,7 +3,7 @@ const crypto = require('crypto'); describe('gravatar', () => { - const gravatar = require('../../../lib/plugins/helper/gravatar'); + const gravatar = require('../../../dist/plugins/helper/gravatar'); function md5(str) { return crypto.createHash('md5').update(str).digest('hex'); diff --git a/test/scripts/helpers/image_tag.js b/test/scripts/helpers/image_tag.js index 0f7b22af4c..f8a87edbbe 100644 --- a/test/scripts/helpers/image_tag.js +++ b/test/scripts/helpers/image_tag.js @@ -1,14 +1,14 @@ 'use strict'; describe('image_tag', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { config: hexo.config }; - const img = require('../../../lib/plugins/helper/image_tag').bind(ctx); + const img = require('../../../dist/plugins/helper/image_tag').bind(ctx); it('path', () => { img('https://hexo.io/image.jpg').should.eql(''); diff --git a/test/scripts/helpers/is.js b/test/scripts/helpers/is.js index fa8acd9551..cec0990aa9 100644 --- a/test/scripts/helpers/is.js +++ b/test/scripts/helpers/is.js @@ -1,9 +1,9 @@ 'use strict'; describe('is', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const is = require('../../../lib/plugins/helper/is'); + const is = require('../../../dist/plugins/helper/is'); it('is_current', async () => { await is.current.call({path: 'index.html', config: hexo.config}).should.be.true; diff --git a/test/scripts/helpers/js.js b/test/scripts/helpers/js.js index cee95d2c85..22a23805b1 100644 --- a/test/scripts/helpers/js.js +++ b/test/scripts/helpers/js.js @@ -3,14 +3,14 @@ const cheerio = require('cheerio'); describe('js', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { config: hexo.config }; - const js = require('../../../lib/plugins/helper/js').bind(ctx); + const js = require('../../../dist/plugins/helper/js').bind(ctx); function assertResult(result, expected) { const $ = cheerio.load(result); @@ -82,4 +82,17 @@ describe('js', () => { assertResult(js({src: '/foo.js', 'async': true}), {src: '/foo.js', 'async': true}); assertResult(js({src: '/bar.js', 'defer': true}), {src: '/bar.js', 'defer': true}); }); + + it('relative link', () => { + ctx.config.relative_link = true; + ctx.config.root = '/'; + + ctx.path = ''; + assertResult(js('script'), 'script.js'); + + ctx.path = 'foo/bar/'; + assertResult(js('script'), '../../script.js'); + + ctx.config.relative_link = false; + }); }); diff --git a/test/scripts/helpers/link_to.js b/test/scripts/helpers/link_to.js index d569eb8f30..528a0ce7b8 100644 --- a/test/scripts/helpers/link_to.js +++ b/test/scripts/helpers/link_to.js @@ -1,14 +1,14 @@ 'use strict'; describe('link_to', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { config: hexo.config }; - const linkTo = require('../../../lib/plugins/helper/link_to').bind(ctx); + const linkTo = require('../../../dist/plugins/helper/link_to').bind(ctx); it('path', () => { linkTo('https://hexo.io/').should.eql('hexo.io'); diff --git a/test/scripts/helpers/list_archives.js b/test/scripts/helpers/list_archives.js index 468b498b04..b733a60c6c 100644 --- a/test/scripts/helpers/list_archives.js +++ b/test/scripts/helpers/list_archives.js @@ -1,7 +1,7 @@ 'use strict'; describe('list_archives', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); @@ -10,7 +10,7 @@ describe('list_archives', () => { page: {} }; - const listArchives = require('../../../lib/plugins/helper/list_archives').bind(ctx); + const listArchives = require('../../../dist/plugins/helper/list_archives').bind(ctx); function resetLocals() { hexo.locals.invalidate(); diff --git a/test/scripts/helpers/list_categories.js b/test/scripts/helpers/list_categories.js index a8c1120112..5cdb46e01f 100644 --- a/test/scripts/helpers/list_categories.js +++ b/test/scripts/helpers/list_categories.js @@ -1,7 +1,7 @@ 'use strict'; describe('list_categories', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); const Category = hexo.model('Category'); @@ -10,7 +10,7 @@ describe('list_categories', () => { config: hexo.config }; - const listCategories = require('../../../lib/plugins/helper/list_categories').bind(ctx); + const listCategories = require('../../../dist/plugins/helper/list_categories').bind(ctx); before(async () => { await hexo.init(); diff --git a/test/scripts/helpers/list_posts.js b/test/scripts/helpers/list_posts.js index 88cd6c4be7..dbb6810331 100644 --- a/test/scripts/helpers/list_posts.js +++ b/test/scripts/helpers/list_posts.js @@ -1,7 +1,7 @@ 'use strict'; describe('list_posts', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); @@ -9,7 +9,7 @@ describe('list_posts', () => { config: hexo.config }; - const listPosts = require('../../../lib/plugins/helper/list_posts').bind(ctx); + const listPosts = require('../../../dist/plugins/helper/list_posts').bind(ctx); hexo.config.permalink = ':title/'; diff --git a/test/scripts/helpers/list_tags.js b/test/scripts/helpers/list_tags.js index 7e07280fb7..c5e196cc91 100644 --- a/test/scripts/helpers/list_tags.js +++ b/test/scripts/helpers/list_tags.js @@ -1,7 +1,7 @@ 'use strict'; describe('list_tags', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); const Tag = hexo.model('Tag'); @@ -10,7 +10,7 @@ describe('list_tags', () => { config: hexo.config }; - const listTags = require('../../../lib/plugins/helper/list_tags').bind(ctx); + const listTags = require('../../../dist/plugins/helper/list_tags').bind(ctx); before(async () => { await hexo.init(); @@ -207,7 +207,7 @@ describe('list_tags', () => { }); describe('list_tags transform', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); @@ -215,7 +215,7 @@ describe('list_tags transform', () => { config: hexo.config }; - const listTags = require('../../../lib/plugins/helper/list_tags').bind(ctx); + const listTags = require('../../../dist/plugins/helper/list_tags').bind(ctx); before(async () => { await hexo.init(); diff --git a/test/scripts/helpers/mail_to.js b/test/scripts/helpers/mail_to.js index bcac9970c7..8dc09ded62 100644 --- a/test/scripts/helpers/mail_to.js +++ b/test/scripts/helpers/mail_to.js @@ -1,14 +1,14 @@ 'use strict'; describe('mail_to', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { config: hexo.config }; - const mailto = require('../../../lib/plugins/helper/mail_to').bind(ctx); + const mailto = require('../../../dist/plugins/helper/mail_to').bind(ctx); it('path', () => { mailto('abc@example.com').should.eql('abc@example.com'); diff --git a/test/scripts/helpers/markdown.js b/test/scripts/helpers/markdown.js index 524c64b7fe..5fd2cd6c38 100644 --- a/test/scripts/helpers/markdown.js +++ b/test/scripts/helpers/markdown.js @@ -1,14 +1,14 @@ 'use strict'; describe('markdown', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { - render: require('../../../lib/plugins/helper/render')(hexo) + render: require('../../../dist/plugins/helper/render')(hexo) }; - const markdown = require('../../../lib/plugins/helper/markdown').bind(ctx); + const markdown = require('../../../dist/plugins/helper/markdown').bind(ctx); before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked')))); diff --git a/test/scripts/helpers/meta_generator.js b/test/scripts/helpers/meta_generator.js index c5a180d6f1..972820453e 100755 --- a/test/scripts/helpers/meta_generator.js +++ b/test/scripts/helpers/meta_generator.js @@ -1,10 +1,10 @@ 'use strict'; describe('meta_generator', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const metaGeneratorHelper = require('../../../lib/plugins/helper/meta_generator').bind(hexo); + const metaGeneratorHelper = require('../../../dist/plugins/helper/meta_generator').bind(hexo); it('default', () => { const { version } = hexo; diff --git a/test/scripts/helpers/number_format.js b/test/scripts/helpers/number_format.js index c47c819e62..32390637a5 100644 --- a/test/scripts/helpers/number_format.js +++ b/test/scripts/helpers/number_format.js @@ -1,7 +1,7 @@ 'use strict'; describe('number_format', () => { - const numberFormat = require('../../../lib/plugins/helper/number_format'); + const numberFormat = require('../../../dist/plugins/helper/number_format'); it('default', () => { numberFormat(1234.567).should.eql('1,234.567'); diff --git a/test/scripts/helpers/open_graph.js b/test/scripts/helpers/open_graph.js index ba78dc3bd3..433cddbe24 100644 --- a/test/scripts/helpers/open_graph.js +++ b/test/scripts/helpers/open_graph.js @@ -3,13 +3,13 @@ const moment = require('moment'); const cheerio = require('cheerio'); const { encodeURL } = require('hexo-util'); -const defaultConfig = require('../../../lib/hexo/default_config'); +const defaultConfig = require('../../../dist/hexo/default_config'); describe('open_graph', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const openGraph = require('../../../lib/plugins/helper/open_graph'); - const isPost = require('../../../lib/plugins/helper/is').post; + const openGraph = require('../../../dist/plugins/helper/open_graph'); + const isPost = require('../../../dist/plugins/helper/is').post; const tag = require('hexo-util').htmlTag; const Post = hexo.model('Post'); diff --git a/test/scripts/helpers/paginator.js b/test/scripts/helpers/paginator.js index f1aeb63390..fe46d685c8 100644 --- a/test/scripts/helpers/paginator.js +++ b/test/scripts/helpers/paginator.js @@ -3,7 +3,7 @@ const { url_for } = require('hexo-util'); describe('paginator', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const ctx = { @@ -15,7 +15,7 @@ describe('paginator', () => { config: hexo.config }; - const paginator = require('../../../lib/plugins/helper/paginator').bind(ctx); + const paginator = require('../../../dist/plugins/helper/paginator').bind(ctx); function link(i) { return url_for.call(ctx, i === 1 ? '' : 'page/' + i + '/'); @@ -341,4 +341,22 @@ describe('paginator', () => { '' ].join('')); }); + + it('force_prev_next - 2', () => { + const result = paginator({ + current: 1, + prev_next: false, + force_prev_next: true + }); + + result.should.eql([ + '', + '1', + '2', + '3', + '', + '10', + '' + ].join('')); + }); }); diff --git a/test/scripts/helpers/partial.js b/test/scripts/helpers/partial.js index 0f7a1015dd..ca4b2133d4 100644 --- a/test/scripts/helpers/partial.js +++ b/test/scripts/helpers/partial.js @@ -5,7 +5,7 @@ const fs = require('hexo-fs'); const Promise = require('bluebird'); describe('partial', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(pathFn.join(__dirname, 'partial_test'), {silent: true}); const themeDir = pathFn.join(hexo.base_dir, 'themes', 'test'); const viewDir = pathFn.join(themeDir, 'layout') + pathFn.sep; @@ -20,11 +20,11 @@ describe('partial', () => { cache: true }; - ctx.fragment_cache = require('../../../lib/plugins/helper/fragment_cache')(hexo); + ctx.fragment_cache = require('../../../dist/plugins/helper/fragment_cache')(hexo); hexo.env.init = true; - const partial = require('../../../lib/plugins/helper/partial')(hexo).bind(ctx); + const partial = require('../../../dist/plugins/helper/partial')(hexo).bind(ctx); before(async () => { await Promise.all([ diff --git a/test/scripts/helpers/relative_url.js b/test/scripts/helpers/relative_url.js index 60930788bc..f9f691f602 100644 --- a/test/scripts/helpers/relative_url.js +++ b/test/scripts/helpers/relative_url.js @@ -1,7 +1,7 @@ 'use strict'; describe('relative_url', () => { - const relativeURL = require('../../../lib/plugins/helper/relative_url'); + const relativeURL = require('../../../dist/plugins/helper/relative_url'); it('from root', () => { relativeURL('', 'css/style.css').should.eql('css/style.css'); diff --git a/test/scripts/helpers/render.js b/test/scripts/helpers/render.js index 568af34c0f..04b8c9cc62 100644 --- a/test/scripts/helpers/render.js +++ b/test/scripts/helpers/render.js @@ -1,9 +1,9 @@ 'use strict'; describe('render', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const render = require('../../../lib/plugins/helper/render')(hexo); + const render = require('../../../dist/plugins/helper/render')(hexo); before(() => hexo.init()); diff --git a/test/scripts/helpers/search_form.js b/test/scripts/helpers/search_form.js index 685dbe3eef..6f086008e4 100644 --- a/test/scripts/helpers/search_form.js +++ b/test/scripts/helpers/search_form.js @@ -1,7 +1,7 @@ 'use strict'; describe('search_form', () => { - const searchForm = require('../../../lib/plugins/helper/search_form').bind({ + const searchForm = require('../../../dist/plugins/helper/search_form').bind({ config: {url: 'https://hexo.io'} }); diff --git a/test/scripts/helpers/tagcloud.js b/test/scripts/helpers/tagcloud.js index dba089a256..47ffe87e51 100644 --- a/test/scripts/helpers/tagcloud.js +++ b/test/scripts/helpers/tagcloud.js @@ -3,7 +3,7 @@ const Promise = require('bluebird'); describe('tagcloud', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const Post = hexo.model('Post'); const Tag = hexo.model('Tag'); @@ -12,7 +12,7 @@ describe('tagcloud', () => { config: hexo.config }; - const tagcloud = require('../../../lib/plugins/helper/tagcloud').bind(ctx); + const tagcloud = require('../../../dist/plugins/helper/tagcloud').bind(ctx); before(async () => { await hexo.init(); @@ -50,7 +50,7 @@ describe('tagcloud', () => { await hexo.init(); hexo.locals.invalidate(); hexo.site = hexo.locals.toObject(); - const tagcloud = require('../../../lib/plugins/helper/tagcloud').bind(hexo); + const tagcloud = require('../../../dist/plugins/helper/tagcloud').bind(hexo); const result = tagcloud(); diff --git a/test/scripts/helpers/toc.js b/test/scripts/helpers/toc.js index 47f0014790..b93d7e1d6d 100644 --- a/test/scripts/helpers/toc.js +++ b/test/scripts/helpers/toc.js @@ -3,7 +3,7 @@ const { encodeURL, escapeHTML } = require('hexo-util'); describe('toc', () => { - const toc = require('../../../lib/plugins/helper/toc'); + const toc = require('../../../dist/plugins/helper/toc'); const html = [ '

Title 1

', diff --git a/test/scripts/helpers/url_for.js b/test/scripts/helpers/url_for.js index b0b2187c7c..8f953a7d10 100644 --- a/test/scripts/helpers/url_for.js +++ b/test/scripts/helpers/url_for.js @@ -3,10 +3,10 @@ describe('url_for', () => { const ctx = { config: { url: 'https://example.com' }, - relative_url: require('../../../lib/plugins/helper/relative_url') + relative_url: require('../../../dist/plugins/helper/relative_url') }; - const urlFor = require('../../../lib/plugins/helper/url_for').bind(ctx); + const urlFor = require('../../../dist/plugins/helper/url_for').bind(ctx); it('should encode path', () => { ctx.config.root = '/'; diff --git a/test/scripts/hexo/hexo.js b/test/scripts/hexo/hexo.js index 9c0a33110d..b039ec1fb0 100644 --- a/test/scripts/hexo/hexo.js +++ b/test/scripts/hexo/hexo.js @@ -9,7 +9,7 @@ const { full_url_for } = require('hexo-util'); describe('Hexo', () => { const base_dir = join(__dirname, 'hexo_test'); - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(base_dir, { silent: true }); const coreDir = join(__dirname, '../../..'); const { version } = require('../../../package.json'); @@ -24,7 +24,7 @@ describe('Hexo', () => { } function loadAssetGenerator() { - hexo.extend.generator.register('asset', require('../../../lib/plugins/generator/asset')); + hexo.extend.generator.register('asset', require('../../../dist/plugins/generator/asset')); } before(async () => { @@ -48,7 +48,7 @@ describe('Hexo', () => { /* eslint-disable no-path-concat */ hexo.core_dir.should.eql(coreDir + sep); - hexo.lib_dir.should.eql(join(coreDir, 'lib') + sep); + hexo.lib_dir.should.eql(join(coreDir, 'dist') + sep); hexo.version.should.eql(version); hexo.base_dir.should.eql(__dirname + sep); hexo.public_dir.should.eql(join(__dirname, 'public') + sep); diff --git a/test/scripts/hexo/load_config.js b/test/scripts/hexo/load_config.js index ce9698d068..05150ff1ba 100644 --- a/test/scripts/hexo/load_config.js +++ b/test/scripts/hexo/load_config.js @@ -5,10 +5,10 @@ const { writeFile, unlink, mkdirs, rmdir } = require('hexo-fs'); const { makeRe } = require('micromatch'); describe('Load config', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'config_test'), { silent: true }); - const loadConfig = require('../../../lib/hexo/load_config'); - const defaultConfig = require('../../../lib/hexo/default_config'); + const loadConfig = require('../../../dist/hexo/load_config'); + const defaultConfig = require('../../../dist/hexo/default_config'); hexo.env.init = true; diff --git a/test/scripts/hexo/load_database.js b/test/scripts/hexo/load_database.js index 8e4de33e17..27c257e9be 100644 --- a/test/scripts/hexo/load_database.js +++ b/test/scripts/hexo/load_database.js @@ -4,9 +4,9 @@ const { join } = require('path'); const { exists, mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); describe('Load database', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'db_test'), {silent: true}); - const loadDatabase = require('../../../lib/hexo/load_database'); + const loadDatabase = require('../../../dist/hexo/load_database'); const dbPath = hexo.database.options.path; const fixture = { @@ -63,9 +63,9 @@ describe('Load database', () => { // Clean-up is not necessary (unlike the above tests), // because the db file is already removed if invalid describe('Load database - load failed', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname), {silent: true}); - const loadDatabase = require('../../../lib/hexo/load_database'); + const loadDatabase = require('../../../dist/hexo/load_database'); const dbPath = hexo.database.options.path; it('database load failed', async () => { diff --git a/test/scripts/hexo/load_plugins.js b/test/scripts/hexo/load_plugins.js index 087d7365f0..50dd9620b3 100644 --- a/test/scripts/hexo/load_plugins.js +++ b/test/scripts/hexo/load_plugins.js @@ -5,9 +5,9 @@ const { join, dirname } = require('path'); const Promise = require('bluebird'); describe('Load plugins', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'plugin_test'), { silent: true }); - const loadPlugins = require('../../../lib/hexo/load_plugins'); + const loadPlugins = require('../../../dist/hexo/load_plugins'); const script = [ 'hexo._script_test = {', @@ -18,6 +18,18 @@ describe('Load plugins', () => { '}' ].join('\n'); + const asyncScript = [ + 'async function afunc() {', + ' return new Promise(resolve => resolve());', + '}', + 'await afunc()', + 'hexo._script_test = {', + ' filename: __filename,', + ' dirname: __dirname,', + ' module: module,', + ' require: require', + '}' + ].join('\n'); function validate(path) { const result = hexo._script_test; @@ -81,6 +93,19 @@ describe('Load plugins', () => { }); }); + it('load async plugins', () => { + const name = 'hexo-async-plugin-test'; + const path = join(hexo.plugin_dir, name, 'index.js'); + + return Promise.all([ + createPackageFile(name), + fs.writeFile(path, asyncScript) + ]).then(() => loadPlugins(hexo)).then(() => { + validate(path); + return fs.unlink(path); + }); + }); + it('load scoped plugins', () => { const name = '@some-scope/hexo-plugin-test'; const path = join(hexo.plugin_dir, name, 'index.js'); diff --git a/test/scripts/hexo/load_theme_config.js b/test/scripts/hexo/load_theme_config.js index e20e14f3ae..7dc742305b 100644 --- a/test/scripts/hexo/load_theme_config.js +++ b/test/scripts/hexo/load_theme_config.js @@ -4,9 +4,9 @@ const { join } = require('path'); const { mkdirs, unlink, writeFile, rmdir } = require('hexo-fs'); describe('Load alternate theme config', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'config_test'), {silent: true}); - const loadThemeConfig = require('../../../lib/hexo/load_theme_config'); + const loadThemeConfig = require('../../../dist/hexo/load_theme_config'); hexo.env.init = true; diff --git a/test/scripts/hexo/locals.js b/test/scripts/hexo/locals.js index 290d20f99d..9276b5ccaa 100644 --- a/test/scripts/hexo/locals.js +++ b/test/scripts/hexo/locals.js @@ -1,7 +1,7 @@ 'use strict'; describe('Locals', () => { - const Locals = require('../../../lib/hexo/locals'); + const Locals = require('../../../dist/hexo/locals'); const locals = new Locals(); it('get() - name must be a string', () => { diff --git a/test/scripts/hexo/multi_config_path.js b/test/scripts/hexo/multi_config_path.js index 37ba61f160..3d6cd423b7 100644 --- a/test/scripts/hexo/multi_config_path.js +++ b/test/scripts/hexo/multi_config_path.js @@ -6,10 +6,10 @@ const fs = require('hexo-fs'); const yml = require('js-yaml'); describe('config flag handling', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(pathFn.join(__dirname, 'test_dir')); - const mcp = require('../../../lib/hexo/multi_config_path')(hexo); + const mcp = require('../../../dist/hexo/multi_config_path')(hexo); const base = hexo.base_dir; function ConsoleReader() { @@ -129,6 +129,8 @@ describe('config flag handling', () => { fs.writeFileSync(base + 'test2.yml', testYaml2); fs.writeFileSync(base + 'test1.json', testJson1); fs.writeFileSync(base + 'test2.json', testJson2); + // not supported type + fs.writeFileSync(base + 'test1.xml', ''); fs.writeFileSync('/tmp/test3.json', testJson3); }); @@ -147,6 +149,12 @@ describe('config flag handling', () => { hexo.log.reader[0].msg.should.eql('No config file entered.'); }); + it('not supported type', () => { + mcp(base, 'test1.xml,test1.json').should.equal(base + '_multiconfig.yml'); + hexo.log.reader[0].type.should.eql('warning'); + hexo.log.reader[0].msg.should.eql('Config file test1.xml not supported type.'); + }); + it('1 file', () => { mcp(base, 'test1.yml').should.eql( pathFn.resolve(base + 'test1.yml')); diff --git a/test/scripts/hexo/post.js b/test/scripts/hexo/post.js index 23f39c9b1a..6dd792f617 100644 --- a/test/scripts/hexo/post.js +++ b/test/scripts/hexo/post.js @@ -10,7 +10,7 @@ const fixture = require('../../fixtures/post_render'); const escapeSwigTag = str => str.replace(/{/g, '{').replace(/}/g, '}'); describe('Post', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'post_test')); require('../../../lib/plugins/highlight/')(hexo); const { post } = hexo; @@ -903,7 +903,8 @@ describe('Post', () => { ].join('\n'); const data = await post.render(null, { - content + content, + engine: 'markdown' }); data.content.trim().should.eql([ '

test1

', @@ -913,6 +914,16 @@ describe('Post', () => { ].join('\n')); }); + it('render() - swig comments', async () => { + const content = '{# blockquote #}'; + + const data = await post.render(null, { + content, + engine: 'markdown' + }); + data.content.trim().should.eql(''); + }); + it('render() - shouln\'t break curly brackets', async () => { hexo.config.syntax_highlighter = 'prismjs'; diff --git a/test/scripts/hexo/render.js b/test/scripts/hexo/render.js index ce82338cfb..5ebb6f16db 100644 --- a/test/scripts/hexo/render.js +++ b/test/scripts/hexo/render.js @@ -6,7 +6,7 @@ const yaml = require('js-yaml'); const { spy, assert: sinonAssert } = require('sinon'); describe('Render', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'render_test')); hexo.config.meta_generator = false; @@ -104,6 +104,15 @@ describe('Render', () => { } }); + it('render() - null path and text', async () => { + try { + await hexo.render.render({text: null, engine: null}); + should.fail('Return value must be rejected'); + } catch (err) { + err.message.should.eql('No input file or string!'); + } + }); + it('render() - options', async () => { const result = await hexo.render.render({ text: [ @@ -228,6 +237,10 @@ describe('Render', () => { should.throw(() => hexo.render.renderSync(), 'No input file or string!'); }); + it('renderSync() - null path and text', () => { + should.throw(() => hexo.render.renderSync({text: null, engine: null}), 'No input file or string!'); + }); + it('renderSync() - options', () => { const result = hexo.render.renderSync({ text: [ diff --git a/test/scripts/hexo/router.js b/test/scripts/hexo/router.js index 92c7f39ed2..5e590ee8f3 100644 --- a/test/scripts/hexo/router.js +++ b/test/scripts/hexo/router.js @@ -9,7 +9,7 @@ const { spy, assert: sinonAssert } = require('sinon'); const testUtil = require('../../util'); describe('Router', () => { - const Router = require('../../../lib/hexo/router'); + const Router = require('../../../dist/hexo/router'); const router = new Router(); function checkStream(stream, expected) { diff --git a/test/scripts/hexo/scaffold.js b/test/scripts/hexo/scaffold.js index 0a7ebbdaf4..0f432aad7f 100644 --- a/test/scripts/hexo/scaffold.js +++ b/test/scripts/hexo/scaffold.js @@ -4,7 +4,7 @@ const { join } = require('path'); const { exists, readFile, rmdir, unlink, writeFile } = require('hexo-fs'); describe('Scaffold', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); const scaffold = hexo.scaffold; const scaffoldDir = hexo.scaffold_dir; diff --git a/test/scripts/hexo/update_package.js b/test/scripts/hexo/update_package.js index 3369bc56e6..fac448627f 100644 --- a/test/scripts/hexo/update_package.js +++ b/test/scripts/hexo/update_package.js @@ -4,9 +4,9 @@ const { join } = require('path'); const { readFile, unlink, writeFile } = require('hexo-fs'); describe('Update package.json', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname, {silent: true}); - const updatePkg = require('../../../lib/hexo/update_package'); + const updatePkg = require('../../../dist/hexo/update_package'); const packagePath = join(hexo.base_dir, 'package.json'); beforeEach(() => { diff --git a/test/scripts/hexo/validate_config.js b/test/scripts/hexo/validate_config.js index 3f2b80b903..be5a9bbdb8 100644 --- a/test/scripts/hexo/validate_config.js +++ b/test/scripts/hexo/validate_config.js @@ -3,10 +3,10 @@ const { spy } = require('sinon'); describe('Validate config', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - const validateConfig = require('../../../lib/hexo/validate_config'); - const defaultConfig = require('../../../lib/hexo/default_config'); + const validateConfig = require('../../../dist/hexo/validate_config'); + const defaultConfig = require('../../../dist/hexo/default_config'); let logSpy; beforeEach(() => { diff --git a/test/scripts/models/asset.js b/test/scripts/models/asset.js index 0c3e62a107..889ba9738e 100644 --- a/test/scripts/models/asset.js +++ b/test/scripts/models/asset.js @@ -3,7 +3,7 @@ const { join } = require('path'); describe('Asset', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Asset = hexo.model('Asset'); diff --git a/test/scripts/models/cache.js b/test/scripts/models/cache.js index 32bc5a7efd..de8ad9d7c9 100644 --- a/test/scripts/models/cache.js +++ b/test/scripts/models/cache.js @@ -1,7 +1,7 @@ 'use strict'; describe('Cache', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Cache = hexo.model('Cache'); diff --git a/test/scripts/models/category.js b/test/scripts/models/category.js index 96553e68ca..d1a0da40a1 100644 --- a/test/scripts/models/category.js +++ b/test/scripts/models/category.js @@ -3,12 +3,12 @@ const { deepMerge, full_url_for } = require('hexo-util'); describe('Category', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Category = hexo.model('Category'); const Post = hexo.model('Post'); const PostCategory = hexo.model('PostCategory'); - const defaults = require('../../../lib/hexo/default_config'); + const defaults = require('../../../dist/hexo/default_config'); before(() => hexo.init()); diff --git a/test/scripts/models/moment.js b/test/scripts/models/moment.js index 5cc80fb0c0..e5054575b8 100644 --- a/test/scripts/models/moment.js +++ b/test/scripts/models/moment.js @@ -1,9 +1,9 @@ 'use strict'; const moment = require('moment-timezone'); +const SchemaTypeMoment = require('../../../dist/models/types/moment'); describe('SchemaTypeMoment', () => { - const SchemaTypeMoment = require('../../../lib/models/types/moment'); const type = new SchemaTypeMoment('test'); it('cast()', () => { diff --git a/test/scripts/models/page.js b/test/scripts/models/page.js index da6a520009..8a6356861e 100644 --- a/test/scripts/models/page.js +++ b/test/scripts/models/page.js @@ -4,10 +4,10 @@ const { join } = require('path'); const { deepMerge, full_url_for } = require('hexo-util'); describe('Page', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Page = hexo.model('Page'); - const defaults = require('../../../lib/hexo/default_config'); + const defaults = require('../../../dist/hexo/default_config'); beforeEach(() => { hexo.config = deepMerge({}, defaults); }); diff --git a/test/scripts/models/post.js b/test/scripts/models/post.js index 1c9887e9cb..ded1dc66be 100644 --- a/test/scripts/models/post.js +++ b/test/scripts/models/post.js @@ -5,7 +5,7 @@ const Promise = require('bluebird'); const { full_url_for } = require('hexo-util'); describe('Post', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Post = hexo.model('Post'); const Tag = hexo.model('Tag'); @@ -31,7 +31,6 @@ describe('Post', () => { data.comments.should.be.true; data.layout.should.eql('post'); data._content.should.eql(''); - data.link.should.eql(''); data.raw.should.eql(''); data.published.should.be.true; should.not.exist(data.updated); diff --git a/test/scripts/models/post_asset.js b/test/scripts/models/post_asset.js index 97e5ddde8f..8e6d765304 100644 --- a/test/scripts/models/post_asset.js +++ b/test/scripts/models/post_asset.js @@ -3,12 +3,12 @@ const { join } = require('path'); describe('PostAsset', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const PostAsset = hexo.model('PostAsset'); const Post = hexo.model('Post'); let post; - const defaults = require('../../../lib/hexo/default_config'); + const defaults = require('../../../dist/hexo/default_config'); before(async () => { await hexo.init(); diff --git a/test/scripts/models/tag.js b/test/scripts/models/tag.js index 5e77692da5..7fb86124ca 100644 --- a/test/scripts/models/tag.js +++ b/test/scripts/models/tag.js @@ -3,12 +3,12 @@ const { deepMerge, full_url_for } = require('hexo-util'); describe('Tag', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); const Tag = hexo.model('Tag'); const Post = hexo.model('Post'); const PostTag = hexo.model('PostTag'); - const defaults = require('../../../lib/hexo/default_config'); + const defaults = require('../../../dist/hexo/default_config'); before(() => hexo.init()); diff --git a/test/scripts/processors/asset.js b/test/scripts/processors/asset.js index 882023898f..2a268714c3 100644 --- a/test/scripts/processors/asset.js +++ b/test/scripts/processors/asset.js @@ -2,15 +2,16 @@ const { dirname, join } = require('path'); const { mkdirs, rmdir, stat, unlink, writeFile } = require('hexo-fs'); +const { spy } = require('sinon'); const dateFormat = 'YYYY-MM-DD HH:mm:ss'; describe('asset', () => { - const Hexo = require('../../../lib/hexo'); - const defaults = require('../../../lib/hexo/default_config'); + const Hexo = require('../../../dist/hexo'); + const defaults = require('../../../dist/hexo/default_config'); const baseDir = join(__dirname, 'asset_test'); const hexo = new Hexo(baseDir); - const asset = require('../../../lib/plugins/processor/asset')(hexo); + const asset = require('../../../dist/plugins/processor/asset')(hexo); const process = asset.process.bind(hexo); const { pattern } = asset; const { source } = hexo; @@ -188,6 +189,19 @@ describe('asset', () => { should.not.exist(Asset.findById(id)); }); + it('asset - type: delete - not exist', async () => { + const file = newFile({ + path: 'foo.jpg', + type: 'delete', + renderable: false + }); + + const id = 'source/' + file.path; + await process(file); + + should.not.exist(Asset.findById(id)); + }); + it('page - type: create', async () => { const body = [ 'title: "Hello world"', @@ -222,6 +236,46 @@ describe('asset', () => { ]); }); + it('page - type: create - exist', async () => { + const logSpy = spy(); + hexo.log.warn = logSpy; + + const body = [ + 'title: "Hello world"', + 'date: 2006-01-02 15:04:05', + 'updated: 2014-12-13 01:02:03', + '---', + 'The quick brown fox jumps over the lazy dog' + ].join('\n'); + + const file = newFile({ + path: 'hello.njk', + type: 'create', + renderable: true + }); + + await writeFile(file.source, body); + await process(file); + await process(file); + + const page = Page.findOne({ source: file.path }); + page.title.should.eql('Hello world'); + page.date.format(dateFormat).should.eql('2006-01-02 15:04:05'); + page.updated.format(dateFormat).should.eql('2014-12-13 01:02:03'); + page._content.should.eql('The quick brown fox jumps over the lazy dog'); + page.source.should.eql(file.path); + page.raw.should.eql(body); + page.path.should.eql('hello.html'); + page.layout.should.eql('page'); + + logSpy.called.should.be.true; + logSpy.args[0][0].should.contains('Trying to "create" \x1B[35mhello.njk\x1B[39m, but the file already exists!'); + await Promise.all([ + page.remove(), + unlink(file.source) + ]); + }); + it('page - type: update', async () => { const body = [ 'title: "Hello world"', @@ -249,6 +303,25 @@ describe('asset', () => { ]); }); + it('page - type: skip', async () => { + const file = newFile({ + path: 'hello.njk', + type: 'skip', + renderable: true + }); + + await Page.insert({ + source: file.path, + path: 'hello.html' + }); + const page = Page.findOne({source: file.path}); + await process(file); + should.exist(page); + await Promise.all([ + page.remove() + ]); + }); + it('page - type: delete', async () => { const file = newFile({ path: 'hello.njk', @@ -264,6 +337,17 @@ describe('asset', () => { should.not.exist(Page.findOne({ source: file.path })); }); + it('page - type: delete - not exist', async () => { + const file = newFile({ + path: 'hello.njk', + type: 'delete', + renderable: true + }); + + await process(file); + should.not.exist(Page.findOne({ source: file.path })); + }); + it('page - use the status of the source file if date not set', async () => { const file = newFile({ path: 'hello.njk', diff --git a/test/scripts/processors/common.js b/test/scripts/processors/common.js index c0930038ca..1c4a54cd50 100644 --- a/test/scripts/processors/common.js +++ b/test/scripts/processors/common.js @@ -3,7 +3,7 @@ const moment = require('moment'); describe('common', () => { - const common = require('../../../lib/plugins/processor/common'); + const common = require('../../../dist/plugins/processor/common'); it('isTmpFile()', () => { common.isTmpFile('foo').should.be.false; diff --git a/test/scripts/processors/data.js b/test/scripts/processors/data.js index d7dab3e598..ef5493e469 100644 --- a/test/scripts/processors/data.js +++ b/test/scripts/processors/data.js @@ -5,10 +5,10 @@ const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); const { join } = require('path'); describe('data', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const baseDir = join(__dirname, 'data_test'); const hexo = new Hexo(baseDir); - const processor = require('../../../lib/plugins/processor/data')(hexo); + const processor = require('../../../dist/plugins/processor/data')(hexo); const process = Promise.method(processor.process).bind(hexo); const { source } = hexo; const { File } = source; @@ -128,6 +128,22 @@ describe('data', () => { unlink(file.source); }); + it('type: skip', async () => { + const file = newFile({ + path: 'users.yml', + type: 'skip' + }); + + await Data.insert({ + _id: 'users', + data: {foo: 'bar'} + }); + const data = Data.findById('users'); + await process(file); + should.exist(data); + data.remove(); + }); + it('type: delete', async () => { const file = newFile({ path: 'users.yml', @@ -141,4 +157,15 @@ describe('data', () => { await process(file); should.not.exist(Data.findById('users')); }); + + it('type: delete - not exist', async () => { + const file = newFile({ + path: 'users.yml', + type: 'delete' + }); + + await process(file); + should.not.exist(Data.findById('users')); + }); + }); diff --git a/test/scripts/processors/post.js b/test/scripts/processors/post.js index 5132f4670e..fea2563d62 100644 --- a/test/scripts/processors/post.js +++ b/test/scripts/processors/post.js @@ -3,15 +3,15 @@ const { join } = require('path'); const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); const Promise = require('bluebird'); -const defaultConfig = require('../../../lib/hexo/default_config'); +const defaultConfig = require('../../../dist/hexo/default_config'); const dateFormat = 'YYYY-MM-DD HH:mm:ss'; describe('post', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const baseDir = join(__dirname, 'post_test'); const hexo = new Hexo(baseDir); - const post = require('../../../lib/plugins/processor/post')(hexo); + const post = require('../../../dist/plugins/processor/post')(hexo); const process = Promise.method(post.process.bind(hexo)); const { pattern } = post; const { source } = hexo; @@ -250,6 +250,31 @@ describe('post', () => { Post.removeById(postId); }); + it('asset - type: delete - not exist', async () => { + hexo.config.post_asset_folder = true; + + const file = newFile({ + path: 'foo/bar.jpg', + published: true, + type: 'delete', + renderable: false + }); + + const id = 'source/' + file.path; + + const post = await Post.insert({ + source: '_posts/foo.html', + slug: 'foo' + }); + const postId = post._id; + + await process(file); + should.not.exist(PostAsset.findById(id)); + + Post.removeById(postId); + }); + + it('asset - skip if can\'t find a matching post', async () => { hexo.config.post_asset_folder = true; @@ -358,6 +383,36 @@ describe('post', () => { ]); }); + it('post - type: skip', async () => { + const file = newFile({ + path: 'foo.html', + published: true, + type: 'skip', + renderable: true + }); + + await Post.insert({ + source: file.path, + slug: 'foo' + }); + await process(file); + const post = Post.findOne({ source: file.path }); + should.exist(post); + post.remove(); + }); + + it('post - type: delete - not exist', async () => { + const file = newFile({ + path: 'foo.html', + published: true, + type: 'delete', + renderable: true + }); + + await process(file); + should.not.exist(Post.findOne({ source: file.path })); + }); + it('post - type: delete', async () => { const file = newFile({ path: 'foo.html', @@ -374,6 +429,18 @@ describe('post', () => { should.not.exist(Post.findOne({ source: file.path })); }); + it('post - type: delete - not exist', async () => { + const file = newFile({ + path: 'foo.html', + published: true, + type: 'delete', + renderable: true + }); + + await process(file); + should.not.exist(Post.findOne({ source: file.path })); + }); + it('post - parse file name', async () => { const body = [ 'title: "Hello world"', @@ -711,33 +778,7 @@ describe('post', () => { ]); }); - it('post - link without title', async () => { - const body = [ - 'link: https://hexo.io/', - '---' - ].join('\n'); - - const file = newFile({ - path: 'foo.html', - published: true, - type: 'create', - renderable: true - }); - - await writeFile(file.source, body); - await process(file); - const post = Post.findOne({ source: file.path }); - - post.link.should.eql('https://hexo.io/'); - post.title.should.eql('hexo.io'); - - return Promise.all([ - post.remove(), - unlink(file.source) - ]); - }); - - it('post - link without title and link', async () => { + it('post - without title', async () => { const body = ''; const file = newFile({ @@ -1006,6 +1047,50 @@ describe('post', () => { ]); }); + it('post - delete existing draft assets if draft posts are hidden', async () => { + hexo.config.post_asset_folder = true; + + const body = [ + 'title: "Hello world"', + 'published: false', + '---' + ].join('\n'); + + const file = newFile({ + path: 'foo.html', + published: true, + type: 'create', + renderable: true + }); + + const assetId = 'source/_posts/foo/bar.jpg'; + const assetPath = join(hexo.base_dir, assetId); + + await Promise.all([ + writeFile(file.source, body), + writeFile(assetPath, '') + ]); + + // drafts disabled - no draft assets should be generated + await process(file); + const post = Post.findOne({ source: file.path }); + await PostAsset.insert({ + _id: 'source/_posts/foo/bar.jpg', + slug: 'bar.jpg', + post: post._id + }); + await process(file); + + post.published.should.be.false; + should.not.exist(PostAsset.findById(assetId)); + + await Promise.all([ + post.remove(), + unlink(file.source), + unlink(assetPath) + ]); + }); + it('post - post_asset_folder disabled', async () => { hexo.config.post_asset_folder = false; @@ -1171,4 +1256,76 @@ describe('post', () => { unlink(file.source) ]); }); + + it('asset - post - common render', async () => { + hexo.config.post_asset_folder = true; + + const file = newFile({ + path: 'foo.md', + published: true, + type: 'create', + renderable: true + }); + + const assetFile = newFile({ + path: 'foo/test.yml', + published: true, + type: 'create' + }); + + await Promise.all([ + writeFile(file.source, 'test'), + writeFile(assetFile.source, 'test') + ]); + await process(file); + const id = 'source/' + assetFile.path; + const post = Post.findOne({ source: file.path }); + PostAsset.findById(id).renderable.should.be.true; + + hexo.config.post_asset_folder = false; + + return Promise.all([ + unlink(file.source), + unlink(assetFile.source), + post.remove(), + PostAsset.removeById(id) + ]); + }); + + it('asset - post - skip render', async () => { + hexo.config.post_asset_folder = true; + hexo.config.skip_render = '**.yml'; + + const file = newFile({ + path: 'foo.md', + published: true, + type: 'create', + renderable: true + }); + + const assetFile = newFile({ + path: 'foo/test.yml', + published: true, + type: 'create' + }); + + await Promise.all([ + writeFile(file.source, 'test'), + writeFile(assetFile.source, 'test') + ]); + await process(file); + const id = 'source/' + assetFile.path; + const post = Post.findOne({ source: file.path }); + PostAsset.findById(id).renderable.should.be.false; + + hexo.config.post_asset_folder = false; + hexo.config.skip_render = ''; + + return Promise.all([ + unlink(file.source), + unlink(assetFile.source), + post.remove(), + PostAsset.removeById(id) + ]); + }); }); diff --git a/test/scripts/renderers/json.js b/test/scripts/renderers/json.js index aa7896229b..bdcf516a21 100644 --- a/test/scripts/renderers/json.js +++ b/test/scripts/renderers/json.js @@ -1,7 +1,7 @@ 'use strict'; describe('json', () => { - const r = require('../../../lib/plugins/renderer/json'); + const r = require('../../../dist/plugins/renderer/json'); it('normal', () => { const data = { diff --git a/test/scripts/renderers/nunjucks.js b/test/scripts/renderers/nunjucks.js index bc8717d5b3..4159f424b0 100644 --- a/test/scripts/renderers/nunjucks.js +++ b/test/scripts/renderers/nunjucks.js @@ -1,7 +1,7 @@ 'use strict'; require('chai').should(); -const r = require('../../../lib/plugins/renderer/nunjucks'); +const r = require('../../../dist/plugins/renderer/nunjucks'); const { dirname, join } = require('path'); describe('nunjucks', () => { @@ -123,6 +123,14 @@ describe('nunjucks', () => { r({ text: forLoop }, data).should.eql('123'); }); + it('toarray other case', () => { + const data = { + arr: 1 + }; + + r({ text: forLoop }, data).should.eql(''); + }); + it('safedump undefined', () => { const text = [ '{{ items | safedump }}' diff --git a/test/scripts/renderers/plain.js b/test/scripts/renderers/plain.js index 18d1ec0382..24982baa14 100644 --- a/test/scripts/renderers/plain.js +++ b/test/scripts/renderers/plain.js @@ -1,7 +1,7 @@ 'use strict'; describe('plain', () => { - const r = require('../../../lib/plugins/renderer/plain'); + const r = require('../../../dist/plugins/renderer/plain'); it('normal', () => { r({text: '123'}).should.eql('123'); diff --git a/test/scripts/renderers/yaml.js b/test/scripts/renderers/yaml.js index 3f734d1766..bf26b6fde0 100644 --- a/test/scripts/renderers/yaml.js +++ b/test/scripts/renderers/yaml.js @@ -1,7 +1,7 @@ 'use strict'; describe('yaml', () => { - const r = require('../../../lib/plugins/renderer/yaml'); + const r = require('../../../dist/plugins/renderer/yaml'); it('normal', () => { r({text: 'foo: 1'}).should.eql({foo: 1}); diff --git a/test/scripts/tags/asset_img.js b/test/scripts/tags/asset_img.js index d8f0709c09..3fa8817087 100644 --- a/test/scripts/tags/asset_img.js +++ b/test/scripts/tags/asset_img.js @@ -3,9 +3,9 @@ const Promise = require('bluebird'); describe('asset_img', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const assetImgTag = require('../../../lib/plugins/tag/asset_img')(hexo); + const assetImgTag = require('../../../dist/plugins/tag/asset_img')(hexo); const Post = hexo.model('Post'); const PostAsset = hexo.model('PostAsset'); let post; diff --git a/test/scripts/tags/asset_link.js b/test/scripts/tags/asset_link.js index 8a1140bc17..a75c8effba 100644 --- a/test/scripts/tags/asset_link.js +++ b/test/scripts/tags/asset_link.js @@ -3,9 +3,9 @@ const Promise = require('bluebird'); describe('asset_link', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const assetLinkTag = require('../../../lib/plugins/tag/asset_link')(hexo); + const assetLinkTag = require('../../../dist/plugins/tag/asset_link')(hexo); const Post = hexo.model('Post'); const PostAsset = hexo.model('PostAsset'); let post; diff --git a/test/scripts/tags/asset_path.js b/test/scripts/tags/asset_path.js index 20d1d4a065..281a015232 100644 --- a/test/scripts/tags/asset_path.js +++ b/test/scripts/tags/asset_path.js @@ -3,9 +3,9 @@ const Promise = require('bluebird'); describe('asset_path', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const assetPathTag = require('../../../lib/plugins/tag/asset_path')(hexo); + const assetPathTag = require('../../../dist/plugins/tag/asset_path')(hexo); const Post = hexo.model('Post'); const PostAsset = hexo.model('PostAsset'); let post; diff --git a/test/scripts/tags/blockquote.js b/test/scripts/tags/blockquote.js index 09be0d9010..d1b1661557 100644 --- a/test/scripts/tags/blockquote.js +++ b/test/scripts/tags/blockquote.js @@ -1,9 +1,9 @@ 'use strict'; describe('blockquote', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const blockquote = require('../../../lib/plugins/tag/blockquote')(hexo); + const blockquote = require('../../../dist/plugins/tag/blockquote')(hexo); before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked')))); diff --git a/test/scripts/tags/code.js b/test/scripts/tags/code.js index 406fe69c24..a9c2d5c966 100644 --- a/test/scripts/tags/code.js +++ b/test/scripts/tags/code.js @@ -4,10 +4,10 @@ const util = require('hexo-util'); const cheerio = require('cheerio'); describe('code', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(); - require('../../../lib/plugins/highlight/')(hexo); - const codeTag = require('../../../lib/plugins/tag/code')(hexo); + require('../../../dist/plugins/highlight/')(hexo); + const codeTag = require('../../../dist/plugins/tag/code')(hexo); const { escapeHTML } = util; const fixture = [ diff --git a/test/scripts/tags/full_url_for.js b/test/scripts/tags/full_url_for.js new file mode 100644 index 0000000000..ea01912392 --- /dev/null +++ b/test/scripts/tags/full_url_for.js @@ -0,0 +1,62 @@ +'use strict'; + +const cheerio = require('cheerio'); + +describe('full_url_for', () => { + const ctx = { + config: { url: 'https://example.com' } + }; + + const fullUrlForTag = require('../../../dist/plugins/tag/full_url_for')(ctx); + const fullUrlFor = args => fullUrlForTag(args.split(' ')); + + it('no path input', () => { + const $ = cheerio.load(fullUrlFor('nopath')); + $('a').attr('href').should.eql(ctx.config.url + '/'); + $('a').html().should.eql('nopath'); + }); + + it('internal url', () => { + let $ = cheerio.load(fullUrlFor('index index.html')); + $('a').attr('href').should.eql(ctx.config.url + '/index.html'); + $('a').html().should.eql('index'); + + $ = cheerio.load(fullUrlFor('index /')); + $('a').attr('href').should.eql(ctx.config.url + '/'); + $('a').html().should.eql('index'); + + $ = cheerio.load(fullUrlFor('index /index.html')); + $('a').attr('href').should.eql(ctx.config.url + '/index.html'); + $('a').html().should.eql('index'); + }); + + it('internel url (pretty_urls.trailing_index disabled)', () => { + ctx.config.pretty_urls = { trailing_index: false }; + let $ = cheerio.load(fullUrlFor('index index.html')); + $('a').attr('href').should.eql(ctx.config.url + '/'); + $('a').html().should.eql('index'); + + $ = cheerio.load(fullUrlFor('index /index.html')); + $('a').attr('href').should.eql(ctx.config.url + '/'); + $('a').html().should.eql('index'); + }); + + it('external url', () => { + [ + 'https://hexo.io/', + '//google.com/', + // 'index.html' in external link should not be removed + '//google.com/index.html' + ].forEach(url => { + const $ = cheerio.load(fullUrlFor(`external ${url}`)); + $('a').attr('href').should.eql(url); + $('a').html().should.eql('external'); + }); + }); + + it('only hash', () => { + const $ = cheerio.load(fullUrlFor('hash #test')); + $('a').attr('href').should.eql(ctx.config.url + '/#test'); + $('a').html().should.eql('hash'); + }); +}); diff --git a/test/scripts/tags/iframe.js b/test/scripts/tags/iframe.js index b87806fc31..8f07063ccb 100644 --- a/test/scripts/tags/iframe.js +++ b/test/scripts/tags/iframe.js @@ -3,7 +3,7 @@ const cheerio = require('cheerio'); describe('iframe', () => { - const iframe = require('../../../lib/plugins/tag/iframe'); + const iframe = require('../../../dist/plugins/tag/iframe'); it('url', () => { const $ = cheerio.load(iframe(['https://zespia.tw'])); diff --git a/test/scripts/tags/img.js b/test/scripts/tags/img.js index 7030c92a41..10cd72f549 100644 --- a/test/scripts/tags/img.js +++ b/test/scripts/tags/img.js @@ -4,9 +4,9 @@ const pathFn = require('path'); const cheerio = require('cheerio'); describe('img', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(pathFn.join(__dirname, 'img_test')); - const img = require('../../../lib/plugins/tag/img')(hexo); + const img = require('../../../dist/plugins/tag/img')(hexo); before(() => hexo.init()); diff --git a/test/scripts/tags/include_code.js b/test/scripts/tags/include_code.js index ca8b4c7884..3c18068609 100644 --- a/test/scripts/tags/include_code.js +++ b/test/scripts/tags/include_code.js @@ -6,10 +6,10 @@ const { highlight, prismHighlight } = require('hexo-util'); const Promise = require('bluebird'); describe('include_code', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'include_code_test')); - require('../../../lib/plugins/highlight/')(hexo); - const includeCode = Promise.method(require('../../../lib/plugins/tag/include_code')(hexo)); + require('../../../dist/plugins/highlight/')(hexo); + const includeCode = Promise.method(require('../../../dist/plugins/tag/include_code')(hexo)); const path = join(hexo.source_dir, hexo.config.code_dir, 'test.js'); const defaultCfg = JSON.parse(JSON.stringify(hexo.config)); diff --git a/test/scripts/tags/index.js b/test/scripts/tags/index.js index 7ff000647c..21dc58a9b3 100644 --- a/test/scripts/tags/index.js +++ b/test/scripts/tags/index.js @@ -6,6 +6,7 @@ describe('Tags', () => { require('./asset_path'); require('./blockquote'); require('./code'); + require('./full_url_for'); require('./iframe'); require('./img'); require('./include_code'); @@ -13,4 +14,5 @@ describe('Tags', () => { require('./post_link'); require('./post_path'); require('./pullquote'); + require('./url_for'); }); diff --git a/test/scripts/tags/link.js b/test/scripts/tags/link.js index 4e75d6d146..4adcdcf0cb 100644 --- a/test/scripts/tags/link.js +++ b/test/scripts/tags/link.js @@ -3,7 +3,7 @@ const cheerio = require('cheerio'); describe('link', () => { - const link = require('../../../lib/plugins/tag/link'); + const link = require('../../../dist/plugins/tag/link'); it('text + url', () => { const $ = cheerio.load(link('Click here to Google https://google.com'.split(' '))); diff --git a/test/scripts/tags/post_link.js b/test/scripts/tags/post_link.js index 22696cf553..999f2cad48 100644 --- a/test/scripts/tags/post_link.js +++ b/test/scripts/tags/post_link.js @@ -1,9 +1,9 @@ 'use strict'; describe('post_link', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const postLink = require('../../../lib/plugins/tag/post_link')(hexo); + const postLink = require('../../../dist/plugins/tag/post_link')(hexo); const Post = hexo.model('Post'); hexo.config.permalink = ':title/'; @@ -22,6 +22,11 @@ describe('post_link', () => { source: 'fôo', slug: 'fôo', title: 'Hello world' + }, + { + source: 'no-title', + slug: 'no-title', + title: '' }]))); it('default', () => { @@ -36,6 +41,10 @@ describe('post_link', () => { postLink(['foo', 'test']).should.eql('test'); }); + it('no title', () => { + postLink(['no-title']).should.eql('no-title'); + }); + it('should escape tag in title by default', () => { postLink(['title-with-tag']).should.eql('"Hello" <new world>!'); }); diff --git a/test/scripts/tags/post_path.js b/test/scripts/tags/post_path.js index 2b73c1edc8..5a00ecbf66 100644 --- a/test/scripts/tags/post_path.js +++ b/test/scripts/tags/post_path.js @@ -1,9 +1,9 @@ 'use strict'; describe('post_path', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const postPath = require('../../../lib/plugins/tag/post_path')(hexo); + const postPath = require('../../../dist/plugins/tag/post_path')(hexo); const Post = hexo.model('Post'); hexo.config.permalink = ':title/'; diff --git a/test/scripts/tags/pullquote.js b/test/scripts/tags/pullquote.js index acc0dc9f62..983f2a4a73 100644 --- a/test/scripts/tags/pullquote.js +++ b/test/scripts/tags/pullquote.js @@ -1,9 +1,9 @@ 'use strict'; describe('pullquote', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(__dirname); - const pullquote = require('../../../lib/plugins/tag/pullquote')(hexo); + const pullquote = require('../../../dist/plugins/tag/pullquote')(hexo); before(() => hexo.init().then(() => hexo.loadPlugin(require.resolve('hexo-renderer-marked')))); diff --git a/test/scripts/tags/url_for.js b/test/scripts/tags/url_for.js new file mode 100644 index 0000000000..1b905df59a --- /dev/null +++ b/test/scripts/tags/url_for.js @@ -0,0 +1,121 @@ +'use strict'; + +const cheerio = require('cheerio'); + +describe('url_for', () => { + const ctx = { + config: { url: 'https://example.com' } + }; + + const urlForTag = require('../../../dist/plugins/tag/url_for')(ctx); + const urlFor = args => urlForTag(args.split(' ')); + + it('should encode path', () => { + ctx.config.root = '/'; + let $ = cheerio.load(urlFor('foo fôo.html')); + $('a').attr('href').should.eql('/f%C3%B4o.html'); + $('a').html().should.eql('foo'); + + ctx.config.root = '/fôo/'; + $ = cheerio.load(urlFor('foo bár.html')); + $('a').attr('href').should.eql('/f%C3%B4o/b%C3%A1r.html'); + $('a').html().should.eql('foo'); + }); + + it('internal url (relative off)', () => { + ctx.config.root = '/'; + let $ = cheerio.load(urlFor('index index.html')); + $('a').attr('href').should.eql('/index.html'); + $('a').html().should.eql('index'); + + $ = cheerio.load(urlFor('index /')); + $('a').attr('href').should.eql('/'); + $('a').html().should.eql('index'); + + $ = cheerio.load(urlFor('index /index.html')); + $('a').attr('href').should.eql('/index.html'); + $('a').html().should.eql('index'); + + ctx.config.root = '/blog/'; + $ = cheerio.load(urlFor('index index.html')); + $('a').attr('href').should.eql('/blog/index.html'); + $('a').html().should.eql('index'); + + $ = cheerio.load(urlFor('index /')); + $('a').attr('href').should.eql('/blog/'); + $('a').html().should.eql('index'); + + $ = cheerio.load(urlFor('index /index.html')); + $('a').attr('href').should.eql('/blog/index.html'); + $('a').html().should.eql('index'); + }); + + it('internal url (relative on)', () => { + ctx.config.relative_link = true; + ctx.config.root = '/'; + + ctx.path = ''; + let $ = cheerio.load(urlFor('index index.html')); + $('a').attr('href').should.eql('index.html'); + $('a').html().should.eql('index'); + + ctx.path = 'foo/bar/'; + $ = cheerio.load(urlFor('index index.html')); + $('a').attr('href').should.eql('../../index.html'); + $('a').html().should.eql('index'); + + ctx.config.relative_link = false; + }); + + it('internal url (options.relative)', () => { + ctx.path = ''; + let $ = cheerio.load(urlFor('index index.html true')); + $('a').attr('href').should.eql('index.html'); + $('a').html().should.eql('index'); + + ctx.config.relative_link = true; + $ = cheerio.load(urlFor('index index.html false')); + $('a').attr('href').should.eql('/index.html'); + $('a').html().should.eql('index'); + ctx.config.relative_link = false; + }); + + it('internel url (pretty_urls.trailing_index disabled)', () => { + ctx.config.pretty_urls = { trailing_index: false }; + ctx.path = ''; + ctx.config.root = '/'; + let $ = cheerio.load(urlFor('index index.html')); + $('a').attr('href').should.eql('/'); + $('a').html().should.eql('index'); + $ = cheerio.load(urlFor('index /index.html')); + $('a').attr('href').should.eql('/'); + $('a').html().should.eql('index'); + + ctx.config.root = '/blog/'; + $ = cheerio.load(urlFor('index index.html')); + $('a').attr('href').should.eql('/blog/'); + $('a').html().should.eql('index'); + $ = cheerio.load(urlFor('index /index.html')); + $('a').attr('href').should.eql('/blog/'); + $('a').html().should.eql('index'); + }); + + it('external url', () => { + [ + 'https://hexo.io/', + '//google.com/', + // 'index.html' in external link should not be removed + '//google.com/index.html' + ].forEach(url => { + const $ = cheerio.load(urlFor(`external ${url}`)); + $('a').attr('href').should.eql(url); + $('a').html().should.eql('external'); + }); + }); + + it('only hash', () => { + const $ = cheerio.load(urlFor('hash #test')); + $('a').attr('href').should.eql('#test'); + $('a').html().should.eql('hash'); + }); +}); diff --git a/test/scripts/theme/theme.js b/test/scripts/theme/theme.js index 5ac61bffd1..fc3b498cf7 100644 --- a/test/scripts/theme/theme.js +++ b/test/scripts/theme/theme.js @@ -4,7 +4,7 @@ const { join } = require('path'); const { mkdirs, rmdir, writeFile } = require('hexo-fs'); describe('Theme', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'theme_test'), {silent: true}); const themeDir = join(hexo.base_dir, 'themes', 'test'); diff --git a/test/scripts/theme/view.js b/test/scripts/theme/view.js index 190e2ed98b..20d1a60aa9 100644 --- a/test/scripts/theme/view.js +++ b/test/scripts/theme/view.js @@ -6,7 +6,7 @@ const moment = require('moment'); const { fake, assert: sinonAssert } = require('sinon'); describe('View', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'theme_test')); const themeDir = join(hexo.base_dir, 'themes', 'test'); const { compile } = Object.assign({}, hexo.extend.renderer.store.njk); diff --git a/test/scripts/theme_processors/config.js b/test/scripts/theme_processors/config.js index 5363df0f84..34a61330de 100644 --- a/test/scripts/theme_processors/config.js +++ b/test/scripts/theme_processors/config.js @@ -6,10 +6,10 @@ const { mkdirs, rmdir, unlink, writeFile} = require('hexo-fs'); const Promise = require('bluebird'); describe('config', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'config_test'), {silent: true}); - const processor = require('../../../lib/theme/processors/config'); - const process = Promise.method(processor.process.bind(hexo)); + const processor = require('../../../dist/theme/processors/config'); + const process = Promise.method(processor.config.process.bind(hexo)); const themeDir = join(hexo.base_dir, 'themes', 'test'); function newFile(options) { @@ -30,7 +30,7 @@ describe('config', () => { after(() => rmdir(hexo.base_dir)); it('pattern', () => { - const pattern = processor.pattern; + const pattern = processor.config.pattern; pattern.match('_config.yml').should.be.ok; pattern.match('_config.json').should.be.ok; diff --git a/test/scripts/theme_processors/i18n.js b/test/scripts/theme_processors/i18n.js index 2e5e9e2671..84088e31af 100644 --- a/test/scripts/theme_processors/i18n.js +++ b/test/scripts/theme_processors/i18n.js @@ -5,10 +5,10 @@ const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); const Promise = require('bluebird'); describe('i18n', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'config_test'), {silent: true}); - const processor = require('../../../lib/theme/processors/i18n'); - const process = Promise.method(processor.process.bind(hexo)); + const processor = require('../../../dist/theme/processors/i18n'); + const process = Promise.method(processor.i18n.process.bind(hexo)); const themeDir = join(hexo.base_dir, 'themes', 'test'); function newFile(options) { @@ -33,7 +33,7 @@ describe('i18n', () => { after(() => rmdir(hexo.base_dir)); it('pattern', () => { - const pattern = processor.pattern; + const pattern = processor.i18n.pattern; pattern.match('languages/default.yml').should.be.ok; pattern.match('languages/zh-TW.yml').should.be.ok; diff --git a/test/scripts/theme_processors/source.js b/test/scripts/theme_processors/source.js index 4166abe1a1..b84546e3d0 100644 --- a/test/scripts/theme_processors/source.js +++ b/test/scripts/theme_processors/source.js @@ -5,10 +5,10 @@ const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); const Promise = require('bluebird'); describe('source', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'source_test'), {silent: true}); - const processor = require('../../../lib/theme/processors/source'); - const process = Promise.method(processor.process.bind(hexo)); + const processor = require('../../../dist/theme/processors/source'); + const process = Promise.method(processor.source.process.bind(hexo)); const themeDir = join(hexo.base_dir, 'themes', 'test'); const Asset = hexo.model('Asset'); @@ -33,7 +33,7 @@ describe('source', () => { after(() => rmdir(hexo.base_dir)); it('pattern', () => { - const { pattern } = processor; + const { pattern } = processor.source; pattern.match('source/foo.jpg').should.eql({path: 'foo.jpg'}); pattern.match('source/_foo.jpg').should.be.false; @@ -136,4 +136,16 @@ describe('source', () => { await process(file); should.not.exist(Asset.findById(id)); }); + + it('type: delete - not -exist', async () => { + const file = newFile({ + path: 'style.css', + type: 'delete' + }); + + const id = 'themes/test/' + file.path; + + await process(file); + should.not.exist(Asset.findById(id)); + }); }); diff --git a/test/scripts/theme_processors/view.js b/test/scripts/theme_processors/view.js index f6171e5e33..145fc85f8e 100644 --- a/test/scripts/theme_processors/view.js +++ b/test/scripts/theme_processors/view.js @@ -5,10 +5,10 @@ const { mkdirs, rmdir, unlink, writeFile } = require('hexo-fs'); const Promise = require('bluebird'); describe('view', () => { - const Hexo = require('../../../lib/hexo'); + const Hexo = require('../../../dist/hexo'); const hexo = new Hexo(join(__dirname, 'view_test'), {silent: true}); - const processor = require('../../../lib/theme/processors/view'); - const process = Promise.method(processor.process.bind(hexo)); + const processor = require('../../../dist/theme/processors/view'); + const process = Promise.method(processor.view.process.bind(hexo)); const themeDir = join(hexo.base_dir, 'themes', 'test'); hexo.env.init = true; @@ -34,7 +34,7 @@ describe('view', () => { after(() => rmdir(hexo.base_dir)); it('pattern', () => { - const { pattern } = processor; + const { pattern } = processor.view; pattern.match('layout/index.njk').path.should.eql('index.njk'); should.not.exist(pattern.match('index.njk')); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..44cec41e42 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "sourceMap": true, + "outDir": "dist", + "declaration": true, + "esModuleInterop": true, + "types": [ + "node" + ] + }, + "include": [ + "lib/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file