import { createRequire } from 'module' ; import { basename , dirname , relative , resolve as resolvePath } from 'path' ; import ts , { CompilerOptions } from 'typescript' ; import { fileURLToPath , pathToFileURL } from 'url' ; import { findFiles } from './findFiles' ; import { DynamicInstantiateResponse , GetFormatHook , GetFormatResponse , GetSourceContext , GetSourceHook , GetSourceResponse , ModuleFormat , ResolveContext , ResolveHook , ResolveResponse , Source , TransformContext , TransformResponse , TransformSourceHook , } from './types' ; import { getTSConfig } from './Utils' ; const rootModulePath = process . cwd ( ) ; const baseURL = pathToFileURL ( rootModulePath ) . href ; const relativePathRegex = /^\.{1,2}[/]?/ ; const hasExtensionRegex = /\.\w+$/ ; const extensions = [ '.ts' , '.tsx' ] ; const extensionsRegex = new RegExp ( `\\${ extensions . join ( '|\\' ) }` ) ; let TSConfig ; export async function resolve ( specifier , context , defaultResolve ) { const { parentURL = baseURL } = context ; if ( TSConfig ?. paths ) { for ( const tsPath of Object . keys ( TSConfig . paths ) ) { const tsPathKey = tsPath . replace ( '*' , '' ) ; if ( specifier . startsWith ( tsPathKey ) ) { const tsResolvedPath = resolvePath ( resolvePath ( baseURL , TSConfig . baseUrl ! ) , TSConfig . paths [ tsPath ] [ 0 ] . replace ( '*' , specifier . split ( tsPathKey ) [ 1 ] ) ) ; specifier = `./${ relative ( dirname ( fileURLToPath ( parentURL ) ) , tsResolvedPath ) }` ; break ; } } } const resolvedUrl = new URL ( specifier , parentURL ) ; const fileName = basename ( resolvedUrl . pathname ) ; if ( extensionsRegex . test ( fileName ) ) { return { url : resolvedUrl . href , } ; } /**
     * If no extension is passed and is a relative import then let's try to find a `.ts` or `.tsx` file at the path
     */ if ( relativePathRegex . test ( specifier ) && ! hasExtensionRegex . test ( fileName ) ) { const filePath = fileURLToPath ( resolvedUrl ) ; const file = await findFiles ( dirname ( filePath ) , { fileName , extensions , } ) ; return { url : file . href , } ; } return defaultResolve ( specifier , { ... context , parentURL } , defaultResolve ) ; } param url export async function dynamicInstantiate ( url ) { const urlParts = url . split ( '/node_modules/' ) ; const moduleName = urlParts . pop ( ) ! ; const nodeModulesPath = urlParts . join ( '/node_modules/' ) ; const require = createRequire ( `${ nodeModulesPath }/noop.js` ) ; let dynModule = require ( moduleName ) ; if ( dynModule . default && dynModule !== dynModule . default ) { dynModule = { ... dynModule . default , ... dynModule , } ; } const linkKeys = Object . keys ( dynModule ) ; const exports = dynModule . default ? linkKeys : [ ... linkKeys , 'default' ] ; return { exports , execute : ( module ) : void => { module . default . set ( dynModule ) ; for ( const linkKey of linkKeys ) { module [ linkKey ] . set ( dynModule [ linkKey ] ) ; } } , } ; } const formatCache = new Map < string , ModuleFormat > ( ) ; const CommonJSMap = new Set < string > ( ) ; export async function getFormat ( url , context , defaultGetFormat ) { let format = formatCache . get ( url ) ; if ( format ) return { format } ; const resolvedUrl = new URL ( url ) ; const fileName = basename ( resolvedUrl . pathname ) ; if ( extensionsRegex . test ( fileName ) ) format = 'module' ; if ( ! format ) { const defaultResolve = await defaultGetFormat ( url , context , defaultGetFormat ) ; format = defaultResolve . format ; } formatCache . set ( url , format ) ; if ( format === 'commonjs' ) { format = 'module' ; CommonJSMap . add ( url ) ; } return { format , } ; } export async function getSource ( url , context , defaultGetSource ) { if ( CommonJSMap . has ( url ) ) { const urlParts = url . split ( '/node_modules/' ) ; const moduleName = urlParts . pop ( ) ! ; const nodeModulesPath = urlParts . join ( '/node_modules/' ) ; const require = createRequire ( `${ nodeModulesPath }/noop.js` ) ; const dynModule = require ( moduleName ) ; let isDefault = false ; let defaultKeys = [ ] ; const moduleKeys = Object . keys ( dynModule ) ; if ( dynModule . default ) { if ( dynModule . default !== dynModule ) { isDefault = true ; defaultKeys = Object . keys ( dynModule . default ) . filter ( ( defaultKey ) => ! moduleKeys . includes ( defaultKey ) ) ; } } const linkKeys = Object . keys ( dynModule ) . filter ( ( key ) => key !== 'default' ) ; const code = `
import {createRequire} from 'module';

const require = createRequire('${ nodeModulesPath }/noop.js');
const cjs = require('${ moduleName }');

${ linkKeys . map ( ( prop ) => `let $${ prop } = cjs[${ JSON . stringify ( prop ) }];` ) . join ( ';\n' ) }

${ defaultKeys . map ( ( prop ) => `let $default${ prop } = cjs.default[${ JSON . stringify ( prop ) }];` ) . join ( ';\n' ) }

export {
${ linkKeys . map ( ( prop ) => ` $${ prop } as ${ prop },` ) . join ( '\n' ) }
}

export {
${ defaultKeys . map ( ( prop ) => ` $default${ prop } as ${ prop },` ) . join ( '\n' ) }
}

${ isDefault ? `export default cjs['default'];` : 'export default cjs;' }
` ; return { source : code , } ; } return defaultGetSource ( url , context , defaultGetSource ) ; } export async function transformSource ( source , context , defaultTransformSource ) { const resolvedUrl = new URL ( context . url ) ; const fileName = basename ( resolvedUrl . pathname ) ; if ( extensionsRegex . test ( fileName ) ) { const sourceFilePath = fileURLToPath ( context . url ) ; const tsConfig = await getTSConfig ( dirname ( sourceFilePath ) ) ; TSConfig = tsConfig ; const transpiledModule = ts . transpileModule ( source . toString ( ) , { compilerOptions : TSConfig , reportDiagnostics : true , fileName : resolvedUrl . pathname , moduleName : fileName , } ) ; return { source : transpiledModule . outputText , } ; } return defaultTransformSource ( source , context , defaultTransformSource ) ; }