diff --git a/lib/box/file.ts b/lib/box/file.ts index 99bb509ddd..30d71cdddf 100644 --- a/lib/box/file.ts +++ b/lib/box/file.ts @@ -19,12 +19,12 @@ class File { this.type = type; } - read(options?: ReadFileOptions): Promise { - return readFile(this.source, options); + read(options?: ReadFileOptions): Promise { + return readFile(this.source, options) as Promise; } - readSync(options?: ReadFileOptions): string | Buffer { - return readFileSync(this.source, options); + readSync(options?: ReadFileOptions): string { + return readFileSync(this.source, options) as string; } stat(): Promise { diff --git a/lib/box/index.ts b/lib/box/index.ts index 3222b55880..e55079c8de 100644 --- a/lib/box/index.ts +++ b/lib/box/index.ts @@ -7,12 +7,13 @@ import { magenta } from 'picocolors'; import { EventEmitter } from 'events'; import { isMatch, makeRe } from 'micromatch'; import type Hexo from '../hexo'; +import type { NodeJSLikeCallback } from '../types'; const defaultPattern = new Pattern(() => ({})); interface Processor { pattern: Pattern; - process: (file: File) => void; + process: (file?: File) => any; } class Box extends EventEmitter { diff --git a/lib/extend/console.ts b/lib/extend/console.ts index a5b7868af9..0c32af063c 100644 --- a/lib/extend/console.ts +++ b/lib/extend/console.ts @@ -1,5 +1,6 @@ import Promise from 'bluebird'; import abbrev from 'abbrev'; +import type { NodeJSLikeCallback } from '../types'; type Option = Partial<{ usage: string; @@ -19,7 +20,7 @@ interface Args { _: string[]; [key: string]: string | boolean | string[]; } -type AnyFn = (args: Args) => any; +type AnyFn = (args: Args, callback?: NodeJSLikeCallback) => any; interface StoreFunction extends AnyFn { desc?: string; options?: Option; @@ -29,7 +30,7 @@ interface Store { [key: string]: StoreFunction } interface Alias { - [key: string]: string + [abbreviation: string]: string } class Console { diff --git a/lib/extend/deployer.ts b/lib/extend/deployer.ts index ae88664cdc..5014433c4e 100644 --- a/lib/extend/deployer.ts +++ b/lib/extend/deployer.ts @@ -1,10 +1,11 @@ import Promise from 'bluebird'; +import type { NodeJSLikeCallback } from '../types'; interface StoreFunction { (deployArg: { type: string; [key: string]: any - }) : any; + }, callback?: NodeJSLikeCallback) : any; } interface Store { [key: string]: StoreFunction diff --git a/lib/extend/filter.ts b/lib/extend/filter.ts index 2af06fae87..98b34de1f3 100644 --- a/lib/extend/filter.ts +++ b/lib/extend/filter.ts @@ -11,6 +11,7 @@ interface FilterOptions { args?: any[]; } + interface StoreFunction { (data?: any, ...args: any[]): any; priority?: number; @@ -75,7 +76,7 @@ class Filter { if (index !== -1) list.splice(index, 1); } - exec(type: string, data: any[], options: FilterOptions = {}): Promise { + exec(type: string, data: any, options: FilterOptions = {}): Promise { const filters = this.list(type); if (filters.length === 0) return Promise.resolve(data); @@ -90,7 +91,7 @@ class Filter { })).then(() => args[0]); } - execSync(type: string, data: any[], options: FilterOptions = {}) { + execSync(type: string, data: any, options: FilterOptions = {}) { const filters = this.list(type); const filtersLen = filters.length; if (filtersLen === 0) return data; diff --git a/lib/extend/generator.ts b/lib/extend/generator.ts index 2854f8f869..125d2d3de9 100644 --- a/lib/extend/generator.ts +++ b/lib/extend/generator.ts @@ -1,4 +1,5 @@ import Promise from 'bluebird'; +import type { NodeJSLikeCallback } from '../types'; interface BaseObj { path: string; @@ -9,7 +10,7 @@ type ReturnType = BaseObj | BaseObj[]; type GeneratorReturnType = ReturnType | Promise; interface GeneratorFunction { - (locals: object): GeneratorReturnType; + (locals: object, callback?: NodeJSLikeCallback): GeneratorReturnType; } type StoreFunctionReturn = Promise; diff --git a/lib/extend/migrator.ts b/lib/extend/migrator.ts index 5ab0f7063e..ce5ae77544 100644 --- a/lib/extend/migrator.ts +++ b/lib/extend/migrator.ts @@ -1,6 +1,8 @@ import Promise from 'bluebird'; +import type { NodeJSLikeCallback } from '../types'; + interface StoreFunction { - (args: any): any + (args: any, callback?: NodeJSLikeCallback): any } interface Store { diff --git a/lib/extend/renderer.ts b/lib/extend/renderer.ts index 15f90c6de6..530b071e18 100644 --- a/lib/extend/renderer.ts +++ b/lib/extend/renderer.ts @@ -1,5 +1,6 @@ import { extname } from 'path'; import Promise from 'bluebird'; +import type { NodeJSLikeCallback } from '../types'; const getExtname = (str: string) => { if (typeof str !== 'string') return ''; @@ -13,7 +14,7 @@ export interface StoreFunctionData { text?: string; engine?: string; toString?: any; - onRenderEnd?: any; + onRenderEnd?: (...args: any[]) => any; } export interface StoreSyncFunction { @@ -21,7 +22,7 @@ export interface StoreSyncFunction { ( data: StoreFunctionData, options: object, - // callback: NodeJSLikeCallback + // callback?: NodeJSLikeCallback ): any; output?: string; compile?: (local: object) => any; @@ -30,6 +31,7 @@ export interface StoreFunction { ( data: StoreFunctionData, options: object, + callback?: NodeJSLikeCallback ): Promise; ( data: StoreFunctionData, @@ -57,7 +59,7 @@ class Renderer { this.storeSync = {}; } - list(sync: boolean): Store | SyncStore { + list(sync = false): Store | SyncStore { return sync ? this.storeSync : this.store; } @@ -97,7 +99,6 @@ class Renderer { 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); diff --git a/lib/extend/tag.ts b/lib/extend/tag.ts index d44b5dd993..b9648d3297 100644 --- a/lib/extend/tag.ts +++ b/lib/extend/tag.ts @@ -2,12 +2,14 @@ import { stripIndent } from 'hexo-util'; import { cyan, magenta, red, bold } from 'picocolors'; import { Environment } from 'nunjucks'; import Promise from 'bluebird'; +import type { NodeJSLikeCallback } from '../types'; + const rSwigRawFullBlock = /{% *raw *%}/; const rCodeTag = /]*>[\s\S]+?<\/code>/g; const escapeSwigTag = (str: string) => str.replace(/{/g, '{').replace(/}/g, '}'); interface TagFunction { - (args: any[], content: string): string; + (args: any[], content: string, callback?: NodeJSLikeCallback): string | PromiseLike; } interface AsyncTagFunction { (args: any[], content: string): Promise; @@ -251,14 +253,17 @@ class Tag { if (env.hasExtension(name)) env.removeExtension(name); } - render(str: string, options: { source?: string } = {}, callback?: NodeJSLikeCallback): Promise { + render(str: string): Promise; + render(str: string, callback: NodeJSLikeCallback): Promise; + render(str: string, options: { source?: string, [key: string]: any }, callback?: NodeJSLikeCallback): Promise; + render(str: string, options: { source?: string, [key: string]: any } | NodeJSLikeCallback = {}, callback?: NodeJSLikeCallback): Promise { if (!callback && typeof options === 'function') { callback = options; options = {}; } // Get path of post from source - const { source = '' } = options; + const { source = '' } = options as { source?: string }; return Promise.fromCallback(cb => { this.env.renderString( diff --git a/lib/hexo/default_config.ts b/lib/hexo/default_config.ts index 5e01f5781d..bea0921ddd 100644 --- a/lib/hexo/default_config.ts +++ b/lib/hexo/default_config.ts @@ -46,7 +46,9 @@ export = { wrap: true, exclude_languages: [], language_attr: false, - hljs: false + hljs: false, + line_threshold: 0, + first_line_number: 'always1' }, prismjs: { preprocess: true, diff --git a/lib/hexo/index.ts b/lib/hexo/index.ts index abd2b05387..2da92f6b90 100644 --- a/lib/hexo/index.ts +++ b/lib/hexo/index.ts @@ -7,7 +7,7 @@ import { EventEmitter } from 'events'; import { readFile } from 'hexo-fs'; import Module from 'module'; import { runInThisContext } from 'vm'; -const {version} = require('../../package.json'); +const { version } = require('../../package.json'); import logger from 'hexo-log'; import { @@ -37,6 +37,8 @@ import loadDatabase from './load_database'; import multiConfigPath from './multi_config_path'; import { deepMerge, full_url_for } from 'hexo-util'; import type Box from '../box'; +import type { AssetGenerator, LocalsType, NodeJSLikeCallback, NormalPageGenerator, NormalPostGenerator, PageGenerator, PostGenerator, SiteLocals } from '../types'; + let resolveSync; // = require('resolve'); const libDir = dirname(__dirname); @@ -48,7 +50,8 @@ const routeCache = new WeakMap(); const castArray = (obj: any) => { return Array.isArray(obj) ? obj : [obj]; }; -const mergeCtxThemeConfig = ctx => { +// eslint-disable-next-line no-use-before-define +const mergeCtxThemeConfig = (ctx: Hexo) => { // Merge hexo.config.theme_config into hexo.theme.config before post rendering & generating // config.theme_config has "_config.[theme].yml" merged in load_theme_config.js if (ctx.config.theme_config) { @@ -56,11 +59,12 @@ const mergeCtxThemeConfig = ctx => { } }; -const createLoadThemeRoute = function(generatorResult, locals, ctx) { +// eslint-disable-next-line no-use-before-define +const createLoadThemeRoute = function(generatorResult: NormalPageGenerator | NormalPostGenerator, locals: LocalsType, ctx: Hexo) { const { log, theme } = ctx; const { path, cache: useCache } = locals; - const layout = [...new Set(castArray(generatorResult.layout))]; + const layout: string[] = [...new Set(castArray(generatorResult.layout))]; const layoutLength = layout.length; // always use cache in fragment_cache @@ -94,7 +98,7 @@ const createLoadThemeRoute = function(generatorResult, locals, ctx) { }; }; -function debounce(func: () => void, wait: number) { +function debounce(func: () => void, wait: number): () => void { let timeout: NodeJS.Timeout; return function() { clearTimeout(timeout); @@ -105,12 +109,15 @@ function debounce(func: () => void, wait: number) { } interface Args { - debug?: any; - safe?: any; - silent?: any; - _?: any[]; - output?: any; - config?: any; + debug?: boolean; + safe?: boolean; + silent?: boolean; + draft?: boolean; + drafts?: boolean; + _?: string[]; + output?: string; + config?: string; + [key: string]: any; } interface Query { @@ -132,6 +139,17 @@ interface Extend { tag: Tag } +interface Env { + args: Args; + debug: boolean; + safe: boolean; + silent: boolean; + env: string; + version: string; + cmd: string; + init: boolean; +} + type DefaultConfigType = typeof defaultConfig; interface Config extends DefaultConfigType { [key: string]: any; @@ -236,7 +254,7 @@ class Hexo extends EventEmitter { public scaffold_dir: string; public theme_dir: string; public theme_script_dir: string; - public env: any; + public env: Env; public extend: Extend; public config: Config; public log: ReturnType; @@ -253,16 +271,6 @@ class Hexo extends EventEmitter { 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; @@ -348,7 +356,7 @@ class Hexo extends EventEmitter { this._bindLocals(); } - _bindLocals() { + _bindLocals(): void { const db = this.database; const { locals } = this; @@ -391,7 +399,7 @@ class Hexo extends EventEmitter { }); } - init() { + init(): Promise { this.log.debug('Hexo version: %s', magenta(this.version)); this.log.debug('Working directory: %s', magenta(tildify(this.base_dir))); @@ -418,15 +426,16 @@ class Hexo extends EventEmitter { }); } - call(name: string, args: any, callback?: NodeJSLikeCallback) { + call(name: string, callback?: NodeJSLikeCallback): Promise; + call(name: string, args: object, callback?: NodeJSLikeCallback): Promise; + call(name: string, args?: object | NodeJSLikeCallback, callback?: NodeJSLikeCallback): Promise { if (!callback && typeof args === 'function') { - callback = args; + callback = args as NodeJSLikeCallback; args = {}; } const c = this.extend.console.get(name); - // 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!`)); } @@ -435,7 +444,7 @@ class Hexo extends EventEmitter { return this.database.model(name, schema); } - resolvePlugin(name: string, basedir: string) { + resolvePlugin(name: string, basedir: string): string { try { // Try to resolve the plugin with the Node.js's built-in require.resolve. return require.resolve(name, { paths: [basedir] }); @@ -453,9 +462,9 @@ class Hexo extends EventEmitter { } } - loadPlugin(path: string, callback?: NodeJSLikeCallback) { + loadPlugin(path: string, callback?: NodeJSLikeCallback): Promise { return readFile(path).then(script => { - // Based on: https://github.com/joyent/node/blob/v0.10.33/src/node.js#L516 + // Based on: https://github.com/nodejs/node-v0.x-archive/blob/v0.10.33/src/node.js#L516 const module = new Module(path); module.filename = path; module.paths = Module._nodeModulePaths(path); @@ -478,12 +487,12 @@ class Hexo extends EventEmitter { }).asCallback(callback); } - _showDrafts() { + _showDrafts(): boolean { const { args } = this.env; return args.draft || args.drafts || this.config.render_drafts; } - load(callback?: NodeJSLikeCallback) { + load(callback?: NodeJSLikeCallback): Promise { return loadDatabase(this).then(() => { this.log.info('Start processing'); @@ -497,7 +506,7 @@ class Hexo extends EventEmitter { }).asCallback(callback); } - watch(callback?: NodeJSLikeCallback) { + watch(callback?: NodeJSLikeCallback): Promise { let useCache = false; const { cache } = Object.assign({ cache: false @@ -530,7 +539,7 @@ class Hexo extends EventEmitter { }).asCallback(callback); } - unwatch() { + unwatch(): void { if (this._watchBox != null) { this.source.removeListener('processAfter', this._watchBox); this.theme.removeListener('processAfter', this._watchBox); @@ -545,12 +554,10 @@ class Hexo extends EventEmitter { _generateLocals() { const { config, env, theme, theme_dir } = this; const ctx = { config: { url: this.config.url } }; - const localsObj = this.locals.toObject(); + const localsObj = this.locals.toObject() as SiteLocals; class Locals { - page: { - path: string; - }; + page: NormalPageGenerator | NormalPostGenerator; path: string; url: string; config: object; @@ -558,10 +565,10 @@ class Hexo extends EventEmitter { layout: string; env: any; view_dir: string; - site: object; + site: SiteLocals; cache?: boolean; - constructor(path: string, locals) { + constructor(path: string, locals: NormalPageGenerator | NormalPostGenerator) { this.page = { ...locals }; if (this.page.path == null) this.page.path = path; this.path = path; @@ -578,9 +585,9 @@ class Hexo extends EventEmitter { return Locals; } - _runGenerators() { + _runGenerators(): Promise<(AssetGenerator | PostGenerator | PageGenerator)[]> { this.locals.invalidate(); - const siteLocals = this.locals.toObject(); + const siteLocals = this.locals.toObject() as SiteLocals; const generators = this.extend.generator.list(); const { log } = this; @@ -595,17 +602,19 @@ class Hexo extends EventEmitter { }, []); } - _routerRefresh(runningGenerators: Promise, useCache: boolean) { + _routerRefresh(runningGenerators: Promise<(AssetGenerator | PostGenerator | PageGenerator)[]>, useCache: boolean): Promise { const { route } = this; const routeList = route.list(); const Locals = this._generateLocals(); Locals.prototype.cache = useCache; - return runningGenerators.map(generatorResult => { + return runningGenerators.map((generatorResult: AssetGenerator | PostGenerator | PageGenerator) => { if (typeof generatorResult !== 'object' || generatorResult.path == null) return undefined; // add Route const path = route.format(generatorResult.path); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const { data, layout } = generatorResult; if (!layout) { @@ -613,8 +622,8 @@ class Hexo extends EventEmitter { return path; } - return this.execFilter('template_locals', new Locals(path, data), { context: this }) - .then(locals => { route.set(path, createLoadThemeRoute(generatorResult, locals, this)); }) + return this.execFilter('template_locals', new Locals(path, data as unknown as NormalPageGenerator | NormalPostGenerator), { context: this }) + .then(locals => { route.set(path, createLoadThemeRoute(generatorResult as NormalPageGenerator | NormalPostGenerator, locals, this)); }) .thenReturn(path); }).then(newRouteList => { // Remove old routes @@ -628,7 +637,7 @@ class Hexo extends EventEmitter { }); } - _generate(options: { cache?: boolean } = {}) { + _generate(options: { cache?: boolean } = {}): Promise { if (this._isGenerating) return; const useCache = options.cache; @@ -649,7 +658,7 @@ class Hexo extends EventEmitter { }); } - exit(err) { + exit(err: Error): Promise { if (err) { this.log.fatal( { err }, diff --git a/lib/hexo/load_config.ts b/lib/hexo/load_config.ts index 489f4e03ff..009aa4587c 100644 --- a/lib/hexo/load_config.ts +++ b/lib/hexo/load_config.ts @@ -8,7 +8,7 @@ import { deepMerge } from 'hexo-util'; import validateConfig from './validate_config'; import type Hexo from './index'; -export = async (ctx: Hexo) => { +export = async (ctx: Hexo): Promise => { if (!ctx.env.init) return; const baseDir = ctx.base_dir; @@ -53,7 +53,7 @@ export = async (ctx: Hexo) => { const themeDirFromNodeModules = join(ctx.plugin_dir, 'hexo-theme-' + theme) + sep; // base_dir/node_modules/hexo-theme-[config.theme]/ // themeDirFromThemes has higher priority than themeDirFromNodeModules - let ignored = []; + let ignored: string[] = []; if (await exists(themeDirFromThemes)) { ctx.theme_dir = themeDirFromThemes; ignored = ['**/themes/*/node_modules/**', '**/themes/*/.git/**']; @@ -66,7 +66,7 @@ export = async (ctx: Hexo) => { }; -async function findConfigPath(path: string) { +async function findConfigPath(path: string): Promise { const { dir, name } = parse(path); const files = await readdir(dir); diff --git a/lib/hexo/load_database.ts b/lib/hexo/load_database.ts index f0ac5decb0..f816a6f0ea 100644 --- a/lib/hexo/load_database.ts +++ b/lib/hexo/load_database.ts @@ -2,7 +2,7 @@ import { exists, unlink } from 'hexo-fs'; import Promise from 'bluebird'; import type Hexo from './index'; -export = (ctx: Hexo) => { +export = (ctx: Hexo): Promise => { if (ctx._dbLoaded) return Promise.resolve(); const db = ctx.database; diff --git a/lib/hexo/load_plugins.ts b/lib/hexo/load_plugins.ts index 289474f00f..49802f9292 100644 --- a/lib/hexo/load_plugins.ts +++ b/lib/hexo/load_plugins.ts @@ -4,13 +4,13 @@ import Promise from 'bluebird'; import { magenta } from 'picocolors'; import type Hexo from './index'; -export = (ctx: Hexo) => { +export = (ctx: Hexo): Promise => { if (!ctx.env.init || ctx.env.safe) return; return loadModules(ctx).then(() => loadScripts(ctx)); }; -function loadModuleList(ctx: Hexo, basedir: string) { +function loadModuleList(ctx: Hexo, basedir: string): Promise { const packagePath = join(basedir, 'package.json'); // Make sure package.json exists diff --git a/lib/hexo/load_theme_config.ts b/lib/hexo/load_theme_config.ts index 54c700960a..11056504aa 100644 --- a/lib/hexo/load_theme_config.ts +++ b/lib/hexo/load_theme_config.ts @@ -4,8 +4,9 @@ import { exists, readdir } from 'hexo-fs'; import { magenta } from 'picocolors'; import { deepMerge } from 'hexo-util'; import type Hexo from './index'; +import type Promise from 'bluebird'; -export = (ctx: Hexo) => { +export = (ctx: Hexo): Promise => { if (!ctx.env.init) return; if (!ctx.config.theme) return; @@ -31,7 +32,7 @@ export = (ctx: Hexo) => { }); }; -function findConfigPath(path: string) { +function findConfigPath(path: string): Promise { const { dir, name } = parse(path); return readdir(dir).then(files => { diff --git a/lib/hexo/locals.ts b/lib/hexo/locals.ts index 7153cb0a48..ef3b51eb27 100644 --- a/lib/hexo/locals.ts +++ b/lib/hexo/locals.ts @@ -20,7 +20,7 @@ class Locals { }); } - set(name: string, value: any) { + set(name: string, value: any): this { if (typeof name !== 'string') throw new TypeError('name must be a string!'); if (value == null) throw new TypeError('value is required!'); @@ -32,7 +32,7 @@ class Locals { return this; } - remove(name: string) { + remove(name: string): this { if (typeof name !== 'string') throw new TypeError('name must be a string!'); this.getters[name] = null; @@ -41,7 +41,7 @@ class Locals { return this; } - invalidate() { + invalidate(): this { this.cache.flush(); return this; diff --git a/lib/hexo/multi_config_path.ts b/lib/hexo/multi_config_path.ts index 7402657e90..27c9babd78 100644 --- a/lib/hexo/multi_config_path.ts +++ b/lib/hexo/multi_config_path.ts @@ -4,7 +4,7 @@ import yml from 'js-yaml'; import { deepMerge } from 'hexo-util'; import type Hexo from './index'; -export = (ctx: Hexo) => function multiConfigPath(base: string, configPaths: string, outputDir: string) { +export = (ctx: Hexo) => function multiConfigPath(base: string, configPaths: string, outputDir: string): string { const { log } = ctx; const defaultPath = join(base, '_config.yml'); @@ -43,7 +43,7 @@ export = (ctx: Hexo) => function multiConfigPath(base: string, configPaths: stri } // files read synchronously to ensure proper overwrite order - const file = readFileSync(configPath); + const file = readFileSync(configPath) as string; const ext = extname(paths[i]).toLowerCase(); if (ext === '.yml') { diff --git a/lib/hexo/post.ts b/lib/hexo/post.ts index f9269484e2..b7ecf3e988 100644 --- a/lib/hexo/post.ts +++ b/lib/hexo/post.ts @@ -8,6 +8,8 @@ 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'; import type Hexo from './index'; +import type { NodeJSLikeCallback, RenderData } from '../types'; + const preservedKeys = ['title', 'slug', 'path', 'layout', 'date', 'content']; const rHexoPostRenderEscape = /([\s\S]+?)<\/hexoPostRenderCodeBlock>/g; @@ -224,14 +226,6 @@ interface Result { content?: string; } -interface Data { - engine?: string; - content?: string; - disableNunjucks?: boolean; - markdown?: object; - source?: string; -} - interface PostData { title?: string; layout?: string; @@ -388,7 +382,7 @@ class Post { }).thenReturn(result).asCallback(callback); } - render(source: string, data: Data = {}, callback?: NodeJSLikeCallback) { + render(source: string, data: RenderData = {}, callback?: NodeJSLikeCallback) { const ctx = this.context; const { config } = ctx; const { tag } = ctx.extend; diff --git a/lib/hexo/register_models.ts b/lib/hexo/register_models.ts index 88a73e6081..f6bb39b3ae 100644 --- a/lib/hexo/register_models.ts +++ b/lib/hexo/register_models.ts @@ -1,7 +1,7 @@ import * as models from '../models'; import type Hexo from './index'; -export = (ctx: Hexo) => { +export = (ctx: Hexo): void => { const db = ctx.database; const keys = Object.keys(models); diff --git a/lib/hexo/render.ts b/lib/hexo/render.ts index 4d1ebda922..91ce8e01ff 100644 --- a/lib/hexo/render.ts +++ b/lib/hexo/render.ts @@ -3,16 +3,17 @@ import Promise from 'bluebird'; import { readFile, readFileSync } from 'hexo-fs'; import type Hexo from './index'; import type { Renderer } from '../extend'; -import type { StoreFunctionData } from '../extend/renderer'; +import type { StoreFunction, StoreFunctionData, StoreSyncFunction } from '../extend/renderer'; +import { NodeJSLikeCallback } from '../types'; -const getExtname = (str: string) => { +const getExtname = (str: string): string => { if (typeof str !== 'string') return ''; const ext = extname(str); return ext.startsWith('.') ? ext.slice(1) : ext; }; -const toString = (result, options) => { +const toString = (result: any, options: StoreFunctionData) => { if (!Object.prototype.hasOwnProperty.call(options, 'toString') || typeof result === 'string') return result; if (typeof options.toString === 'function') { @@ -35,27 +36,27 @@ class Render { this.renderer = ctx.extend.renderer; } - isRenderable(path: string) { + isRenderable(path: string): boolean { return this.renderer.isRenderable(path); } - isRenderableSync(path: string) { + isRenderableSync(path: string): boolean { return this.renderer.isRenderableSync(path); } - getOutput(path: string) { + getOutput(path: string): string { return this.renderer.getOutput(path); } - getRenderer(ext: string, sync?: boolean) { + getRenderer(ext: string, sync?: boolean): StoreSyncFunction | StoreFunction { return this.renderer.get(ext, sync); } - getRendererSync(ext: string) { + getRendererSync(ext: string): StoreSyncFunction | StoreFunction { return this.getRenderer(ext, true); } - render(data: StoreFunctionData, options?: { highlight?: boolean; }, callback?: undefined) { + render(data: StoreFunctionData, options?: { highlight?: boolean; }, callback?: NodeJSLikeCallback): Promise { if (!callback && typeof options === 'function') { callback = options; options = {}; @@ -64,7 +65,7 @@ class Render { const ctx = this.context; let ext = ''; - let promise; + let promise: Promise; if (!data) return Promise.reject(new TypeError('No input file or string!')); @@ -73,7 +74,7 @@ class Render { } else if (!data.path) { return Promise.reject(new TypeError('No input file or string!')); } else { - promise = readFile(data.path); + promise = readFile(data.path) as Promise; } return promise.then(text => { @@ -99,7 +100,7 @@ class Render { }).asCallback(callback); } - renderSync(data: StoreFunctionData, options = {}) { + renderSync(data: StoreFunctionData, options = {}): any { if (!data) throw new TypeError('No input file or string!'); const ctx = this.context; diff --git a/lib/hexo/router.ts b/lib/hexo/router.ts index 33edb61478..8d9965e483 100644 --- a/lib/hexo/router.ts +++ b/lib/hexo/router.ts @@ -17,7 +17,7 @@ declare module 'stream' { class RouteStream extends Readable { public _data: any; public _ended: boolean; - public modified: any; + public modified: boolean; constructor(data: Data) { super({ objectMode: true }); @@ -28,7 +28,7 @@ class RouteStream extends Readable { } // Assume we only accept Buffer, plain object, or string - _toBuffer(data) { + _toBuffer(data: Buffer | object | string): Buffer | null { if (data instanceof Buffer) { return data; } @@ -41,7 +41,7 @@ class RouteStream extends Readable { return null; } - _read() { + _read(): boolean { const data = this._data; if (typeof data !== 'function') { @@ -84,7 +84,7 @@ class RouteStream extends Readable { } } -const _format = (path: string) => { +const _format = (path: string): string => { path = path || ''; if (typeof path !== 'string') throw new TypeError('path must be a string!'); @@ -113,16 +113,16 @@ class Router extends EventEmitter { this.routes = {}; } - list() { + list(): string[] { const { routes } = this; return Object.keys(routes).filter(key => routes[key]); } - format(path: string) { + format(path: string): string { return _format(path); } - get(path: string) { + get(path: string): RouteStream { if (typeof path !== 'string') throw new TypeError('path must be a string!'); const data = this.routes[this.format(path)]; @@ -131,14 +131,14 @@ class Router extends EventEmitter { return new RouteStream(data); } - isModified(path: string) { + isModified(path: string): boolean { if (typeof path !== 'string') throw new TypeError('path must be a string!'); const data = this.routes[this.format(path)]; return data ? data.modified : false; } - set(path: string, data: any) { + set(path: string, data: any): this { if (typeof path !== 'string') throw new TypeError('path must be a string!'); if (data == null) throw new TypeError('data is required!'); @@ -173,7 +173,7 @@ class Router extends EventEmitter { return this; } - remove(path: string) { + remove(path: string): this { if (typeof path !== 'string') throw new TypeError('path must be a string!'); path = this.format(path); diff --git a/lib/hexo/scaffold.ts b/lib/hexo/scaffold.ts index 37b6e65e86..8b97c6305f 100644 --- a/lib/hexo/scaffold.ts +++ b/lib/hexo/scaffold.ts @@ -1,10 +1,15 @@ import { extname, join } from 'path'; import { exists, listDir, readFile, unlink, writeFile } from 'hexo-fs'; import type Hexo from './index'; +import type { NodeJSLikeCallback } from '../types'; +import type Promise from 'bluebird'; + class Scaffold { public context: Hexo; public scaffoldDir: string; - public defaults: any; + public defaults: { + normal: string + }; constructor(context: Hexo) { this.context = context; @@ -21,7 +26,10 @@ class Scaffold { }; } - _listDir() { + _listDir(): Promise<{ + name: string; + path: string; + }[]> { const { scaffoldDir } = this; return exists(scaffoldDir).then(exist => { @@ -36,11 +44,14 @@ class Scaffold { })); } - _getScaffold(name: string) { + _getScaffold(name: string): Promise<{ + name: string; + path: string; + }> { return this._listDir().then(list => list.find(item => item.name === name)); } - get(name: string, callback?: NodeJSLikeCallback) { + get(name: string, callback?: NodeJSLikeCallback): Promise { return this._getScaffold(name).then(item => { if (item) { return readFile(item.path); @@ -50,7 +61,7 @@ class Scaffold { }).asCallback(callback); } - set(name: string, content: any, callback: NodeJSLikeCallback) { + set(name: string, content: any, callback?: NodeJSLikeCallback): Promise { const { scaffoldDir } = this; return this._getScaffold(name).then(item => { @@ -61,7 +72,7 @@ class Scaffold { }).asCallback(callback); } - remove(name: string, callback: NodeJSLikeCallback) { + remove(name: string, callback?: NodeJSLikeCallback): Promise { return this._getScaffold(name).then(item => { if (!item) return; diff --git a/lib/hexo/update_package.ts b/lib/hexo/update_package.ts index 4b8f3593bd..7913a67f67 100644 --- a/lib/hexo/update_package.ts +++ b/lib/hexo/update_package.ts @@ -1,8 +1,9 @@ import { join } from 'path'; import { writeFile, exists, readFile } from 'hexo-fs'; import type Hexo from './index'; +import type Promise from 'bluebird'; -export = (ctx: Hexo) => { +export = (ctx: Hexo): Promise => { const pkgPath = join(ctx.base_dir, 'package.json'); return readPkg(pkgPath).then(pkg => { @@ -19,7 +20,7 @@ export = (ctx: Hexo) => { }); }; -function readPkg(path: string) { +function readPkg(path: string): Promise { return exists(path).then(exist => { if (!exist) return; diff --git a/lib/hexo/validate_config.ts b/lib/hexo/validate_config.ts index 66af8ceeba..c7ca6477bd 100644 --- a/lib/hexo/validate_config.ts +++ b/lib/hexo/validate_config.ts @@ -1,6 +1,6 @@ import type Hexo from './index'; -export = (ctx: Hexo) => { +export = (ctx: Hexo): void => { const { config, log } = ctx; log.info('Validating config'); diff --git a/lib/models/types/moment.ts b/lib/models/types/moment.ts index 59a86e1479..121be86248 100644 --- a/lib/models/types/moment.ts +++ b/lib/models/types/moment.ts @@ -1,12 +1,13 @@ import warehouse from 'warehouse'; import { moment, toMomentLocale } from '../../plugins/helper/date'; -declare module 'moment' { - export default interface Moment extends moment.Moment { - _d: Date; - // eslint-disable-next-line semi - } -} +// It'll pollute the moment module. +// 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; @@ -90,7 +91,7 @@ class SchemaTypeMoment extends warehouse.SchemaType { function toMoment(value) { // FIXME: Something is wrong when using a moment instance. I try to get the // original date object and create a new moment object again. - if (moment.isMoment(value)) return moment(value._d); + if (moment.isMoment(value)) return moment((value as any)._d); return moment(value); } diff --git a/lib/plugins/console/config.ts b/lib/plugins/console/config.ts index 777bc4e1d0..c46f9793f3 100644 --- a/lib/plugins/console/config.ts +++ b/lib/plugins/console/config.ts @@ -4,7 +4,12 @@ import { extname } from 'path'; import Promise from 'bluebird'; import type Hexo from '../../hexo'; -function configConsole(this: Hexo, args): Promise { +interface ConfigArgs { + _: string[] + [key: string]: any +} + +function configConsole(this: Hexo, args: ConfigArgs): Promise { const key = args._[0]; let value = args._[1]; diff --git a/lib/plugins/console/deploy.ts b/lib/plugins/console/deploy.ts index 1da54819d4..811be33b48 100644 --- a/lib/plugins/console/deploy.ts +++ b/lib/plugins/console/deploy.ts @@ -2,7 +2,14 @@ import { exists } from 'hexo-fs'; import { underline, magenta } from 'picocolors'; import type Hexo from '../../hexo'; -function deployConsole(this: Hexo, args) { +interface DeployArgs { + _?: string[] + g?: boolean + generate?: boolean + [key: string]: any +} + +function deployConsole(this: Hexo, args: DeployArgs) { let config = this.config.deploy; const deployers = this.extend.deployer.list(); @@ -18,10 +25,6 @@ function deployConsole(this: Hexo, args) { return; } - const generateArgs = {...args}; - generateArgs.d = false; - generateArgs.deploy = false; - let promise; if (args.g || args.generate) { diff --git a/lib/plugins/console/generate.ts b/lib/plugins/console/generate.ts index c91b45e2d3..f6bfcd67a7 100644 --- a/lib/plugins/console/generate.ts +++ b/lib/plugins/console/generate.ts @@ -8,20 +8,32 @@ import { PassThrough } from 'stream'; import { createSha1Hash } from 'hexo-util'; import type Hexo from '../../hexo'; +interface GenerateArgs { + f?: boolean + force?: boolean + b?: boolean + bail?: boolean + c?: string + concurrency?: string + w?: boolean + watch?: boolean + d?: boolean + deploy?: boolean + [key: string]: any +} + class Generater { public context: Hexo; - public force: any; - public bail: any; - public concurrency: any; - public watch: any; - public deploy: any; + public force: boolean; + public bail: boolean; + public concurrency: string; + public watch: boolean; + public deploy: boolean; public generatingFiles: Set; public start: [number, number]; - public args: any; - public route: any; - public log: any; + public args: GenerateArgs; - constructor(ctx: Hexo, args) { + constructor(ctx: Hexo, args: GenerateArgs) { this.context = ctx; this.force = args.f || args.force; this.bail = args.b || args.bail; @@ -193,7 +205,7 @@ class Generater { } } -function generateConsole(args = {}) { +function generateConsole(this: Hexo, args: GenerateArgs = {}) { const generator = new Generater(this, args); if (generator.watch) { diff --git a/lib/plugins/console/list/index.ts b/lib/plugins/console/list/index.ts index cd091ca272..f9b3cb4cd5 100644 --- a/lib/plugins/console/list/index.ts +++ b/lib/plugins/console/list/index.ts @@ -6,13 +6,17 @@ import tag from './tag'; import category from './category'; import type Hexo from '../../../hexo'; +interface ListArgs { + _: string[] +} + const store = { page, post, route, tag, category }; const alias = abbrev(Object.keys(store)); -function listConsole(this: Hexo, args) { +function listConsole(this: Hexo, args: ListArgs) { const type = args._.shift(); // Display help message if user didn't input any arguments diff --git a/lib/plugins/console/migrate.ts b/lib/plugins/console/migrate.ts index afe5dfb537..d6b3ad3e47 100644 --- a/lib/plugins/console/migrate.ts +++ b/lib/plugins/console/migrate.ts @@ -1,7 +1,12 @@ import { underline, magenta } from 'picocolors'; import type Hexo from '../../hexo'; -function migrateConsole(this: Hexo, args) { +interface MigrateArgs { + _: string[] + [key: string]: any +} + +function migrateConsole(this: Hexo, args: MigrateArgs) { // Display help message if user didn't input any arguments if (!args._.length) { return this.call('help', {_: ['migrate']}); diff --git a/lib/plugins/console/new.ts b/lib/plugins/console/new.ts index 64e3d83bd1..2b48b77b11 100644 --- a/lib/plugins/console/new.ts +++ b/lib/plugins/console/new.ts @@ -20,9 +20,20 @@ const reservedKeys = { silent: true }; -function newConsole(this: Hexo, args) { +interface NewArgs { + _?: string[] + p?: string + path?: string + s?: string + slug?: string + r?: boolean + replace?: boolean + [key: string]: any +} + +function newConsole(this: Hexo, args: NewArgs) { const path = args.p || args.path; - let title; + let title: string; if (args._.length) { title = args._.pop(); } else if (path) { @@ -30,7 +41,7 @@ function newConsole(this: Hexo, args) { title = basename(path); } else { // Display help message if user didn't input any arguments - return this.call('help', {_: ['new']}); + return this.call('help', { _: ['new'] }); } const data = { diff --git a/lib/plugins/console/publish.ts b/lib/plugins/console/publish.ts index 6f97f9d175..38913e8316 100644 --- a/lib/plugins/console/publish.ts +++ b/lib/plugins/console/publish.ts @@ -2,7 +2,14 @@ import tildify from 'tildify'; import { magenta } from 'picocolors'; import type Hexo from '../../hexo'; -function publishConsole(this: Hexo, args) { +interface PublishArgs { + _: string[] + r?: boolean + replace?: boolean + [key: string]: any +} + +function publishConsole(this: Hexo, args: PublishArgs) { // Display help message if user didn't input any arguments if (!args._.length) { return this.call('help', {_: ['publish']}); diff --git a/lib/plugins/console/render.ts b/lib/plugins/console/render.ts index c40230f728..68cb95a710 100644 --- a/lib/plugins/console/render.ts +++ b/lib/plugins/console/render.ts @@ -5,7 +5,16 @@ import { writeFile } from 'hexo-fs'; import { cyan, magenta } from 'picocolors'; import type Hexo from '../../hexo'; -function renderConsole(this: Hexo, args) { +interface RenderArgs { + _: string[] + o?: string + output?: string + pretty?: boolean + engine?: string + [key: string]: any +} + +function renderConsole(this: Hexo, args: RenderArgs) { // Display help message if user didn't input any arguments if (!args._.length) { return this.call('help', {_: 'render'}); diff --git a/lib/plugins/filter/after_post_render/excerpt.ts b/lib/plugins/filter/after_post_render/excerpt.ts index 5694ce5dec..bd7e49eda2 100644 --- a/lib/plugins/filter/after_post_render/excerpt.ts +++ b/lib/plugins/filter/after_post_render/excerpt.ts @@ -1,6 +1,8 @@ +import type { RenderData } from '../../../types'; + const rExcerpt = //i; -function excerptFilter(data): void { +function excerptFilter(data: RenderData): void { const { content } = data; if (typeof data.excerpt !== 'undefined') { diff --git a/lib/plugins/filter/after_post_render/external_link.ts b/lib/plugins/filter/after_post_render/external_link.ts index dab35b3a9d..8545ada191 100644 --- a/lib/plugins/filter/after_post_render/external_link.ts +++ b/lib/plugins/filter/after_post_render/external_link.ts @@ -1,12 +1,14 @@ import { isExternalLink } from 'hexo-util'; import type Hexo from '../../../hexo'; +import type { RenderData } from '../../../types'; + let EXTERNAL_LINK_POST_ENABLED = true; const rATag = /]+?\s+?)href=["']((?:https?:|\/\/)[^<>"']+)["'][^<>]*>/gi; const rTargetAttr = /target=/i; const rRelAttr = /rel=/i; const rRelStrAttr = /rel=["']([^<>"']*)["']/i; -function externalLinkFilter(this: Hexo, data): void { +function externalLinkFilter(this: Hexo, data: RenderData): void { if (!EXTERNAL_LINK_POST_ENABLED) return; const { external_link, url } = this.config; diff --git a/lib/plugins/filter/before_exit/save_database.ts b/lib/plugins/filter/before_exit/save_database.ts index 9a853eb93e..7c180edfd6 100644 --- a/lib/plugins/filter/before_exit/save_database.ts +++ b/lib/plugins/filter/before_exit/save_database.ts @@ -1,6 +1,6 @@ import type Hexo from '../../../hexo'; -function saveDatabaseFilter(this: Hexo) { +function saveDatabaseFilter(this: Hexo): Promise { if (!this.env.init || !this._dbLoaded) return; return this.database.save().then(() => { diff --git a/lib/plugins/filter/before_post_render/backtick_code_block.ts b/lib/plugins/filter/before_post_render/backtick_code_block.ts index 7952169edd..8b40231875 100644 --- a/lib/plugins/filter/before_post_render/backtick_code_block.ts +++ b/lib/plugins/filter/before_post_render/backtick_code_block.ts @@ -1,10 +1,11 @@ import type Hexo from '../../../hexo'; +import type { RenderData } from '../../../types'; 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, '}'); +const escapeSwigTag = (str: string) => str.replace(/{/g, '{').replace(/}/g, '}'); interface Options { lang: string, @@ -13,8 +14,8 @@ interface Options { firstLineNumber?: string | number } -export = (ctx: Hexo) => { - return function backtickCodeBlock(data): void { +export = (ctx: Hexo): (data: RenderData) => void => { + return function backtickCodeBlock(data: RenderData): void { const dataContent = data.content; if ((!dataContent.includes('```') && !dataContent.includes('~~~')) || !ctx.extend.highlight.query(ctx.config.syntax_highlighter)) return; @@ -27,7 +28,7 @@ export = (ctx: Hexo) => { // Extract language and caption of code blocks const args = _args.split('=').shift(); - let lang, caption; + let lang: string, caption: string; if (args) { const match = rAllOptions.exec(args) || rLangCaption.exec(args); diff --git a/lib/plugins/filter/before_post_render/titlecase.ts b/lib/plugins/filter/before_post_render/titlecase.ts index bd149d3823..3362bda8a5 100644 --- a/lib/plugins/filter/before_post_render/titlecase.ts +++ b/lib/plugins/filter/before_post_render/titlecase.ts @@ -1,6 +1,8 @@ +import type { RenderData } from '../../../types'; + let titlecase; -function titlecaseFilter(data): void { +function titlecaseFilter(data: RenderData): void { if (!(typeof data.titlecase !== 'undefined' ? data.titlecase : this.config.titlecase) || !data.title) return; if (!titlecase) titlecase = require('titlecase'); diff --git a/lib/plugins/filter/new_post_path.ts b/lib/plugins/filter/new_post_path.ts index 1eaa58c9f2..c204303fcf 100644 --- a/lib/plugins/filter/new_post_path.ts +++ b/lib/plugins/filter/new_post_path.ts @@ -4,7 +4,9 @@ import Promise from 'bluebird'; import { createSha1Hash, Permalink } from 'hexo-util'; import { ensurePath } from 'hexo-fs'; import type Hexo from '../../hexo'; -let permalink; +import type { PostSchema } from '../../types'; + +let permalink: Permalink; const reservedKeys = { year: true, @@ -16,14 +18,7 @@ const reservedKeys = { hash: true }; -interface Data { - path?: string; - layout?: string; - slug?: string; - date?: Date; -} - -function newPostPathFilter(this: Hexo, data: Data = {}, replace): Promise { +function newPostPathFilter(this: Hexo, data: PostSchema = {}, replace: boolean): Promise { const sourceDir = this.source_dir; const draftDir = join(sourceDir, '_drafts'); const postDir = join(sourceDir, '_posts'); diff --git a/lib/plugins/filter/post_permalink.ts b/lib/plugins/filter/post_permalink.ts index f066421330..f0830144e7 100644 --- a/lib/plugins/filter/post_permalink.ts +++ b/lib/plugins/filter/post_permalink.ts @@ -1,9 +1,11 @@ import { createSha1Hash, Permalink, slugize } from 'hexo-util'; import { basename } from 'path'; import type Hexo from '../../hexo'; -let permalink; +import type { PostSchema } from '../../types'; -function postPermalinkFilter(this: Hexo, data) { +let permalink: Permalink; + +function postPermalinkFilter(this: Hexo, data: PostSchema): string { const { config } = this; const { id, _id, slug, title, date, __permalink } = data; diff --git a/lib/plugins/filter/template_locals/i18n.ts b/lib/plugins/filter/template_locals/i18n.ts index a709fe8934..87c505280b 100644 --- a/lib/plugins/filter/template_locals/i18n.ts +++ b/lib/plugins/filter/template_locals/i18n.ts @@ -1,7 +1,8 @@ import { Pattern } from 'hexo-util'; import type Hexo from '../../../hexo'; +import type { LocalsType } from '../../../types'; -function i18nLocalsFilter(this: Hexo, locals): void { +function i18nLocalsFilter(this: Hexo, locals: LocalsType): void { const { i18n } = this.theme; const { config } = this; const i18nDir = config.i18n_dir; diff --git a/lib/plugins/generator/asset.ts b/lib/plugins/generator/asset.ts index 074f497620..336c4e2612 100644 --- a/lib/plugins/generator/asset.ts +++ b/lib/plugins/generator/asset.ts @@ -5,6 +5,7 @@ import { extname } from 'path'; import { magenta } from 'picocolors'; import type warehouse from 'warehouse'; import type Hexo from '../../hexo'; +import type { AssetGenerator } from '../../types'; interface Data { modified: boolean; @@ -36,7 +37,7 @@ const process = (name: string, ctx: Hexo) => { data.data = () => ctx.render.render({ path: source, toString: true - }).catch(err => { + }).catch((err: Error) => { ctx.log.error({err}, 'Asset render failed: %s', magenta(path)); }); } else { @@ -47,7 +48,7 @@ const process = (name: string, ctx: Hexo) => { }); }; -function assetGenerator(this: Hexo): Promise { +function assetGenerator(this: Hexo): Promise { return Promise.all([ process('Asset', this), process('PostAsset', this) diff --git a/lib/plugins/generator/page.ts b/lib/plugins/generator/page.ts index 8dbbcd817c..da2cf08c8b 100644 --- a/lib/plugins/generator/page.ts +++ b/lib/plugins/generator/page.ts @@ -1,5 +1,7 @@ -function pageGenerator(locals) { - return locals.pages.map(page => { +import type { PageGenerator, PageSchema, SiteLocals } from '../../types'; + +function pageGenerator(locals: SiteLocals): PageGenerator[] { + return locals.pages.map((page: PageSchema) => { const { path, layout } = page; if (!layout || layout === 'false' || layout === 'off') { diff --git a/lib/plugins/generator/post.ts b/lib/plugins/generator/post.ts index 4deec563dd..d97030b04f 100644 --- a/lib/plugins/generator/post.ts +++ b/lib/plugins/generator/post.ts @@ -1,8 +1,10 @@ -function postGenerator(locals) { +import type { PostGenerator, PostSchema, SiteLocals } from '../../types'; + +function postGenerator(locals: SiteLocals): PostGenerator[] { const posts = locals.posts.sort('-date').toArray(); const { length } = posts; - return posts.map((post, i) => { + return posts.map((post: PostSchema, i: number) => { const { path, layout } = post; if (!layout || layout === 'false') { diff --git a/lib/plugins/helper/date.ts b/lib/plugins/helper/date.ts index 8d45b27147..063a437cb4 100644 --- a/lib/plugins/helper/date.ts +++ b/lib/plugins/helper/date.ts @@ -1,7 +1,7 @@ import moment from 'moment-timezone'; const { isMoment } = moment; import moize from 'moize'; -import type Hexo from '../../hexo'; +import type { LocalsType } from '../../types'; const isDate = (value: moment.MomentInput | moment.Moment): boolean => typeof value === 'object' && value instanceof Date && !isNaN(value.getTime()); @@ -61,7 +61,7 @@ function timeTagHelper(date: string | number | Date | moment.Moment, format: str return ``; } -function getLanguage(ctx) { +function getLanguage(ctx: LocalsType) { return ctx.page.lang || ctx.page.language || ctx.config.language; } diff --git a/lib/plugins/helper/is.ts b/lib/plugins/helper/is.ts index 1f843cf3ff..ef9ec0fc46 100644 --- a/lib/plugins/helper/is.ts +++ b/lib/plugins/helper/is.ts @@ -1,4 +1,4 @@ -function isCurrentHelper(path = '/', strict) { +function isCurrentHelper(path = '/', strict: boolean) { const currentPath = this.path.replace(/^[^/].*/, '/$&'); if (strict) { diff --git a/lib/plugins/highlight/highlight.ts b/lib/plugins/highlight/highlight.ts index 787017fcf7..04d507ec92 100644 --- a/lib/plugins/highlight/highlight.ts +++ b/lib/plugins/highlight/highlight.ts @@ -1,8 +1,22 @@ +import type Hexo from '../../hexo'; + +interface Options { + line_threshold?: number; + line_number?: boolean; + lines_length?: number; + language_attr?: boolean; + caption?: string; + firstLine?: number; + lang?: string; + mark?: number[]; + firstLineNumber?: number; +} + // Lazy require highlight.js let highlight; -module.exports = function highlightFilter(code, options) { - const hljsCfg = this.config.highlight || {}; +module.exports = function highlightFilter(this: Hexo, code: string, options: Options) { + const hljsCfg = this.config.highlight || {} as any; const line_threshold = options.line_threshold || hljsCfg.line_threshold || 0; const shouldUseLineNumbers = typeof options.line_number === 'undefined' ? hljsCfg.line_number : options.line_number; const surpassesLineThreshold = options.lines_length > line_threshold; diff --git a/lib/plugins/injector/index.ts b/lib/plugins/injector/index.ts index 6736417bc8..4c640279c1 100644 --- a/lib/plugins/injector/index.ts +++ b/lib/plugins/injector/index.ts @@ -1,6 +1,6 @@ import type Hexo from '../../hexo'; export = (ctx: Hexo) => { - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { injector } = ctx.extend; }; diff --git a/lib/plugins/renderer/json.ts b/lib/plugins/renderer/json.ts index c5d02377f9..e4833402c3 100644 --- a/lib/plugins/renderer/json.ts +++ b/lib/plugins/renderer/json.ts @@ -1,6 +1,6 @@ import type { StoreFunctionData } from '../../extend/renderer'; -function jsonRenderer(data: StoreFunctionData) { +function jsonRenderer(data: StoreFunctionData): any { return JSON.parse(data.text); } diff --git a/lib/plugins/renderer/nunjucks.ts b/lib/plugins/renderer/nunjucks.ts index ccb0281346..1b854f1ebc 100644 --- a/lib/plugins/renderer/nunjucks.ts +++ b/lib/plugins/renderer/nunjucks.ts @@ -22,7 +22,7 @@ function toArray(value) { return []; } -function safeJsonStringify(json, spacer = undefined) { +function safeJsonStringify(json: any, spacer = undefined): string { if (typeof json !== 'undefined' && json !== null) { return JSON.stringify(json, null, spacer); } @@ -37,12 +37,12 @@ const nunjucksCfg = { lstripBlocks: false }; -const nunjucksAddFilter = (env: Environment) => { +const nunjucksAddFilter = (env: Environment): void => { env.addFilter('toarray', toArray); env.addFilter('safedump', safeJsonStringify); }; -function njkCompile(data: StoreFunctionData) { +function njkCompile(data: StoreFunctionData): nunjucks.Template { let env: Environment; if (data.path) { env = nunjucks.configure(dirname(data.path), nunjucksCfg); @@ -56,11 +56,11 @@ function njkCompile(data: StoreFunctionData) { return nunjucks.compile(text as string, env, data.path); } -function njkRenderer(data: StoreFunctionData, locals: object) { +function njkRenderer(data: StoreFunctionData, locals: object): string { return njkCompile(data).render(locals); } -njkRenderer.compile = (data: StoreFunctionData) => { +njkRenderer.compile = (data: StoreFunctionData): (locals: any) => string => { // Need a closure to keep the compiled template. return locals => njkCompile(data).render(locals); }; diff --git a/lib/plugins/renderer/plain.ts b/lib/plugins/renderer/plain.ts index 99c35a1c1c..f537106a8d 100644 --- a/lib/plugins/renderer/plain.ts +++ b/lib/plugins/renderer/plain.ts @@ -1,6 +1,6 @@ import type { StoreFunctionData } from '../../extend/renderer'; -function plainRenderer(data: StoreFunctionData) { +function plainRenderer(data: StoreFunctionData): string { return data.text; } diff --git a/lib/plugins/renderer/yaml.ts b/lib/plugins/renderer/yaml.ts index fd460d14a0..ceef7f7a15 100644 --- a/lib/plugins/renderer/yaml.ts +++ b/lib/plugins/renderer/yaml.ts @@ -3,7 +3,7 @@ import { escape } from 'hexo-front-matter'; import logger from 'hexo-log'; import type { StoreFunctionData } from '../../extend/renderer'; -let schema = {}; +let schema: yaml.Schema; // FIXME: workaround for https://github.com/hexojs/hexo/issues/4917 try { schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all); @@ -15,7 +15,7 @@ try { } } -function yamlHelper(data: StoreFunctionData) { +function yamlHelper(data: StoreFunctionData): any { return yaml.load(escape(data.text), { schema }); } diff --git a/lib/theme/index.ts b/lib/theme/index.ts index aff5a74711..41c83d6bfe 100644 --- a/lib/theme/index.ts +++ b/lib/theme/index.ts @@ -7,15 +7,19 @@ import { i18n } from './processors/i18n'; import { source } from './processors/source'; import { view } from './processors/view'; import type Hexo from '../hexo'; +import type { Pattern } from 'hexo-util'; class Theme extends Box { - public config: any; - public views: any; + public config: object; + public views: Record>; public i18n: I18n; - public View: any; - public processors: any[]; + public View: typeof View; + public processors: { + pattern: Pattern; + process: (...args: any[]) => any; + }[]; - constructor(ctx: Hexo, options?) { + constructor(ctx: Hexo, options?: object) { super(ctx, ctx.theme_dir, options); this.config = {}; @@ -48,7 +52,7 @@ class Theme extends Box { _View.prototype._helper = ctx.extend.helper; } - getView(path: string) { + getView(path: string): View { // Replace backslashes on Windows path = path.replace(/\\/g, '/'); @@ -65,7 +69,7 @@ class Theme extends Box { return views[Object.keys(views)[0]]; } - setView(path: string, data) { + setView(path: string, data: string): void { const ext = extname(path); const name = path.substring(0, path.length - ext.length); this.views[name] = this.views[name] || {}; @@ -74,7 +78,7 @@ class Theme extends Box { views[ext] = new this.View(path, data); } - removeView(path: string) { + removeView(path: string): void { const ext = extname(path); const name = path.substring(0, path.length - ext.length); const views = this.views[name]; diff --git a/lib/theme/processors/view.ts b/lib/theme/processors/view.ts index c127d02e7c..89d5205423 100644 --- a/lib/theme/processors/view.ts +++ b/lib/theme/processors/view.ts @@ -2,7 +2,7 @@ import { Pattern } from 'hexo-util'; import type { _File } from '../../box'; import type Theme from '..'; -function process(file: _File) { +function process(file: _File): Promise { const { path } = file.params; if (file.type === 'delete') { diff --git a/lib/theme/view.ts b/lib/theme/view.ts index af4c75be62..c693a3ded5 100644 --- a/lib/theme/view.ts +++ b/lib/theme/view.ts @@ -3,6 +3,8 @@ import { parse as yfm } from 'hexo-front-matter'; import Promise from 'bluebird'; import type Theme from '.'; import type Render from '../hexo/render'; +import type { NodeJSLikeCallback } from '../types'; +import type { Helper } from '../extend'; const assignIn = (target: any, ...sources: any[]) => { const length = sources.length; @@ -24,15 +26,15 @@ class Options { class View { public path: string; - public source: any; + public source: string; public _theme: Theme; public data: any; public _compiled: any; public _compiledSync: any; - public _helper: any; + public _helper: Helper; public _render: Render; - constructor(path: string, data) { + constructor(path: string, data: string) { this.path = path; this.source = join(this._theme.base, 'layout', path); this.data = typeof data === 'string' ? yfm(data) : data; @@ -40,16 +42,16 @@ class View { this._precompile(); } - // eslint-disable-next-line @typescript-eslint/ban-types - render(options: Options | Function = {}, callback) { + render(callback: NodeJSLikeCallback): Promise; + render(options: Options, callback?: NodeJSLikeCallback): Promise; + render(options: Options | NodeJSLikeCallback = {}, callback?: NodeJSLikeCallback): Promise { if (!callback && typeof options === 'function') { callback = options; options = {}; } const { data } = this; - // eslint-disable-next-line no-extra-parens const { layout = (options as Options).layout } = data; - const locals = this._buildLocals(options); + const locals = this._buildLocals(options as Options); return this._compiled(this._bindHelpers(locals)).then(result => { if (result == null || !layout) return result; @@ -87,8 +89,8 @@ class View { return layoutView.renderSync(layoutLocals); } - _buildLocals(locals) { - // eslint-disable-next-line no-unused-vars + _buildLocals(locals: Options) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { layout, _content, ...data } = this.data; return assignIn({}, locals, data, { filename: this.source @@ -106,7 +108,7 @@ class View { return locals; } - _resolveLayout(name: string) { + _resolveLayout(name: string): View { // Relative path const layoutPath = join(dirname(this.path), name); let layoutView = this._theme.getView(layoutPath); @@ -118,7 +120,7 @@ class View { if (layoutView && layoutView.source !== this.source) return layoutView; } - _precompile() { + _precompile(): void { const render = this._render; const ctx = render.context; const ext = extname(this.path); diff --git a/lib/types.ts b/lib/types.ts index ea6313c253..6959a456cc 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1 +1,129 @@ -type NodeJSLikeCallback = (err: E, result?: R) => void +import moment from 'moment'; +import type default_config from './hexo/default_config'; + +export type NodeJSLikeCallback = (err: E, result?: R) => void + +export interface RenderData { + engine?: string; + content?: string; + disableNunjucks?: boolean; + markdown?: object; + source?: string; + titlecase?: boolean; + title?: string; + excerpt?: string; + more?: string; +} + +// Schema +export interface PostSchema { + id?: string; + _id?: string; + title?: string; + date?: moment.Moment, + updated?: moment.Moment, + comments?: boolean; + layout?: string; + _content?: string; + source?: string; + slug?: string; + photos?: string[]; + raw?: string; + published?: boolean; + content?: string; + excerpt?: string; + more?: string; + author?: string; + asset_dir?: string; + full_source?: string; + path?: string; + permalink?: string; + categories?: any; + tags?: any; + __permalink?: string; + __post?: boolean; + canonical_path?: string; + lang?: string; + language?: string; + prev?: PostSchema; + next?: PostSchema; +} + +export interface PageSchema { + _id?: string; + title?: string; + date?: moment.Moment, + updated?: moment.Moment, + comments?: boolean; + layout?: string; + _content?: string; + source?: string; + path?: string; + raw?: string; + content?: string; + excerpt?: string; + more?: string; + author?: string; + full_source?: string; + permalink?: string; + tags?: any; + canonical_path?: string; + lang?: string; + language?: string; + __page?: boolean; +} + +export interface LocalsType { + page: PostSchema | PageSchema; + path: string; + url: string; + config: typeof default_config; + theme: object; + layout: string; + env: any; + view_dir: string; + site: object; + cache?: boolean; + __?: (key: string) => string; + _p?: (key: string, options?: any) => string; +} + +// Generator return types +export interface AssetGenerator { + path: string; + data: { + modified: boolean; + data?: () => any; + } +} + +export type SimplePostGenerator = { + path: string; + data: string; +} +export type NormalPostGenerator = { + path: string; + layout: string[]; + data: PostSchema; +} +export type PostGenerator = SimplePostGenerator | NormalPostGenerator; + + +export type SimplePageGenerator = { + path: string; + data: string; +} +export type NormalPageGenerator = { + path: string; + layout: string[]; + data: PageSchema; +} +export type PageGenerator = SimplePageGenerator | NormalPageGenerator; + +export interface SiteLocals { + posts: any; // _Query + pages: any; // _Query + categories: any; // _Model + tags: any; // _Model + data: object; +} diff --git a/package.json b/package.json index 5e4ba32ab6..544c8f9fe7 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,9 @@ "warehouse": "^5.0.0" }, "devDependencies": { + "@types/abbrev": "^1.1.3", "@types/bluebird": "^3.5.37", + "@types/js-yaml": "^4.0.9", "@types/node": "^18.11.8", "@types/nunjucks": "^3.2.2", "@types/text-table": "^0.2.4",