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

refactor: avoid dynamic requires in lib/ folder #3208

Merged
merged 1 commit into from Sep 6, 2018

Conversation

aslushnikov
Copy link
Contributor

@aslushnikov aslushnikov commented Sep 6, 2018

This patch removes all dynamic requires in Puppeteer. This should
make it much simpler to bundle puppeteer/puppeteer-core packages.

We used dynamic requires in a few places in lib/:

  • BrowserFetcher was choosing between http and https based on some
    runtime value. This was easy to fix with explicit require.
  • BrowserFetcher and Launcher needed to know project root to store
    chromium revisions and to read package name and chromium revision from
    package.json. (projectRoot value would be different in node6).
    Instead of doing a backwards logic to infer these
    variables, we now pass them directly from //index.js.

With this patch, I was able to bundle Puppeteer using browserify and
the following config in package.json:

  "browser": {
    "./lib/BrowserFetcher.js": false,
    "ws": "./lib/BrowserWebSocket",
    "fs": false,
    "child_process": false,
    "rimraf": false,
    "readline": false
  }

(where lib/BrowserWebSocket.js is a courtesy of @Janpot from
#2374)

And command:

$ browserify -r puppeteer:./index.js > ppweb.js

References #2119

This patch removes all dynamic requires in Puppeteer. This should
make it much simpler to bundle puppeteer/puppeteer-core packages.

We used dynamic requires in a few places in lib/:
- BrowserFetcher was choosing between `http` and `https` based on some
  runtime value. This was easy to fix with explicit `require`.
- BrowserFetcher and Launcher needed to know project root to store
  chromium revisions and to read package name and chromium revision from
  package.json. (projectRoot value would be different in node6).
  Instead of doing a backwards logic to infer these
  variables, we now pass them directly from `//index.js`.

With this patch, I was able to bundle Puppeteer using browserify and
the following config in `package.json`:

```json
  "browser": {
    "./lib/BrowserFetcher.js": false,
    "ws": "./lib/BrowserWebSocket",
    "fs": false,
    "child_process": false,
    "rimraf": false,
    "readline": false
  }
```

(where `lib/BrowserWebSocket.js` is a courtesy of @Janpot from
puppeteer#2374)

And command:

```sh
$ browserify -r puppeteer:./index.js > ppweb.js
```

References puppeteer#2119
@Janpot
Copy link
Contributor

Janpot commented Sep 6, 2018

@aslushnikov Just want to mention that ws supports both browser EventTarget and node EventEmitter interfaces simultaneously. With minimal refactoring, I think it can be made so the BrowserWebSocket replacement isn't even necessary.

Edit:
nice work btw, 👍

Edit2:
Here you can see ws is already browserifyable, puppeteer is just calling it in a way that is only compatible in node.

Edit3:
this should do it
https://github.com/GoogleChrome/puppeteer/blob/3d7ae2a2591a55e4f94c67d62a22f7103698cba5/lib/Connection.js#L32

new WebSocket(url, null, { perMessageDeflate: false });
ws.addEventListener('open', () => resolve(new Connection(url, ws, delay)));
ws.addEventListener('error', ({ error }) => reject(error));

https://github.com/GoogleChrome/puppeteer/blob/3d7ae2a2591a55e4f94c67d62a22f7103698cba5/lib/Connection.js#L62

this._transport.addEventListener('message', ({ data }) => this._onMessage(data));
this._transport.addEventListener('close', this._onClose.bind(this));

https://github.com/GoogleChrome/puppeteer/blob/3d7ae2a2591a55e4f94c67d62a22f7103698cba5/lib/Connection.js#L138

this._transport.addEventListener('error', () => {});

package.json:

  "browser": {
    "./lib/BrowserFetcher.js": false,
    "fs": false,
    "child_process": false,
    "rimraf": false,
    "readline": false
  }

@aslushnikov
Copy link
Contributor Author

Here you can see ws is already browserifyable, puppeteer is just calling it in a way that is only compatible in node.

@Janpot Ah, how do I call it the right way?

@Janpot
Copy link
Contributor

Janpot commented Sep 6, 2018

add a second null parameter to the constructor, but keep passing the options in the third one
refactor the .on to .addEventListener (the signature is a little different)
I edited my previous answer.

@Janpot
Copy link
Contributor

Janpot commented Sep 6, 2018

🤔 Or second parameter has to be empty array: d68b4f6#diff-0c1fe9dd78cc7f29823c09b251c8aae0R32
sorry, I'm doing this from memory in between work 🙂

@aslushnikov
Copy link
Contributor Author

@Janpot ah, unfortunately we have a Pipe transport that doesn't support addEventListener. I'll play with this separately.

@Janpot
Copy link
Contributor

Janpot commented Sep 6, 2018

Ah yes, indeed, that makes sense. I guess in that case you could probably go the other way around.

const WebSocket = require('ws');
const EventEmitter = require('events');

class WebSocketTransport extends EventEmitter {
  constructor(url) {
    super();
    this._ws = new WebSocket(url, [], { perMessageDeflate: false });
    this._ws.addEventListener('open', event => this.emit('open'));
    this._ws.addEventListener('close', event => this.emit('close'));
    this._ws.addEventListener('error', event => this.emit('error', event.error));
    this._ws.addEventListener('message', event => this.emit('message', event.data));
  }
   send(message) {
    this._ws.send(message);
  }
   close() {
    this._ws.close();
  }
}

// ...

  static async createForWebSocket(url, delay = 0) {
    return new Promise((resolve, reject) => {
      const ws = new WebSocketTransport(url);
      ws.on('open', () => resolve(new Connection(url, ws, delay)));
      ws.on('error', reject);
    });
  }

@aslushnikov aslushnikov merged commit f230722 into puppeteer:master Sep 6, 2018
@aslushnikov aslushnikov deleted the bundlable branch November 2, 2018 19:44
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

Successfully merging this pull request may close these issues.

None yet

3 participants