I would like to use a node.js module from https://github.com/asbjornenge/react-datalist using browserify.
I did install the module locally at my working directory.
In that directory, I created a javascript file, main.jsx
var React = require('react');
var ReactDatalist = require('react-datalist');
var options = ['apple','orange','pear','pineapple','melon'];
React.render(<ReactDatalist list="fruit" options={options} />, document.body);
Then, I reactify like this:
browserify -t reactify main.jsx > main.js
So far so good, but the problem is when I want to browserify:
browserify main.js > bundle.js
I got an error:
Error: Cannot find module './components/DataList' from '...my working directory...'
at /usr/local/lib/node_modules/browserify/node_modules/resolve/lib/async.js:55:21
at load (/usr/local/lib/node_modules/browserify/node_modules/resolve/lib/async.js:69:43)
at onex (/usr/local/lib/node_modules/browserify/node_modules/resolve/lib/async.js:92:31)
at /usr/local/lib/node_modules/browserify/node_modules/resolve/lib/async.js:22:47
at FSReqWrap.oncomplete (fs.js:95:15)
In ...my working directory../node_modules/react-datalist/src/ReactDataList.js, this is defined:
import React from 'react'
import DataList from './components/DataList'
import DataListOption from './components/DataListOption'
import layout from './styles/react-datalist.styl'
This is unclear to me about the scope of import. I thought node.js import mechanism should work locally, but why does it try to find './components/DataList' right from my working directory?
There must be some concept about importing modules I don't know yet. So I would appreciate if you can point out to some references.
I feel embarrassed now. The problem is I didn't realize the transformation step and the bundling step are already combined into a single command line. Hence, instead of
browserify -t reactify main.jsx > main.js
browserify main.js > bundle.js
Do this
browserify -t reactify main.jsx > bundle.js
Related
Before the actual questions (see at the end), please let me show the steps that lead to that question through an example:
Creating the project
tests$ mkdir esm && cd esm
tests/esm$ nvm -v
0.37.2
tests/esm$ nvm use v15
Now using node v15.6.0 (npm v7.5.6)
tests/esm$ node -v
v15.6.0
tests/esm$ npm -v
7.5.6
tests/esm$ npm init
package name: (esm) test-esm
entry point: (index.js)
Installing nodehun
tests/esm$ npm install nodehun
added 2 packages, and audited 3 packages in 11s
tests/esm$ npm ls
test-esm#1.0.0 tests/esm
└── nodehun#3.0.2
dependencies of nodehun here
index.js
import { suggest } from './checker.js'
suggest("misspeling");
checker.js
import Nodehun from 'nodehun'
import fs from 'fs';
const affix = fs.readFileSync('dictionaries/en_NZ.aff')
const dictionary = fs.readFileSync('dictionaries/en_NZ.dic')
const nodehun = new Nodehun(affix, dictionary)
export const suggest = (word) => hun_suggest(word);
async function hun_suggest(word) {
let suggestions = await nodehun.suggest(word);
console.log(suggestions);
}
To obtain the required Hunspell dictionary files (affix and dictionary):
tests/esm$ mkdir dictionaries && cd dictionaries
tests/esm/dictionaries$ curl https://www.softmaker.net/down/hunspell/softmaker-hunspell-english-nz-101.sox > en_NZ.sox
tests/esm/dictionaries$ unzip en_NZ.sox en_NZ.aff en_NZ.dic
Running the project
As per nodejs documentation (Determining Module System) to support the import / export:
Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements within ES module code:
• Files ending in .js when the nearest parent package.json file contains a top-level "type" field with a value of "module".
We add "type": "module" field in the package.json file of the project.
package.json
{
...
"main": "index.js",
"type": "module",
...
}
First Failed Run
tests/esm$ node index.js
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".node" for tests/esm/node_modules/nodehun/build/Release/Nodehun.node
... omitted ...
at async link (node:internal/modules/esm/module_job:64:9) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
Digging a bit on the reason of the above error:
in the documentation on how to load addons, it refers to the use of require
The filename extension of the compiled addon binary is .node (as opposed to .dll or .so). The require() function is written to look for files with the .node file extension and initialize those as dynamically-linked libraries.
once you define your node project as a "type": "module", require it ceases to be supported (as specified in Interoperability with CommonJS):
Using require to load an ES module is not supported because ES modules have asynchronous execution. Instead, use import() to load an ES module from a CommonJS module.
Temporary Solution
After some time searching the documentation, I found a temporary solution: Customizing ESM specifier resolution algorithm:
The current specifier resolution does not support all default behavior of the CommonJS loader. One of the behavior differences is automatic resolution of file extensions and the ability to import directories that have an index file.
The --experimental-specifier-resolution=[mode] flag can be used to customize the extension resolution algorithm.
To enable the automatic extension resolution and importing from directories that include an index file use the node mode.
tests/esm$ node --experimental-specifier-resolution=node index.js
(node:XXXXX) ExperimentalWarning: The Node.js specifier resolution in ESM is experimental.
(Use `node --trace-warnings ...` to show where the warning was created)
[
'misspelling',
'misspending',
'misspeaking',
'misspell',
'dispelling',
'misapplier',
'respelling'
]
There are a some posts that get to this same resolution (ref 1, ref 2).
However, using experimental flags does not seem a proper way to run your application on production.
Failed Alternative with esm package
From that point, several failed attempts have been tried to avoid the use of --experimental-* flags. Doing some search, I found some posts (ref 1, ref 2) recommending the use of the esm package.
esm gets 1.3M downloads per week.
According the read-me file in GitHub, it does not require any changes.
However, at this point, when I try this node -r esm index.js, a new error appears:
tests/esm$ npm install esm
added 1 package, and audited 4 packages in 709ms
tests/esm$ npm ls
test-esm#0.1.0 tests/esm
├── esm#3.2.25
└── nodehun#3.0.2
tests/esm$ node -r esm index.js
tests/esm/index.js:1
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: tests/esm/index.js
at new NodeError (node:internal/errors:329:5)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1125:13) {
code: 'ERR_REQUIRE_ESM'
}
The above could be due to a reported issue (Error [ERR_REQUIRE_ESM]: Must use import to load ES Module / require() of ES modules is not supported).
There is proposed patch to fix it, although I do not know how to use it myself.
const module = require('module');
module.Module._extensions['.js'] = function(module, filename) {
const content = fs.readFileSync(filename, 'utf8');
module._compile(content, filename);
};
Questions
Is there a (standard) way to use import / export (ES Modules) without incurring in issues with import addons?
Avoiding the use of the --experimental-specifier-resolution=node flag.
Perhaps esm could be the solution to the above. Is there anything I am doing wrong with the usage esm package?
If it is a correct usage, is there a way to use the proposed patch myself as a working around?
Any hints to help to solve it would be really appreciated.
Note: the final status of the example can be cloned from https://github.com/rellampec/test-esm.git
I ran into a similar problem and fixed it this way:
https://nodejs.org/api/module.html#module_module_createrequire_filename
// The project is "type": "module" in package json
// createRequire is native in node version >= 12
import { createRequire } from 'module';
import path from 'path';
// Absolute path to node modules (or native addons)
const modulesPath = path.resolve(process.cwd(), 'node_modules');
// Create the require method
const localRequire = createRequire(modulesPath);
// require the native add-on
const myNativeAddon = localRequire('my-native-addon');
After some ramblings trying to figure this out got to the root cause.
When using node -r esm index.js, the esm package does already all the work for your (as noted in other answers), and therefore (not mentioned in other answers):
the package.json should be updated by removing "type:" "module" (as it creates unintended interactions between the native node ES Modules feature and the esm package you installed)
Aside note: if you tried to use node ES Modules and then you try to switch to esm package, it is very easy to miss this point.
I try to use axios in my main.js.
i tried import axios from 'axios'. ... I got error for import. I read here that I should use require instead of import.
after that I tried
var axios=require('axios');
but I got the error ....require is not defined.
after that I read about browserify. I installed it
npm install -g browserify
after that I used this code to bundle it
browserify main.js -o bundle.js
and I add this script to my index.html code.
<script src="bundle.js"></script>
but I got the same error
REQUIRE IS NOT DEFINED!!
I am actually confused. can somebody HELP ME!
appreciated
By default require() is not a valid function in client side Javascript and is primarily used in server side Node.js. I recommend you look into require.js as this does extend the client side to provide you with that function. Or else go with ES6 import.
npm install axios --save-dev
will help you to solve problem
hope you're doing well.
I'm new at react native and i'm stuck with a problem while trying to import a node module.
I need to create an app that will get orders from the API of a Wordpress Website with WooCommerce.
I first created a project with the command create-react-native-app picking then npm install. It's creating a structure like this in the project folder named picking:
node_modules
App.js
app.json
App.test.js
etc....
Then I installed the package woocommerce-api with npm install woocommerce-api --save (https://www.npmjs.com/package/woocommerce-api). This package allow me to do request to the WooCommerce API easier.
I want to not put the config to the WooCommerce API in the App.js, so I created a folder src and a folder woocommerce with a file api.js (should I write it with the first letter in uppercase ?) in it and I added import Api from 'picking/src/woocommerce/api'; in my App.js.
So now the structure is
node_modules
src
-- woocommerce
-- api.js
App.js
app.json
App.test.js
etc....
The problem is that I can't achieve to import the WooCommerceAPI module from woocommerce-api, no matter what I set in path to get the module.
There is the file api.js at the moment :
import WooCommerceAPI from '../../woocommerce-api';
var Api = new WooCommerceAPI({
url: 'http://localhost/mysite',
consumerKey: 'ck_xxxxxxxxxxxxxxxxxxxxxxxxxx',
consumerSecret: 'cs_xxxxxxxxxxxxxxxxxxxxxxxxxxx',
wp_api: true,
version: '/wc/v2',
queryStringAuth: true
});
export default Api;
And I get the error :
Unable to resolve module '../../woocommerce-api' from etc ...
I can't find what is the problem and why this is not working. If you could help me on this, it would be very nice.
Have a nice day everyone :)
EDIT: I changed the line for the import to import WooCommerceAPI from 'woocommerce-api'; and I got a new error : Metro Bundler has encountered an internal error, please check your terminal error output for more details, but there is nothing in the terminal except Failed building JavaScript bundle.
EDIT2: I downgrade node from 9.4 to 8.0.0 and restart the project. I got the same error but in the terminal i now get this in yellow/orange : Problem checking node_modules dependencies: Unexpected end of JSON input
Okay, so I find a workaround. In fact, the import is working. For some reason that i don't know, this is the npm package that is not working and make the app crash.
So I removed the package woocommerce-api and I create a file in src/woocommerce called woocommerce-api.js, then I copied the content of this https://github.com/minhcasi/react-native-woocommerce/blob/master/WooCommerceAPI.js that is the same as the one in the npm package and I pasted it in my woocommerce-api.js. I import it in my api.jsfile and "voilà" !
Seems to work fine.
As you install woocommerce-api in your project there is no need to place the location like ../../woocommerce-api.
just change ../../woocommerce-api to woocommerce-api and your project should work.
I wrote a very basic express.js app. Then tried to make it one .js file. Browserify compiled the whole thing to a one file. But browserify-compiled code didn't work. As far as I know, browserify just replaces require statements with module codes. Error is:
C:\Users\HP\n\express\app.js:27025
__proto__: http.IncomingMessage.prototype
^
TypeError: Cannot read property 'prototype' of undefined
at Object.__dirname.173.accepts (C:\Users\HP\n\express\app.js:27025:34)
at s (C:\Users\HP\n\express\app.js:1:316)
at C:\Users\HP\n\express\app.js:1:367
at Object.__dirname.170../application (C:\Users\HP\n\express\app.js:26823:11)
at s (C:\Users\HP\n\express\app.js:1:316)
at C:\Users\HP\n\express\app.js:1:367
at Object.__dirname.168../lib/express (C:\Users\HP\n\express\app.js:26154:18)
at s (C:\Users\HP\n\express\app.js:1:316)
at C:\Users\HP\n\express\app.js:1:367
at Object.__dirname.153.express (C:\Users\HP\n\express\app.js:24010:15)
Browserify is designed specifically to package code for a browser.
Node.js supports a number of modules that a browser doesn't which have to be emulated by builtins. These modules will be replaced by a browser-specific shim. Some only supply a subset of the Node API that makes sense to have in a browser.
So you are running an app that has converted all the Node.js modules to support running what it can in a browser, back in Node where the modules are available but are no longer being used.
Try rollup or you could possibly configure babel to work like you need
I had this very same issue but like you said the compile code should work on server side. I solved it from this link:
https://www.linkedin.com/pulse/bundling-nodemodules-your-nodejs-app-one-single-file-xuan-son-nguyen/
Use browserify for bundling and terser for minifying. Starting by installing them globally:
npm install -g browserify
npm install -g terser
Next, we have to add a build script to package.json
...
"scripts": {
...
"build": "browserify --node --ignore-missing index.js | terser > bundle.js"
}
...
Each time you want to promote to production, you have to make a new bundle:
npm run build
A new file called "bundle.js" will be created.
Let there be peace, and there was peace. Happy coding.
When I try to import a local file it either throws an error at runtime but compiling it using tsc works. When I make it work for node.js, typescript throws an error at compile time.
When I do
import A = require("./A");
node.js complains, that it can not find the A module and typescript compiles just fine.
But when I change it to
import A = require("./js/A");
node.js can find the module but Typescript tells me there is an error.
The layout is like this:
js
\- A.ts
\- B.ts
I compile the files separately and I already tried searching for changing the root directory for the typescript compiler but I couldn't find anything.
Without seeing how you are compiling the TypeScript, and without seeing how you are attempting to require() these files from node, it's hard to answer this question.
Even so, I can tell you that both TypeScript and node.js are expecting require() to be given a path which is relative to the file doing the require.
If I were to have a js/A.ts that looked like so:
import B = require("./B");
console.log(B.thing);
And a js/B.ts that looked like so:
var myStuff = {
thing: "I'm a thing!"
}
export = myStuff;
I could then compile both files with a single tsc -m commonjs ./js/A.ts
And then I could run node with: node ./js/A.js and would see the output:
I'm a thing!