diff --git a/src/cjs/index.ts b/src/cjs/index.ts index ca3e07c3f..00ee26fc6 100644 --- a/src/cjs/index.ts +++ b/src/cjs/index.ts @@ -8,7 +8,7 @@ import { createFilesMatcher, } from 'get-tsconfig'; import type { TransformOptions } from 'esbuild'; -import { installSourceMapSupport } from '../source-map'; +import { installSourceMapSupport, shouldStripSourceMap, stripSourceMap } from '../source-map'; import { transformSync, transformDynamicImport } from '../utils/transform'; import { resolveTsPath } from '../utils/resolve-ts-path'; import { isESM } from '../utils/esm-pattern'; @@ -67,6 +67,12 @@ const transformer = ( } let code = fs.readFileSync(filePath, 'utf8'); + + // Strip source maps if originally disabled + if (shouldStripSourceMap) { + code = stripSourceMap(code); + } + if (filePath.endsWith('.cjs')) { // Contains native ESM check const transformed = transformDynamicImport(filePath, code); diff --git a/src/esm/loaders.ts b/src/esm/loaders.ts index d32239ef5..deb43c7d5 100644 --- a/src/esm/loaders.ts +++ b/src/esm/loaders.ts @@ -7,8 +7,8 @@ import type { import type { TransformOptions } from 'esbuild'; import { transform, transformDynamicImport } from '../utils/transform'; import { resolveTsPath } from '../utils/resolve-ts-path'; +import { installSourceMapSupport, shouldStripSourceMap, stripSourceMap } from '../source-map'; import { - applySourceMap, tsconfigPathsMatcher, fileMatcher, tsExtensionsPattern, @@ -19,6 +19,8 @@ import { type NodeError, } from './utils.js'; +const applySourceMap = installSourceMapSupport(); + const isDirectoryPattern = /\/(?:$|\?)/; type NextResolve = ( @@ -274,7 +276,12 @@ export const load: LoadHook = async function ( } const filePath = url.startsWith('file://') ? fileURLToPath(url) : url; - const code = loaded.source.toString(); + let code = loaded.source.toString(); + + // Strip source maps if originally disabled + if (shouldStripSourceMap) { + code = stripSourceMap(code); + } if ( // Support named imports in JSON modules diff --git a/src/esm/utils.ts b/src/esm/utils.ts index 002b20103..cfecb1874 100644 --- a/src/esm/utils.ts +++ b/src/esm/utils.ts @@ -6,11 +6,8 @@ import { createPathsMatcher, createFilesMatcher, } from 'get-tsconfig'; -import { installSourceMapSupport } from '../source-map'; import { getPackageType } from './package-json.js'; -export const applySourceMap = installSourceMapSupport(); - const tsconfig = ( process.env.TSX_TSCONFIG_PATH ? { diff --git a/src/source-map.ts b/src/source-map.ts index 148e56028..9e026b5a7 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -10,15 +10,31 @@ type PortMessage = { map: RawSourceMap; }; -const inlineSourceMapPrefix = '\n//# sourceMappingURL=data:application/json;base64,'; +// If Node.js has source map disabled, we should strip source maps to speed up processing +export const shouldStripSourceMap = ( + ('sourceMapsEnabled' in process) + && process.sourceMapsEnabled === false +); -export function installSourceMapSupport( +const sourceMapPrefix = '\n//# sourceMappingURL='; + +export const stripSourceMap = (code: string) => { + const sourceMapIndex = code.indexOf(sourceMapPrefix); + if (sourceMapIndex !== -1) { + return code.slice(0, sourceMapIndex); + } + return code; +}; + +const inlineSourceMapPrefix = `${sourceMapPrefix}data:application/json;base64,`; + +export const installSourceMapSupport = ( /** * To support Node v20 where loaders are executed in its own thread * https://nodejs.org/docs/latest-v20.x/api/esm.html#globalpreload */ loaderPort?: MessagePort, -) { +) => { const hasNativeSourceMapSupport = ( /** * Check if native source maps are supported by seeing if the API is available @@ -77,4 +93,4 @@ export function installSourceMapSupport( } return code; }; -} +}; diff --git a/src/utils/debug.ts b/src/utils/debug.ts index 4fb70d6a4..7525106b6 100644 --- a/src/utils/debug.ts +++ b/src/utils/debug.ts @@ -1,36 +1,37 @@ -export const time = ( +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const time = unknown>( name: string, - _function: (...args: Argument[]) => unknown, -) => function ( - this: unknown, - ...args: Argument[] - ) { - const timeStart = Date.now(); - const logTimeElapsed = () => { - const elapsed = Date.now() - timeStart; + _function: T, + threshold = 100, +): T => function ( + this: unknown, + ...args: Parameters +) { + const timeStart = Date.now(); + const logTimeElapsed = () => { + const elapsed = Date.now() - timeStart; - if (elapsed > 10) { - // console.log({ - // name, - // args, - // elapsed, - // }); - } - }; + if (elapsed > threshold) { + console.log(name, { + args, + elapsed, + }); + } + }; - const result = Reflect.apply(_function, this, args); - if ( - result + const result = Reflect.apply(_function, this, args); + if ( + result && typeof result === 'object' && 'then' in result - ) { - (result as Promise).then( - logTimeElapsed, - // Ignore error in this chain - () => {}, - ); - } else { - logTimeElapsed(); - } - return result; - }; + ) { + (result as Promise).then( + logTimeElapsed, + // Ignore error in this chain + () => {}, + ); + } else { + logTimeElapsed(); + } + return result; +} as T; diff --git a/src/utils/esm-pattern.ts b/src/utils/esm-pattern.ts index 03541e1a1..9b7f0782b 100644 --- a/src/utils/esm-pattern.ts +++ b/src/utils/esm-pattern.ts @@ -1,6 +1,10 @@ import { parseEsm } from './es-module-lexer'; + /* -TODO: Add tests +Previously, this regex was used as a naive ESM catch, +but turns out regex is slower than the lexer so removing +it made the check faster. + Catches: import a from 'b' import 'b'; @@ -12,11 +16,12 @@ Doesn't catch: EXPORT{a} exports.a = 1 module.exports = 1 - */ + const esmPattern = /\b(?:import|export)\b/; +*/ export const isESM = (code: string) => { - if (esmPattern.test(code)) { + if (code.includes('import') || code.includes('export')) { const [imports, exports] = parseEsm(code); return imports.length > 0 || exports.length > 0; }