Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize stat calls in resolvers #1759

Merged
merged 3 commits into from May 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 16 additions & 16 deletions dist-raw/node-internal-modules-esm-resolve.js
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
@@ -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
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
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
@@ -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
@@ -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
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
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));
}
}