-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(typescript-estree): change mjs/mts files to always be parsed as ESM #9121
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(typescript-estree): change mjs/mts files to always be parsed as ESM #9121
Conversation
Thanks for the PR, @goldentrash! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
☁️ Nx Cloud ReportCI is running/has finished running commands for commit 0027099. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this CI Pipeline Execution ✅ Successfully ran 1 targetSent with 💌 from NxCloud. |
Hi @bradzacher , I've spent the last few days thinking of other ways to achieve our goal without modifying the I found the code below and it seems to be acceptable. /** @internal */
export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void {
// TODO: Should this callback be cached?
switch (getEmitModuleDetectionKind(options)) {
case ModuleDetectionKind.Force:
// All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule
return (file: SourceFile) => {
file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined;
};
case ModuleDetectionKind.Legacy:
// Files are modules if they have imports, exports, or import.meta
return (file: SourceFile) => {
file.externalModuleIndicator = isFileProbablyExternalModule(file);
};
case ModuleDetectionKind.Auto:
// If module is nodenext or node16, all esm format files are modules
// If jsx is react-jsx or react-jsxdev then jsx tags force module-ness
// otherwise, the presence of import or export statments (or import.meta) implies module-ness
const checks: ((file: SourceFile, options: CompilerOptions) => Node | true | undefined)[] = [isFileProbablyExternalModule];
if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
checks.push(isFileModuleFromUsingJSXTag);
}
checks.push(isFileForcedToBeModuleByFormat);
const combined = or(...checks);
const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file, options));
return callback;
}
} Would this be acceptable? |
Hi! I'm always ready to update the PR if I need to change my solution to fix this bug! |
Does the detection need to agree with what the TypeScript compiler would do given the file extension and tsconfig options? If so, you do need to implement something like that snippet you found in TS that considers the computed value of |
I think it needs to reflect what TSC sees as you said, and I'll try to modify the PR to take into account things like what you mentioned( Thanks for the review! :) |
Unfortunately, I think you’re essentially going to have to choose between porting some TS implementation details (i.e. what options imply what other options) and accessing internal functions 😕 |
Thanks for the advice @andrewbranch! I think we need to decide something else first to solve this problem: "Which should be dominant in the settings for the module, My suggestion would be to give
Incidentally, if we do this, how about adding a rule to "warn if the module settings in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the logic itself is looking good here - we just need a test to validate that this is working correctly.
can you please add three tests:
Two tests into
https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-estree/tests/lib/parse.test.ts
- one test to test the "non-type-aware" (isolated parsing) case
- one test to test the "type-aware" + "project service" case
- one test to test the "type-aware" + "project: true" case
👋 Ping @goldentrash - is this still something you have time & energy for? |
I'm so sorry for the late response. I'm interested in solving this problem! However, I've been so busy with personal matters over the past three weeks that I haven't been able to give it much attention. I'm really sorry for not responding to this, but thankfully, I'm finally able to wrap up all of my pressing matters, so I can continue my challenge to solve this problem this weekend. I'm still interested in this issue, and I'm willing to continue the challenge to resolve it. Thank you. |
No worries, hope things are ok! |
Sorry for the late response, @bradzacher. As discussed above, I modified the code to determine the
|
7d7ef1c
to
b34e53f
Compare
I modified the First and foremost, the test you mentioned was written with the intent of Project Service in mind, so I thought it was more important to accurately reflect the intent based on your feedback. |
d369ec8
to
385bedb
Compare
I'm sorry. I accidentally closed this PR and when I tried to reopen it, it was impossible, so I opened a new one. Please see the following PR (#10011) |
PR Checklist
Overview
Change
mjs
/mts
files to always be parsed by ESMHow I solved this problem - new apporach
setExternalModuleIndicator
to always returntrue
if the file ismjs
/mts
.How I solved this problemIf the extension of the file ismjs
/mts
viaParseOptions.filePath
, add an empty export at the end of the source code: (export {};
)This method was inspired by a long-time favorite method for resolvingisolatedModules
errors that originate from tsconfig settings.According to MDN documentation, this method does not affect functionality or logic.export {}
does not export an empty object — it's a no-op declaration that exports nothing (an empty name list).Reasons not to modify
convertNode
andcreateSourceFile
./tyscript-estree/src/convert.ts/Converter/convertNode
, modifyingsourceType
when it iscase SyntaxKind.SourceFile
does not make sense, I have left the details in the issue.createSourceFile
relies on an external module called typescript (ts.createSourceFile
), tweaking the parameters of this module is the only way to keep it out of scope of typescript-eslint.AdjustingsetExternalModuleIndicator
doesn't make sense, I've left the details in an issue.impliedNodeFormat
doesn't make sense, I've left the details in an issue.Why I thought it was in-appropriate to look for another API instead of
ts.createSourceFile
.typescript-estree
relies heavily on the return value ofts.createSourceFile
.breaking-change
.If you have any feedback on function/variable naming or code block repositioning, I'll take it on board and fix the PR.