Webpack, (npm) binding, and require(`fs`) not working together

Viewed 1933

Currently working on an SPA that is used to submit user information to a SQL database via an odbc INSERT statement, but my local environment is not being properly constructed and I cant figure out why.

Link to npm's binding: https://www.npmjs.com/package/bindings

macOS 
version 10.12.6

node -v
v8.8.1

webpack -v
3.8.1

As of right now, I can use node post.js to successfully make a post to my database. However, when I run grunt build I receive the following errors:

WARNING in ./node_modules/bindings/bindings.js
81:22-40 Critical dependency: the request of a dependency is an expression

WARNING in ./node_modules/bindings/bindings.js
81:43-53 Critical dependency: the request of a dependency is an expression

ERROR in ./node_modules/bindings/bindings.js
Module not found: Error: Can't resolve 'fs' in '/Users/pat/to/project/node_modules/bindings'
 @ ./node_modules/bindings/bindings.js 6:11-24
 @ ./node_modules/odbc/lib/odbc.js
 @ ./assets/scripts/index.js
 @ ./index.js
Warning:  Use --force to continue.

Aborted due to warnings.

Note: I receive the same issue whenever I require fs in any file (the same error occurs for whichever file I require fs in). The require of fs in binding.js is the last instance of the problem because when I remove it from binding.js the code breaks.

So when I take a look at the binding.js file that is being mentioned above, we can see the following (the require(fs) is the important line to look at):

/**
 * Module dependencies.
 */

  var fs = require('fs')
  , path = require('path')
  , join = path.join
  , dirname = path.dirname
  , exists = ((fs.accessSync && function (path) { try { fs.accessSync(path); } catch (e) { return false; } return true; })
      || fs.existsSync || path.existsSync)
  , defaults = {
        arrow: process.env.NODE_BINDINGS_ARROW || ' → '
      , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled'
      , platform: process.platform
      , arch: process.arch
      , version: process.versions.node
      , bindings: 'bindings.node'
      , try: [
          // node-gyp's linked version in the "build" dir
          [ 'module_root', 'build', 'bindings' ]
          // node-waf and gyp_addon (a.k.a node-gyp)
        , [ 'module_root', 'build', 'Debug', 'bindings' ]
        , [ 'module_root', 'build', 'Release', 'bindings' ]
          // Debug files, for development (legacy behavior, remove for node v0.9)
        , [ 'module_root', 'out', 'Debug', 'bindings' ]
        , [ 'module_root', 'Debug', 'bindings' ]
          // Release files, but manually compiled (legacy behavior, remove for node v0.9)
        , [ 'module_root', 'out', 'Release', 'bindings' ]
        , [ 'module_root', 'Release', 'bindings' ]
          // Legacy from node-waf, node <= 0.4.x
        , [ 'module_root', 'build', 'default', 'bindings' ]
          // Production "Release" buildtype binary (meh...)
        , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ]
        ]
    }

/**
 * The main `bindings()` function loads the compiled bindings for a given module.
 * It uses V8's Error API to determine the parent filename that this function is
 * being invoked from, which is then used to find the root directory.
 */

function bindings (opts) {

  // Argument surgery
  if (typeof opts == 'string') {
    opts = { bindings: opts }
  } else if (!opts) {
    opts = {}
  }

  // maps `defaults` onto `opts` object
  Object.keys(defaults).map(function(i) {
    if (!(i in opts)) opts[i] = defaults[i];
  });

  // Get the module root
  if (!opts.module_root) {
    opts.module_root = exports.getRoot(exports.getFileName())
  }

  // Ensure the given bindings name ends with .node
  if (path.extname(opts.bindings) != '.node') {
    opts.bindings += '.node'
  }

  var tries = []
    , i = 0
    , l = opts.try.length
    , n
    , b
    , err

  for (; i<l; i++) {
    n = join.apply(null, opts.try[i].map(function (p) {
      return opts[p] || p
    }))
    tries.push(n)
    try {
      b = opts.path ? require.resolve(n) : require(n)
      if (!opts.path) {
        b.path = n
      }
      return b
    } catch (e) {
      if (!/not find/i.test(e.message)) {
        throw e
      }
    }
  }

  err = new Error('Could not locate the bindings file. Tried:\n'
    + tries.map(function (a) { return opts.arrow + a }).join('\n'))
  err.tries = tries
  throw err
}
module.exports = exports = bindings


/**
 * Gets the filename of the JavaScript file that invokes this function.
 * Used to help find the root directory of a module.
 * Optionally accepts an filename argument to skip when searching for the invoking filename
 */

exports.getFileName = function getFileName (calling_file) {
  var origPST = Error.prepareStackTrace
    , origSTL = Error.stackTraceLimit
    , dummy = {}
    , fileName

  Error.stackTraceLimit = 10

  Error.prepareStackTrace = function (e, st) {
    for (var i=0, l=st.length; i<l; i++) {
      fileName = st[i].getFileName()
      if (fileName !== __filename) {
        if (calling_file) {
            if (fileName !== calling_file) {
              return
            }
        } else {
          return
        }
      }
    }
  }

  // run the 'prepareStackTrace' function above
  Error.captureStackTrace(dummy)
  dummy.stack

  // cleanup
  Error.prepareStackTrace = origPST
  Error.stackTraceLimit = origSTL

  return fileName
}

/**
 * Gets the root directory of a module, given an arbitrary filename
 * somewhere in the module tree. The "root directory" is the directory
 * containing the `package.json` file.
 *
 *   In:  /home/nate/node-native-module/lib/index.js
 *   Out: /home/nate/node-native-module
 */

exports.getRoot = function getRoot (file) {
  var dir = dirname(file)
    , prev
  while (true) {
    if (dir === '.') {
      // Avoids an infinite loop in rare cases, like the REPL
      dir = process.cwd()
    }
    if (exists(join(dir, 'package.json')) || exists(join(dir, 'node_modules'))) {
      // Found the 'package.json' file or 'node_modules' dir; we're done
      return dir
    }
    if (prev === dir) {
      // Got to the top
      throw new Error('Could not find module root given file: "' + file
                    + '". Do you have a `package.json` file? ')
    }
    // Try the parent dir next
    prev = dir
    dir = join(dir, '..')
  }
}

So it appears that requiring fs in the at the top of the code block is causing the issue. If I remove the fs require and run grunt build again, I get the following warnings but no errors:

WARNING in ./node_modules/bindings/bindings.js
81:22-40 Critical dependency: the request of a dependency is an expression

WARNING in ./node_modules/bindings/bindings.js
81:43-53 Critical dependency: the request of a dependency is an expression

I receive the same warnings when running 'grunt serve' but no error. Then when I visit the page on localhost, I'm receiving the following error:

bindings.js:10 Uncaught ReferenceError: fs is not defined
    at Object.<anonymous> (bindings.js:10)
    at Object.Array.concat.splitPathRe (bindings.js:171)
    at __webpack_require__ (bootstrap 2037e18d07470b1345e0:54)
    at Object.<anonymous> (odbc.js:18)
    at Object.Array.concat.webpackEmptyContext.keys (odbc.js:820)
    at __webpack_require__ (bootstrap 2037e18d07470b1345e0:54)
    at Object.<anonymous> (index.js:7)
    at Object.<anonymous> (application.js:984)
    at __webpack_require__ (bootstrap 2037e18d07470b1345e0:54)
    at Object.<anonymous> (index.js:8)

So removing the fs require is allowing me to build, but then binding.js is not functioning properly because I removed the fs require. In addition, after removing the fs require in binding.js, I cannot use node to make posts to the database because I removed the fs require in binding.js (receive the error message above).

I've tried debugging by adding the following code to my 'grunt/webpack.js' file as suggested in several posts:

target: 'node',
node: {
  fs: 'empty'
}

Link to post that advice is from: https://github.com/webpack-contrib/css-loader/issues/447

Here is my whole 'grunt/webpack.js' file (want to ensure I placed it in the correct location):

'use strict'

const webpack = require('webpack')
const path = require('path')

module.exports = {
  options: {
    entry: {
      application: './index.js',
      specs: './spec/_all.js',
      vendor: ['jquery', 'bootstrap-sass']
    },


    output: {
      filename: '[name].js',
      path: path.join(__dirname, '/../public'),
      publicPath: 'public/'
    },

    plugins: [
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks: Infinity
      }),
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
      })
    ],

    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /(node_modules|bower_components)/,
          loader: 'babel-loader',
          query: {
            presets: ['es2015']
          }
        },
        {
          test: /\.css$/,
          use: [
            { loader: 'style-loader' },
            { loader: 'css-loader' }
          ]
        },
        {
          test: /\.scss$/,
          use: [
            { loader: 'style-loader' },
            { loader: 'css-loader' },
            {
              loader: 'sass-loader',
              options: {
                includePaths: [
                  path.resolve(__dirname, './node_modules')
                ]
              }
            }
          ]
        },
        {
          test: /\.woff[\d]?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
          loader: 'url-loader?limit=10000&mimetype=application/font-woff'
        },
        {
          test: /\.(ttf|eot|svg|png|jpg|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
          loader: 'file-loader'
        },
        {
          test: /\.(hbs|handlebars)$/,
          loader: 'handlebars-loader',
          query: {
            helperDirs: [
              path.join(__dirname, '/../assets/scripts/templates/helpers')
            ]
          }
        }
      ]
    },

    resolve: {
      alias: {
        handlebars: 'handlebars/dist/handlebars.js'
      }
    },
    stats: {
      colors: true,
      modules: true,
      reasons: true
    }
  },

  target: 'node',

  node: {
    fs: 'empty',
    console: 'empty',
    net: 'empty',
    tls: 'empty'
  },


  build: {
    failOnError: true,
    watch: false,
    keepalive: false
  }

}

Here is my package.json as reference for all other relevant installs:

{
  "name": "omnipodeurope",
  "version": "0.0.3",
  "private": true,
  "license": {
    "software": "GNU GPLv3",
    "content": "CC­BY­NC­SA 4.0"
  },
  "dependencies": {
    "bindings": "^1.3.0",
    "fs": "0.0.1-security",
    "jquery": "^3.2.1",
    "odbc": "^1.3.0",
    "videojs": "^1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.25.0",
    "babel-loader": "^7.1.1",
    "babel-preset-es2015": "^6.24.1",
    "bootstrap-sass": "^3.3.7",
    "chai": "^4.1.1",
    "clone": "^2.1.1",
    "css-loader": "^0.27.0",
    "eslint": "^4.4.1",
    "eslint-config-standard": "^10.2.1",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.1.1",
    "eslint-plugin-promise": "^3.5.0",
    "eslint-plugin-standard": "^3.0.1",
    "expose-loader": "^0.7.3",
    "file-loader": "^1.0.0",
    "grunt": "^1.0.1",
    "grunt-concurrent": "^2.3.1",
    "grunt-eslint": "^20.0.0",
    "grunt-jsonlint": "^1.1.0",
    "grunt-mocha-phantomjs": "^4.0.0",
    "grunt-nodemon": "^0.4.2",
    "grunt-open": "^0.2.3",
    "grunt-sass-lint": "^0.2.2",
    "grunt-shell": "^2.1.0",
    "grunt-webpack": "^3.0.2",
    "handlebars": "^4.0.10",
    "handlebars-loader": "^1.5.0",
    "html-loader": "^0.5.1",
    "load-grunt-config": "^0.19.2",
    "mocha": "^3.5.0",
    "node-libs-browser": "^2.0.0",
    "node-sass": "^4.5.3",
    "sass-loader": "^6.0.6",
    "style-loader": "^0.18.2",
    "time-grunt": "^1.4.0",
    "url-loader": "^0.5.9",
    "webpack": "^3.5.1",
    "webpack-dev-server": "^2.7.1"
  }
}

Any help would be appreciated. Please let me know if I should post any additional information that might be relevant.

Thanks all!

#

UPDATE:

I've modified binding.js so that it is in its original form, and added the following to my grunt/webpack.js file as I mentioned above:

target: 'node',
node: {
  fs: 'empty'
}

And now, I'm seeing the following in the DOM in my browser: So it looks like webpack is attemtpting to set fs as its own variable, but for some reason it cannot locate the correct module. What

var fs = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module \"fs\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()))
  , path = __webpack_require__(20)

What would cause webpack to be unable to find the fs module? Could it be because I have multiple fs-related packages? (fs and node-fs);

0 Answers
Related