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(compiler): get to handle .cts files #6199

Merged
merged 7 commits into from
Mar 13, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,759 changes: 668 additions & 1,091 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/compiler/bundle/bundle-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const getRollupOptions = (
browser: true,
rootDir: config.rootDir,
...(config.nodeResolve as any),
extensions: ['.tsx', '.ts', '.js', '.mjs', '.json', '.d.ts', '.d.mts'],
extensions: ['.tsx', '.ts', '.mts', '.cts', '.js', '.mjs', '.cjs', '.json', '.d.ts', '.d.mts', '.d.cts'],
});

// @ts-expect-error - this is required now.
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/bundle/file-load-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { normalizeFsPath } from '@utils';
import { isDtsFile, normalizeFsPath } from '@utils';
import type { Plugin } from 'rollup';

import { InMemoryFileSystem } from '../sys/in-memory-fs';
Expand All @@ -9,7 +9,7 @@ export const fileLoadPlugin = (fs: InMemoryFileSystem): Plugin => {

load(id) {
const fsFilePath = normalizeFsPath(id);
if (id.endsWith('.d.ts') || id.endsWith('.d.mts')) {
if (isDtsFile(fsFilePath)) {
return '';
}
return fs.readFile(fsFilePath);
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/bundle/typescript-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isString, normalizeFsPath } from '@utils';
import { isDtsFile, isString, normalizeFsPath } from '@utils';
import { basename, isAbsolute } from 'path';
import type { LoadResult, Plugin, TransformResult } from 'rollup';
import ts from 'typescript';
Expand Down Expand Up @@ -88,7 +88,7 @@ export const resolveIdWithTypeScript = (config: d.ValidatedConfig, compilerCtx:
// this is probably a .d.ts file for whatever reason in how TS resolves this
// use this resolved file as the "importer"
const tsResolvedPath = tsResolved.resolvedModule.resolvedFileName;
if (isString(tsResolvedPath) && !(tsResolvedPath.endsWith('.d.ts') || tsResolvedPath.endsWith('.d.mts'))) {
if (isString(tsResolvedPath) && !isDtsFile(tsResolvedPath)) {
return tsResolvedPath;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/sys/fetch/fetch-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isFunction, normalizePath } from '@utils';
import { isFunction, isTsFile, isTsxFile, normalizePath } from '@utils';

import type * as d from '../../../declarations';
import { isCommonDirModuleFile, isTsFile, isTsxFile } from '../resolve/resolve-utils';
import { isCommonDirModuleFile } from '../resolve/resolve-utils';

/**
* A fetch wrapper which dispatches to `sys.fetch` if present, and otherwise
Expand Down
38 changes: 1 addition & 37 deletions src/compiler/sys/resolve/resolve-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,7 @@ import { normalizePath } from '@utils';

import type * as d from '../../../declarations';

const COMMON_DIR_MODULE_EXTS = ['.tsx', '.ts', '.mjs', '.js', '.jsx', '.json', '.md'];

/**
* Determine if a stringified file path is a TypeScript declaration file based on the extension at the end of the path.
* @param p the path to evaluate
* @returns `true` if the path ends in `.d.ts` (case-sensitive), `false` otherwise.
*/
export const isDtsFile = (p: string) => p.endsWith('.d.ts') || p.endsWith('.d.mts');

/**
* Determine if a stringified file path is a TypeScript file based on the extension at the end of the path. This
* function does _not_ consider type declaration files (`.d.ts` files) to be TypeScript files.
* @param p the path to evaluate
* @returns `true` if the path ends in `.ts` (case-sensitive) but does _not_ end in `.d.ts`, `false` otherwise.
*/
export const isTsFile = (p: string) => !isDtsFile(p) && p.endsWith('.ts');

/**
* Determine if a stringified file path is a TSX file based on the extension at the end of the path
* @param p the path to evaluate
* @returns `true` if the path ends in `.tsx` (case-sensitive), `false` otherwise.
*/
export const isTsxFile = (p: string) => p.endsWith('.tsx');

/**
* Determine if a stringified file path is a JSX file based on the extension at the end of the path
* @param p the path to evaluate
* @returns `true` if the path ends in `.jsx` (case-sensitive), `false` otherwise.
*/
export const isJsxFile = (p: string) => p.endsWith('.jsx');

/**
* Determine if a stringified file path is a JavaScript file based on the extension at the end of the path
* @param p the path to evaluate
* @returns `true` if the path ends in `.js` (case-sensitive), `false` otherwise.
*/
export const isJsFile = (p: string) => p.endsWith('.js');
const COMMON_DIR_MODULE_EXTS = ['.tsx', '.ts', '.mts', '.cts', '.mjs', '.js', '.cjs', '.jsx', '.json', '.md'];

export const isCommonDirModuleFile = (p: string) => COMMON_DIR_MODULE_EXTS.some((ext) => p.endsWith(ext));

Expand Down
70 changes: 0 additions & 70 deletions src/compiler/sys/resolve/tests/resolve-utils.spec.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/compiler/sys/typescript/typescript-resolve-module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { isString, join, normalizePath, resolve } from '@utils';
import { isDtsFile, isJsFile, isJsxFile, isString, isTsFile, isTsxFile, join, normalizePath, resolve } from '@utils';
import { basename, dirname } from 'path';
import ts from 'typescript';

import type * as d from '../../../declarations';
import { isDtsFile, isJsFile, isJsxFile, isTsFile, isTsxFile } from '../resolve/resolve-utils';
import { patchTsSystemFileSystem } from './typescript-sys';

export const tsResolveModuleName = (
Expand Down
64 changes: 64 additions & 0 deletions src/utils/test/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,68 @@ interface Foo extends Components.Foo, HTMLStencilElement {`);
},
);
});

describe('isTsFile', () => {
it.each(['.ts', 'foo.ts', 'foo.bar.ts', 'foo/bar.ts'])(
'returns true for a file ending with .ts (%s)',
(fileName) => {
expect(util.isTsFile(fileName)).toEqual(true);
},
);

it.each(['.tsx', 'foo.tsx', 'foo.bar.tsx', 'foo/bar.tsx'])(
'returns false for a file ending with .tsx (%s)',
(fileName) => {
expect(util.isTsFile(fileName)).toEqual(false);
},
);

it.each(['foo.js', 'foo.doc', 'foo.css', 'foo.html'])(
'returns false for other a file with another extension (%s)',
(fileName) => {
expect(util.isTsFile(fileName)).toEqual(false);
},
);

it('returns false for .d.ts and .d.tsx files', () => {
expect(util.isTsFile('foo/bar.d.ts')).toEqual(false);
expect(util.isTsFile('foo/bar.d.tsx')).toEqual(false);
});

it('returns true for a file named "spec.ts"', () => {
expect(util.isTsFile('spec.ts')).toEqual(true);
});

it('returns true for a file named "d.ts"', () => {
expect(util.isTsFile('d.ts')).toEqual(true);
});

it.each(['foo.tS', 'foo.Ts', 'foo.TS'])('returns true for non-lowercase extensions (%s)', (fileName) => {
expect(util.isTsFile(fileName)).toEqual(true);
});
});

describe('isJsFile', () => {
it.each(['.js', 'foo.js', 'foo.bar.js', 'foo/bar.js'])(
'returns true for a file ending with .js (%s)',
(fileName) => {
expect(util.isJsFile(fileName)).toEqual(true);
},
);

it.each(['.jsx', 'foo.txt', 'foo/bar.css', 'foo.bar.html'])(
'returns false for other a file with another extension (%s)',
(fileName) => {
expect(util.isJsFile(fileName)).toEqual(false);
},
);

it('returns true for a file named "spec.js"', () => {
expect(util.isJsFile('spec.js')).toEqual(true);
});

it.each(['foo.jS', 'foo.Js', 'foo.JS'])('returns true for non-lowercase extensions (%s)', (fileName) => {
expect(util.isJsFile(fileName)).toEqual(true);
});
});
});
59 changes: 47 additions & 12 deletions src/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,53 @@ export const createJsVarName = (fileName: string): string => {
};

/**
* Determines if a given file path points to a type declaration file (ending in .d.ts) or not. This function is
* case-insensitive in its heuristics.
* @param filePath the path to check
* @returns `true` if the given `filePath` points to a type declaration file, `false` otherwise
*/
export const isDtsFile = (filePath: string): boolean => {
const parts = filePath.toLowerCase().split('.');
if (parts.length > 2) {
return parts[parts.length - 2] === 'd' && parts[parts.length - 1] === 'ts';
}
return false;
};
* Create a function that lowercases the first string parameter before passing it to the provided function
* @param fn the function to pass the lowercased path to
* @returns the result of the provided function
*/
const lowerPathParam = (fn: (p: string) => boolean) => (p: string) => fn(p.toLowerCase());

/**
* Determine if a stringified file path is a TypeScript declaration file based on the extension at the end of the path.
* @param p the path to evaluate
* @returns `true` if the path ends in `.d.ts` (case-sensitive), `false` otherwise.
*/
export const isDtsFile = lowerPathParam((p) => p.endsWith('.d.ts') || p.endsWith('.d.mts') || p.endsWith('.d.cts'));

/**
* Determine if a stringified file path is a TypeScript file based on the extension at the end of the path. This
* function does _not_ consider type declaration files (`.d.ts` files) to be TypeScript files.
* @param p the path to evaluate
* @returns `true` if the path ends in `.ts` (case-sensitive) but does _not_ end in `.d.ts`, `false` otherwise.
*/
export const isTsFile = lowerPathParam(
(p: string) => !isDtsFile(p) && (p.endsWith('.ts') || p.endsWith('.mts') || p.endsWith('.cts')),
);

/**
* Determine if a stringified file path is a TSX file based on the extension at the end of the path
* @param p the path to evaluate
* @returns `true` if the path ends in `.tsx` (case-sensitive), `false` otherwise.
*/
export const isTsxFile = lowerPathParam(
(p: string) => p.endsWith('.tsx') || p.endsWith('.mtsx') || p.endsWith('.ctsx'),
);

/**
* Determine if a stringified file path is a JSX file based on the extension at the end of the path
* @param p the path to evaluate
* @returns `true` if the path ends in `.jsx` (case-sensitive), `false` otherwise.
*/
export const isJsxFile = lowerPathParam(
(p: string) => p.endsWith('.jsx') || p.endsWith('.mjsx') || p.endsWith('.cjsx'),
);

/**
* Determine if a stringified file path is a JavaScript file based on the extension at the end of the path
* @param p the path to evaluate
* @returns `true` if the path ends in `.js` (case-sensitive), `false` otherwise.
*/
export const isJsFile = lowerPathParam((p: string) => p.endsWith('.js') || p.endsWith('.mjs') || p.endsWith('.cjs'));

/**
* Generate the preamble to be placed atop the main file of the build
Expand Down