Skip to content

Commit

Permalink
refactor: refactor types & support warehouse6 (#5483)
Browse files Browse the repository at this point in the history

Signed-off-by: D-Sketon <[email protected]>
  • Loading branch information
D-Sketon authored Jan 22, 2025
1 parent efe66d2 commit 70efca7
Show file tree
Hide file tree
Showing 105 changed files with 1,067 additions and 649 deletions.
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;
}) {
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

0 comments on commit 70efca7

Please sign in to comment.