Skip to content

Commit

Permalink
Adding hint when ts-node is ignoring a file that you might want it to…
Browse files Browse the repository at this point in the history
… compile; addressing todos; adding a new one
  • Loading branch information
cspotcode committed May 15, 2022
1 parent e34adc0 commit 46096ab
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 17 deletions.
1 change: 1 addition & 0 deletions child-loader.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { fileURLToPath } from 'url';
import { createRequire } from 'module';
const require = createRequire(fileURLToPath(import.meta.url));

// TODO why use require() here? I think we can just `import`
/** @type {import('./dist/child-loader')} */
const childLoader = require('./dist/child/child-loader');
export const { resolve, load, getFormat, transformSource } = childLoader;
4 changes: 0 additions & 4 deletions src/child/child-loader.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// TODO same version check as ESM loader, but export stubs
// Also export a binder function that allows re-binding where the stubs
// delegate.

import type { NodeLoaderHooksAPI1, NodeLoaderHooksAPI2 } from '..';
import { filterHooksByAPIVersion } from '../esm';

Expand Down
27 changes: 19 additions & 8 deletions src/esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,7 @@ export interface NodeImportAssertions {
}

// The hooks API changed in node version X so we need to check for backwards compatibility.
// TODO: When the new API is backported to v12, v14, update these version checks accordingly.
const newHooksAPI =
versionGteLt(process.versions.node, '17.0.0') ||
versionGteLt(process.versions.node, '16.12.0', '17.0.0') ||
versionGteLt(process.versions.node, '14.999.999', '15.0.0') ||
versionGteLt(process.versions.node, '12.999.999', '13.0.0');
const newHooksAPI = versionGteLt(process.versions.node, '16.12.0');

/** @internal */
export function filterHooksByAPIVersion(
Expand Down Expand Up @@ -135,6 +130,7 @@ export function createEsmHooks(tsNodeService: Service) {
// Custom implementation that considers additional file extensions and automatically adds file extensions
const nodeResolveImplementation = tsNodeService.getNodeEsmResolver();
const nodeGetFormatImplementation = tsNodeService.getNodeEsmGetFormat();
const extensions = tsNodeService.extensions;

const hooksAPI = filterHooksByAPIVersion({
resolve,
Expand Down Expand Up @@ -335,12 +331,27 @@ export function createEsmHooks(tsNodeService: Service) {
// If file has .ts, .tsx, or .jsx extension, then ask node how it would treat this file if it were .js
const ext = extname(nativePath);
let nodeSays: { format: NodeLoaderHooksFormat };
if (ext !== '.js' && !tsNodeService.ignored(nativePath)) {
const nodeDoesNotUnderstandExt =
extensions.extensionsNodeDoesNotUnderstand.includes(ext);
const tsNodeIgnored = tsNodeService.ignored(nativePath);
if (nodeDoesNotUnderstandExt && !tsNodeIgnored) {
nodeSays = await entrypointFallback(() =>
defer(formatUrl(pathToFileURL(nativePath + '.js')))
);
} else {
nodeSays = await entrypointFallback(defer);
try {
nodeSays = await entrypointFallback(defer);
} catch (e) {
if (e instanceof Error && tsNodeIgnored && nodeDoesNotUnderstandExt) {
e.message +=
`\n\n` +
`Hint:\n` +
`ts-node is configured to ignore this file.\n` +
`If you want ts-node to handle this file, consider enabling the "skipIgnore" option or adjusting your "ignore" patterns.\n` +
`https://typestrong.org/ts-node/docs/scope\n`;
}
throw e;
}
}
// For files compiled by ts-node that node believes are either CJS or ESM, check if we should override that classification
if (
Expand Down
42 changes: 38 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,8 @@ export interface Service {
getNodeCjsLoader: () => ReturnType<
typeof import('../dist-raw/node-internal-modules-cjs-loader').createCjsLoader
>;
/** @internal */
extensions: Extensions;
}

/**
Expand All @@ -571,12 +573,30 @@ export interface DiagnosticFilter {
diagnosticsIgnored: number[];
}

/** @internal */
/**
* Centralized specification of how we deal with file extensions based on
* project options:
* which ones we do/don't support, in what situations, etc. These rules drive
* logic elsewhere.
* @internal
* */
export type Extensions = ReturnType<typeof getExtensions>;

/**
* @internal
*/
export function getExtensions(
config: _ts.ParsedCommandLine,
options: RegisterOptions
) {
const compiledExtensions = [];
const compiledExtensions: string[] = [];
const extensionsNodeDoesNotUnderstand = [
'.ts',
'.tsx',
'.jsx',
'.cts',
'.mts',
];

// .js, .cjs, .mjs take precedence if preferTsExts is off
if (!options.preferTsExts && config.options.allowJs)
Expand All @@ -590,9 +610,19 @@ export function getExtensions(
compiledExtensions.push('.jsx');
if (config.options.preferTsExt && config.options.allowJs)
compiledExtensions.push('.js');

const compiledExtensionsNodeDoesNotUnderstand =
extensionsNodeDoesNotUnderstand.filter((ext) =>
compiledExtensions.includes(ext)
);

return {
/** All file extensions we transform, ordered by resolution preference according to preferTsExts */
compiledExtensions,
/** Resolved extensions that vanilla node will not understand; we should handle them */
extensionsNodeDoesNotUnderstand,
/** Like the above, but only the ones we're compiling */
compiledExtensionsNodeDoesNotUnderstand,
};
}

Expand Down Expand Up @@ -923,7 +953,7 @@ export function createFromPreloadedConfig(
const rootFileNames = new Set(config.fileNames);
const cachedReadFile = cachedLookup(debugFn('readFile', readFile));

// Use language services by default (TODO: invert next major version).
// Use language services by default
if (!options.compilerHost) {
let projectVersion = 1;
const fileVersions = new Map(
Expand Down Expand Up @@ -1428,7 +1458,10 @@ export function createFromPreloadedConfig(
const getNodeEsmGetFormat = once(() =>
(
require('../dist-raw/node-internal-modules-esm-get_format') as typeof _nodeInternalModulesEsmGetFormat
).createGetFormat(options.experimentalSpecifierResolution, getNodeEsmResolver())
).createGetFormat(
options.experimentalSpecifierResolution,
getNodeEsmResolver()
)
);
const getNodeCjsLoader = once(() =>
(
Expand Down Expand Up @@ -1461,6 +1494,7 @@ export function createFromPreloadedConfig(
getNodeEsmResolver,
getNodeEsmGetFormat,
getNodeCjsLoader,
extensions,
};
}

Expand Down
1 change: 0 additions & 1 deletion src/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export interface CreateReplOptions {
stderr?: NodeJS.WritableStream;
/** @internal */
composeWithEvalAwarePartialHost?: EvalAwarePartialHost;
// TODO collapse both of the following two flags into a single `isInteractive` or `isLineByLine` flag.
/**
* @internal
* Ignore diagnostics that are annoying when interactively entering input line-by-line.
Expand Down

0 comments on commit 46096ab

Please sign in to comment.