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

Is there a way to determine how a module is being imported? #1006

Closed
mikecann opened this issue Mar 19, 2021 · 7 comments
Closed

Is there a way to determine how a module is being imported? #1006

mikecann opened this issue Mar 19, 2021 · 7 comments

Comments

@mikecann
Copy link

Im trying to convert an existing webpack based chrome extension to esbuild.

It builds okay but at runtime I get an error about unsafe-eval:

background.js:36793 Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' https://ssl.google-analytics.com https://js.stripe.com/".

    at Function (<anonymous>)
    at background.js:36793
    at background.js:26
    at background.js:36799
    at background.js:26
    at background.js:38154
    at background.js:26
    at background.js:38436
    at background.js:26
    at background.js:39915

I think this is because "eval" is not alowd in chrome extensions.

From the stacktrace above line 36793 of background.js is:

 Function("r", "regeneratorRuntime = r")(runtime);

So it looks like its coming from regenerator-runtime which is confirmed if I scroll up to the top of the block:

// node_modules/regenerator-runtime/runtime.js
var require_runtime = __commonJS((exports, module) => {
...

So I think regenerator runtime is being included because of babel, but im not sure why babel would be getting built in?

Is there a way I can work out the chain of dependencies to why babel is being included in the build? Im suspecting a third party library is doing something odd but im not sure how to work out which one..

@evanw
Copy link
Owner

evanw commented Mar 19, 2021

You should look at the metafile option: https://esbuild.github.io/api/#metafile. It gives you a JSON object with the entire module graph (all input files and what other files they import).

@mikecann
Copy link
Author

mikecann commented Mar 19, 2021

Wow @evanw you are quick!

Thanks for the suggestion. I have combed through it and tried to use bundle-buddy but its not 100% clear where its coming from. I believe it might be AntD again that is pulling in @babel/runtime .

Heres the meta if you are interested: https://gist.github.com/mikecann/c0908d444fd2b6b56041dff1c7376d5a

When I google the error then I come across this post which seems to be talking about the same problem, in that issue he links to the source which has this interesting comment:

try {
  regeneratorRuntime = runtime;
} catch (accidentalStrictMode) {
  // This module should not be running in strict mode, so the above
  // assignment should always work unless something is misconfigured. Just
  // in case runtime.js accidentally runs in strict mode, we can escape
  // strict mode using a global Function call. This could conceivably fail
  // if a Content Security Policy forbids using Function, but in that case
  // the proper solution is to fix the accidental strict mode problem. If
  // you've misconfigured your bundler to force strict mode and applied a
  // CSP to forbid Function, and you're not willing to fix either of those
  // problems, please detail your unique predicament in a GitHub issue.
  Function("r", "regeneratorRuntime = r")(runtime);
}

I must admit I dont fully understand it but does esbuild enforce "strict mode" when it shouldnt be?!

Edit: It appears if the root of the problem is this tho im not exactly sure what to do about it as the "solutions" on there dont seem to work.

@evanw
Copy link
Owner

evanw commented Mar 19, 2021

Here's some code to get you started:

const fs = require('fs')
const json = JSON.parse(fs.readFileSync('./meta.json', 'utf8'))

function why(what) {
  const visited = {}
  let found
  function search(path, trace) {
    if (path === what) {
      found = trace.concat(path)
      return true
    }
    if (visited[path]) return
    visited[path] = true
    const input = json.inputs[path]
    for (const imported of Object.values(input.imports))
      if (search(imported.path, trace.concat(path)))
        return
  }
  for (const output of Object.values(json.outputs))
    if (output.entryPoint && search(output.entryPoint, []))
      break
  if (found)
    console.log(what, 'is included because of', found)
  else
    console.log(what, 'was not found')
}

why('node_modules/node_modules/regenerator-runtime/runtime.js')

That should tell you one of the import paths that is pulling it in. You can modify the code to tell you all import paths instead.

@mikecann
Copy link
Author

Awesome thanks @evanw this is what it kicks out:

node_modules/regenerator-runtime/runtime.js is included because of [
  'apps/extension/src/background.tsx',
  'apps/extension/src/state/utils/syncMiddleware.ts',
  'libs/client-shared/src/index.ts',
  'libs/client-shared/src/components/ads/kofiDonate/ConnectedKofiDonateDashButton.tsx',
  'node_modules/antd/es/index.js',
  'node_modules/antd/es/affix/index.js',
  'node_modules/antd/es/config-provider/index.js',
  'node_modules/rc-field-form/lib/index.js',
  'node_modules/rc-field-form/lib/Field.js',
  'node_modules/rc-field-form/lib/utils/validateUtil.js',
  'node_modules/@babel/runtime/regenerator/index.js',
  'node_modules/regenerator-runtime/runtime.js'
]

Which is what I suspected its used by quite a few things but mainly AntD. Because removing AntD is not an option for me I need to try to fix the source of the problem, the CSP violation in regenerator-runtime.

As I mentioned in my previous comment it looks like its a known issue in regenerator-runtime, the problem is im not sure what to do about it.

I suspect I have to wait until regenerator-runtime fixes the problem.

As a work around is it possible to disable "strict mode" in esbuild?

@evanw
Copy link
Owner

evanw commented Mar 19, 2021

As a work around is it possible to disable "strict mode" in esbuild?

This isn't possible because esbuild doesn't enable or disable strict mode itself. You can double-check by seeing if your bundle starts with "use strict". If it's not at the start of your bundle, then your bundle does not force itself to be run in strict mode.

Whether your bundle is run in strict mode or not is up to your JavaScript runtime. For example, loading the bundle with <script type="module"> will cause your bundle to be in strict mode since all ECMAScript modules must be evaluated in strict mode according to the ECMAScript specification.

I have no idea how Chrome extensions work, but it's possible they force strict mode too. It's also possible that they aren't run in strict mode but are run in a special environment where you aren't allowed to declare a global variable by assigning to an undeclared variable. That would break this library's strict mode check.

One potential workaround to try could be to use esbuild's banner feature to insert var regeneratorRuntime; at the top of your bundle. Then this code should hopefully at least not enter into the catch block. I'm not sure if that will mess anything else up or not though since I'm not familiar with that library.

@mikecann
Copy link
Author

mikecann commented Mar 19, 2021

@evanw woooooooooo!! You did it once again mate!

banner: {
      js: `var regeneratorRuntime;`,
    },

Fixes the issue.

Not entirely sure either what is causing it, just happy to have a workaround.

Massive thanks again for all your hard work and detailed explainations.

@wepsree
Copy link

wepsree commented Jul 22, 2022

I am in a similar situation as well, but js: \var regeneratorRuntime;`,tobannersdid not removeeval` from my output.

I see things like:

r = r || new Function("return this")();
self.Math == Math ? self : Function("return this")();
"function" != typeof t4 && (t4 = new Function("" + t4));
s("function" == typeof t2 ? t2 : Function(t2), e2);
self.Math == Math ? self : Function("return this")();
s[e2] = Function("F,a", "return new F(" + n2.join(",") + ")");
("function" == typeof e2 ? e2 : Function(e2)).apply(this, i2);
Function("r", "regeneratorRuntime = r")(n);
var t3 = Function("return this")().require("vertx");
t3 = Function("return this")();

which I believe are using eval. What are my options at this point to remove eval?

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

3 participants