-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: fix events order, bind TypeDoc prototypes to watch custom events
Related to option set, after theme loaded, before options freeze
- Loading branch information
Showing
12 changed files
with
236 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,37 @@ | ||
import assert from 'assert'; | ||
|
||
import { MarkdownEvent, PageEvent, ProjectReflection, Reflection, RenderTemplate, Renderer, SourceFile } from 'typedoc'; | ||
import { isNil } from 'lodash'; | ||
import { PageEvent, ProjectReflection, Reflection, RenderTemplate, Renderer, SourceFile } from 'typedoc'; | ||
|
||
import { setupCaptureEvent } from './capture-event'; | ||
|
||
export const setupMockPageMemo = () => { | ||
const capture = setupCaptureEvent( Renderer, Renderer.EVENT_BEGIN_PAGE ); | ||
return { | ||
captureEventRegistration: capture.captureEventRegistration, | ||
setCurrentPage: <T extends Reflection>( url: string, source: string, model: T, template: RenderTemplate<PageEvent<T>> = () => '', project = new ProjectReflection( 'Fake' ) ) => { | ||
setCurrentPage: <T extends Reflection>( | ||
url: string, | ||
source: string, | ||
model: T, | ||
template: RenderTemplate<PageEvent<T>> = () => '', | ||
project = new ProjectReflection( 'Fake' ), | ||
listenerIndex?: number, | ||
) => { | ||
const listeners = capture.getListeners(); | ||
assert.equal( listeners.length, 1, `Invalid listeners count for event ${MarkdownEvent.PARSE}` ); | ||
const event = new PageEvent<T>( PageEvent.BEGIN ); | ||
event.project = project; | ||
event.url = url; | ||
event.model = model; | ||
event.template = template ?? ( () => '' ); | ||
event.filename = url; | ||
assert( listeners.length >= 1, `Invalid listeners count for event ${Renderer.EVENT_BEGIN_PAGE}` ); | ||
const pageEvent = new PageEvent<T>( PageEvent.BEGIN ); | ||
pageEvent.project = project; | ||
pageEvent.url = url; | ||
pageEvent.model = model; | ||
pageEvent.template = template ?? ( () => '' ); | ||
pageEvent.filename = url; | ||
Object.defineProperty( model, 'project', { value: project } ); | ||
model.sources = [ | ||
...( model.sources ?? [] ), | ||
{ fileName: source, character: 1, line: 1, file: new SourceFile( source ) }, | ||
]; | ||
listeners[0]( event ); | ||
const listenersToTrigger = isNil( listenerIndex ) ? listeners : [ listeners[listenerIndex] ]; | ||
listenersToTrigger.forEach( l => l( pageEvent ) ); | ||
}, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import assert from 'assert'; | ||
|
||
import { Application } from 'typedoc'; | ||
|
||
type Fn = ( ...args: any[] ) => any; | ||
type MethodKeys<T> = {[k in keyof T]: T[k] extends Fn ? k : never}[keyof T] & string | ||
type Params<T> = T extends Fn ? Parameters<T> : unknown[]; | ||
type Ret<T> = T extends Fn ? ReturnType<T> : unknown; | ||
export class EventsExtra { | ||
private static readonly _apps = new WeakMap<Application, EventsExtra>(); | ||
|
||
/** | ||
* Get events extra for the given application. | ||
* | ||
* @param application - The application to bind. | ||
* @returns the events extra instance. | ||
*/ | ||
public static for( application: Application ){ | ||
const e = this._apps.get( application ) ?? new EventsExtra( application ); | ||
this._apps.set( application, e ); | ||
return e; | ||
} | ||
|
||
private constructor( private readonly application: Application ){} | ||
|
||
/** | ||
* Execute a function after the option {@link name} has been set. | ||
* | ||
* @param name - The option name to watch. | ||
* @param cb - The function to execute. | ||
* @returns this. | ||
*/ | ||
public onSetOption( name: string, cb: ( value: unknown ) => void ){ | ||
// eslint-disable-next-line @typescript-eslint/dot-notation -- Private property | ||
this._hookInstanceAfter( this.application.options['_setOptions'] as Set<string>, 'add', ( set, v ) => { | ||
if( v === name ){ | ||
cb( this.application.options.getValue( name ) ); | ||
} | ||
return set; | ||
} ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Execute a function just after theme have been set. | ||
* | ||
* @param cb - The function to execute. | ||
* @returns this. | ||
*/ | ||
public onThemeReady( cb: () => void ){ | ||
this._hookInstanceAfter( this.application.renderer, 'prepareTheme' as any, ( success: boolean ) => { | ||
if( success ){ | ||
cb(); | ||
} | ||
return success; | ||
} ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Execute a function just before options freezing. | ||
* | ||
* @param cb - The function to execute. | ||
* @returns this. | ||
*/ | ||
public beforeOptionsFreeze( cb: () => void ){ | ||
this._hookInstanceBefore( this.application.options, 'freeze', ( ...args ) => { | ||
cb(); | ||
return args; | ||
} ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Replace the method {@link key} of {@link instance} with a method calling the original method, then the custom {@link hook}. | ||
* The original method return value is passed as the 1st parameter of the hook. | ||
* | ||
* @param instance - The instance to bind. | ||
* @param key - The method name. | ||
* @param hook - The function to execute after the original one. | ||
*/ | ||
private _hookInstanceAfter< | ||
T extends {}, // eslint-disable-line @typescript-eslint/ban-types -- Inspired from jest `spyOn` types. | ||
K extends MethodKeys<T>, | ||
>( instance: T, key: K, hook: ( initialRet: Ret<T[K]>, ...args: Params<T[K]> ) => Ret<T[K]> ){ | ||
const bck = ( instance[key] as T[K] & Fn ).bind( instance ) as T[K] & Fn; | ||
assert( bck ); | ||
( instance[key] as any ) = ( ...args: Params<T[K]> ) => { | ||
const ret = bck( ...args ); | ||
return hook( ret, ...args ); | ||
}; | ||
} | ||
|
||
/** | ||
* Replace the method {@link key} of {@link instance} with a method calling the the custom {@link hook}, then the original method. | ||
* The hook should return arguments to pass to the original method. | ||
* | ||
* @param instance - The instance to bind. | ||
* @param key - The method name. | ||
* @param hook - The function to execute before the original one. | ||
*/ | ||
private _hookInstanceBefore< | ||
T extends {}, // eslint-disable-line @typescript-eslint/ban-types -- Inspired from jest `spyOn` types. | ||
K extends MethodKeys<T>, | ||
>( instance: T, key: K, hook: ( ...args: Params<T[K]> ) => Params<T[K]> ){ | ||
const bck = ( instance[key] as T[K] & Fn ).bind( instance ) as T[K] & Fn; | ||
assert( bck ); | ||
( instance[key] as any ) = ( ...args: Params<T[K]> ) => { | ||
const newArgs = hook( ...args ); | ||
return bck( ...newArgs ); | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.