Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: refactor types #5483

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
26 changes: 24 additions & 2 deletions lib/box/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,38 @@ import { readFile, readFileSync, stat, statSync, type ReadFileOptions } from 'he
import type fs from 'fs';

class File {

/**
* Full path of the file
*/
public source: string;

/**
* Relative path to the box of the file
*/
public path: string;

/**
* The information from path matching.
*/
public params: any;
public type: string;

/**
* File type. The value can be create, update, skip, delete.
*/
// eslint-disable-next-line no-use-before-define
public type: typeof File.TYPE_CREATE | typeof File.TYPE_UPDATE | typeof File.TYPE_SKIP | typeof File.TYPE_DELETE;
static TYPE_CREATE: 'create';
static TYPE_UPDATE: 'update';
static TYPE_SKIP: 'skip';
static TYPE_DELETE: 'delete';

constructor({ source, path, params, type }) {
constructor({ source, path, params, type }: {
source: string;
path: string;
params: any;
type: typeof File.TYPE_CREATE | typeof File.TYPE_UPDATE | typeof File.TYPE_SKIP | typeof File.TYPE_DELETE;
SukkaW marked this conversation as resolved.
Show resolved Hide resolved
}) {
this.source = source;
this.path = path;
this.params = params;
Expand Down
63 changes: 35 additions & 28 deletions lib/box/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { EventEmitter } from 'events';
import { isMatch, makeRe } from 'micromatch';
import type Hexo from '../hexo';
import type { NodeJSLikeCallback } from '../types';
import type fs from 'fs';

const defaultPattern = new Pattern(() => ({}));

Expand All @@ -16,20 +17,26 @@ interface Processor {
process: (file?: File) => any;
}

interface BoxOptions {
persistent: boolean;
awaitWriteFinish: { stabilityThreshold: number };
ignored: RegExp[];
[key: string]: any;
}

class Box extends EventEmitter {
public options: any;
public options: BoxOptions;
public context: Hexo;
public base: string;
public processors: Processor[];
public _processingFiles: any;
public watcher: any;
public _processingFiles: Record<string, boolean>;
public watcher: Awaited<ReturnType<typeof watch>> | null;
public Cache: any;
// TODO: replace runtime class _File
public File: any;
public ignore: any[];
public source: any;
public ignore: string[];

constructor(ctx: Hexo, base: string, options?: object) {
constructor(ctx: Hexo, base: string, options?: any) {
super();

this.options = Object.assign({
Expand All @@ -50,7 +57,7 @@ class Box extends EventEmitter {
this.watcher = null;
this.Cache = ctx.model('Cache');
this.File = this._createFileClass();
let targets = this.options.ignored || [];
let targets = this.options.ignored as unknown as string[] || [];
if (ctx.config.ignore && ctx.config.ignore.length) {
targets = targets.concat(ctx.config.ignore);
}
Expand All @@ -64,13 +71,13 @@ class Box extends EventEmitter {
class _File extends File {
public box: Box;

render(options?: object) {
render(options?: any) {
return ctx.render.render({
path: this.source
}, options);
}

renderSync(options?: object) {
renderSync(options?: any) {
return ctx.render.renderSync({
path: this.source
}, options);
Expand All @@ -83,8 +90,8 @@ class Box extends EventEmitter {
}

addProcessor(pattern: (...args: any[]) => any): void;
addProcessor(pattern: string | RegExp | Pattern | ((...args: any[]) => any), fn: (...args: any[]) => any): void;
addProcessor(pattern: string | RegExp | Pattern | ((...args: any[]) => any), fn?: (...args: any[]) => any): void {
addProcessor(pattern: string | RegExp | Pattern | ((str: string) => any), fn: (...args: any[]) => any): void;
addProcessor(pattern: string | RegExp | Pattern | ((str: string) => any), fn?: (...args: any[]) => any): void {
if (!fn && typeof pattern === 'function') {
fn = pattern;
pattern = defaultPattern;
Expand All @@ -99,16 +106,16 @@ class Box extends EventEmitter {
});
}

_readDir(base: string, prefix = ''): BlueBirdPromise<any> {
_readDir(base: string, prefix = ''): BlueBirdPromise<string[]> {
const { context: ctx } = this;
const results = [];
const results: string[] = [];
return readDirWalker(ctx, base, results, this.ignore, prefix)
.return(results)
.map(path => this._checkFileStatus(path))
.map(file => this._processFile(file.type, file.path).return(file.path));
}

_checkFileStatus(path: string) {
_checkFileStatus(path: string): { type: string; path: string } {
const { Cache, context: ctx } = this;
const src = join(this.base, path);

Expand All @@ -122,26 +129,26 @@ class Box extends EventEmitter {
}));
}

process(callback?: NodeJSLikeCallback<any>): BlueBirdPromise<any> {
process(callback?: NodeJSLikeCallback<any>): BlueBirdPromise<void | (string | void)[]> {
const { base, Cache, context: ctx } = this;

return stat(base).then(stats => {
if (!stats.isDirectory()) return;

// Check existing files in cache
const relativeBase = escapeBackslash(base.substring(ctx.base_dir.length));
const cacheFiles = Cache.filter(item => item._id.startsWith(relativeBase)).map(item => item._id.substring(relativeBase.length));
const cacheFiles: string[] = Cache.filter(item => item._id.startsWith(relativeBase)).map(item => item._id.substring(relativeBase.length));

// Handle deleted files
return this._readDir(base)
.then((files: string[]) => cacheFiles.filter((path: string) => !files.includes(path)))
.map((path: string) => this._processFile(File.TYPE_DELETE, path) as PromiseLike<any>);
.then(files => cacheFiles.filter(path => !files.includes(path)))
.map(path => this._processFile(File.TYPE_DELETE, path));
}).catch(err => {
if (err && err.code !== 'ENOENT') throw err;
}).asCallback(callback);
}

_processFile(type: string, path: string): BlueBirdPromise<void> | BlueBirdPromise<string> {
_processFile(type: string, path: string): BlueBirdPromise<void | string> {
if (this._processingFiles[path]) {
return BlueBirdPromise.resolve();
}
Expand All @@ -159,7 +166,7 @@ class Box extends EventEmitter {
const params = processor.pattern.match(path);
if (!params) return count;

const file = new File({
const file: File = new File({
// source is used for filesystem path, keep backslashes on Windows
source: join(base, path),
// path is used for URL path, replace backslashes on Windows
Expand Down Expand Up @@ -266,30 +273,30 @@ function toRegExp(ctx: Hexo, arg: string): RegExp | null {
return result;
}

function isIgnoreMatch(path: string, ignore: string | any[]): boolean {
function isIgnoreMatch(path: string, ignore: string | string[]): boolean {
return path && ignore && ignore.length && isMatch(path, ignore);
}

function readDirWalker(ctx: Hexo, base: string, results: any[], ignore: any, prefix: string): BlueBirdPromise<any> {
function readDirWalker(ctx: Hexo, base: string, results: string[], ignore: string | string[], prefix: string): BlueBirdPromise<any> {
if (isIgnoreMatch(base, ignore)) return BlueBirdPromise.resolve();

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;
}), async path => {
const fullpath = join(base, path);
const stats = await stat(fullpath).catch(err => {
ctx.log.error({ err }, 'Failed to stat file: %s', fullpath);
}), async (path: string) => {
const fullPath = join(base, path);
const stats: fs.Stats | null = await stat(fullPath).catch(err => {
ctx.log.error({ err }, 'Failed to stat file: %s', fullPath);
if (err && err.code === 'ENOENT') return null;
throw err;
});
const prefixPath = `${prefix}${path}`;
if (stats) {
if (stats.isDirectory()) {
return readDirWalker(ctx, fullpath, results, ignore, `${prefixPath}/`);
return readDirWalker(ctx, fullPath, results, ignore, `${prefixPath}/`);
}
if (!isIgnoreMatch(fullpath, ignore)) {
if (!isIgnoreMatch(fullPath, ignore)) {
results.push(prefixPath);
}
}
Expand Down
9 changes: 7 additions & 2 deletions lib/extend/console.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Promise from 'bluebird';
import abbrev from 'abbrev';
import type { NodeJSLikeCallback } from '../types';
import type Hexo from '../hexo';

type Option = Partial<{
usage: string;
Expand All @@ -20,8 +21,9 @@ interface Args {
_: string[];
[key: string]: string | boolean | string[];
}
type AnyFn = (args: Args, callback?: NodeJSLikeCallback<any>) => any;
interface StoreFunction extends AnyFn {
type AnyFn = (this: Hexo, args: Args, callback?: NodeJSLikeCallback<any>) => any;
interface StoreFunction {
(this: Hexo, args: Args): Promise<any>;
desc?: string;
options?: Option;
}
Expand All @@ -33,6 +35,9 @@ interface Alias {
[abbreviation: string]: string
}

/**
* The console forms the bridge between Hexo and its users. It registers and describes the available console commands.
*/
class Console {
public store: Store;
public alias: Alias;
Expand Down
23 changes: 17 additions & 6 deletions lib/extend/deployer.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import Promise from 'bluebird';
import type { NodeJSLikeCallback } from '../types';
import type Hexo from '../hexo';

interface StoreFunction {
(deployArg: {
type: string;
[key: string]: any
}, callback?: NodeJSLikeCallback<any>) : any;
(this: Hexo, deployArg: { type: string; [key: string]: any }): Promise<any>;
}
interface Store {
[key: string]: StoreFunction
[key: string]: StoreFunction;
}

/**
* A deployer helps users quickly deploy their site to a remote server without complicated commands.
*/
class Deployer {
public store: Store;

Expand All @@ -26,7 +27,17 @@ class Deployer {
return this.store[name];
}

register(name: string, fn: StoreFunction): void {
register(
name: string,
fn: (
this: Hexo,
deployArg: {
type: string;
[key: string]: any;
},
callback?: NodeJSLikeCallback<any>
) => any
): void {
if (!name) throw new TypeError('name is required');
if (typeof fn !== 'function') throw new TypeError('fn must be a function');

Expand Down
11 changes: 5 additions & 6 deletions lib/extend/filter.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import Promise from 'bluebird';
import { FilterOptions } from '../types';

const typeAlias = {
pre: 'before_post_render',
post: 'after_post_render',
'after_render:html': '_after_html_render'
};

interface FilterOptions {
context?: any;
args?: any[];
}


interface StoreFunction {
(data?: any, ...args: any[]): any;
priority?: number;
Expand All @@ -21,6 +16,10 @@ interface Store {
[key: string]: StoreFunction[]
}

/**
* A filter is used to modify some specified data. Hexo passes data to filters in sequence and the filters then modify the data one after the other.
* This concept was borrowed from WordPress.
*/
class Filter {
public store: Store;

Expand Down
16 changes: 7 additions & 9 deletions lib/extend/generator.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import Promise from 'bluebird';
import type { NodeJSLikeCallback } from '../types';
import type { BaseGeneratorReturn, NodeJSLikeCallback, SiteLocals } from '../types';

interface BaseObj {
path: string;
data?: any;
layout?: string | string[];
}
type ReturnType = BaseObj | BaseObj[];
type ReturnType = BaseGeneratorReturn | BaseGeneratorReturn[];
type GeneratorReturnType = ReturnType | Promise<ReturnType>;

interface GeneratorFunction {
(locals: any, callback?: NodeJSLikeCallback<any>): GeneratorReturnType;
(locals: SiteLocals, callback?: NodeJSLikeCallback<any>): GeneratorReturnType;
}

type StoreFunctionReturn = Promise<ReturnType>;

interface StoreFunction {
(locals: any): StoreFunctionReturn;
(locals: SiteLocals): StoreFunctionReturn;
}

interface Store {
[key: string]: StoreFunction
}

/**
* A generator builds routes based on processed files.
*/
class Generator {
public id: number;
public store: Store;
Expand Down
3 changes: 3 additions & 0 deletions lib/extend/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ interface Store {
[key: string]: StoreFunction;
}

/**
* A helper makes it easy to quickly add snippets to your templates. We recommend using helpers instead of templates when you’re dealing with more complicated code.
*/
class Helper {
public store: Store;

Expand Down
Loading
Loading