Skip to content

Commit

Permalink
feat: Added support for chaining transformers in single factory (closes
Browse files Browse the repository at this point in the history
#122 closes #120)
  • Loading branch information
nonara committed Dec 5, 2023
1 parent 15570d0 commit aabf389
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 32 deletions.
6 changes: 3 additions & 3 deletions projects/core/shared/plugin-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ export type PluginFactory =
LSPattern | ProgramPattern | ConfigPattern | CompilerOptionsPattern | TypeCheckerPattern | RawPattern;

export interface TransformerBasePlugin {
before?: ts.TransformerFactory<ts.SourceFile>;
after?: ts.TransformerFactory<ts.SourceFile>;
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle>;
before?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
after?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle> | ts.TransformerFactory<ts.SourceFile | ts.Bundle>[];
}

// endregion
Expand Down
75 changes: 49 additions & 26 deletions projects/patch/src/plugin/plugin-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace tsp {
/* ********************************************************* */

/** @internal */
interface CreateTransformerFromPatternOptions {
interface CreateTransformersFromPatternOptions {
factory: PluginFactory;
config: PluginConfig;
registerConfig: RegisterConfig;
Expand All @@ -26,13 +26,18 @@ namespace tsp {
// region: Helpers
/* ********************************************************* */

function createTransformerFromPattern(opt: CreateTransformerFromPatternOptions): TransformerBasePlugin {
function createTransformersFromPattern(opt: CreateTransformersFromPatternOptions): TransformerBasePlugin {
const { factory, config, program, ls, registerConfig } = opt;
const { transform, after, afterDeclarations, name, type, transformProgram, ...cleanConfig } = config;

if (!transform) throw new TsPatchError('Not a valid config entry: "transform" key not found');

const transformerKind = after ? 'after' : afterDeclarations ? 'afterDeclarations' : 'before';
// @formatter:off
const transformerKind =
after ? 'after' :
afterDeclarations ? 'afterDeclarations' :
'before';
// @formatter:on

let pluginFactoryResult: TransformerPlugin;
switch (config.type) {
Expand Down Expand Up @@ -74,38 +79,57 @@ namespace tsp {
throw new TsPatchError(`Invalid plugin type found in tsconfig.json: '${config.type}'`);
}

/* Handle result */
let transformerFactory: TsTransformerFactory | undefined;
/* Extract factories */
let transformerFactories: TsTransformerFactory[];
// noinspection FallThroughInSwitchStatementJS
switch (typeof pluginFactoryResult) {
// Single transformer factory
case 'function':
transformerFactory = pluginFactoryResult;
transformerFactories = [ pluginFactoryResult ];
break;
// TransformerBasePlugin
case 'object':
transformerFactory = pluginFactoryResult[transformerKind];
break;
const factoryOrFactories = pluginFactoryResult[transformerKind];

// Single transformer factory
if (typeof factoryOrFactories === 'function') {
transformerFactories = [ pluginFactoryResult[transformerKind] ];
}
// Array of transformer factories
else if (Array.isArray(factoryOrFactories)) {
transformerFactories = [ ...factoryOrFactories ];
}
// Deliberate fall-through
default:
throw new TsPatchError(`Invalid plugin result: expected a function or an object with a '${transformerKind}' property`);
}

if (!transformerFactory || typeof transformerFactory !== 'function')
throw new TsPatchError(
`Invalid plugin entry point! Expected a transformer factory function or an object with a '${transformerKind}' property`
);
/* Wrap factories */
const wrappedFactories: TsTransformerFactory[] = [];
for (const transformerFactory of transformerFactories) {
if (!transformerFactory || typeof transformerFactory !== 'function')
throw new TsPatchError(
`Invalid plugin entry point! Expected a transformer factory function or an object with a '${transformerKind}' property`
);

/* Wrap w/ register */
const wrapper = wrapTransformer(transformerFactory, registerConfig, true);
/* Wrap w/ register */
const wrapper = wrapTransformerFactory(transformerFactory, registerConfig, true);
wrappedFactories.push(wrapper);
}

const res: TransformerBasePlugin = {
[transformerKind]: wrapper
[transformerKind]: wrappedFactories
};

return res;
}

function wrapTransformer<T extends PluginFactory | ProgramTransformer>(
transformerFn: T,
function wrapTransformerFactory(
transformerFn: TsTransformerFactory,
requireConfig: RegisterConfig,
wrapInnerFunction: boolean
): T {
const wrapper = function tspWrappedFactory(...args: any[]) {
): TsTransformerFactory {
const wrapper: TsTransformerFactory = function tspWrappedFactory(...args: any[]) {
let res: any;
try {
registerPlugin(requireConfig);
Expand All @@ -114,14 +138,15 @@ namespace tsp {
} else {
const resFn = (transformerFn as Function)(...args);
if (typeof resFn !== 'function') throw new TsPatchError('Invalid plugin: expected a function');
res = wrapTransformer(resFn, requireConfig, false);
res = wrapTransformerFactory(resFn, requireConfig, false);
}
} finally {
}
finally {
unregisterPlugin();
}

return res;
} as T;
}

return wrapper;
}
Expand Down Expand Up @@ -155,8 +180,6 @@ namespace tsp {
private mergeTransformers(into: TransformerList, source: tsShim.CustomTransformers | TransformerBasePlugin) {
const slice = <T>(input: T | T[]) => (Array.isArray(input) ? input.slice() : [ input ]);

// TODO : Consider making this optional https://github.com/nonara/ts-patch/issues/122

if (source.before) into.before.push(...slice(source.before));
if (source.after) into.after.push(...slice(source.after));
if (source.afterDeclarations) into.afterDeclarations.push(...slice(source.afterDeclarations));
Expand Down Expand Up @@ -184,7 +207,7 @@ namespace tsp {

this.mergeTransformers(
transformers,
createTransformerFromPattern({
createTransformersFromPattern({
factory: factory as PluginFactory,
registerConfig,
config,
Expand All @@ -211,7 +234,7 @@ namespace tsp {
if (createFactoryResult === undefined) continue;

const { registerConfig, factory: unwrappedFactory } = createFactoryResult;
const factory = wrapTransformer(unwrappedFactory as ProgramTransformer, registerConfig, false);
const factory = wrapTransformerFactory(unwrappedFactory as ProgramTransformer, registerConfig, false);

const transformerKey = crypto
.createHash('md5')
Expand Down
6 changes: 3 additions & 3 deletions projects/patch/src/types/plugin-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ declare namespace tsp {
export type TsTransformerFactory = ts.TransformerFactory<ts.SourceFile>;
export type PluginFactory = LSPattern | ProgramPattern | ConfigPattern | CompilerOptionsPattern | TypeCheckerPattern | RawPattern;
export interface TransformerBasePlugin {
before?: ts.TransformerFactory<ts.SourceFile>;
after?: ts.TransformerFactory<ts.SourceFile>;
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle>;
before?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
after?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle> | ts.TransformerFactory<ts.SourceFile | ts.Bundle>[];
}
export type DiagnosticMap = WeakMap<ts.Program, ts.Diagnostic[]>;
export type TransformerExtras = {
Expand Down

0 comments on commit aabf389

Please sign in to comment.