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

Cannot use runtime provided dependencies without workarounds #9919

Open
realGWM opened this issue Aug 15, 2024 · 6 comments
Open

Cannot use runtime provided dependencies without workarounds #9919

realGWM opened this issue Aug 15, 2024 · 6 comments

Comments

@realGWM
Copy link

realGWM commented Aug 15, 2024

🐛 bug report

The bundled file produced by Parcel is ran inside an environment where some dependencies (e.g. "uuid") are provided, therefore I'd like to NOT include those dependencies in the bundle.
Using "shim aliases" successfully makes Parcel not include the dependency in the bundle, but it replaces the import with an empty object.

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

MRE looks like this:

package.json

{
  "name": "parcel-test",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "scripts": {
    "build": "parcel build src/*"
  },
  "dependencies": {
    "uuid": "10.0.0"
  },
  "devDependencies": {
    "@types/node": "20.12.2",
    "@types/uuid": "10.0.0",
    "parcel": "^2.12.0",
    "typescript": "^5.5.4"
  },
  "alias": {
    "uuid": false
  }
}

src/test.ts

// Option 1 - does not work
// import { v4 } from "uuid";

// Option 2 - does not work
// const { v4 } = require("uuid");

// Option 3 - works
// const { v4 } = eval(`require("uuid")`);

const generatedUuid = v4();

console.log(`generated uuid = ${generatedUuid}`);

Option 1 & 2 generate the following bundle:

(()=>{let e=(0,({}).v4)();console.log(`generated uuid = ${e}`)})();
//# sourceMappingURL=test.js.map

Obviously this does not work, because .v4() is called on an empty object here.

Option 3 generates the following bundle:

(()=>{var $parcel$global=globalThis,$parcel$modules={},$parcel$inits={},parcelRequire=$parcel$global.parcelRequire5c22;null==parcelRequire&&((parcelRequire=function(e){if(e in $parcel$modules)return $parcel$modules[e].exports;if(e in $parcel$inits){var r=$parcel$inits[e];delete $parcel$inits[e];var i={id:e,exports:{}};return $parcel$modules[e]=i,r.call(i.exports,i,i.exports),i.exports}var o=Error("Cannot find module '"+e+"'");throw o.code="MODULE_NOT_FOUND",o}).register=function(e,r){$parcel$inits[e]=r},$parcel$global.parcelRequire5c22=parcelRequire);var parcelRegister=parcelRequire.register;parcelRegister("kI9eG",function(module,exports){let{v4}=eval('require("uuid")'),generatedUuid=v4();console.log(`generated uuid = ${generatedUuid}`)}),parcelRequire("kI9eG")})();
//# sourceMappingURL=test.js.map

This works, but

  • It uses an ugly workaround of wrapping require in eval.
  • Produces significantly more (unnecessary) code.

Is there a less ugly solution for this problem, or is this use-case not supported by Parcel?

In webpack I'd simply put the uuid in externals section.

🤔 Expected Behavior

Parcel does not remove import/require of a dependency.

😯 Current Behavior

Parcel replaces import/require of a dependency with an empty object.

🌍 Your Environment

Software Version(s)
Parcel 2.12.0
Node 20.12.2
npm/Yarn 1.22.22
Operating System MacOS
@yrral86
Copy link

yrral86 commented Aug 15, 2024

https://www.npmjs.com/package/parcel-resolver-ignore

If I'm understanding you correctly, this plugin does exactly what you want. Wire it up, add the external dependencies to a parcelIgnore array in your package.json and then import normally.

@realGWM
Copy link
Author

realGWM commented Aug 16, 2024

https://www.npmjs.com/package/parcel-resolver-ignore

If I'm understanding you correctly, this plugin does exactly what you want. Wire it up, add the external dependencies to a parcelIgnore array in your package.json and then import normally.

Thank you for your reply!

Unless I misunderstood how to use that plugin, it doesn't solve the problem :(

I've created a .parcelrc with the following content, like instructed:

{
  "extends": "@parcel/config-default",
  "resolvers": ["parcel-resolver-ignore", "..."]
}

My package.json looks like this now:

{
  "name": "parcel-test",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "scripts": {
    "clean": "rm -rf dist/",
    "build": "parcel build src/*",
    "build:debug": "parcel build src/* --no-optimize"
  },
  "dependencies": {
    "uuid": "10.0.0"
  },
  "devDependencies": {
    "@types/node": "20.12.2",
    "@types/uuid": "10.0.0",
    "parcel": "^2.12.0",
    "parcel-resolver-ignore": "^2.2.0",
    "typescript": "^5.5.4"
  },
  "parcelIgnore": [
    "uuid"
  ],
  "alias": {
    "uuid": false
  }
}

Now I'm getting the following error with either Option 1 or 2:

Image 16-08-2024 at 10 23

@realGWM
Copy link
Author

realGWM commented Aug 16, 2024

Okay I just needed to add

"engines": {
    "node": ">= 20"
  }

to my package.json, and now it works, I'm getting the following bundle:

const e=(0,require("uuid").v4)();console.log(`generated uuid = ${e}`);
//# sourceMappingURL=test.js.map

Thanks @yrral86 !

I'm not sure if I should close the issue now? It would be nice if that could be done without using externals plugins, just with Parcel :)

@realGWM
Copy link
Author

realGWM commented Aug 16, 2024

Eh, actually this doesn't fix every import Parcel screws up :(

My environment also provides some globals, for example $logger.

And if I introduce them directly in the main file, like this:

import { v4 } from "uuid";

declare const $logger;

const generatedUuid = v4();

$logger.info(`generated uuid = ${generatedUuid}`);

It produces a correct bundle:

const e=(0,require("uuid").v4)();$logger.info(`generated uuid = ${e}`);
//# sourceMappingURL=test.js.map

But as soon as I extract it into a separate file and try to import it in the main file:

declare const $logger;

export { $logger };
import { v4 } from "uuid";
import { $logger } from "../common/provided";

const generatedUuid = v4();

$logger.info(`generated uuid = ${generatedUuid}`);

It produces the following bundle, which obviously does not work:

const e=(0,require("uuid").v4)();$f91a14e0afac82af$export$8e50ddd5c960e8af.info(`generated uuid = ${e}`);
//# sourceMappingURL=test.js.map

Do you know if Parcel/parcel-resolver-ignore supports this use-case, or am I out of luck now?

Adding $logger to parcelIgnore does not change the generated bundle, while adding .*provided or ../common/provided results in a following, also broken, bundle:

var e=require("uuid"),r=require("../common/provided");const i=(0,e.v4)();(0,r.$logger).info(`generated uuid = ${i}`);
//# sourceMappingURL=test.js.map

@yrral86
Copy link

yrral86 commented Aug 20, 2024

I haven't done much with env-provided globals. Perhaps you could also build common/provided as an additional target? Presumably it would compile the same as when you put things directly in the main file and then whatever imports it should work.

@realGWM
Copy link
Author

realGWM commented Aug 22, 2024

Thanks for the suggestion!

Unfortunately, it wouldn't work in my case, as the env requires that everything is bundled into a single JS file :(

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

No branches or pull requests

2 participants