@@ -2,24 +2,26 @@ import fs from "node:fs";
22import Module , { type ResolveHookContext } from "node:module" ;
33import path from "node:path" ;
44import process from "node:process" ;
5+ import { ModuleTransformer , type TransformerHook } from "t-packer" ;
56
67// Get Node.js major version for compatibility handling
7- const major = Number ( process . versions . node . split ( "." ) [ 0 ] . slice ( 1 ) ) ;
8+ // `process.versions.node` is like "20.11.1"; take the first segment as a number
9+ const majorVersion = process . versions . node . split ( "." ) [ 0 ] ;
10+ const major = Number (
11+ majorVersion [ 0 ] === "v" ?majorVersion . slice ( 1 ) :majorVersion ,
12+ ) ;
813
9- /**
10- * Interface for transformer hooks that can transform specific file types
11- * Each transformer defines which file extensions it can handle and provides
12- * a transformation function to convert the source code
13- */
14- export interface TransformerHook {
15- /** File extensions this transformer can handle (e.g., ['.ts', '.tsx']) */
16- exts :string [ ] ;
17- /** The transformation function that converts source code */
18- hook :TransformProgram ;
14+ interface NodeModuleLike {
15+ _compile :( code :string , filename :string ) => unknown ;
1916}
2017
21- /** Type definition for the transformation function */
22- export type TransformProgram = ( code :string , src :string ) => string ;
18+ interface ModuleWithInternals {
19+ _resolveFilename :( request :string , parent :unknown ) => string ;
20+ _extensions :Record <
21+ string ,
22+ ( mod :NodeModuleLike , filename :string ) => unknown
23+ > ;
24+ }
2325
2426/**
2527 * Module resolver class that handles module path resolution and caching
@@ -33,16 +35,17 @@ export type TransformProgram = (code: string, src: string) => string;
3335 * The resolver integrates with Node.js module system by overriding
3436 * the module resolution and loading mechanisms.
3537 */
36- export class ModuleResolver {
38+ export class ModuleResolver extends ModuleTransformer {
3739/** Cache for resolved module paths to improve performance */
3840private cache :Map < string , string > = new Map ( ) ;
3941/** Map of module aliases to their target paths */
4042private aliases :Map < string , string [ ] > = new Map ( ) ;
41- /** List of registered transformers for different file types */
42- private transformers :TransformerHook [ ] = [ ] ;
43+
4344/** Store original loaders for cleanup */
44- private oldLoaders :Map < string , ( mod :any , filename :string ) => any > =
45- new Map ( ) ;
45+ private oldLoaders :Map <
46+ string ,
47+ ( mod :NodeModuleLike , filename :string ) => unknown
48+ > = new Map ( ) ;
4649/** Registration cleanup function */
4750private revertRegister ?:{
4851deregister :( ) => void ;
@@ -53,6 +56,7 @@ export class ModuleResolver {
5356 *@param transformers - Initial list of transformers to register
5457 */
5558constructor ( transformers :TransformerHook [ ] = [ ] ) {
59+ super ( ) ;
5660for ( const transformer of transformers ) {
5761this . addTransformer ( transformer ) ;
5862}
@@ -83,26 +87,6 @@ export class ModuleResolver {
8387}
8488}
8589
86- /**
87- * Register a transformer hook for specific file types
88- *
89- * Transformers are responsible for converting source code from one format
90- * to another (e.g., TypeScript to JavaScript, CSS to JS modules).
91- *
92- *@param transformer - The transformer hook to register
93- */
94- addTransformer ( transformer :TransformerHook ) {
95- this . transformers . push ( transformer ) ;
96- }
97-
98- /**
99- * Remove a previously registered transformer
100- *@param transformer - The transformer hook to remove
101- */
102- removeTransformer ( transformer :TransformerHook ) {
103- this . transformers = this . transformers . filter ( ( t ) => t !== transformer ) ;
104- }
105-
10690/**
10791 * Register hooks with Node.js module system
10892 *
@@ -128,27 +112,41 @@ export class ModuleResolver {
128112} ) ;
129113} else {
130114// Polyfill for Node.js <24
131- const origResolve = ( Module as any ) . _resolveFilename ;
132- ( Module as any ) . _resolveFilename = ( specifier , context ) => {
133- const { url} = this . resolve ( specifier , context ) ;
115+ const origResolve = ( Module as unknown as ModuleWithInternals )
116+ . _resolveFilename ;
117+ ( Module as unknown as ModuleWithInternals ) . _resolveFilename = (
118+ specifier ,
119+ context ,
120+ ) => {
121+ const { url} = this . resolve ( specifier , context as ResolveHookContext ) ;
134122return origResolve . apply ( this , [ url ?? specifier , context ] ) ;
135123} ;
136124
137125// Register custom loaders for supported file extensions
138- const extensions = this . transformers . flatMap ( ( t ) => t . exts ) ;
139- const originJSLoader = ( Module as any ) . _extensions [ ".js" ] ;
126+ const extensions = Array . from ( this . transformers . keys ( ) ) ;
127+ const originJSLoader = ( Module as unknown as ModuleWithInternals )
128+ . _extensions [ ".js" ] ;
140129extensions . forEach ( ( ext ) => {
141- const originLoader = ( Module as any ) . _extensions [ ext ] || originJSLoader ;
142- ( Module as any ) . _extensions [ ext ] = ( mod , filename ) =>
143- this . loader ( mod , filename , ext ) ;
130+ const originLoader =
131+ ( Module as unknown as ModuleWithInternals ) . _extensions [ ext ] ||
132+ originJSLoader ;
133+ ( Module as unknown as ModuleWithInternals ) . _extensions [ ext ] = (
134+ mod ,
135+ filename ,
136+ ) => this . loader ( mod , filename , ext ) ;
144137this . oldLoaders . set ( ext , originLoader ) ;
145138} ) ;
146139
147140this . revertRegister = {
148141deregister :( ) => {
149- ( Module as any ) . _resolveFilename = origResolve ;
142+ ( Module as unknown as ModuleWithInternals ) . _resolveFilename =
143+ origResolve ;
150144extensions . forEach ( ( ext ) => {
151- ( Module as any ) . _extensions [ ext ] = this . oldLoaders . get ( ext ) ;
145+ ( Module as unknown as ModuleWithInternals ) . _extensions [ ext ] =
146+ this . oldLoaders . get ( ext ) as (
147+ mod :NodeModuleLike ,
148+ filename :string ,
149+ ) => unknown ;
152150} ) ;
153151} ,
154152} ;
@@ -226,7 +224,7 @@ export class ModuleResolver {
226224 *@param filename - The filename being loaded
227225 *@param ext - The file extension
228226 */
229- private loader ( mod :any , filename :string , ext :string ) {
227+ private loader ( mod :NodeModuleLike , filename :string , ext :string ) {
230228const compile = mod . _compile ;
231229const oldLoader = this . oldLoaders . get ( ext ) ;
232230mod . _compile = ( code , filename ) => {
@@ -248,14 +246,12 @@ export class ModuleResolver {
248246 *@returns Transformed code, or empty string if no transformers match
249247 */
250248private transformCode ( code :string , filename :string ) {
251- const transformers = this . transformers . filter ( ( t ) =>
252- t . exts . includes ( path . extname ( filename ) ) ,
253- ) ;
254- if ( transformers . length === 0 ) {
249+ if ( ! this . transformers . has ( path . extname ( filename ) ) ) {
255250return "" ;
256251}
257- return transformers . reduce ( ( code , transformer ) => {
258- return transformer . hook ( code , filename ) ;
259- } , code ) ;
252+ return this . transformSync ( Buffer . from ( code ) , filename , {
253+ target :"es2022" ,
254+ module :"commonjs" ,
255+ } ) . code . toString ( ) ;
260256}
261257}