Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.4k
module: improve named cjs import errors#35453
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
Uh oh!
There was an error while loading. Please reload this page.
Conversation
nodejs-github-bot commented Oct 1, 2020 • edited by RaisinTen
Loading Uh oh!
There was an error while loading. Please reload this page.
edited by RaisinTen
Uh oh!
There was an error while loading. Please reload this page.
Review requested:
|
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.
Is it OK to "lazy-load" inline here? My assumption is that this code is executed exactly once before the node process crashes. Is my assumption correct?
I saw a different way of lazy loading acorn in assert
Lines 216 to 218 in 4a6005c
| // Lazy load acorn. | |
| if(parseExpressionAt===undefined){ | |
| constacorn=require('internal/deps/acorn/acorn/dist/acorn'); |
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.
Is it OK to readFileSync?
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.
That code would run only if the process is crashing, right? I think it's perfectly fine to use readFileSync for that kind of things.
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.
This is precisely the question: I don't understand 100% yet of this error will always lead to termination of the node process. If so, I also think that readFileSync is the way to go.
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.
Unless I find a way to parse the input file in a streaming fashion… Since imports typically come in the beginning of a file this would allow us to reduce the parsing work to a minimum… But maybe that's all unnecessary micro-optimizations for the crash case.
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.
This has been benchmarked already, see nodejs/cjs-module-lexer#4 (comment). The gist of it is that streaming is not worth it for most JS files.
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.
Thanks for the context!
Great, so the one possible optimization would be, having read the entire file, around not parsing the entire file with with acorn, see #35453 (comment)
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.
Is there any way around parsing the full file? I tried working with parseExpressionAt to only part the first few lines of a file but didn't have success (received unexpected token errors resulting from the import statements).
Uh oh!
There was an error while loading. Please reload this page.
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.
For reasons I don't understand this import statement only errors out on line 3 (everybody). I do not understand why it doesn't error out on the renamed import statement. If I swap the order it will error on line 2 (still everybody).
Any clues why this is the case?
test/fixtures/es-modules/package-cjs-named-error/default-and-renamed-import.mjs Outdated Show resolvedHide resolved
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
f0498e2 to 6c22d54CompareThere 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.
My line splitting logic is pretty "simplistic"… Any idea on how to do this better?
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.
I wonder if we do anything similar elsewhere? a nice to have would be checking the terminal width, rather than assuming 80.
codecov-commenter commented Oct 2, 2020
Codecov Report
@@ Coverage Diff @@## master #35453 +/- ## ======================================= Coverage 96.84% 96.84% ======================================= Files 212 212 Lines 69609 69609 ======================================= Hits 67410 67410 Misses 2199 2199 Continue to review full report at Codecov.
|
mcollina left a comment
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.
+1 on the idea
68f2978 to e7a0b5cComparectavan commented Oct 16, 2020
@MylesBorins@mcollina@Trott is there any interest in landing this change? I'm happy to work on it further. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
aduh95 commented Oct 16, 2020
@guybedford Does #35249 change something for this PR? |
mcollina commented Oct 16, 2020
Can you please make example of the error message before and after this PR? |
nodejs-github-bot commented Oct 16, 2020
ctavan commented Oct 16, 2020 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Sure. Upon encountering a multi-line named import statement like: import{comeOn,everybody,}from'./fail.cjs';Before: After: Diff: --- before-short 2020-10-16 12:54:45.000000000 +0200+++ after-short 2020-10-16 12:55:05.000000000 +0200@@ -5,8 +5,9 @@ CommonJS modules can always be imported via the default export, for example using: import pkg from './fail.cjs'; +const{comeOn, everybody } = pkg;- at ModuleJob._instantiate (internal/modules/esm/module_job.js:98:21)- at async ModuleJob.run (internal/modules/esm/module_job.js:143:5)+ at ModuleJob._instantiate (internal/modules/esm/module_job.js:164:21)+ at async ModuleJob.run (internal/modules/esm/module_job.js:205:5) at async Loader.import (internal/modules/esm/loader.js:165:24) at async Object.loadESM (internal/process/esm_loader.js:68:5)For a single-line statement like: import{comeOn}from'./fail.cjs';nothing changes, it's the following before and after this patch: Also with this patch, very long import statements that don't fit into an 80 character line are split like this: |
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.
What if findNodeAt returns undefined?
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.
Can you imagine a test case that would reproduce this?
aduh95Oct 16, 2020 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
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.
I'm not sure, but I assume it may happen because it's in your while condition.
Have you tried to use findNodeAround instead of findNodeAt? It seems to fit better our use case.
| node=findNodeAt(parsed,start); | |
| node=findNodeAround(parsed,lineNumber,'ImportDeclaration'); |
mcollina left a comment
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.
lgtm
aduh95 left a comment
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.
There are still some unresolved conversations on this thread that I'd like to be addressed before landing.
809c06a to 36db7dfComparectavan commented Oct 16, 2020
The most serious issue that I must understand before landing is: #35453 (comment) The same happens with the long-multiline test that I just added: Node throws on the second import ( How is that possible? |
guybedford left a comment
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.
This gives a better message for users, so it seems good to me.
Approval conditional on the following:
If v8 changes the error output for some class of cases we don't notice, can this code ever error itself? It is very important that these decorations can never break so the JS must be written incredibly defensively. Eg what if the JS doesn't parse? What if it is a test framework and there was no JS at that location? What if the v8 output changes slightly for some case? What if it uses language features Acorn doesn't support. It otherwise gives the user a far far worse experience.
As this is the first time Acorn is being used in core proper (and not just REPL), it would be good to have some ok from others involved in this integration previously this is a path we want to go down for the project as it sets precedent.
ctavan commented Oct 20, 2020
Thank you @guybedford for the input. I will put some more thought and effort into the parsing code to increase its defensiveness. One of the things that immediately came to my mind: I'm currently using the line-number from the V8 error to determine the offending import statement. @bcoe I recall you have added sourcemap support to Node.js and I believe errors are also rewritten when sourcemaps are in use. Could this interfere with this PR? Would be great if you could shed some light on this. |
bcoe commented Oct 21, 2020
@ctavan where this might break with source maps would be the extra line of context Node.js adds to stack traces: Perhaps we could add an additional test, with a transpiled file that has a multi-line import, and make sure it works? I find these a good opportunity to add a regression test for an additional transpiler; perhaps we could use |
bcoe commented Dec 1, 2020
@ctavan 👋 let me know when you'd like to dust this off, would be happy to pair. |
ctavan commented Dec 10, 2020
@bcoe thanks for chiming in and for offering your help. Now that #35249 landed and named exports of CJS modules are pretty well supported, do we expect this error to be still relevant? I searched open issues for the error message and – apart from my own issue #35259 – only found one issue that pre-date the named CJS import support which landed in v14.13.0/v12.20.0: #32137 (comment) @guybedford could you summarize, under which conditions the error that this PR tries to improve would still be triggered? |
ctavan commented Jan 25, 2021
@guybedford sorry for the ping. Can you answer my question off the top of your head? My assumption is that we can just close this PR and the corresponding issue, but getting confirmation from your end would be nice. |
guybedford commented Jan 29, 2021
@ctavan with cjs-module-lexer we now have detectable CJS exports being handled ok, so that this message only applies to cases where that detection fails. The rebase would still be useful to users in those specific scenarios I think. Entirely up to you if you want to pick this up again. |
When trying to import named exports from a CommonJS module an error is thrown. Unfortunately the V8 error only contains the single line that causes the error, it is therefore impossible to construct an equivalent code consisting of default import + object descructuring assignment. This was the reason why the example code was removed for multi line import statements in nodejs#35275 To generate a helpful error messages for any case we can parse the file where the error happens using acorn and construct a valid example code from the parsed ImportDeclaration. This will work for _any_ valid import statement. Since this code is only executed shortly before the node process crashes anyways performance should not be a concern here. Fixes: nodejs#35259 Refs: nodejs#35275
Co-authored-by: Antoine du Hamel <[email protected]>
Co-authored-by: Antoine du Hamel <[email protected]>
Co-authored-by: Antoine du Hamel <[email protected]>
Co-authored-by: Antoine du Hamel <[email protected]>
This reverts commit 22f700c.
c7931bd to 533560fComparectavan commented May 15, 2021
@guybedford since my priorities have shifted a little bit recently I doubt that I'll find the time to complete this PR any time soon. I'll therefore close it and leave it to other folks to pick it up again in case there's interest. |
guybedford commented May 15, 2021
@ctavan thanks for the update. If you do find time again in future and come across any areas we can improve in future your contributions would always be appreciated. |
When trying to import named exports from a CommonJS module an error is
thrown. Unfortunately the V8 error only contains the single line that
causes the error, it is therefore impossible to construct an equivalent
code consisting of default import + object descructuring assignment.
This was the reason why the example code was removed for multi line
import statements in #35275
To generate a helpful error messages for any case we can parse the file
where the error happens using acorn and construct a valid example code
from the parsed ImportDeclaration. This will work for any valid import
statement.
Since this code is only executed shortly before the node process crashes
anyways performance should not be a concern here.
Fixes: #35259
Refs: #35275
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes