closure-compiler icon indicating copy to clipboard operation
closure-compiler copied to clipboard

ERROR - [JSC_JS_MODULE_LOAD_WARNING] Failed to load module "util"

Open mistersomebody opened this issue 5 years ago • 5 comments

Context

I would like to compile a node based project which dependencies include util and other node builtins.

However, the compiler won't load them despite using the --process_common_js_modules and --module_resolution NODE flags. Every other dependencies is loaded properly with --js.

My understanding was that, by using --externs and --output_wrapper (or --output_wrapper_file), the compiler would inject and use them as global variables within the final output. I've used @externs/nodejs to that effect.

Following here is a very basic failing setup:

Setup

Compiler

npm i -D google-closure-compiler
# + [email protected]

I also tried [email protected] from a year ago in case it would be related to the latest version.

Source

const util = require('util')

console.log('it works...')

module.exports = {}

Command

google-closure-compiler index.js --process_common_js_modules --compilation_level ADVANCED --module_resolution NODE

Error

index.js:1:13: ERROR - [JSC_JS_MODULE_LOAD_WARNING] Failed to load module "util"
  1| const util = require('util')
                  ^

1 error(s), 0 warning(s)

Question

Is my initial assumption correct? Or is it not possible to compile with node builtins. If yes, could you help out with the configuration?

mistersomebody avatar Nov 03 '20 20:11 mistersomebody

I believe this is working as expected; you need to pass every JS source with the --js flag, including sources from node modules.

mprobst avatar Nov 04 '20 08:11 mprobst

Ok, I see. It works indeed if I place mock packages of builtins in node_modules and pass them with --js.

Thank you.

mistersomebody avatar Nov 06 '20 18:11 mistersomebody

@mprobst, util is a Node builtin and does not exist in node_modules/. This does not work as expected, as these cannot be flagged as externs and apparently only work if you spoof them in node_modules/ and pass them in as normal JS (?).

This is entirely unintuitive, see https://github.com/google/closure-compiler/issues/3740.

ctjlewis avatar Dec 27 '20 22:12 ctjlewis

@mistersomebody, could you please upload your project directory to a repo so that I can replicate the hack? Your creativity is much appreciated.

ctjlewis avatar Dec 27 '20 22:12 ctjlewis

@ctjlewis

Here's an adaptation of what I ended up doing:

Sample

./index.js

const util = require('util')

console.log('It works:', util.isArray([]))

Mock

./node_modules/util/index.js

module.exports = util

Wrapper

./wrapper.txt

(util => {
  %output%  
})(require('util'))

Externs

Copy the util extern and remove module.exports = util at the end

./externs.js

/* LICENCE ... */

/**
 * @const
 */
var util = {};

/**
 * @param {string} format
 * @param {...*} var_args
 * @return {string}
 * @nosideeffects
 */
util.format;

/**
 * @param {string} string
 * @return {void}
 */
util.debug;

/**
 * @param {...*} var_args
 * @return {void}
 */
util.error;

/**
 * @param {...*} var_args
 * @return {void}
 */
util.puts;

/**
 * @param {...*} var_args
 * @return {void}
 */
util.print;

/**
 * @param {string} string
 * @return {void}
 */
util.log;

/**
 * @param {*} object
 * @param {{showHidden: (boolean|undefined),
 *          depth: (number|null|undefined),
 *          colors: (boolean|undefined),
 *          customInspect: (boolean|undefined)}=} options
 * @return {string}
 * @nosideeffects
 */
util.inspect;

/**
 * @param {*} object
 * @return {boolean}
 * @nosideeffects
 */
util.isArray;

/**
 * @param {*} object
 * @return {boolean}
 * @nosideeffects
 */
util.isRegExp;

/**
 * @param {*} object
 * @return {boolean}
 * @nosideeffects
 */
util.isDate;

/**
 * @param {*} object
 * @return {boolean}
 * @nosideeffects
 */
util.isError;

/**
 * @param {Function} constructor
 * @param {Function} superConstructor
 * @return {void}
 */
util.inherits;

Command

google-closure-compiler \
  --compilation_level ADVANCED \
  --js_output_file dist/index.js \
  --module_resolution NODE \
  --process_common_js_modules \
  --export_local_property_definitions \
  --generate_exports \
  --externs ./externs.js \
  --js node_modules/util/index.js \
  --output_wrapper_file ./wrapper.txt \
  index.js

Output

./dist/index.js

(util => {
  'use strict';console.log("It works",util.isArray([]));  
})(require('util'))

Cheers

mistersomebody avatar Dec 30 '20 14:12 mistersomebody