Skip to content

Commit c6010aa

Browse files
authored
optimize stat calls in resolvers (#1759)
* optimize stat calls in resolvers * fix
1 parent 5521f80 commit c6010aa

8 files changed

+66
-50
lines changed

dist-raw/node-internal-modules-esm-resolve.js

+16-16
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,12 @@
22

33
'use strict';
44

5-
const [nodeMajor, nodeMinor, nodePatch] = process.versions.node.split('.').map(s => parseInt(s, 10))
5+
const {versionGteLt} = require('../dist/util');
6+
67
// Test for node >14.13.1 || (>=12.20.0 && <13)
7-
const builtinModuleProtocol = nodeMajor > 14 || (
8-
nodeMajor === 14 && (
9-
nodeMinor > 13 || (
10-
nodeMinor === 13 && nodePatch > 0
11-
)
12-
)
13-
) || (
14-
nodeMajor === 12 && (
15-
nodeMinor > 20 || (
16-
nodeMinor === 20
17-
)
18-
)
19-
)
8+
const builtinModuleProtocol =
9+
versionGteLt(process.versions.node, '14.13.1') ||
10+
versionGteLt(process.versions.node, '12.20.0', '13.0.0')
2011
? 'node:'
2112
: 'nodejs:';
2213

@@ -149,11 +140,20 @@ function getConditionsSet(conditions) {
149140
const realpathCache = new SafeMap();
150141
const packageJSONCache = new SafeMap(); /* string -> PackageConfig */
151142

152-
function tryStatSync(path) {
143+
const statSupportsThrowIfNoEntry = versionGteLt(process.versions.node, '15.3.0') ||
144+
versionGteLt(process.versions.node, '14.17.0', '15.0.0');
145+
const tryStatSync = statSupportsThrowIfNoEntry ? tryStatSyncWithoutErrors : tryStatSyncWithErrors;
146+
const statsIfNotFound = new Stats();
147+
function tryStatSyncWithoutErrors(path) {
148+
const stats = statSync(path, { throwIfNoEntry: false });
149+
if(stats != null) return stats;
150+
return statsIfNotFound;
151+
}
152+
function tryStatSyncWithErrors(path) {
153153
try {
154154
return statSync(path);
155155
} catch {
156-
return new Stats();
156+
return statsIfNotFound;
157157
}
158158
}
159159

dist-raw/node-internalBinding-fs.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const fs = require('fs');
2+
const {versionGteLt} = require('../dist/util');
23

34
// In node's core, this is implemented in C
45
// https://github.com/nodejs/node/blob/v15.3.0/src/node_file.cc#L891-L985
@@ -28,6 +29,17 @@ function internalModuleReadJSON(path) {
2829
* @returns {number} 0 = file, 1 = dir, negative = error
2930
*/
3031
function internalModuleStat(path) {
32+
const stat = fs.statSync(path, { throwIfNoEntry: false });
33+
if(!stat) return -1;
34+
if(stat.isFile()) return 0;
35+
if(stat.isDirectory()) return 1;
36+
}
37+
38+
/**
39+
* @param {string} path
40+
* @returns {number} 0 = file, 1 = dir, negative = error
41+
*/
42+
function internalModuleStatInefficient(path) {
3143
try {
3244
const stat = fs.statSync(path);
3345
if(stat.isFile()) return 0;
@@ -37,7 +49,10 @@ function internalModuleStat(path) {
3749
}
3850
}
3951

52+
const statSupportsThrowIfNoEntry = versionGteLt(process.versions.node, '15.3.0') ||
53+
versionGteLt(process.versions.node, '14.17.0', '15.0.0');
54+
4055
module.exports = {
4156
internalModuleReadJSON,
42-
internalModuleStat
57+
internalModuleStat: statSupportsThrowIfNoEntry ? internalModuleStat : internalModuleStatInefficient
4358
};

src/bin.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { join, resolve, dirname, parse as parsePath, relative } from 'path';
44
import { inspect } from 'util';
55
import Module = require('module');
66
let arg: typeof import('arg');
7-
import { parse, createRequire, hasOwnProperty } from './util';
7+
import { parse, createRequire, hasOwnProperty, versionGteLt } from './util';
88
import {
99
EVAL_FILENAME,
1010
EvalState,
@@ -21,7 +21,6 @@ import {
2121
VERSION,
2222
TSError,
2323
register,
24-
versionGteLt,
2524
createEsmHooks,
2625
createFromPreloadedConfig,
2726
DEFAULTS,

src/child/spawn-child.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { BootstrapState } from '../bin';
22
import { spawn } from 'child_process';
33
import { brotliCompressSync } from 'zlib';
44
import { pathToFileURL } from 'url';
5-
import { versionGteLt } from '..';
5+
import { versionGteLt } from '../util';
66

77
const argPrefix = '--brotli-base64-config=';
88

src/esm.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { register, RegisterOptions, Service, versionGteLt } from './index';
1+
import { register, RegisterOptions, Service } from './index';
22
import {
33
parse as parseUrl,
44
format as formatUrl,
@@ -8,7 +8,7 @@ import {
88
} from 'url';
99
import { extname } from 'path';
1010
import * as assert from 'assert';
11-
import { normalizeSlashes } from './util';
11+
import { normalizeSlashes, versionGteLt } from './util';
1212
import { createRequire } from 'module';
1313

1414
// Note: On Windows, URLs look like this: file:///D:/dev/@TypeStrong/ts-node-examples/foo.ts

src/file-extensions.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type * as _ts from 'typescript';
2-
import { RegisterOptions, versionGteLt } from '.';
2+
import type { RegisterOptions } from '.';
3+
import { versionGteLt } from './util';
34

45
/**
56
* Centralized specification of how we deal with file extensions based on

src/index.ts

+1-27
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
parse,
1818
ProjectLocalResolveHelper,
1919
split,
20+
versionGteLt,
2021
yn,
2122
} from './util';
2223
import { findAndReadConfig, loadCompiler } from './configuration';
@@ -66,33 +67,6 @@ export type {
6667
const engineSupportsPackageTypeField =
6768
parseInt(process.versions.node.split('.')[0], 10) >= 12;
6869

69-
/** @internal */
70-
export function versionGteLt(
71-
version: string,
72-
gteRequirement: string,
73-
ltRequirement?: string
74-
) {
75-
const [major, minor, patch, extra] = parse(version);
76-
const [gteMajor, gteMinor, gtePatch] = parse(gteRequirement);
77-
const isGte =
78-
major > gteMajor ||
79-
(major === gteMajor &&
80-
(minor > gteMinor || (minor === gteMinor && patch >= gtePatch)));
81-
let isLt = true;
82-
if (ltRequirement) {
83-
const [ltMajor, ltMinor, ltPatch] = parse(ltRequirement);
84-
isLt =
85-
major < ltMajor ||
86-
(major === ltMajor &&
87-
(minor < ltMinor || (minor === ltMinor && patch < ltPatch)));
88-
}
89-
return isGte && isLt;
90-
91-
function parse(requirement: string) {
92-
return requirement.split(/[\.-]/).map((s) => parseInt(s, 10));
93-
}
94-
}
95-
9670
/**
9771
* Assert that script can be loaded as CommonJS when we attempt to require it.
9872
* If it should be loaded as ESM, throw ERR_REQUIRE_ESM like node does.

src/util.ts

+27
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,30 @@ export function once<Fn extends (...args: any[]) => any>(fn: Fn) {
169169
}
170170
return onceFn;
171171
}
172+
173+
/** @internal */
174+
export function versionGteLt(
175+
version: string,
176+
gteRequirement: string,
177+
ltRequirement?: string
178+
) {
179+
const [major, minor, patch, extra] = parse(version);
180+
const [gteMajor, gteMinor, gtePatch] = parse(gteRequirement);
181+
const isGte =
182+
major > gteMajor ||
183+
(major === gteMajor &&
184+
(minor > gteMinor || (minor === gteMinor && patch >= gtePatch)));
185+
let isLt = true;
186+
if (ltRequirement) {
187+
const [ltMajor, ltMinor, ltPatch] = parse(ltRequirement);
188+
isLt =
189+
major < ltMajor ||
190+
(major === ltMajor &&
191+
(minor < ltMinor || (minor === ltMinor && patch < ltPatch)));
192+
}
193+
return isGte && isLt;
194+
195+
function parse(requirement: string) {
196+
return requirement.split(/[\.-]/).map((s) => parseInt(s, 10));
197+
}
198+
}

0 commit comments

Comments
 (0)