From 8f6e1a6ee915219586060b029219f63b5983acc6 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:01:26 +0700 Subject: [PATCH 01/20] chore: moved interfaces and using ES Export style - StoreFunction & StoreSyncFunction & StoreFunctionData - SyncStore & Store to easily import the interfaces between `extend` source files --- lib/extend/renderer-d.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/extend/renderer-d.ts diff --git a/lib/extend/renderer-d.ts b/lib/extend/renderer-d.ts new file mode 100644 index 0000000000..93fb475f3a --- /dev/null +++ b/lib/extend/renderer-d.ts @@ -0,0 +1,40 @@ +/** + * to cast `data` type look example below + * @example + * type rendererData = Parameters[2]>[0]; + * // or + * type rendererData = Parameters[2]>[0]; + * // or + * type rendererData = import('hexo/extend/renderer-d.ts').StoreFunctionData; + */ +export interface StoreFunctionData { + [key: string]: any; + path?: string; + text: string; +} + +export interface StoreSyncFunction { + ( + data: StoreFunctionData, + options: Record + // callback: (err: Error, value: string) => any + ): any; + output?: string; + compile?: (local: Record) => string; + disableNunjucks?: boolean; +} + +export interface StoreFunction { + (data: StoreFunctionData, options: Record): Promise; + (data: StoreFunctionData, options: Record, callback: (err: Error, value: string) => any): void; + output?: string; + compile?: (local: Record) => string; + disableNunjucks?: boolean; +} + +export interface SyncStore { + [key: string]: StoreSyncFunction; +} +export interface Store { + [key: string]: StoreFunction; +} From cc9518b24d62662d3476efe7379ab271ff2abaf9 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:02:53 +0700 Subject: [PATCH 02/20] feat: add data type `before_post_render` --- lib/plugins/filter/before_post_render/dataType.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 lib/plugins/filter/before_post_render/dataType.ts diff --git a/lib/plugins/filter/before_post_render/dataType.ts b/lib/plugins/filter/before_post_render/dataType.ts new file mode 100644 index 0000000000..433ef2cbd1 --- /dev/null +++ b/lib/plugins/filter/before_post_render/dataType.ts @@ -0,0 +1,12 @@ +/** + * before_post_render `data` parameter + * + * @example + * hexo.extend.filter.register('before_post_render', function(data){ console.log(data.content) }) + */ +export interface extend_filter_before_post_render_data { + [key: string]: any; + content: string | null | undefined; + source: string; + config: import('../../../hexo')['config']; +} From 1a11ad5ea15ecc9a6c444510897a59c9e59f1f5d Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:04:05 +0700 Subject: [PATCH 03/20] chore: change old type `object` to `Record --- lib/extend/generator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/extend/generator.ts b/lib/extend/generator.ts index 6360d1eb2c..c7c518de3d 100644 --- a/lib/extend/generator.ts +++ b/lib/extend/generator.ts @@ -9,13 +9,13 @@ type ReturnType = BaseObj | BaseObj[]; type GeneratorReturnType = ReturnType | Promise; interface GeneratorFunction { - (locals: object): GeneratorReturnType; + (locals: Record): GeneratorReturnType; } type StoreFunctionReturn = Promise; interface StoreFunction { - (locals: object): StoreFunctionReturn; + (locals: Record): StoreFunctionReturn; } interface Store { From 6aee1f3bedf4c51662caf05543787da3e979217b Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:04:38 +0700 Subject: [PATCH 04/20] chore: add missing type param `ctx` --- lib/plugins/filter/before_post_render/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/filter/before_post_render/index.ts b/lib/plugins/filter/before_post_render/index.ts index 07bde83da3..81da2f0a60 100644 --- a/lib/plugins/filter/before_post_render/index.ts +++ b/lib/plugins/filter/before_post_render/index.ts @@ -1,4 +1,4 @@ -export = ctx => { +export = (ctx: import('../../../hexo')) => { const { filter } = ctx.extend; filter.register('before_post_render', require('./backtick_code_block')(ctx)); From 4a61ca5452a869dd54ee3562de1e489cb113e0c0 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:06:10 +0700 Subject: [PATCH 05/20] fix: cast `pattern` as StoreFunction fix: conditional when `fn` is not function --- lib/extend/processor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/extend/processor.ts b/lib/extend/processor.ts index 0af6998494..fe6e731bd2 100644 --- a/lib/extend/processor.ts +++ b/lib/extend/processor.ts @@ -26,9 +26,9 @@ class Processor { register(fn: StoreFunction): void; register(pattern: patternType, fn: StoreFunction): void; register(pattern: patternType | StoreFunction, fn?: StoreFunction) { - if (!fn) { + if (typeof fn !== 'function') { if (typeof pattern === 'function') { - fn = pattern; + fn = pattern as StoreFunction; pattern = /(.*)/; } else { throw new TypeError('fn must be a function'); From 83aa298914724b6d9fd20880f098020f38226b45 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:06:44 +0700 Subject: [PATCH 06/20] chore(lint): eslint --fix --- lib/extend/syntax_highlight.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/extend/syntax_highlight.ts b/lib/extend/syntax_highlight.ts index 1ece414224..34bac64071 100644 --- a/lib/extend/syntax_highlight.ts +++ b/lib/extend/syntax_highlight.ts @@ -1,12 +1,12 @@ import type Hexo from '../hexo'; export interface HighlightOptions { - lang: string | undefined, - caption: string | undefined, - lines_length: number, + lang: string | undefined; + caption: string | undefined; + lines_length: number; - // plulgins/filter/before_post_render/backtick_code_block - firstLineNumber?: string | number + // plugins/filter/before_post_render/backtick_code_block + firstLineNumber?: string | number; // plugins/tag/code.ts language_attr?: boolean | undefined; @@ -15,7 +15,6 @@ export interface HighlightOptions { line_threshold?: number | undefined; mark?: number[]; wrap?: boolean | undefined; - } interface HighlightExecArgs { @@ -29,7 +28,7 @@ interface StoreFunction { } interface Store { - [key: string]: StoreFunction + [key: string]: StoreFunction; } class SyntaxHighlight { @@ -52,7 +51,9 @@ class SyntaxHighlight { exec(name: string, options: HighlightExecArgs): string { const fn = this.store[name]; - if (!fn) throw new TypeError(`syntax highlighter ${name} is not registered`); + if (!fn) { + throw new TypeError(`syntax highlighter ${name} is not registered`); + } const ctx = options.context; const args = options.args || []; From 31ef31410355ab87de7cf155f93eb7a02a509e3a Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:10:08 +0700 Subject: [PATCH 07/20] chore: convert interface `TagFunction` to overload type chore: convert interface `AsyncTagFunction` to overload type docs: add some JSDoc chore: add prefix `_` to unused variables feat: add interface `RegisterAsyncOptions` fix: missing types feat(Tag.register): add overload types and JSDoc --- lib/extend/tag.ts | 143 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 110 insertions(+), 33 deletions(-) diff --git a/lib/extend/tag.ts b/lib/extend/tag.ts index dbebd5bfac..3af9e571b2 100644 --- a/lib/extend/tag.ts +++ b/lib/extend/tag.ts @@ -4,14 +4,33 @@ 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, '}'); +const escapeSwigTag = (str: string) => str.replace(/{/g, '{').replace(/}/g, '}'); -interface TagFunction { - (args: any[], content: string): string; -} -interface AsyncTagFunction { - (args: any[], content: string): Promise; -} +/** + * synchronous callback - shortcode tag + * @example + * // to get args type + * type args = Parameters[1]>[0]; + * // to get content type + * type content = Parameters[1]>[1]; + */ +type TagFunction = + | ((arg: string) => string) + | ((...args: any[]) => string) + | ((args: any[], content: string) => string); + +/** + * asynchronous callback - shortcode tag + * @example + * // to get args type + * type args = Parameters[1]>[0]; + * // to get content type + * type content = Parameters[1]>[1]; + */ +type AsyncTagFunction = + | ((arg: string) => PromiseLike | Promise) + | ((...args: any[]) => PromiseLike | Promise) + | ((args: any[], content: string) => PromiseLike | Promise); class NunjucksTag { public tags: string[]; @@ -57,7 +76,7 @@ class NunjucksTag { return node; } - run(context, args, body, callback) { + run(context, args, _body, _callback) { return this._run(context, args, ''); } @@ -78,14 +97,14 @@ class NunjucksBlock extends NunjucksTag { return new nodes.CallExtension(this, 'run', node, [body]); } - _parseBody(parser, nodes, lexer) { + _parseBody(parser, _nodes?, _lexer?) { const body = parser.parseUntilBlocks(`end${this.tags[0]}`); parser.advanceAfterBlockEnd(); return body; } - run(context, args, body, callback) { + run(context, args, body, _callback?) { return this._run(context, args, trimBody(body)); } } @@ -145,18 +164,16 @@ const getContext = (lines, errLine, location, type) => { message.push( // get LINES_OF_CONTEXT lines surrounding `errLine` - ...getContextLineNums(1, lines.length, errLine, LINES_OF_CONTEXT) - .map(lnNum => { - const line = ' ' + lnNum + ' | ' + lines[lnNum - 1]; - if (lnNum === errLine) { - return cyan(bold(line)); - } + ...getContextLineNums(1, lines.length, errLine, LINES_OF_CONTEXT).map(lnNum => { + const line = ' ' + lnNum + ' | ' + lines[lnNum - 1]; + if (lnNum === errLine) { + return cyan(bold(line)); + } - return cyan(line); - }) + return cyan(line); + }) ); - message.push(cyan( - ' ===== Context Dump Ends =====')); + message.push(cyan(' ===== Context Dump Ends =====')); return message; }; @@ -169,11 +186,11 @@ class NunjucksError extends Error { /** * Provide context for Nunjucks error - * @param {Error} err Nunjucks error - * @param {string} str string input for Nunjucks - * @return {Error} New error object with embedded context + * @param err Nunjucks error + * @param input string input for Nunjucks + * @return New error object with embedded context */ -const formatNunjucksError = (err, input, source = '') => { +const formatNunjucksError = (err: Error, input: string, source = ''): Error => { err.message = err.message.replace('(unknown path)', source ? magenta(source) : ''); const match = err.message.match(/Line (\d+), Column \d+/); @@ -196,10 +213,14 @@ const formatNunjucksError = (err, input, source = '') => { type RegisterOptions = { async?: boolean; ends?: boolean; +}; + +interface RegisterAsyncOptions extends RegisterOptions { + async: boolean; } class Tag { - public env: any; + public env: Environment; public source: any; constructor() { @@ -208,10 +229,60 @@ class Tag { }); } - 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) { + /** + * register shortcode tag + * @param name shortcode tag name + * @param fn shortcode tag function + */ + register(name: string, fn: TagFunction): void; + + /** + * register shortcode tag with RegisterOptions.ends boolean directly + * @param name shortcode tag name + * @param fn callback shortcode tag + * @param ends use endblock + */ + register(name: string, fn: TagFunction, ends: boolean): void; + + /** + * register shortcode tag with synchronous function callback + * @param name shortcode tag name + * @param fn synchronous function callback + * @param options register options + */ + register(name: string, fn: TagFunction, options: RegisterOptions): void; + + /** + * register shortcode tag with synchronous function callback + * @param name shortcode tag name + * @param fn synchronous function callback + * @param options register options + */ + register(name: string, fn: TagFunction, options: { async: false; ends?: boolean }): void; + + /** + * register shortcode tag with asynchronous function callback + * @param name shortcode tag name + * @param fn asynchronous function callback + * @param options register options + */ + register( + name: string, + fn: AsyncTagFunction, + options: { async: true; ends?: boolean } | { async: true; ends: boolean } + ): void; + + /** + * register shortcode tag + * @param name shortcode tag name + * @param fn asynchronous or synchronous function callback + * @param options register options + */ + register( + name: string, + fn: TagFunction | AsyncTagFunction, + options?: RegisterOptions | RegisterAsyncOptions | boolean + ) { if (!name) throw new TypeError('name is required'); if (typeof fn !== 'function') throw new TypeError('fn must be a function'); @@ -243,7 +314,11 @@ class Tag { this.env.addExtension(name, tag); } - unregister(name) { + /** + * unregister shortcode tag + * @param name shortcode tag name + */ + unregister(name: string) { if (!name) throw new TypeError('name is required'); const { env } = this; @@ -251,7 +326,8 @@ class Tag { if (env.hasExtension(name)) env.removeExtension(name); } - render(str, options: { source?: string } = {}, callback) { + render(str: string, options: { source?: string }): Promise; + render(str: string, options: { source?: string } = {}, callback?: (err: any, result: string) => any) { if (!callback && typeof options === 'function') { callback = options; options = {}; @@ -271,9 +347,10 @@ class Tag { options, cb ); - }).catch(err => { - return Promise.reject(formatNunjucksError(err, str, source)); }) + .catch(err => { + return Promise.reject(formatNunjucksError(err, str, source)); + }) .asCallback(callback); } } From f2e17aa3f7217a7f600a19d25e942a45de849b32 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:11:28 +0700 Subject: [PATCH 08/20] chore(StoreFunction): add explicit this marked as internal hexo function fix(lint): eslint --fix --- lib/extend/helper.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/extend/helper.ts b/lib/extend/helper.ts index 7903557c97..0ed93f5bb1 100644 --- a/lib/extend/helper.ts +++ b/lib/extend/helper.ts @@ -1,12 +1,11 @@ interface StoreFunction { - (...args: any[]): string; + (this: import('../hexo'), ...args: any[]): any; } interface Store { - [key: string]: StoreFunction + [key: string]: StoreFunction; } - class Helper { public store: Store; From 1ab1d588140032fcb7a597a8c2a169d928cb8791 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:12:17 +0700 Subject: [PATCH 09/20] feat(Filter.register): add overload method types fix(lint): eslint --fix --- lib/extend/filter.ts | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/extend/filter.ts b/lib/extend/filter.ts index 78113d01c2..999f59929f 100644 --- a/lib/extend/filter.ts +++ b/lib/extend/filter.ts @@ -1,4 +1,5 @@ import Promise from 'bluebird'; +import { extend_filter_before_post_render_data } from '../plugins/filter/before_post_render/dataType'; const typeAlias = { pre: 'before_post_render', @@ -17,7 +18,7 @@ interface StoreFunction { } interface Store { - [key: string]: StoreFunction[] + [key: string]: StoreFunction[]; } class Filter { @@ -34,11 +35,27 @@ class Filter { return this.store[type] || []; } - 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) { + register(fn: StoreFunction): void; + register(fn: StoreFunction, priority: number): void; + register(type: string, fn: StoreFunction): void; + register( + type: 'server_middleware', + fn: (app: import('connect').Server) => void + ): void; + register( + type: 'before_post_render', + fn: (data: extend_filter_before_post_render_data) => void + ): void; + register( + type: 'before_post_render', + fn: (data: extend_filter_before_post_render_data) => Promise + ): 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 as number; @@ -84,10 +101,12 @@ class Filter { args.unshift(data); - return Promise.each(filters, filter => Reflect.apply(Promise.method(filter), ctx, args).then(result => { - args[0] = result == null ? args[0] : result; - return args[0]; - })).then(() => args[0]); + return Promise.each(filters, filter => + Reflect.apply(Promise.method(filter), ctx, args).then(result => { + args[0] = result == null ? args[0] : result; + return args[0]; + }) + ).then(() => args[0]); } execSync(type: string, data: any[], options: FilterOptions = {}) { From b33d7e72a645c52f93daa49e8f7f3cc0e6f0c8cd Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:14:11 +0700 Subject: [PATCH 10/20] chore: moved interfaces to `renderer-d.ts` using ES Export style to easily import between source files chore(Renderer.register): add overload method types docs: add JSDoc --- lib/extend/renderer.ts | 81 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/lib/extend/renderer.ts b/lib/extend/renderer.ts index c70e054f18..7514e541d0 100644 --- a/lib/extend/renderer.ts +++ b/lib/extend/renderer.ts @@ -1,5 +1,6 @@ import { extname } from 'path'; import Promise from 'bluebird'; +import { Store, SyncStore, StoreSyncFunction, StoreFunction } from './renderer-d'; const getExtname = (str: string) => { if (typeof str !== 'string') return ''; @@ -8,46 +9,6 @@ const getExtname = (str: string) => { 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; @@ -80,10 +41,50 @@ class Renderer { return renderer ? renderer.output : ''; } + /** + * register renderer engine + * - [hexo-renderer-nunjucks example](https://github.com/hexojs/hexo-renderer-nunjucks/blob/c71a1979535c47c3949ff6bf3a85691641841e12/lib/renderer.js#L55-L56) + * - [typescript example](https://github.com/dimaslanjaka/hexo-renderers/blob/feed801e90920bea8a5e7000b275b912e4ef6c43/src/renderer-sass.ts#L37-L38) + * @param name input extension name. ex: ejs + * @param output output extension name. ex: html + * @param fn renderer function + */ register(name: string, output: string, fn: StoreFunction): void; + + /** + * register renderer engine asynchronous + * @param name input extension name. ex: ejs + * @param output output extension name. ex: html + * @param fn renderer asynchronous function + * @param sync is synchronous? + */ register(name: string, output: string, fn: StoreFunction, sync: false): void; + + /** + * register renderer engine + * @param name input extension name. ex: ejs + * @param output output extension name. ex: html + * @param fn renderer function + * @param sync is synchronous? + */ register(name: string, output: string, fn: StoreSyncFunction, sync: true): void; + + /** + * register renderer engine + * @param name input extension name. ex: ejs + * @param output output extension name. ex: html + * @param fn renderer function + * @param sync is synchronous? + */ register(name: string, output: string, fn: StoreFunction | StoreSyncFunction, sync: boolean): void; + + /** + * register renderer engine + * @param name input extension name. ex: ejs + * @param output output extension name. ex: html + * @param fn renderer function + * @param sync is synchronous? + */ 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'); From a5aef69cd9c3b20ed39b0999be57a8eb5b9ee983 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:15:53 +0700 Subject: [PATCH 11/20] fix: conditional of data is actually an `object` chore: add `Pattern` result `boolean` type - PR hexojs/hexo-util/pull/311 - changes hexojs/hexo-util@9ddb7e2 --- lib/plugins/filter/template_locals/i18n.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/plugins/filter/template_locals/i18n.ts b/lib/plugins/filter/template_locals/i18n.ts index 73367d8df0..daca419db3 100644 --- a/lib/plugins/filter/template_locals/i18n.ts +++ b/lib/plugins/filter/template_locals/i18n.ts @@ -13,7 +13,13 @@ function i18nLocalsFilter(locals) { const pattern = new Pattern(`${i18nDir}/*path`); const data = pattern.match(locals.path); - if (data && data.lang && i18nLanguages.includes(data.lang)) { + if ( + typeof data !== 'undefined' + && !Array.isArray(data) + && typeof data !== 'boolean' + && data.lang + && i18nLanguages.includes(data.lang) + ) { lang = data.lang; page.canonical_path = data.path; } else { From 244be2ff019a4bf7c39efc57a5a8c8e4ce8555c2 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 18 May 2023 11:18:30 +0700 Subject: [PATCH 12/20] fix(TS2345): Argument of type 'string | Buffer' is not assignable to parameter of type 'string'. --- lib/hexo/post.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hexo/post.ts b/lib/hexo/post.ts index 6eda66cc90..7a067c69d1 100644 --- a/lib/hexo/post.ts +++ b/lib/hexo/post.ts @@ -356,7 +356,7 @@ class Post { return readFile(src); }).then(content => { // Create post - Object.assign(data, yfmParse(content)); + Object.assign(data, yfmParse(String(content))); data.content = data._content; data._content = undefined; From b1312fc301c2f0c3f0d400ecf5f391d869cbdf8b Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Fri, 19 May 2023 14:53:18 +0700 Subject: [PATCH 13/20] chore(hexo.loadPlugin): make `callback` as optional sample without callback https://github.com/hexojs/hexo/blob/8b95bbc722e5c77a7e8125441ed64d2ea3524ac0/test/scripts/tags/blockquote.js#LL8C16-L8C96 --- lib/hexo/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hexo/index.ts b/lib/hexo/index.ts index 15a0d6403e..a49fa0c94b 100644 --- a/lib/hexo/index.ts +++ b/lib/hexo/index.ts @@ -370,7 +370,7 @@ class Hexo extends EventEmitter { } } - loadPlugin(path: string, callback: (...args: any[]) => any) { + 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); From cfd7acbe0640d7c406c9d2d39e8aa7164786fd5a Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Fri, 19 May 2023 15:01:00 +0700 Subject: [PATCH 14/20] feat(loadPlugin): add JSDoc --- lib/hexo/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/hexo/index.ts b/lib/hexo/index.ts index a49fa0c94b..96a738c3fa 100644 --- a/lib/hexo/index.ts +++ b/lib/hexo/index.ts @@ -370,6 +370,14 @@ class Hexo extends EventEmitter { } } + /** + * load installed plugin + * @param path absolute path to plugin directory + * @param callback + * @returns + * @example + * hexo.loadPlugin(require.resolve('hexo-renderer-marked')); + */ 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 From 1e572c8f0046fc6657bffbb326dfa72e82e992f5 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Fri, 19 May 2023 20:51:11 +0700 Subject: [PATCH 15/20] feat: add context interface `ExtendedTagProperty` chore: cast tag function callback as custom context useful for determining which post or page source the tag processor is currently processing from --- lib/extend/tag.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/extend/tag.ts b/lib/extend/tag.ts index 3af9e571b2..b20c85a15d 100644 --- a/lib/extend/tag.ts +++ b/lib/extend/tag.ts @@ -6,6 +6,14 @@ const rSwigRawFullBlock = /{% *raw *%}/; const rCodeTag = /]*>[\s\S]+?<\/code>/g; const escapeSwigTag = (str: string) => str.replace(/{/g, '{').replace(/}/g, '}'); +interface ExtendedTagProperty { + + /** + * current absolute file source + */ + full_source: string; +} + /** * synchronous callback - shortcode tag * @example @@ -15,9 +23,9 @@ const escapeSwigTag = (str: string) => str.replace(/{/g, '{').replace(/}/g, * type content = Parameters[1]>[1]; */ type TagFunction = - | ((arg: string) => string) - | ((...args: any[]) => string) - | ((args: any[], content: string) => string); + | ((this: ExtendedTagProperty, arg: string) => string) + | ((this: ExtendedTagProperty, ...args: any[]) => string) + | ((this: ExtendedTagProperty, args: any[], content: string) => string); /** * asynchronous callback - shortcode tag @@ -28,9 +36,9 @@ type TagFunction = * type content = Parameters[1]>[1]; */ type AsyncTagFunction = - | ((arg: string) => PromiseLike | Promise) - | ((...args: any[]) => PromiseLike | Promise) - | ((args: any[], content: string) => PromiseLike | Promise); + | ((this: ExtendedTagProperty, arg: string) => PromiseLike | Promise) + | ((this: ExtendedTagProperty, ...args: any[]) => PromiseLike | Promise) + | ((this: ExtendedTagProperty, args: any[], content: string) => PromiseLike | Promise); class NunjucksTag { public tags: string[]; From 28a2e1f9fb0b288a270f8eafbebf09ff543d9249 Mon Sep 17 00:00:00 2001 From: Dimas Lanjaka Date: Sat, 20 May 2023 02:36:51 +0700 Subject: [PATCH 16/20] chore: export as type prevent indexed as source by VSCode, Intellij IDEA, Sublime Text v3 Co-authored-by: Sukka --- lib/extend/renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/extend/renderer.ts b/lib/extend/renderer.ts index 7514e541d0..3c74694d91 100644 --- a/lib/extend/renderer.ts +++ b/lib/extend/renderer.ts @@ -1,6 +1,6 @@ import { extname } from 'path'; import Promise from 'bluebird'; -import { Store, SyncStore, StoreSyncFunction, StoreFunction } from './renderer-d'; +import type { Store, SyncStore, StoreSyncFunction, StoreFunction } from './renderer-d'; const getExtname = (str: string) => { if (typeof str !== 'string') return ''; From eeaacce15e545b54879952816725a7100c070e15 Mon Sep 17 00:00:00 2001 From: Dimas Lanjaka Date: Sat, 20 May 2023 02:47:53 +0700 Subject: [PATCH 17/20] chore: update context type import Co-authored-by: Sukka --- lib/plugins/filter/before_post_render/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/filter/before_post_render/index.ts b/lib/plugins/filter/before_post_render/index.ts index 81da2f0a60..501b483df7 100644 --- a/lib/plugins/filter/before_post_render/index.ts +++ b/lib/plugins/filter/before_post_render/index.ts @@ -1,4 +1,6 @@ -export = (ctx: import('../../../hexo')) => { +import type Hexo from '../../../hexo'; + +export = (ctx: Hexo) => { const { filter } = ctx.extend; filter.register('before_post_render', require('./backtick_code_block')(ctx)); From e1fd7d7a9e06d78dac2ef4c364450e0a85b187b9 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 15 Feb 2024 00:43:25 +0700 Subject: [PATCH 18/20] fix(TS2322): Type 'unknown' is not assignable to type 'string'. --- lib/plugins/filter/template_locals/i18n.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/filter/template_locals/i18n.ts b/lib/plugins/filter/template_locals/i18n.ts index 520fbfbcad..76feaa2c43 100644 --- a/lib/plugins/filter/template_locals/i18n.ts +++ b/lib/plugins/filter/template_locals/i18n.ts @@ -13,7 +13,9 @@ function i18nLocalsFilter(this: Hexo, locals: LocalsType): void { if (!lang) { const pattern = new Pattern(`${i18nDir}/*path`); - const data = pattern.match(locals.path); + // fix hexo-util/dist/pattern.d.ts is not object + // fix(TS2322): Type 'unknown' is not assignable to type 'string'. + const data = pattern.match(locals.path) as Record; if ( typeof data !== 'undefined' From e36d0249a4645eee0f584415d14032c1333cc599 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 15 Feb 2024 00:44:44 +0700 Subject: [PATCH 19/20] feat(deps-dev): install `@types/connect` for middleware `hexo-server` type --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0150fb28b1..f88e6d79e6 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,7 @@ "version": "7.1.1", "description": "A fast, simple & powerful blog framework, powered by Node.js.", "main": "dist/hexo", - "bin": { - "hexo": "./bin/hexo" - }, + "bin": "./bin/hexo", "scripts": { "prepublishOnly": "npm install && npm run clean && npm run build", "build": "tsc -b", @@ -66,13 +64,14 @@ "warehouse": "^5.0.1" }, "devDependencies": { + "0x": "^5.1.2", "@types/abbrev": "^1.1.3", "@types/bluebird": "^3.5.37", + "@types/connect": "^3.4.38", "@types/js-yaml": "^4.0.9", "@types/node": "^18.11.8 <18.19.9", "@types/nunjucks": "^3.2.2", "@types/text-table": "^0.2.4", - "0x": "^5.1.2", "c8": "^9.0.0", "chai": "^4.3.6", "cheerio": "0.22.0", From 233dcb77c498ad9c067e352b2d2ac9b10597728a Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Thu, 15 Feb 2024 00:47:15 +0700 Subject: [PATCH 20/20] chore: import hexo from parent folder --- lib/plugins/filter/before_post_render/dataType.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/filter/before_post_render/dataType.ts b/lib/plugins/filter/before_post_render/dataType.ts index 433ef2cbd1..626e6404ce 100644 --- a/lib/plugins/filter/before_post_render/dataType.ts +++ b/lib/plugins/filter/before_post_render/dataType.ts @@ -1,3 +1,5 @@ +import Hexo from '../../../hexo'; + /** * before_post_render `data` parameter * @@ -8,5 +10,5 @@ export interface extend_filter_before_post_render_data { [key: string]: any; content: string | null | undefined; source: string; - config: import('../../../hexo')['config']; + config: Hexo['config']; }