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
Migrate package/source to ES module(s) #537
Conversation
Both variants should sit in a https://www.npmjs.com/package/release-it EDIT: Once this is in place (separate PR!) we can close #501. |
449ead4
to
7d5fad2
Compare
As amd + umd compatible modules are now being build by rollup we shouldn't need to test this any longer.
We need to reference the rollup umd output file here.
@mkay581 Do you want to take a look? |
We produce variants of the source code by rollup. Note using a "module" property is not (yet?) standard but the approach both webpack and rollup.js suggest going forward.
Rollup provides an out-of-the-box option for this, thus not going to test (generated).
06ab811
to
b243a44
Compare
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 didn't look too deeply at the internal code but this overall looks good! I had a comment about the mjs file extension. Nice work!
|
||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script> | ||
<script type="module" src="/path/to/js.cookie.mjs"></script> |
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.
Hmmm enforcing consumers to use a JS file with a .mjs
file extension at this level seems a little odd. Would all browsers know how to interpret a .mjs file extension?
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 didn't mean to force consumers, more like provide an example of how to include the module of that package directly, along with the fallback. In general, browsers do not care about the extension, though it's important a server serves mjs
files with the proper mime type of text/javascript
.
Generally I'm assuming the more likely use case for ES modules is to be bundled up, so I might add that here...
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.
Or do you mean it's too early to use mjs
files in general? I was looking at https://v8.dev/features/modules and it seemed 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.
AFAIK, mjs files only work with node out of the box. The only way browsers would parse a file with this extension is if, as you say, the
server serves mjs files with the proper mime type of text/javascript
In other words, this won't work for consumers if they don't know to configure their server to serve the proper mime type. I would suggest we change the .mjs extension to .js like mentioned in #537 (comment). Then it wouldn't require a server change.
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.
It seems the value for outputting a .mjs
extension is to assist on documentation. Should we stick with one file for simplicity? Is there another reason for having both .js
and .mjs
?
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 now I'm siding with the node developers and wish to keep the extension:
Still, we recommend using the .mjs extension for modules, for two reasons:
- During development, the .mjs extension makes it crystal clear to you and anyone else looking at your project that the file is a module as opposed to a classic script. (It’s not always possible to tell just by looking at the code.) As mentioned before, modules are treated differently than classic scripts, so the difference is hugely important!
- It ensures that your file is parsed as a module by runtimes such as Node.js and d8, and build tools such as Babel. While these environments and tools each have proprietary ways via configuration to interpret files with other extensions as modules, the .mjs extension is the cross-compatible way to ensure that files are treated as modules.
I'm assuming it will be rather rare that the source will be consumed directly, rather than being bundled up with a tool. In the latter case the extension matters less, stressing the aforementioned arguments further.
Let's see how it goes. We can always change that back later on, but let's get some feedback first and start releasing a v3 beta once we're ready...
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.
Sounds good, if it's a convention it doesn't hurt simply following it.
@@ -0,0 +1,143 @@ | |||
/*! |
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 this file just be named src/js.cookie.js
?
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.
See my comment in the other discussion.. for now I'd like to keep it as it is, and be very explicit about this being an ES module.
|
||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script> | ||
<script type="module" src="/path/to/js.cookie.mjs"></script> |
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.
AFAIK, mjs files only work with node out of the box. The only way browsers would parse a file with this extension is if, as you say, the
server serves mjs files with the proper mime type of text/javascript
In other words, this won't work for consumers if they don't know to configure their server to serve the proper mime type. I would suggest we change the .mjs extension to .js like mentioned in #537 (comment). Then it wouldn't require a server change.
What do you mean by this part? |
I was referring to that we're not (yet) using modern ES syntax at all, but I wish to start doing so. E.g. arrow functions, This PR provides the foundation for that (Rollup.js), we'd need an additional babel transpilation step in order to maintain compatibility with some older browsers I assume (not sure whether IE 10 already supports these newer syntax features). Or we're going for TypeScript, which by now I'm open to.. 😬 |
I believe TypeScript is an unnecessary build complexity for almost no gain in js-cookie. It's not like we have tons of modules calling each other where an interface becomes strictly necessary to ensure program correctness. Our test suite is pretty thorough, it's not like TypeScript is gonna save us from bugs. Instead of relying on babel to transpile the code, what if we make sure that we run the tests on every supported browser to make sure we're not using unsupported features (Browserstack)? That way we avoid adding more build steps, we won't need them. That also allows us to focus on reducing the size of the lib by working directly on the code we control instead of magically generating code we don't control. The only build steps or tools I see value here without an unnecessary level of complexity are the test runs, the minification step, and the byte size check. What do you think? |
I agree with all of that. At the same time, though, using the very same talking points, you could argue that this whole PR is not needed. I believe it's worth it starting to write JS code in a more modern fashion. |
Also, using newer syntax idioms will likely produce smaller sizes. It really depends on which browsers we need to target though. I haven't thought that through yet. |
Can't we use the modern syntax straight away now that IE support is gone? |
Not as long as we’re supporting IE 10 + 11: |
I see. From what I looked at the code of seems we still have both options of using the js file or the ES6 one so ppl can still choose only modern code. Also might need to notify jsdelivr about which bundle is the suportted one, it seems they're minifying themselves when a new js-cookie comes out since we don't have minified version in the SCM. |
..if I find some more time I’m going to experiment with this, in order to see how much bigger a transpiled source would become. Maybe we can save somewhere else, using for instance |
Started a small fun experiment with the syntax for the At the moment it looks like this: function extend () {
var i = 0
var result = {}
for (; i < arguments.length; i++) {
var attributes = arguments[i]
for (var key in attributes) {
result[key] = attributes[key]
}
}
return result
} Let's use some of the newer Array methods, without the need for transpiling, it's all still supported by IE 10+. This doesn't look much smaller though, and also not necessarily more readable, so maybe there's not much to gain: function extend () {
return [].slice.call(arguments).reduce(function (a, c) {
Object.keys(c).forEach(function (k) {
a[k] = c[k]
})
return a
})
} Let's start using arrow functions! This is starting to look better: function extend () {
return [].slice
.call(arguments)
.reduce((a, c) => (Object.keys(c).forEach(k => (a[k] = c[k])), a))
} Still we have to deal with function extend () {
return Array.from(arguments)
.reduce((a, c) => (Object.keys(c).forEach(k => (a[k] = c[k])), a))
} Still too much code though, let others do all the heavy-lifting: function extend () {
return Object.assign({}, ...arguments)
} So yeah, at this point the attributes = Object.assign(
{
path: '/'
},
api.defaults,
attributes
) |
yeah, I think we built the extend() function at the time for the very reason Object.assign wasn't supported by the browsers using js-cookie |
Maybe it didn't even exist, I remember when we were "unjqueryfying" the source we needed a replacement for I was curious about a thing. In a bundling scenario I could imagine we were actually saving bytes, at least when other packages were also requiring the same polyfill, the bundler should only add it once... (?) |
We would be actually saving bytes in a bundling scenario if we were still using I believe the path that was taken so far is to reduce the number of dependencies required for developers using js-cookie other than on the ECMAScript spec. Now with React, Vue, etc. I see that decision as making even more sense. When React hype goes by, JSX becomes an HTTP media type, or something else comes along, js-cookie will still survive. It will only die once cookies are no more. That's why I'm very weary on adding too many dependencies to it, although dev dependencies are easy to pivot so it's ok. |
I think you misunderstood me. I wasn’t advocating for bringing back a dependency to jQuery, nor any other dependency. (And in codebases where there was a polyfill for |
Oh ok, you were talking about a polyfill to |
This is the first part (foundation) of the effort to modernize the package. We start providing the package as an ES module.
While the package's
main
remains a pointer to a CommonJS UMD module we're also providing the ES module variant via the package'smodule
property, so that bundlers are aware of it.Syntax hasn't been modernized yet (out of scope of this PR); it will require adding Babel to the Rollup.js based build tooling for browser compatibility. Similarly, only in a follow-up PR I'm going to provide an automated release process, so we can produce the required distributions in a fail-safe manner.
(Along the way I've updated QUnit "accidentally", this wasn't strictly required for this PR.)
main
for commonjs/nomodule variant, andmodule
, distributing minified versions viafiles
would take care of Please provide a compressed package of es6's module syntax in NPM. #501)Drop support fornoConflict()
Notes:
See #528
Closes #536, #535, #423