Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
module: prevent type-stripping in node_modules
  • Loading branch information
marco-ippolito committed Jul 24, 2024
commit 13ecb1da973793997aefe3018887e870f51c48ec
10 changes: 10 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -4032,6 +4032,16 @@ The public key in the certificate SubjectPublicKeyInfo could not be read.

An error occurred trying to allocate memory. This should never happen.

<a id="ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING"></a>

#### `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`
Comment thread
marco-ippolito marked this conversation as resolved.

<!-- YAML
added: REPLACEME
-->

Type stripping is not supported for files descendent of a `node_modules` directory.

[ES Module]: esm.md
[ICU]: intl.md#internationalization-support
[JSON Web Key Elliptic Curve Registry]: https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve
Expand Down
9 changes: 9 additions & 0 deletions doc/api/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,14 @@ import { Type1, Type2 } from './module.ts';
import { fn, FnParams } from './fn.ts';
```

### Type stripping in `node_modules` directories

To avoid encouraging package authors to publish TypeScript only modules,
Node.js will by default refuse to handle TypeScript files inside `node_modules` directories.
When attempting to resolve a `.ts`, `.cts`, or `.mts` file that is a children of a
`node_modules` directory, `defaultResolve` will throw
a [`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`][] error.

### Non-file forms of input

Type stripping can be enabled for `--eval` and STDIN input. The module system
Expand Down Expand Up @@ -1167,6 +1175,7 @@ source maps support, see
[`--enable-source-maps`]: cli.md#--enable-source-maps
[`--experimental-strip-types`]: cli.md#--experimental-strip-types
[`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
[`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`]: errors.md#err_unsupported_node_modules_type_stripping
[`NODE_V8_COVERAGE=dir`]: cli.md#node_v8_coveragedir
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
[`SourceMap`]: #class-modulesourcemap
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,9 @@ E('ERR_UNSUPPORTED_ESM_URL_SCHEME', (url, supported) => {
msg += `. Received protocol '${url.protocol}'`;
return msg;
}, Error);
E('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING', function(url) {
return `Type-stripping is currently not supported for files inside node_modules. Resolving ${url}`;
}, Error);
E('ERR_UNSUPPORTED_RESOLVE_REQUEST',
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
TypeError);
Expand Down
12 changes: 12 additions & 0 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const {
StringPrototypeCharAt,
StringPrototypeCharCodeAt,
StringPrototypeEndsWith,
StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeRepeat,
StringPrototypeSlice,
Expand Down Expand Up @@ -168,6 +169,7 @@ const {
ERR_REQUIRE_CYCLE_MODULE,
ERR_REQUIRE_ESM,
ERR_UNKNOWN_BUILTIN_MODULE,
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
},
setArrowMessage,
} = require('internal/errors');
Expand Down Expand Up @@ -1564,6 +1566,9 @@ function getMaybeCachedSource(mod, filename) {
* @param {string} filename Absolute path of the file.
*/
function loadMTS(mod, filename) {
if (StringPrototypeIncludes(filename, 'node_modules')) {
Comment thread
marco-ippolito marked this conversation as resolved.
Outdated
throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename);
}
const source = getMaybeCachedSource(mod, filename);
const { tsParse } = require('internal/modules/helpers');
const content = tsParse(source);
Expand All @@ -1590,6 +1595,9 @@ function loadMTS(mod, filename) {
}

function loadCTS(module, filename) {
if (StringPrototypeIncludes(filename, 'node_modules')) {
throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename);
}
const source = getMaybeCachedSource(module, filename);
const { tsParse } = require('internal/modules/helpers');
const content = tsParse(source);
Expand All @@ -1602,6 +1610,10 @@ function loadCTS(module, filename) {
* @param {string} filename The file path of the module
*/
function loadTS(module, filename) {
if (StringPrototypeIncludes(filename, 'node_modules')) {
throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename);
}

// If already analyzed the source, then it will be cached.
const source = getMaybeCachedSource(module, filename);
const { tsParse } = require('internal/modules/helpers');
Expand Down
8 changes: 8 additions & 0 deletions lib/internal/modules/esm/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const {
ArrayPrototypePush,
RegExpPrototypeExec,
StringPrototypeIncludes,
decodeURIComponent,
} = primordials;
const { kEmptyObject } = require('internal/util');
Expand All @@ -24,6 +25,7 @@ const {
ERR_INVALID_URL,
ERR_UNKNOWN_MODULE_FORMAT,
ERR_UNSUPPORTED_ESM_URL_SCHEME,
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
} = require('internal/errors').codes;

const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(?:[^,]*?)(;base64)?,([\s\S]*)$/;
Expand Down Expand Up @@ -147,6 +149,12 @@ async function defaultLoad(url, context = kEmptyObject) {
format = 'commonjs-sync';
}

if (getOptionValue('--experimental-strip-types') &&
StringPrototypeIncludes(format, 'typescript') &&
Comment thread
marco-ippolito marked this conversation as resolved.
Outdated
StringPrototypeIncludes(url, 'node_modules')) {
throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(url);
}

return {
__proto__: null,
format,
Expand Down
28 changes: 14 additions & 14 deletions test/es-module/test-typescript-commonjs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -120,47 +120,47 @@ test('expect failure of a .cts file with default type module', async () => {
strictEqual(result.code, 0);
});

test('expect failure of a .cts file with default type module', async () => {
test('expect failure of a .cts file in node_modules', async () => {
const result = await spawnPromisified(process.execPath, [
'--experimental-strip-types',
fixtures.path('typescript/cts/test-cts-node_modules.cts'),
]);

strictEqual(result.stderr, '');
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
strictEqual(result.stdout, '');
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.code, 1);
});

test('expect failure of a .cts file with default type module', async () => {
test('expect failure of a .ts file in node_modules', async () => {
const result = await spawnPromisified(process.execPath, [
'--experimental-strip-types',
fixtures.path('typescript/cts/test-ts-node_modules.cts'),
]);

strictEqual(result.stderr, '');
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
strictEqual(result.stdout, '');
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.code, 1);
});

test('expect failure of a .cts file with default type module', async () => {
test('expect failure of a .cts requiring esm without default type module', async () => {
const result = await spawnPromisified(process.execPath, [
'--experimental-strip-types',
fixtures.path('typescript/cts/test-mts-node_modules.cts'),
]);

strictEqual(result.stdout, '');
match(result.stderr, /Error \[ERR_REQUIRE_ESM\]: require\(\)/);
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.code, 1);
});

test('expect failure of a .cts file with default type module', async () => {
test('expect failure of a .cts file requiring esm in node_modules', async () => {
const result = await spawnPromisified(process.execPath, [
'--experimental-strip-types',
'--experimental-require-module',
fixtures.path('typescript/cts/test-mts-node_modules.cts'),
]);

match(result.stderr, /Support for loading ES Module in require\(\) is an experimental feature and might change at any time/);
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
strictEqual(result.stdout, '');
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.code, 1);
});
18 changes: 9 additions & 9 deletions test/es-module/test-typescript-module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ test('execute an .mts file from node_modules', async () => {
fixtures.path('typescript/mts/test-mts-node_modules.mts'),
]);

strictEqual(result.stderr, '');
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.stdout, '');
strictEqual(result.code, 1);
});

test('execute a .cts file from node_modules', async () => {
Expand All @@ -80,9 +80,9 @@ test('execute a .cts file from node_modules', async () => {
fixtures.path('typescript/mts/test-cts-node_modules.mts'),
]);

strictEqual(result.stderr, '');
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.stdout, '');
strictEqual(result.code, 1);
});

test('execute a .ts file from node_modules', async () => {
Expand All @@ -91,7 +91,7 @@ test('execute a .ts file from node_modules', async () => {
fixtures.path('typescript/mts/test-ts-node_modules.mts'),
]);

strictEqual(result.stderr, '');
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.stdout, '');
strictEqual(result.code, 1);
});
6 changes: 3 additions & 3 deletions test/es-module/test-typescript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ test('execute CommonJS TypeScript file from node_modules with require-module', a
fixtures.path('typescript/ts/test-import-ts-node-modules.ts'),
]);

strictEqual(result.stderr, '');
match(result.stdout, /Hello, TypeScript!/);
strictEqual(result.code, 0);
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
strictEqual(result.stdout, '');
strictEqual(result.code, 1);
});

test('execute a TypeScript file with CommonJS syntax but default type module', async () => {
Expand Down