parcel-bundler / parcel

The zero configuration build tool for the web. ๐Ÿ“ฆ๐Ÿš€
https://parceljs.org
MIT License
43.45k stars 2.27k forks source link

How do i mark some modules to external? #144

Closed garrydzeng closed 4 years ago

garrydzeng commented 6 years ago

Choose one: is this a ๐Ÿ› bug report or ๐Ÿ™‹ feature request?

๐Ÿ™‹ feature request

๐Ÿค” Expected Behavior

Don't include external module in bundled file everywhere. Like rollup globals option. https://rollupjs.org/#big-list-of-options

๐ŸŒ Your Environment

Software Version(s)
Parcel 1.0.3
Node 9.2.0
npm/Yarn Yarn 1.2.1
Operating System Windows 10
devongovett commented 6 years ago

Can you elaborate on the behavior you'd like to see here? I'm not sure how the option as documented by rollup is useful. Why not just use the global variable? Why is it an import at all?

garrydzeng commented 6 years ago

@devongovett

Let me take React as an example.

I can use React as global variable in my project, but many third-party library imported React in their code and I can't control that, so, if I include React through a CDN, like <script src="//cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script> and the bundle in my index.html, then browser has to download React twice.

In order to avoid this problem, I have to import React in my project. If Parcel can replace React as global variable when it handling third-party library, then it can reduce bundle size & I can use CDN to speed up my website. React can be any library also.

I made an example: parcel-example.zip. Hope this help!

devongovett commented 6 years ago

I see. This seems somewhat related to aliasing actually. See #25.

MalcolmDwyer commented 6 years ago

Another vote for this issue... I really like what you've done here with Parcel. It was super-easy to get my project 95% set up. This issue, (external resources), along with dev-server proxying are the two things holding me back.

Motivation for both is that I'm developing components that will be used within a larger application. There are external dependencies (that I don't want packaged up in the component build), and there are external APIs I want to hit while developing. In both cases, these are things that will be there when the project is deployed, but which are not a part of this component.

garrydzeng mention Rollup's "globals" option. Webpack similarly has "externals" (https://webpack.js.org/configuration/externals/).

You mentioned babel aliasing. That, I think, is a separate issue, because it's after the packager has decided to include a file, you can tell babel an alternate path to get it. There doesn't appear to be a way to tell babel that it should be ignored entirely. (At least not in a way that will keep other things happy). The "external" concept would really need to be configured at the packager.

I'd be happy to help out and contribute code to solving these issues, but given the 'zero-configuration' mantra, it's not clear to me where or how either could/should be done in Parcel. If you have ideas along those lines, please let me know. --Thanks

starkwang commented 6 years ago

+1 on this issue. The aliases implemented in https://github.com/parcel-bundler/parcel/pull/850 can not solve this issue. Just like globals in Rollup and externals in webpack, Parcel also need an option to map modules to a global value.

I'd like to help with this issue if needed : )

derolf commented 6 years ago

I have a patched version that allows external scripts. The idea is to mark scripts as โ€œcopyโ€ and provide a mapping of globale to modules that. But that still is kind of configuration...

davidnagli commented 6 years ago

@starkwang Cool! Iโ€™d be great if you can take this issue ๐Ÿ˜ƒ

DeMoorJasper commented 6 years ago

@derolf @davidnagli Perhaps change the aliasing a slight bit and add something like this:

aliases {
  "react": false // This will ignore the package
}

This would cause it to skip without adding extra complex configurations. (Of course we still have to implement it and i'm not sure if it's sorta allowed by the standards) This behaviour would be kinda similar to how browser.fs === false works for fs resolves

derolf commented 6 years ago

I have a proposal to extend the "alias" syntax to support externals:

"<module>": "<file>!<evaluated export term>"

Example (for cesiumjs):

"cesium": "./node_modules/cesium/Build/Cesium/Cesium.js!Cesium"

Semantics:

Usage: HTML inline load: <script src="cesium"></script>

DeMoorJasper commented 6 years ago

@derolf isn't this different than the described issue? The issue is about using external packages from cdn's wouldn't this just be lazyloading everything from the local server or cdn?

derolf commented 6 years ago

@DeMoorJasper

For CDN:

"cesium": "http://cdn.xyz.com/foo/bar/Cesium.js!Cesium"

could work the same way. But instead of copying into dest folder it's is directly linked.

I think the main idea is to somehow refer to "prebuilt" folders -- disable parcel parsing -- and link them to a fake module.

So the exclamation mark disables parcel parsing of the content and the term afterwards creates the export directive for the fake module.

woubuc commented 6 years ago

@derolf I think the ! syntax is fairly non-obvious, at least I haven't seen it used before. Is this 'standard' across similar implementations? Why not just use the key?

derolf commented 6 years ago

@woubuc So, if you look at webpack's external, you need a key plus which global symbol to bind to that key. So, we need to tell parcel which symbol to bind to the fake module.

Example: three-js exports a global symbol called THREE, so the syntax would be:

"three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/90/three.min.js!THREE"

This follows the same approach in webpack.

MalcolmDwyer commented 6 years ago

@DeMoorJasper 's <PackageName>: false makes sense to me (but misses renaming... which may be important in certain cases). I don't see the advantage of @derolf's <PackageName>: <CDN path>:!<import name>, unless it's solving a different problem than what I was talking about.

Could the webpack externals concept be followed directly, except inside package.json. Then it's a 1-to-1 with a well-known feature, and keeps the ability to rename the import.

package.json:

  "dependencies": { },
  "parcel": { /* Or should externals be at the top level? */
    "externals": {
      "MyProjectConfig": "config",
      "react": "react"
    }
  }
}

Unlike the aliases: {packageName: false}, this allows for renaming. You can import from the proper name of the package (as if you were really packaging it up), but still have the code get linked into whatever the actual named variable is in the CDN/inline script.

My deployed index.html/jsp that pulls in the parceled project...

   <script>
      config = {
        someValue: 123
      }
   </script>
   <script src="://some-cdn/react.min.js"></script>
   <script src="./build/parceled-up-project.js></script>

And finally... some random source file in my project:

import MyProjectConfig from 'MyProjectConfig'
import React, { Component } from 'react'

For both those imports, the packager can find the 'from' in the externals list, and know that it doesn't need to actually fetch/import anything. And the actual variable names can get assigned with the desired renaming.

Something like this makes the most sense to me... but it's obviously getting away from 'zero-config', so I assume it would need some consensus before building it in. What do you think?

derolf commented 6 years ago

Package.json is not available to the parser. Because resolution is done afterwards.

So, you need two things:

derolf commented 6 years ago

@MalcomDwyer okay, I can implement your โ€œexternalsโ€ in package.json proposal and create module stubs the same way that Webpack does.

But, what is a good syntax to tell parcel to NOT bundle a script that is already in the public folder?

Like: Githubissues.

  • Githubissues is a development platform for aggregating issues.