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

Add moduleType option to override module type on certain files. #1371

Merged
merged 22 commits into from Jul 9, 2021
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
21 changes: 16 additions & 5 deletions dist-raw/node-cjs-loader-utils.js
Expand Up @@ -5,17 +5,28 @@
const path = require('path');
const packageJsonReader = require('./node-package-json-reader');
const {JSONParse} = require('./node-primordials');
const {normalizeSlashes} = require('../dist/util');

module.exports.assertScriptCanLoadAsCJSImpl = assertScriptCanLoadAsCJSImpl;

// copied from Module._extensions['.js']
// https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L1113-L1120
function assertScriptCanLoadAsCJSImpl(filename) {
/**
* copied from Module._extensions['.js']
* https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L1113-L1120
* @param {import('../src/index').Service} service
* @param {NodeJS.Module} module
* @param {string} filename
*/
function assertScriptCanLoadAsCJSImpl(service, module, filename) {
const pkg = readPackageScope(filename);

// ts-node modification: allow our configuration to override
const tsNodeClassification = service.moduleTypeClassifier.classifyModule(normalizeSlashes(filename));
if(tsNodeClassification.moduleType === 'cjs') return;

// Function require shouldn't be used in ES modules.
if (pkg && pkg.data && pkg.data.type === 'module') {
if (tsNodeClassification.moduleType === 'esm' || (pkg && pkg.data && pkg.data.type === 'module')) {
const parentPath = module.parent && module.parent.filename;
const packageJsonPath = path.resolve(pkg.path, 'package.json');
const packageJsonPath = pkg ? path.resolve(pkg.path, 'package.json') : null;
throw createErrRequireEsm(filename, parentPath, packageJsonPath);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/bin.ts
Expand Up @@ -296,6 +296,7 @@ export function main(
const json = {
['ts-node']: {
...service.options,
optionBasePaths: undefined,
experimentalEsmLoader: undefined,
compilerOptions: undefined,
project: service.configFilePath ?? service.options.project,
Expand Down
26 changes: 24 additions & 2 deletions src/configuration.ts
@@ -1,6 +1,12 @@
import { resolve, dirname } from 'path';
import type * as _ts from 'typescript';
import { CreateOptions, DEFAULTS, TSCommon, TsConfigOptions } from './index';
import {
CreateOptions,
DEFAULTS,
OptionBasePaths,
TSCommon,
TsConfigOptions,
} from './index';
import type { TSInternal } from './ts-compiler-types';
import { createTsInternals } from './ts-internals';
import { getDefaultTsconfigJsonForNodeVersion } from './tsconfigs';
Expand Down Expand Up @@ -70,6 +76,7 @@ export function readConfig(
* this function.
*/
tsNodeOptionsFromTsconfig: TsConfigOptions;
optionBasePaths: OptionBasePaths;
} {
// Ordered [a, b, c] where config a extends b extends c
const configChain: Array<{
Expand Down Expand Up @@ -110,6 +117,7 @@ export function readConfig(
configFilePath,
config: { errors: [result.error], fileNames: [], options: {} },
tsNodeOptionsFromTsconfig: {},
optionBasePaths: {},
};
}

Expand Down Expand Up @@ -140,6 +148,7 @@ export function readConfig(
configFilePath,
config: { errors, fileNames: [], options: {} },
tsNodeOptionsFromTsconfig: {},
optionBasePaths: {},
};
}
if (resolvedExtendedConfigPath == null) break;
Expand All @@ -152,6 +161,7 @@ export function readConfig(

// Merge and fix ts-node options that come from tsconfig.json(s)
const tsNodeOptionsFromTsconfig: TsConfigOptions = {};
const optionBasePaths: OptionBasePaths = {};
for (let i = configChain.length - 1; i >= 0; i--) {
const { config, basePath, configPath } = configChain[i];
const options = filterRecognizedTsConfigTsNodeOptions(config['ts-node'])
Expand All @@ -169,6 +179,11 @@ export function readConfig(
options.scopeDir = resolve(basePath, options.scopeDir!);
}

// Downstream code uses the basePath; we do not do that here.
if (options.moduleTypes) {
optionBasePaths.moduleTypes = basePath;
}

assign(tsNodeOptionsFromTsconfig, options);
}

Expand Down Expand Up @@ -222,7 +237,12 @@ export function readConfig(
)
);

return { configFilePath, config: fixedConfig, tsNodeOptionsFromTsconfig };
return {
configFilePath,
config: fixedConfig,
tsNodeOptionsFromTsconfig,
optionBasePaths,
};
}

/**
Expand Down Expand Up @@ -251,6 +271,7 @@ function filterRecognizedTsConfigTsNodeOptions(
transpiler,
scope,
scopeDir,
moduleTypes,
...unrecognized
} = jsonObject as TsConfigOptions;
const filteredTsConfigOptions = {
Expand All @@ -271,6 +292,7 @@ function filterRecognizedTsConfigTsNodeOptions(
transpiler,
scope,
scopeDir,
moduleTypes,
};
// Use the typechecker to make sure this implementation has the correct set of properties
const catchExtraneousProps: keyof TsConfigOptions = (null as any) as keyof typeof filteredTsConfigOptions;
Expand Down
23 changes: 20 additions & 3 deletions src/esm.ts
Expand Up @@ -8,6 +8,7 @@ import {
} from 'url';
import { extname } from 'path';
import * as assert from 'assert';
import { normalizeSlashes } from './util';
const {
createResolve,
} = require('../dist-raw/node-esm-resolve-implementation');
Expand Down Expand Up @@ -96,11 +97,27 @@ export function registerAndCreateEsmHooks(opts?: RegisterOptions) {

// 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: Format };
if (ext !== '.js' && !tsNodeInstance.ignored(nativePath)) {
return defer(formatUrl(pathToFileURL(nativePath + '.js')));
nodeSays = await defer(formatUrl(pathToFileURL(nativePath + '.js')));
} else {
nodeSays = await defer();
}

return defer();
// For files compiled by ts-node that node believes are either CJS or ESM, check if we should override that classification
if (
!tsNodeInstance.ignored(nativePath) &&
(nodeSays.format === 'commonjs' || nodeSays.format === 'module')
) {
const { moduleType } = tsNodeInstance.moduleTypeClassifier.classifyModule(
normalizeSlashes(nativePath)
);
if (moduleType === 'cjs') {
return { format: 'commonjs' };
} else if (moduleType === 'esm') {
return { format: 'module' };
}
}
return nodeSays;
}

async function transformSource(
Expand Down