-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
feat: add support for import assertions #4267
Conversation
Codecov Report
@@ Coverage Diff @@
## master #4267 +/- ##
=======================================
Coverage 98.39% 98.39%
=======================================
Files 204 204
Lines 7288 7288
Branches 2081 2081
=======================================
Hits 7171 7171
Misses 58 58
Partials 59 59
Continue to review full report at Codecov.
|
Not so sure yet.
Update to clarify: You were talking about using JSON.parse at runtime, right? In the end, the solution could be to
|
The longer I think about this, the more I start to rethink this. To make it clear, I think import assertions will become really important and useful if we manage to get them "right", the question is just what that would be.
|
the original ecma proposal was split into 2 proposals: import assertions and json modules (which is built on top of import assertions). browsers, at least chrome, already ship json modules (I believe css modules are on their way). node.js will ship it in v17.1.0 nodejs/node#40758
good point. would it be possible to use JSON.parse() as an assert-kind-of-thing, but keep going with the static analysis afterwards? unfortunately that's beyond my knowledge. 😞
I was just throwing out ideas. I think it could be done at user runtime or rollup built time. I have to think it thru again, but I think it should definitely go thru JSON.parse one way or another. Personally I also strongly believe json should be exported as default only, to lessen confusion. otherwise user code will not run unbundled (in node.js and the browsers), which is being used a lot in development and tests. on the other hand I guess people will eventually figure it out on their own. |
the assertion was added to the ecma spec for browsers, as they use the mime-type and not the file extension to get the module type. whereas node.js uses file extensions as well as the package.json type property. with an assertion the developer can make sure that the server does not unexpectedly send a different mime-type and code could potentially be executed: https://github.com/tc39/proposal-import-assertions#motivation that's why node.js is a bit confusing in that regard, and one could argue the type assertion is not needed. while this would run in he browser, the same would throw throw in node.js: import foo from './foo.js' assert { type: 'json' } // if we have valid json in the js file there was, and probably still is, a big debate about if assertion-less syntax should be allowed in node.js or not. currently you need a flag to use either.
interestingly this came up in the node.js PR as well, and I'm not quite sure if it has been resolved, nor why this was an issue to begin with. I'll have to read thru the PR comments again. that said, I'm 99% sure that currently the module instance is the same when loaded with and without an assertion. (keep in mind, this would not be possible in the browser, only in node.js).
yes. Should be easy to verify this for
see above.
Not quite sure, that might be up to the host to decide what to do. currently chrome throws if there's an unknown assert type:
good question. for json, and soon css, plugins should do what the spec recommends, and what engines implement. I think I would not allow other assertion types as well, otherwise you'd deviate from the spec. that way future type assertions can be supported easily without breaking the entire ecosystem. |
Thanks for looking into this, wow, I did not expect it worked this way. But I could confirm: You can actually add "nonsense assertions" in Chrome safely, and they will just give you the same module instance: // foo.js
export default {};
// main.js
import a from './foo.js' assert { foo: 'a' };
import b from './foo.js' assert { foo: 'b' };
import c from './foo.js';
console.log(a === b); // true
console.log(a === c); // true I guess as "type" is the only one that has an effect right now, they additionally dodged the question here by doing file type checking in Node and MIME type checking in the browser (which is mostly the same thing as servers usually infer the MIME type from the file extension). That means my suggestion of extending ids is actually irrelevant as the loaded id is the same and they work more like module flags, similar to The problem is now: What do we do if assertions are asymmetric i.e. differ between imports? Would then the first import decide what the flag is? I took the time to read through the proposal and it seems they are also aware of the question and decided to not answer it as "all solutions have issues" (i.e. first wins, throw if different, clone if different). However it seems that there is a strong willingness to not go for "clone", at least not at the moment and definitely not for JSON. That means we can make a decision here:
In any case, we need to figure out how the assertion is forwarded to the loader. |
yeah, the assert options are optional as well: import a from './foo.js' assert {} // works while this seems to be ok: import a from './foo.js' assert { foo: 'a' }
import b from './foo.js' assert { foo: 'b' } for import config from "./config.json";
// Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type
// of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec. |
update: node.js removed assertion-less json module imports: nodejs/node#40758 (comment) in v17.1.0 ( |
eca6a6c
to
71d20c9
Compare
This is now part of Rollup 3 |
This PR contains:
Are tests included?
Breaking Changes?
List any relevant issue numbers:
#3799
node.js support: json modules import assertions was recently merged into master
(not yet released)released with v17.1.0: nodejs/node#40250webpack support: webpack/webpack#12278 (parsing only)
typescript support: microsoft/TypeScript#40698 (parsing only)
babel support: https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-import-assertions (parsing only)
Description
there's a couple questions we need to figure out about the differences on what is currently supported, and what and how support should look like going forward:
current json support:
needs a plugin
plugin
always
exports adefault
export, and named exports if it's a plain object with propertiesall other types are being exported as default export only
node.js support behind a flag, support behind the flag has been removed in v17.1.0.--experimental-json-modules
import assertions
are now required, including the same flag. only supports default exportsnot supported by browsers (and likely never will be)
this should be not possible, but currently is supported:
// foo.json
bundled to:
<html />
import assertions/json modules
--experimental-json-modules
)I think the most straight forward and spec compliant way would be to export as default and use JSON.parse() at user runtime on the file content. if not json, throw. in addition retire named exports - in a major version - otherwise it would just add more confusion in an ecosystem with a lot of weirdness, fragmentation and inconsistencies. (javascript in general, but also the cjs, esm, amd, umd, systemjs module nightmare [and transition] we are currently in.)
another possible spec compliant alternative could also be to read the file contents and run JSON.parse(...) at rollup runtime. I think I prefer the former, as it aligns with the non-bundled runtime. on the end of the day it might not matter, other than performance #3799 (comment) and the fact that one would throws at user runtime, and the other would throw at rollup runtime (if we don't have valid json). throwing at user runtime should not be considered much of a problem, since javascript (or any scripting language) is doing the same.
edit:
forgot to mention a scenario where import assertions are being used, but declared as external. the import assertion should remain in the code as it otherwise would fail loading unbundled. (currently it is being removed)
becomes: