Skip to content

feat: add TypeScript related extensions support #85

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

JounQin
Copy link
Member

@JounQin JounQin commented Jul 8, 2025

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and discussions and couldn’t find anything or linked relevant results below
  • I made sure the docs are up to date
  • I included tests (or that’s not needed)

Description of changes

close #84

Note

It only works on Node 24+ for now: nodejs/node#57756 (comment)

@JounQin JounQin requested a review from Copilot July 8, 2025 04:43
Copy link

codecov bot commented Jul 8, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (6f35eae) to head (d740285).

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #85   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           23        23           
  Lines         3214      3217    +3     
=========================================
+ Hits          3214      3217    +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot added 👋 phase/new Post is being triaged automatically 🤞 phase/open Post is being triaged manually and removed 👋 phase/new Post is being triaged automatically labels Jul 8, 2025
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds support for TypeScript-based configuration files by recognizing .cts, .mts, and .ts extensions in the loader lookup.

  • Introduces .cts, .mts, and .ts keys in the loaders map to route TS files through loadScriptOrModule.
Comments suppressed due to low confidence (2)

lib/configuration.js:102

  • Consider updating the README or configuration documentation to mention support for .cts, .mts, and .ts files so users know these extensions are now recognized.
  '.cts': loadScriptOrModule,

lib/configuration.js:102

  • Add or update tests to cover loading of .cts, .mts, and .ts configuration files, ensuring the new extensions are properly recognized in practice.
  '.cts': loadScriptOrModule,

@JounQin JounQin added 🦋 type/enhancement This is great to have and removed 🤞 phase/open Post is being triaged manually labels Jul 8, 2025
Copy link

github-actions bot commented Jul 8, 2025

Hi team! I don’t know what’s up as there’s no phase label. Please add one so I know where it’s at.

Thanks,
— bb

@remcohaszing
Copy link
Member

I like it, but this needs to be documented.

@JounQin
Copy link
Member Author

JounQin commented Jul 10, 2025

I like it, but this needs to be documented.

I thought it would only work with custom rcPath, but after reading the source codes https://github.com/unifiedjs/unified-engine/blob/main/lib/configuration.js#L161-L169, it actually would also work for rcName, I'll add the related document tomorrow.

@wooorm
Copy link
Member

wooorm commented Jul 10, 2025

rcPath is a full path, so people should be using .ts explicitly in those cases.
This would be rcName, which is a filename, for which extensions are inferred.

What should happen if both .ts and .js are found? That’s kinda typical for TS right, to build files.

What happens in older Node, when this finds .ts but cannot parse it?

Is this really worth 3 extra stats per folder above each file? That affects everyone.

@@ -99,6 +99,9 @@ const loaders = {
'.cjs': loadScriptOrModule,
'.mjs': loadScriptOrModule,
'.js': loadScriptOrModule,
'.cts': loadScriptOrModule,
'.mts': loadScriptOrModule,
'.ts': loadScriptOrModule,
Copy link
Member

@wooorm wooorm Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not come before .js? Or is it intentional that built JS is preferred over TS

Next to docs, tests are missing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I think js configs should be preferred because they're supported natively and this is just old behavior, what means if a user have a .remarkrc.ts compiled into .remarkrx.js manually, this will continue work even on unsupported Node versions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, probably!

@JounQin
Copy link
Member Author

JounQin commented Jul 10, 2025

rcPath is a full path, so people should be using .ts explicitly in those cases.

rcPath is also loaded via these loaders, so it's same here.

What happens in older Node, when this finds .ts but cannot parse it?

It throws different errors compared with current behavior: parse TypeScript as json, and aftet this PR: parse as JavaScript failed.

@wooorm
Copy link
Member

wooorm commented Jul 18, 2025

okay I’m up for this, if there are docs and tests. Looks like Remco is too. @ChristianMurphy?

Copy link
Member

@ChristianMurphy ChristianMurphy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@remcohaszing
Copy link
Member

Considering the number of fs calls, can node --strip-types actually handle .cts? I have my doubts about the usefulness of .cjs / .cts anyway.

@JounQin
Copy link
Member Author

JounQin commented Jul 21, 2025

can node --strip-types actually handle .cts?

Sure: https://github.com/unifiedjs/unified-engine/pull/85/files#diff-ecf1a139df459ba97de02629e7c9f7e416079a70e54880d4921307eb861f9c5d.

I have my doubts about the usefulness of .cjs / .cts anyway.

Good point while I think it should be discussed in another issue/PR instead?

@JounQin JounQin requested a review from wooorm July 21, 2025 04:07
Comment on lines +1 to +5
import type {Settings} from 'unified'

exports.settings = {
foo: 'bar'
} satisfies Settings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript doesn’t understand the exports object inside TypeScript files. Instead, you are supposed to either use the special export = syntax

Suggested change
import type {Settings} from 'unified'
exports.settings = {
foo: 'bar'
} satisfies Settings
import type {PresetSupportingSpecifiers} from 'unified'
const config: PresetSupportingSpecifiers = {
settings: {
foo: 'bar'
}
}
export = config

or use an ESM style export (doesn’t work with verbatimModuleSyntax)

Suggested change
import type {Settings} from 'unified'
exports.settings = {
foo: 'bar'
} satisfies Settings
import type {Settings} from 'unified'
export const settings: Settings = {
foo: 'bar'
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all features are supported by Node type stripping.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, though I don’t know entirely for sure which features are or aren’t supported. If neither of these syntaxes is supported by Node.js type stripping, then it doesn’t support .cts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import type {Settings} from 'unified' work well in .cts, and .mts also doesn't support all TypeScript features, both of them are partial supported, I treat .cts same as .mts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider a module format supported, if there’s a way to import and export things in a way that TypeScript understands. In CJS, for imports means at least one of:

import module = require('module')
import {member} from 'module'

And for exports that means at least one of:

export = {}
export const member = {}

If there’s no (proper) way to import or export from a .cts file, then it’s not properly supported. In that case IMO we should not support it either, as it promotes writing incorrect TypeScript code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test case is still a valid .cts usage, import for typings, exports for runtime codes.

We're talking about Node type stripping, and it's how .cts works in Node itself which is partial support.

Using .cts doesn't mean all module features are supported.

So IMO, .cts is supported by Node itself this way, then we could support how it works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not valid .cts usage. The use of the module, exports, or require variables in .cts is a hacky workaround. It only works, because the file isn’t imported by user code.

We shouldn’t promote this anti-pattern, especially considering this is already a topic of confusion in the TypeScript community.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🦋 type/enhancement This is great to have
Development

Successfully merging this pull request may close these issues.

[feat] TypeScript related extensions should be supported in favor of Node.js type stripping
4 participants