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

[BUG] _interopRequireWildcard not defined in browser when using "import()" in page.evaluate() #23255

Closed
Ademsk1 opened this issue May 24, 2023 · 5 comments · Fixed by #31285
Closed

Comments

@Ademsk1
Copy link

Ademsk1 commented May 24, 2023

System info

  • Playwright Version: [v1.34.0]
  • Operating System: [macOS 13.2]
  • Browser: [All]
  • Other info:
    Trying to import a url module in page.evaluate raises the following error:
    Error: page.evaluate: ReferenceError: _interopRequireWildcard is not defined
    despite me only using the following:
page.evaluate(()=> {
const {getAuth} = import('some-url-link-to-module')
}

I found this strange, as I don't use _interopRequireWildcard at all. When in the debugging browser session, I plugged my commands in manually, and had no issues at all. On further inspection within the evaluate function, it seems as though the command import(...) gets converted into:

await Promise.resolve().then(() => _interopRequireWildcard(require('some-url-link-to-module')));

within the pageFunction variable in page.evaluate. I decided to quickly try and change it back to its regular import(...) and it worked! However, even if _interopRequireWildcard was defined in the browser, require wouldn't be, and so if this could be altered so that import() doesn't get modified, this would work.

Steps

  • Run a test with page.evaluate(...) where within it we use the import() method to import a url module

Expected
Module should be imported and usable

Actual
Error occurs due to some action causing import() to be rewritten as a promise that uses _interopRequireWildcard

@Smrtnyk
Copy link

Smrtnyk commented May 24, 2023

seems like they transpile that code that is supposed to be evaluated in the browser

@Ademsk1
Copy link
Author

Ademsk1 commented May 24, 2023

seems like they transpile that code that is supposed to be evaluated in the browser

Yeah, which is a bit strange right? Surely a TS dev would know to write Javascript within the page.evaluate?

@Ademsk1
Copy link
Author

Ademsk1 commented May 24, 2023

I've been able to get around the issue by running

page.addScriptTag({url: 'http://X.com/Y', type: 'module'})
...
page.addScriptTag({content: 'import {Z} from http://X.com/Y', type: 'module'})

to enable me to use Z from the imported module. Despite this, I still think that the the import() method should work in page.evaluate.

@mxschmitt
Copy link
Member

It's caused by the @babel/plugin-proposal-dynamic-import plugin which we use.

Wrote a test for it.
test('should support dynamic import inside page', async ({ runInlineTest, server }) => {
  server.setRoute('/helper.js', (req, res) => {
    res.setHeader('Content-Type', 'application/javascript');
    res.end(`export const foo = 'from helper';`);
  });
  server.setRoute('/empty.html', (req, res) => {
    res.setHeader('Content-Type', 'text/html');
    res.end(`<script type="module">
      import { foo } from './helper.js';
      window.foo = foo;
    </script>`);
  });
  const result = await runInlineTest({
    'playwright.config.ts': `
    import { PlaywrightTestConfig } from '@playwright/test';

    const config: PlaywrightTestConfig = {
      use: {
        headless: false,
      },
    };
    module.exports = config;
  `,
    'a.test.ts': `
      const {test, expect} = require('@playwright/test');

      test('pass', async ({ page }) => {
        await page.goto('${server.EMPTY_PAGE}');
        expect(await page.evaluate(async () => {
          const foo = await import('./helper.js');
          throw new Error(foo);
          return foo;
        )).toBe('from helper');
      });
    `,
  });
  expect(result.passed).toBe(1);
  expect(result.exitCode).toBe(0);
});

I don't think we can easily solve it, since we want the plugin to be enabled in node but not in the "evaluate functions" which get stringified to the browser. As a workaround I recommend for now to pass it as a string, then it does not get transpiled with Babel:

 expect(await page.evaluate(`
  (async () => {
    const { foo } = await import ('./helper.js');
    return foo;
  })()
`)).toBe('from helper');

@pavelfeldman
Copy link
Member

Why was this issue closed?

Thank you for your contribution to our project. This issue has been closed due to its limited upvotes and recent activity, and insufficient feedback for us to effectively act upon. Our priority is to focus on bugs that reflect higher user engagement and have actionable feedback, to ensure our bug database stays manageable.

Should you feel this closure was in error, please create a new issue and reference this one. We're open to revisiting it given increased support or additional clarity. Your understanding and cooperation are greatly appreciated.

dgozman added a commit that referenced this issue Jun 13, 2024
…#31285)

Historically, this plugin was important to translate dynamic imports
into require calls so that we can intercept them and transpile.

This is not needed anymore with ESM loader enabled by default, so we can
avoid this transformation and support dynamic imports of ESM-only
packages/files.

Fixes #17075, fixes #23255, fixes #31140, references #23662.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants