Skip to content

Instantly share code, notes, and snippets.

@marcj
Created October 1, 2025 17:41
Show Gist options
  • Select an option

  • Save marcj/a3f7c207dfd923d18395a98255e2d04c to your computer and use it in GitHub Desktop.

Select an option

Save marcj/a3f7c207dfd923d18395a98255e2d04c to your computer and use it in GitHub Desktop.
import ts, { CustomTransformerFactory } from 'typescript';
import { ReflectionTransformer, transformer } from '@deepkit/type-compiler';
import type { Plugin } from 'esbuild';
import { readFileSync } from 'fs';
import { join } from 'path';
let resolver: ReturnType<ReflectionTransformer['getConfigResolver']> | undefined = undefined;
function getConfigResolver(configFilePath: string): ReturnType<ReflectionTransformer['getConfigResolver']> {
if (resolver) return resolver;
let reflectionTransformer: ReflectionTransformer | undefined = undefined;
const catchTransformers = {
before: [(context) => reflectionTransformer = transformer(context) as ReflectionTransformer] as CustomTransformerFactory[]
};
ts.transpileModule('', {
compilerOptions: Object.assign({
configFilePath,
}),
fileName: 'test.ts',
transformers: catchTransformers
});
if (!reflectionTransformer) {
throw new Error('Reflection transformer not initialized');
}
return resolver = (reflectionTransformer as ReflectionTransformer as any).getConfigResolver({ fileName: 'test.ts' });
}
function loadCompilerOptions(configFilePath: string): ts.CompilerOptions {
const configFile = ts.readConfigFile(configFilePath, ts.sys.readFile);
if (configFile.error) throw new Error(ts.formatDiagnosticsWithColorAndContext([configFile.error], {
getCanonicalFileName: f => f,
getCurrentDirectory: ts.sys.getCurrentDirectory,
getNewLine: () => ts.sys.newLine
}));
const parsed = ts.parseJsonConfigFileContent(
configFile.config,
ts.sys,
'./'
);
return parsed.options;
}
export function deepkitPlugin(configFilePath: string): Plugin {
return {
name: 'deepkit-loader',
setup(build) {
const resolver = getConfigResolver(configFilePath);
const compilerOptions = {
...loadCompilerOptions(configFilePath),
skipDefaultLibCheck: true,
skipLibCheck: true,
sourceMap: true,
inlineSources: true,
};
const node_modules = join(process.cwd(), 'node_modules');
build.onLoad({ filter: /.*/, }, async (args) => {
// we skip node_modules
if (args.path.startsWith(node_modules)) return undefined;
const match = resolver.match(args.path);
if (match.mode === 'never') return undefined;
const source = readFileSync(args.path, 'utf8');
const output = ts.transpileModule(source, {
compilerOptions,
fileName: args.path,
transformers: {
before: [transformer],
}
});
return {
contents: output.outputText,
loader: 'js',
};
});
},
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment