Skip to content

Commit

Permalink
optimize stat calls in resolvers (#1759)
Browse files Browse the repository at this point in the history
* optimize stat calls in resolvers

* fix
  • Loading branch information
cspotcode authored May 20, 2022
1 parent 5521f80 commit c6010aa
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 50 deletions.
32 changes: 16 additions & 16 deletions dist-raw/node-internal-modules-esm-resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:';

Expand Down Expand Up @@ -149,11 +140,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;
}
}

Expand Down
17 changes: 16 additions & 1 deletion dist-raw/node-internalBinding-fs.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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
};
3 changes: 1 addition & 2 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -21,7 +21,6 @@ import {
VERSION,
TSError,
register,
versionGteLt,
createEsmHooks,
createFromPreloadedConfig,
DEFAULTS,
Expand Down
2 changes: 1 addition & 1 deletion src/child/spawn-child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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=';

Expand Down
4 changes: 2 additions & 2 deletions src/esm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { register, RegisterOptions, Service, versionGteLt } from './index';
import { register, RegisterOptions, Service } from './index';
import {
parse as parseUrl,
format as formatUrl,
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/file-extensions.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
28 changes: 1 addition & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
parse,
ProjectLocalResolveHelper,
split,
versionGteLt,
yn,
} from './util';
import { findAndReadConfig, loadCompiler } from './configuration';
Expand Down Expand Up @@ -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.
Expand Down
27 changes: 27 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,30 @@ export function once<Fn extends (...args: any[]) => 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));
}
}

0 comments on commit c6010aa

Please sign in to comment.