From 1a3db8800a215582ff1e32dc9afed13a75185f8f Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 19 May 2022 23:31:26 -0400 Subject: [PATCH 1/2] optimize stat calls in resolvers --- dist-raw/node-internal-modules-esm-resolve.js | 13 +++++++-- dist-raw/node-internalBinding-fs.js | 17 ++++++++++- src/bin.ts | 3 +- src/child/spawn-child.ts | 2 +- src/esm.ts | 4 +-- src/file-extensions.ts | 3 +- src/index.ts | 28 +------------------ src/util.ts | 27 ++++++++++++++++++ 8 files changed, 61 insertions(+), 36 deletions(-) diff --git a/dist-raw/node-internal-modules-esm-resolve.js b/dist-raw/node-internal-modules-esm-resolve.js index 24df6164c..b4a3a2921 100644 --- a/dist-raw/node-internal-modules-esm-resolve.js +++ b/dist-raw/node-internal-modules-esm-resolve.js @@ -149,11 +149,20 @@ function getConditionsSet(conditions) { const realpathCache = new SafeMap(); const packageJSONCache = new SafeMap(); /* string -> PackageConfig */ -function tryStatSync(path) { +const statSupportsThrowIfNoEntry = versionGteLt(process.versions.node, '15.3.0') || + versionGteLt(process.versions.node, '14.17.0', '15.0.0'); +const tryStatSync = statSupportsThrowIfNoEntry ? tryStatSyncWithoutErrors : tryStatSyncWithErrors; +const statsIfNotFound = new Stats(); +function tryStatSyncWithoutErrors(path) { + const stats = statSync(path, { throwIfNoEntry: false }); + if(stats != null) return stats; + return statsIfNotFound; +} +function tryStatSyncWithErrors(path) { try { return statSync(path); } catch { - return new Stats(); + return statsIfNotFound; } } diff --git a/dist-raw/node-internalBinding-fs.js b/dist-raw/node-internalBinding-fs.js index 20a42c36c..e85bc8258 100644 --- a/dist-raw/node-internalBinding-fs.js +++ b/dist-raw/node-internalBinding-fs.js @@ -1,4 +1,5 @@ const fs = require('fs'); +const {versionGteLt} = require('../dist/util'); // In node's core, this is implemented in C // https://github.com/nodejs/node/blob/v15.3.0/src/node_file.cc#L891-L985 @@ -28,6 +29,17 @@ function internalModuleReadJSON(path) { * @returns {number} 0 = file, 1 = dir, negative = error */ function internalModuleStat(path) { + const stat = fs.statSync(path, { throwIfNoEntry: false }); + if(!stat) return -1; + if(stat.isFile()) return 0; + if(stat.isDirectory()) return 1; +} + +/** + * @param {string} path + * @returns {number} 0 = file, 1 = dir, negative = error + */ +function internalModuleStatInefficient(path) { try { const stat = fs.statSync(path); if(stat.isFile()) return 0; @@ -37,7 +49,10 @@ function internalModuleStat(path) { } } +const statSupportsThrowIfNoEntry = versionGteLt(process.versions.node, '15.3.0') || + versionGteLt(process.versions.node, '14.17.0', '15.0.0'); + module.exports = { internalModuleReadJSON, - internalModuleStat + internalModuleStat: statSupportsThrowIfNoEntry ? internalModuleStat : internalModuleStatInefficient }; diff --git a/src/bin.ts b/src/bin.ts index 3256649e6..693045707 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -4,7 +4,7 @@ import { join, resolve, dirname, parse as parsePath, relative } from 'path'; import { inspect } from 'util'; import Module = require('module'); let arg: typeof import('arg'); -import { parse, createRequire, hasOwnProperty } from './util'; +import { parse, createRequire, hasOwnProperty, versionGteLt } from './util'; import { EVAL_FILENAME, EvalState, @@ -21,7 +21,6 @@ import { VERSION, TSError, register, - versionGteLt, createEsmHooks, createFromPreloadedConfig, DEFAULTS, diff --git a/src/child/spawn-child.ts b/src/child/spawn-child.ts index 74bf4017c..12368fcef 100644 --- a/src/child/spawn-child.ts +++ b/src/child/spawn-child.ts @@ -2,7 +2,7 @@ import type { BootstrapState } from '../bin'; import { spawn } from 'child_process'; import { brotliCompressSync } from 'zlib'; import { pathToFileURL } from 'url'; -import { versionGteLt } from '..'; +import { versionGteLt } from '../util'; const argPrefix = '--brotli-base64-config='; diff --git a/src/esm.ts b/src/esm.ts index 102d32d9b..cb1280451 100644 --- a/src/esm.ts +++ b/src/esm.ts @@ -1,4 +1,4 @@ -import { register, RegisterOptions, Service, versionGteLt } from './index'; +import { register, RegisterOptions, Service } from './index'; import { parse as parseUrl, format as formatUrl, @@ -8,7 +8,7 @@ import { } from 'url'; import { extname } from 'path'; import * as assert from 'assert'; -import { normalizeSlashes } from './util'; +import { normalizeSlashes, versionGteLt } from './util'; import { createRequire } from 'module'; // Note: On Windows, URLs look like this: file:///D:/dev/@TypeStrong/ts-node-examples/foo.ts diff --git a/src/file-extensions.ts b/src/file-extensions.ts index 4f73413fb..3ae1097e6 100644 --- a/src/file-extensions.ts +++ b/src/file-extensions.ts @@ -1,5 +1,6 @@ import type * as _ts from 'typescript'; -import { RegisterOptions, versionGteLt } from '.'; +import type { RegisterOptions } from '.'; +import { versionGteLt } from './util'; /** * Centralized specification of how we deal with file extensions based on diff --git a/src/index.ts b/src/index.ts index a0079bd24..dafc9f85d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ import { parse, ProjectLocalResolveHelper, split, + versionGteLt, yn, } from './util'; import { findAndReadConfig, loadCompiler } from './configuration'; @@ -66,33 +67,6 @@ export type { const engineSupportsPackageTypeField = parseInt(process.versions.node.split('.')[0], 10) >= 12; -/** @internal */ -export function versionGteLt( - version: string, - gteRequirement: string, - ltRequirement?: string -) { - const [major, minor, patch, extra] = parse(version); - const [gteMajor, gteMinor, gtePatch] = parse(gteRequirement); - const isGte = - major > gteMajor || - (major === gteMajor && - (minor > gteMinor || (minor === gteMinor && patch >= gtePatch))); - let isLt = true; - if (ltRequirement) { - const [ltMajor, ltMinor, ltPatch] = parse(ltRequirement); - isLt = - major < ltMajor || - (major === ltMajor && - (minor < ltMinor || (minor === ltMinor && patch < ltPatch))); - } - return isGte && isLt; - - function parse(requirement: string) { - return requirement.split(/[\.-]/).map((s) => parseInt(s, 10)); - } -} - /** * Assert that script can be loaded as CommonJS when we attempt to require it. * If it should be loaded as ESM, throw ERR_REQUIRE_ESM like node does. diff --git a/src/util.ts b/src/util.ts index b8454ca26..ec7f3485c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -169,3 +169,30 @@ export function once any>(fn: Fn) { } return onceFn; } + +/** @internal */ +export function versionGteLt( + version: string, + gteRequirement: string, + ltRequirement?: string +) { + const [major, minor, patch, extra] = parse(version); + const [gteMajor, gteMinor, gtePatch] = parse(gteRequirement); + const isGte = + major > gteMajor || + (major === gteMajor && + (minor > gteMinor || (minor === gteMinor && patch >= gtePatch))); + let isLt = true; + if (ltRequirement) { + const [ltMajor, ltMinor, ltPatch] = parse(ltRequirement); + isLt = + major < ltMajor || + (major === ltMajor && + (minor < ltMinor || (minor === ltMinor && patch < ltPatch))); + } + return isGte && isLt; + + function parse(requirement: string) { + return requirement.split(/[\.-]/).map((s) => parseInt(s, 10)); + } +} From 57c7d17997bce5d9371a47928bfb11191472a7f6 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 19 May 2022 23:42:30 -0400 Subject: [PATCH 2/2] fix --- dist-raw/node-internal-modules-esm-resolve.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/dist-raw/node-internal-modules-esm-resolve.js b/dist-raw/node-internal-modules-esm-resolve.js index b4a3a2921..e863d9b8d 100644 --- a/dist-raw/node-internal-modules-esm-resolve.js +++ b/dist-raw/node-internal-modules-esm-resolve.js @@ -2,21 +2,12 @@ 'use strict'; -const [nodeMajor, nodeMinor, nodePatch] = process.versions.node.split('.').map(s => parseInt(s, 10)) +const {versionGteLt} = require('../dist/util'); + // Test for node >14.13.1 || (>=12.20.0 && <13) -const builtinModuleProtocol = nodeMajor > 14 || ( - nodeMajor === 14 && ( - nodeMinor > 13 || ( - nodeMinor === 13 && nodePatch > 0 - ) - ) - ) || ( - nodeMajor === 12 && ( - nodeMinor > 20 || ( - nodeMinor === 20 - ) - ) - ) +const builtinModuleProtocol = + versionGteLt(process.versions.node, '14.13.1') || + versionGteLt(process.versions.node, '12.20.0', '13.0.0') ? 'node:' : 'nodejs:';