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

fix: support .mts and .cts out of the box #1249

Closed
wants to merge 4 commits into from
Closed
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: 27 additions & 5 deletions packages/typescript/src/index.ts
Expand Up @@ -3,7 +3,7 @@ import * as path from 'path';
import { createFilter } from '@rollup/pluginutils';

import { Plugin, RollupOptions, SourceDescription } from 'rollup';
import type { Watch } from 'typescript';
import type { ResolvedModuleFull, Watch } from 'typescript';

import { RollupTypescriptOptions } from '../types';

Expand All @@ -17,6 +17,11 @@ import { preflight } from './preflight';
import createWatchProgram, { WatchProgramHelper } from './watchProgram';
import TSCache from './tscache';

/**
* The glob pattern used to test if a given file extension corresponds to a TypeScript file.
*/
const defaultTsExtensionsGlob = '?(m|c)ts+(|x)';
RebeccaStevens marked this conversation as resolved.
Show resolved Hide resolved

export default function typescript(options: RollupTypescriptOptions = {}): Plugin {
const {
cacheDir,
Expand All @@ -36,9 +41,13 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
const watchProgramHelper = new WatchProgramHelper();

const parsedOptions = parseTypescriptConfig(ts, tsconfig, compilerOptions, noForceEmit);
const filter = createFilter(include || ['*.ts+(|x)', '**/*.ts+(|x)'], exclude, {
resolve: filterRoot ?? parsedOptions.options.rootDir
});
const filter = createFilter(
include || [`*.${defaultTsExtensionsGlob}`, `**/*.${defaultTsExtensionsGlob}`],
exclude,
{
resolve: filterRoot ?? parsedOptions.options.rootDir
}
);
parsedOptions.fileNames = parsedOptions.fileNames.filter(filter);

const formatHost = createFormattingHost(ts, parsedOptions.options);
Expand Down Expand Up @@ -110,7 +119,7 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
const resolved = resolveModule(importee, containingFile);

if (resolved) {
if (resolved.extension === '.d.ts') return null;
if (isTypeDeclarationFile(resolved)) return null;
return path.normalize(resolved.resolvedFileName);
}

Expand Down Expand Up @@ -173,3 +182,16 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
}
};
}

/**
* Check if the given resolved TypeScript module is a type declaration.
*/
function isTypeDeclarationFile(resolved: ResolvedModuleFull) {
const lastDotDDot = resolved.extension.lastIndexOf('.d.');
if (lastDotDDot < 0) return false;

const lastDot = resolved.extension.lastIndexOf('.');

// Ensure the dot is the second one in `.d.`
return lastDot === lastDotDDot + 2;
}
3 changes: 3 additions & 0 deletions packages/typescript/test/fixtures/cts/main.mts
@@ -0,0 +1,3 @@
const answer = 42;
// eslint-disable-next-line no-console
console.log(`the answer is ${answer}`);
6 changes: 6 additions & 0 deletions packages/typescript/test/fixtures/cts/tsconfig.json
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16"
}
}
5 changes: 5 additions & 0 deletions packages/typescript/test/fixtures/dcts/main.cts
@@ -0,0 +1,5 @@
/* eslint-disable */
// @ts-ignore
import { foo } from 'an-import';

foo();
6 changes: 6 additions & 0 deletions packages/typescript/test/fixtures/dcts/tsconfig.json
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16"
}
}
5 changes: 5 additions & 0 deletions packages/typescript/test/fixtures/dmts/main.mts
@@ -0,0 +1,5 @@
/* eslint-disable */
// @ts-ignore
import { foo } from 'an-import';

foo();
6 changes: 6 additions & 0 deletions packages/typescript/test/fixtures/dmts/tsconfig.json
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16"
}
}
3 changes: 3 additions & 0 deletions packages/typescript/test/fixtures/mts/main.mts
@@ -0,0 +1,3 @@
const answer = 42;
// eslint-disable-next-line no-console
console.log(`the answer is ${answer}`);
6 changes: 6 additions & 0 deletions packages/typescript/test/fixtures/mts/tsconfig.json
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16"
}
}
48 changes: 47 additions & 1 deletion packages/typescript/test/test.js
Expand Up @@ -16,7 +16,7 @@ test.beforeEach(() => process.chdir(__dirname));

const outputOptions = { format: 'esm' };

test.serial('runs code through typescript', async (t) => {
test.serial('runs ts code through typescript', async (t) => {
const bundle = await rollup({
input: 'fixtures/basic/main.ts',
plugins: [typescript({ tsconfig: 'fixtures/basic/tsconfig.json', target: 'es5' })],
Expand All @@ -28,6 +28,30 @@ test.serial('runs code through typescript', async (t) => {
t.false(code.includes('const'), code);
});

test.serial('runs mts code through typescript', async (t) => {
const bundle = await rollup({
input: 'fixtures/mts/main.ts',
plugins: [typescript({ tsconfig: 'fixtures/mts/tsconfig.json', target: 'es5' })],
onwarn
});
const code = await getCode(bundle, outputOptions);

t.false(code.includes('number'), code);
t.false(code.includes('const'), code);
});

test.serial('runs cts code through typescript', async (t) => {
const bundle = await rollup({
input: 'fixtures/cts/main.ts',
plugins: [typescript({ tsconfig: 'fixtures/cts/tsconfig.json', target: 'es5' })],
onwarn
});
const code = await getCode(bundle, outputOptions);

t.false(code.includes('number'), code);
t.false(code.includes('const'), code);
});

test.serial('runs code through typescript with compilerOptions', async (t) => {
const bundle = await rollup({
input: 'fixtures/basic/main.ts',
Expand Down Expand Up @@ -375,6 +399,28 @@ test.serial('should not resolve .d.ts files', async (t) => {
t.deepEqual(imports, ['an-import']);
});

test.serial('should not resolve .d.cts files', async (t) => {
const bundle = await rollup({
input: 'fixtures/dts/main.cts',
plugins: [typescript({ tsconfig: 'fixtures/dcts/tsconfig.json' })],
onwarn,
external: ['an-import']
});
const imports = bundle.cache.modules[0].dependencies;
t.deepEqual(imports, ['an-import']);
});

test.serial('should not resolve .d.mts files', async (t) => {
const bundle = await rollup({
input: 'fixtures/dts/main.mts',
plugins: [typescript({ tsconfig: 'fixtures/dmts/tsconfig.json' })],
onwarn,
external: ['an-import']
});
const imports = bundle.cache.modules[0].dependencies;
t.deepEqual(imports, ['an-import']);
});

test.serial('should transpile JSX if enabled', async (t) => {
process.chdir('fixtures/jsx');

Expand Down