vercel / ncc

Compile a Node.js project into a single file. Supports TypeScript, binary addons, dynamic requires.
https://npmjs.com/@vercel/ncc
MIT License
9.25k stars 287 forks source link

Running a file that requires lighthouse breaks #290

Open rlindskog opened 5 years ago

rlindskog commented 5 years ago

The problem I'm attempting to run lighthouse with the @now/node builder, and I am running into a problem. Here is the src and logs of the lambda. I believe below is the simplest way to replicate the bug.

The code

// test.js
require('lighthouse')

The command ncc run test.js

The module resolution warnings

ncc: Module directory "/~/api/node_modules/lighthouse/lighthouse-core/lib/i18n" attempted to require "intl" but could not be resolved, assuming external.
ncc: Module directory "/~/api/node_modules/ws/lib" attempted to require "utf-8-validate" but could not be resolved, assuming external.
ncc: Module directory "/~/api/node_modules/ws/lib" attempted to require "bufferutil" but could not be resolved, assuming external.

The error

...
5837kB  [2471ms] - ncc 0.15.2
Error: EBADF: bad file descriptor, fstat
    at tryStatSync (fs.js:517:13)
    at Object.fs.readFileSync (fs.js:553:3)
    at Object.282 (/node_modules/lighthouse/lighthouse-core/report/html/html-report-assets.js:16:1)
    at __webpack_require__ (/webpack:bootstrap:19:1)
    at Object.180 (/node_modules/lighthouse/lighthouse-core/report/report-generator.js:8:1)
    at __webpack_require__ (/webpack:bootstrap:19:1)
    at Object.9 (/node_modules/lighthouse/lighthouse-core/runner.js:20:1)
    at __webpack_require__ (/webpack:bootstrap:19:1)
    at Object.701 (/node_modules/lighthouse/lighthouse-core/index.js:8:1)
    at __webpack_require__ (/webpack:bootstrap:19:1)
evalmachine.<anonymous>:1
(function (exports, require, module, __filename, __dirname) { module.exports=function(t,e){"use strict";var r={};function __webpack_require__(e){if(r[e]){return r[e].exports}var n=r[e]={i:e,l:false,exports:{}};t[e].call(n.exports,n,n.exports,__webpack_require__);n.l=true;return n.exports}function startup(){return __webpack_require__(610)}return startup()}({29:function(t,e,r){t.exports=glob;var n=r(66);var i=r(316);var o=r(978);var s=o.Minimatch;var a=r(831);var c=r(485).EventEmitter;var f=r(589);var u=r(393);var h=r(403);var l=r(109);var p=r(778);var d=p.alphasort;var v=p.alphasorti;var m=p.setopts;var y=p.ownProp;var b=r(200);var _=r(64);var g=p.childrenIgnored;var w=p.isIgnored;var k=r(834);function glob(t,e,r){if(typeof e==="function")r=e,e={};if(!e)e={};if(e.sync){if(r)throw new TypeError("callback provided to sync glob");return l(t,e)}return new Glob(t,e,r)}glob.sync=l;var E=glob.GlobSync=l.GlobSync;glob.glob=glob;function extend(t,e){if(e===null||typeof e!=="object"){return t

TypeError: process.off is not a function
    at ChildProcess.exit (evalmachine.<anonymous>:1:31377)
    at emitTwo (events.js:126:13)
    at ChildProcess.emit (events.js:214:7)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)

spectrum reference

guybedford commented 5 years ago

Thanks for reporting. This is due to the code:

'use strict';

const fs = require('fs');

const REPORT_TEMPLATE = fs.readFileSync(__dirname + '/report-template.html', 'utf8');
const REPORT_JAVASCRIPT = [
  fs.readFileSync(__dirname + '/renderer/util.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/dom.js', 'utf8'),
  // COMPAT: Remove when Microsoft Edge supports <details>/<summary>
  // https://developer.microsoft.com/en-us/microsoft-edge/platform/status/detailssummary/?q=details
  fs.readFileSync(require.resolve('details-element-polyfill'), 'utf8'),
  fs.readFileSync(__dirname + '/renderer/details-renderer.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/crc-details-renderer.js', 'utf8'),
  fs.readFileSync(__dirname + '/../../lib/file-namer.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/logger.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/report-ui-features.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/category-renderer.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/performance-category-renderer.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/pwa-category-renderer.js', 'utf8'),
  fs.readFileSync(__dirname + '/renderer/report-renderer.js', 'utf8'),
].join(';\n');
const REPORT_CSS = fs.readFileSync(__dirname + '/report-styles.css', 'utf8');
const REPORT_TEMPLATES = fs.readFileSync(__dirname + '/templates.html', 'utf8');

module.exports = {
  REPORT_TEMPLATE,
  REPORT_TEMPLATES,
  REPORT_JAVASCRIPT,
  REPORT_CSS,
};

where require.resolve('details-element-polyfill') is being rewritten by Webpack into the module number (234), and thus isn't being supported in the readFile call.

This is effectively a duplicate of https://github.com/zeit/ncc/issues/41, so I'm marking as such and closing.

danoc commented 5 years ago

@rlindskog — Did you ever find a way to work around this?

guybedford commented 5 years ago

Reopening as I think there is a way we could support this case specifically.

rlindskog commented 5 years ago

@danoc I switched to AWS, native node, and @serverless-chrome/lambda temporarily.

danoc commented 5 years ago

Hi! I've made a codebase to help debug the efforts to get Lighthouse working with ncc and now: https://github.com/danoc/now-lighthouse

I also tested out ncc@0.19.0-beta.1 with the latest 4.x version of Lighthouse. While it does compile, I get this error when it runs:

Unhandled rejection: { Error: Cannot find module "../gather/gatherers/undefined".
    at Object.<anonymous> (/var/task/packages/lighthouse/index.js:18627:13)
    at __webpack_require__ (/webpack/bootstrap:19:1)
    at Function.requireGathererFromPath (/var/task/packages/lighthouse/index.js:20772:63)
    at Config.expandGathererShorthand.map.gathererDefn (/var/task/packages/lighthouse/index.js:20818:25)
    at Array.map (<anonymous>)
    at passes.map.pass (/var/task/packages/lighthouse/index.js:20799:76)
    at Array.map (<anonymous>)
    at Function.requireGatherers (/var/task/packages/lighthouse/index.js:20798:31)
    at new Config (/var/task/packages/lighthouse/index.js:20289:27)
    at generateConfig (/var/task/packages/tmp/a590644/node_modules/lighthouse/lighthouse-core/index.js:68:1) code: 'MODULE_NOT_FOUND' }

Haven't been able to dig in further.

guybedford commented 5 years ago

Thanks @danoc for the clear replication on this. I'm looking into it.

guybedford commented 5 years ago

Ok, Lighthouse support is effectively pending this asset emission feature then - https://github.com/zeit/webpack-asset-relocator-loader/issues/36.

Will keep this issue open to track the overall Lighthouse support and integration test.

danoc commented 5 years ago

Looks like @nkzawa was able to get it to work with Now here: https://github.com/zeit/integrations/tree/master/lighthouse

Can't tell if it's using ncc or not since I can't find the source code for @nkzawa/now-node-raw.

nkzawa commented 5 years ago

@danoc Unfortunately, it's not ncc. I couldn't make lighthouse work with it.

btw I just made @nkzawa/now-node-raw public https://github.com/nkzawa/now-node-raw

guybedford commented 5 years ago

The remaining issue on Lighthouse seems to be the following code:

  static requireGathererFromPath(path, options, coreAuditList, configDir) {
    const coreGatherer = coreAuditList.find(a => a === `${path}.js`);

    let requirePath = `../gather/gatherers/${path}`;
    if (!coreGatherer) {
      // Otherwise, attempt to find it elsewhere. This throws if not found.
      requirePath = Config.resolveModule(path, configDir, 'gatherer');
    }

    const GathererClass = /** @type {GathererConstructor} */ (require(requirePath));

    return {
      instance: new GathererClass(),
      implementation: GathererClass,
      path,
      options: options || {},
    };
  }

where the requirePath is effectively a dynamic wildcard value.

With https://github.com/zeit/webpack-asset-relocator-loader/pull/39 merged we will support wildcard requires, but the above support can only be completed when we have support for dynamic wildcard passing through the analysis.

I've created a tracking issue for this in https://github.com/zeit/webpack-asset-relocator-loader/issues/40.