Skip to content
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

Improving ESM support and documentation, esp. for browsers #3217

Closed
3 tasks done
ayjayt opened this issue Jan 31, 2024 · 16 comments
Closed
3 tasks done

Improving ESM support and documentation, esp. for browsers #3217

ayjayt opened this issue Jan 31, 2024 · 16 comments
Assignees
Labels
pinned A long-lived issue, such as a discussion
Milestone

Comments

@ayjayt
Copy link
Contributor

ayjayt commented Jan 31, 2024

Update the package to better support ESM, especially in the browser, by default:

  • Include external dependencies in the regular .esm.js build, which removes the need for specifying import maps. They are small dependencies, and other builds already include them (like the UMD build).
  • Update package.json to include module, pointing to the ESM build.
  • Include the exports key in package.json for import and require.

The package.json updates for ESM will work in the browser, in bundlers like Webpack, and in Node, because it doesn't have any external dependencies.

Original post follows:


I made it work. This about using cytoscape esm or ANY esm in the browser WITHOUT bundling, which in cytoscape is currently brokenish.

You have 2 dependencies, 1 of which, lodash, you split into a few.

Browser only supports ESM w/ relative imports because they haven't figured out how they want to handle non-URI indicators like import { myFunc } as "SomeWebGlobalModule".

So you use an inline <script type=importmap> in <head>, mine looks like this:

{
  "imports": {
      "lodash":"https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js",
      "heap": "./js/vendor/heap.mjs"
   }
}

(no src available, only one declaration, first thing).

I also had to change the cytoscape ESM file:

import { default as Heap } from 'heap';
import { debounce, get, set, toPath } from 'lodash';

Now, with esm, because of static analysis, you don't need to split lodash off into separate things to make a bundler understand not to use the whole library. It can shake the tree. The network doesn't shake trees tho, and so we pull the whole file over the network. I wanted to use a CDN, and I didn't find esm versions of the individual methods, only the whole lodash.

Heap is an unmaintained library, 3 years now. Its also like 80 lines of code and you should probably just absorb it. I did fix it to support esm qiao/heap.js#34 (there is no way to import a cjs-only package except in node or webpack or rollup or etc which understand require because the import is effectively transpiled to require since that's all it supports.)

My suggestions for better supporting ESM:

  1. Absorb heap's few lines of code (ultimately reducing your size because no glue) or publish the updated package yourself. It's unmaintained.
  2. Then give people my importmap w/o heap and also change your _esm file for my combined import.
    or
  3. Or someone publish on CDN an esm version of the lodash individual packages and add each one to importmap.

Hi @TpmKranz and @maxkfranz . Just @ ing you because I see you on the git-blame for how to use ESM w/ browser :-)

Thanks!

ps. thinking about making a pull request to npm like npm install --web which could solve some of these issues.

@maxkfranz
Copy link
Member

Good analysis.

What about absorbing all the dependencies?

They're small and it would simplify the bundles -- i.e. '.min.js' would be the same as '.js' w.r.t. dependencies. You also wouldn't need to specify a mapping.

PRs welcome.

@ayjayt
Copy link
Contributor Author

ayjayt commented Jan 31, 2024 via email

@maxkfranz
Copy link
Member

The min builds include the dependencies within the one file.

@ayjayt
Copy link
Contributor Author

ayjayt commented Jan 31, 2024

Oh so even in ESM you bundle them 🤔. I'm not sure I would expect that behavior as an end-user.

Resolved by your suggestion though! I will see maybe if I get around to it. Thanks!

Copy link

stale bot commented Feb 16, 2024

This issue has been automatically marked as stale, because it has not had activity within the past 14 days. It will be closed if no further activity occurs within the next 7 days. If a feature request is important to you, please consider making a pull request. Thank you for your contributions.

@stale stale bot added the stale label Feb 16, 2024
@maxkfranz maxkfranz added pinned A long-lived issue, such as a discussion and removed stale labels Feb 20, 2024
@maxkfranz maxkfranz added this to the 3.29.0 milestone Feb 20, 2024
@maxkfranz maxkfranz changed the title On improving ESM support and documentation: Improving ESM support and documentation, esp. for browsers Feb 20, 2024
maxkfranz added a commit that referenced this issue Feb 20, 2024
Ref:

- Improving ESM support and documentation, esp. for browsers #3217
- Change rollup.config to bundle deps in esm.js #3221
maxkfranz added a commit that referenced this issue Feb 20, 2024
Ref:

- Improving ESM support and documentation, esp. for browsers #3217
- Change rollup.config to bundle deps in esm.js #3221
@maxkfranz
Copy link
Member

maxkfranz commented Feb 20, 2024

Tested package.json updates from 9bd7a73 in Enrichment Map:

https://github.com/cytoscape/enrichment-map-webapp

  1. cd cytoscape
  2. git checkout unstable && git pull
  3. npm link
  4. cd ../enrichment-map-webapp
  5. npm link cytoscape
  6. npm run watch
  7. Go to http://localhost:3000 in the browser
  8. Run the demo query

@mikekucera, @chrtannus -- this is an important but potentially disruptive change. Would you test the steps above and confirm that the EM build still works for you? I'd like to confirm that this is working on machines other than my Macbook.

@maxkfranz
Copy link
Member

@ayjayt, would you also confirm the steps in the previous comment -- replacing enrichment-map-webapp with your own project that uses Cytoscape with a build system, like Webpack or Rollup.

@ayjayt
Copy link
Contributor Author

ayjayt commented Feb 20, 2024

I can do it before Monday

maxkfranz added a commit that referenced this issue Feb 27, 2024
- Improving ESM support and documentation, esp. for browsers #3217
- Adding exports map in package.json #3218
@maxkfranz
Copy link
Member

maxkfranz commented Feb 27, 2024

I've updated the dist files on the unstable branch: https://github.com/cytoscape/cytoscape.js/blob/4372a14065ed26aa781f3b563d5907b9fee80054/dist/cytoscape.esm.js

That should make your testing a bit simpler. You can verify that the ESM file linked above doesn't use any import statements whatsoever.

Steps:

  1. git checkout git@github.com:cytoscape/cytoscape.js.git
  2. cd cytoscape.js
  3. git checkout unstable && git pull
  4. npm link
  5. cd ../my-app
  6. npm link cytoscape
  7. npm run watch or npm run dev or npm run build -- however you do it
  8. Verify your app builds and works

@maxkfranz
Copy link
Member

I've updated the dist files on the unstable branch: https://github.com/cytoscape/cytoscape.js/blob/4372a14065ed26aa781f3b563d5907b9fee80054/dist/cytoscape.esm.js

That should make your testing a bit simpler. You can verify that the ESM file linked above doesn't use any import statements whatsoever.

Steps:

  1. git checkout git@github.com:cytoscape/cytoscape.js.git
  2. cd cytoscape.js
  3. git checkout unstable && git pull
  4. npm link
  5. cd ../my-app
  6. npm link cytoscape
  7. npm run watch or npm run dev or npm run build -- however you do it
  8. Verify your app builds and works

These steps only work if the cytoscape library is imported the proper way with ESM import. It should always look like this:

import cytoscape from 'cytoscape';

Note that only the default may be used. So this would also work:

import { default as cytoscape } from 'cytoscape'

Note: This will not work:

import * as cytoscape from 'cytoscape'

@maxkfranz
Copy link
Member

Documentation updates:

https://js.cytoscape.org/#getting-started/including-cytoscape.js

<script type="module">
import cytoscape from "./cytoscape.esm.min.js";
</script>

This example should be:

<script type="module" src="cytoscape.esm.min.js"></script>

To use Cytoscape.js in an ESM environment with npm (e.g. Webpack or Node.js with the esm package):

import cytoscape from 'cytoscape';

Around this text, there should be a note that import * as cytoscape from 'cytoscape' does not work.

@maxkfranz
Copy link
Member

I can do it before Monday

@ayjayt, how did it go with your build?

maxkfranz added a commit that referenced this issue Mar 6, 2024
Parts of lodash are used:
- lodash/get
- lodash/set
- lodash/toPath
- lodash/debounce

`heap` is used, but it hasn't been updated in a long time.  It probably never needs to be updated unless there's a serious bug.  Doubtful.

Ref:
- Improving ESM support and documentation, esp. for browsers #3217
- Adding exports map in package.json #3218
@ayjayt
Copy link
Contributor Author

ayjayt commented Mar 15, 2024

Hey sorry, had some good news over here that I had to act on, I'm free now to do it this weekend!

@ayjayt
Copy link
Contributor Author

ayjayt commented Mar 18, 2024

My app is 100% web so that buildflow doesn't work for me- but I did set up a test project to see if I could import and dump the contents of cytoscape into stdout. So no bundler, just node.

I get the "you must include type: "module"" error- as in the unstable branch of cytoscape needs to add that to package.json. That's been my experience whenever using esm. Also, I've use type: "module" in other hybrid cjs/esm repositories and have never had it interfere with cjs, so AFAIK it's safe to add even if you're serving several ways to import that are NOT modules.

Around this text, there should be a note that import * as cytoscape from 'cytoscape' does not work.

My least favorite thing about ESM. There are some strong opinions floating around about it. But yes, easiest is to recommend NOT to use import *.

Documentation updates:

https://js.cytoscape.org/#getting-started/including-cytoscape.js

<script type="module"> import cytoscape from "./cytoscape.esm.min.js"; </script>

This example should be:

<script type="module" src="cytoscape.esm.min.js"></script>

Is this true? AFAIK there is no automatic global scope when using type="module", so cytoscape the module is inaccessible when being accessed like that, it's always the application in <script> and the module under import.

In my app, I have to use the original syntax.

@ayjayt
Copy link
Contributor Author

ayjayt commented Mar 18, 2024

I will note that it doesn't work if I add type="module"

Also, I've never seen exports w/ a nested ".":{} dictionary. Obviously it works, maybe that's the default value? Usually I just
"exports": { "module": "...", "require": "..."}

Nitpicking here

@mikekucera
Copy link
Contributor

Released in 3.29.x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pinned A long-lived issue, such as a discussion
Projects
None yet
Development

No branches or pull requests

4 participants