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

Unable to create UMD bundle #7312

Open
bhovhannes opened this issue Nov 16, 2021 · 7 comments
Open

Unable to create UMD bundle #7312

bhovhannes opened this issue Nov 16, 2021 · 7 comments

Comments

@bhovhannes
Copy link
Contributor

🐛 bug report

I cannot get Parcel to create UMD bundle.

I've found #766 and #453, which makes me think that producing UMD bundle is supported, but it doesn't look like the UMD prelude code is in place in Parcel v2 and I didn't found any mentions of UMD or AMD formats in documentation either.

I was not able to join discord and ask if this is producing UMD bundles is even supported, so opening an issue. At the end it will be awesome to add documentation around this topic to stop any confusion (I'll be happy to do a PR).

🎛 Configuration (.babelrc, package.json, cli command)

No other config, I only have a package.json:

{
  "name": "parcel-umd",
  "private": true,
  "scripts": {
    "build": "parcel build"
  },
  "targets": {
    "app": {
      "source": "src/app.js",
      "optimize": true,
      "context": "browser",
      "includeNodeModules": true,
      "distDir": "dist/"
    }
  },
  "devDependencies": {
    "parcel": "2.0.1"
  },
  "dependencies": {
    "regenerator-runtime": "^0.13.9"
  }
}

🤔 Expected Behavior

Generated bundle should contain UMD prelude.

😯 Current Behavior

Generated bundle does not contain UMD prelude and cannot be loaded using RequireJS.

💁 Possible Solution

I don't know, but I am ready to help :)

🔦 Context

We're using webpack to combine polyfills imported from es-shims and regenerator-runtime packages into a single JS file, which is minified and loadable using RequireJS.

The command was something like:

webpack ./src/app.js -o ./dist/app.js --mode production --output-library-target umd --optimize-max-chunks 1

I wanted to do the same using Parcel and I cannot get Parcel to create UMD bundle. I've put a link to the repo below.

💻 Code Sample

Repo: https://github.com/bhovhannes/parcel-umd-build-issue

Do npm install and then npm run build to invoke parcel. Output will be in dist/app.js. It does not contain UMD prelude (searching for define.amd inside dist/app.js returns no matches).

🌍 Your Environment

Software Version(s)
Parcel 2.0.1
Node 14.17.3
npm/Yarn npm 6.14.13
Operating System macOS Monterey 12.0.1
@mischnic
Copy link
Member

The linked issues are indeed about Parcel 1. Of course it would be great to support this, #7240 would implement the "expose as global variable" part of UMD

@bhovhannes
Copy link
Contributor Author

Thanks!

Can you give some guidance on where in the code this should be implemented, so I can work on an MR?

This can probably be discussed on MR, but do I understand correctly that the support for UMD bundles needs to be implemented as an outputFormat: "UMD" option?

@mischnic
Copy link
Member

The output format is selected here:

let OutputFormat = OUTPUT_FORMATS[this.bundle.env.outputFormat];
there is one class per output format.

@bhovhannes
Copy link
Contributor Author

Status update.
I begin work of adding AMD support to Parcel. I thought that adding AMD will be easier than UMD and it will be better to have it as a first step. I got basic cases working, need to write tests and think about more cases and will open MR.

@FirstWhack
Copy link

oof

@spacecheeserocks
Copy link

I've done some digging around and found that, when using "outputFormat": "global" in a target (see #7080), Parcel does already emit the correct UMD declaration to support naming assigning the module to a global variable.

// https://github.com/parcel-bundler/parcel/blob/v2.9.3/packages/packagers/js/src/dev-prelude.js#L142
(function (modules, entry, mainEntry, parcelRequireName, globalName) {
// -- snip --
    } else if (globalName) {
      this[globalName] = mainExports;
    }
// -- snip --
});

However, during the build process, Parcel never provides a value for globalName, so this code path is never reached.
The code responsible for invoking this IIFE currently only specifies the first 4 parameters.

// https://github.com/parcel-bundler/parcel/blob/v2.9.3/packages/packagers/js/src/DevPackager.js#L173
    let contents =
      prefix +
      '({' +
      assets +
      '},' +
      JSON.stringify(
        entries.map(asset => this.bundleGraph.getAssetPublicId(asset)),
      ) +
      ', ' +
      JSON.stringify(
        mainEntry ? this.bundleGraph.getAssetPublicId(mainEntry) : null,
      ) +
      ', ' +
      JSON.stringify(this.parcelRequireName) +
      ')' + // <-- NOTE: missing parameter 5 (globalName)
      '\n';

I would like to submit a PR to add this parameter, but I would like to request some comments on where the global variable name should actually be configured/specified by the developer. In Parcel v1, the UMD name was specified by the --global CLI parameter, but I dont't think this is practical in Parcel v2.

In my opinion, the global name should:

  • Be optional - this falls back to the current behaviour anyway, and perhaps there is some case where a dev may want to emit a UMD module with no global definition.
  • Not tied to target name, input or output names
  • Defined per-target, rather than defined globally (such as a CLI parameter or at the top-level of package.json).

My proposal would be to add a "globalName" parameter to a target, like so:

{
    "name": "my-library",
    "version": "1.0.0",
    "source": "src/index.ts",
    "tgt-global": "dist/mylib.browserbundle.js",
    "types": "dist/index.d.ts",
    "targets": {
        "tgt-global": {
            "context": "browser",
            "engines": {
                "browsers": "> 0.5%, last 2 versions, not dead"
            },
            "outputFormat": "global",
            "scopeHoist": false,
            "includeNodeModules": true,
            "globalName": "MyBrowserLibName" // <-- Proposal for UMD global name
        }
    }
}

I understand that this might not be the best place to introduce such a parameter, especially as it is only required for outputFormat: global, and may increase the amount of schema validation required for all other targets where this setting is not required/should not be specified at all.

Other than the specific location to read this parameter from, I have already written (rough, unlinted, untested) code that would be ready to PR this feature if there are no other concerns!

@nyan-left
Copy link

@spacecheeserocks this would be super handy! 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants