I'm using module-alias for my Node.js + Express.js project, running it with Babel for ES2015 support.
App works perfect when started with babel-node, however, if I first build it with babel (from package.json):
"build": "babel ./app --out-dir ./app_dist"
And then start:
"start": "node ./app_dist/bin/www"
It obviously cannot find the correct path specified with module-alias. Instead of looking into app_dist, Node.js searches for import in app, finds ES2015 import directive which it does not understand and throws:
SyntaxError: Unexpected token import
If I change aliases before start of that build, from app to app_dist, it works, but the question is, how to map those aliases (or how to use different _moduleAliases configuration) so that app resolves the paths correctly on development and production?
Maybe there is another way to alias modules with such stack? Thanks in advance.
Found solution for this issue.
In order to set the relative path aliases with module-alias, they shoul be defined not in package.json, but in JavaScript file inside the root directory that will be transpiled with Babel.
In my case, creating an aliases.js script inside config directory like this:
import path from 'path';
import moduleAlias from 'module-alias';
moduleAlias.addAlias('#root', path.resolve(__dirname, '../../'));
Will result in path resolving relatively to the current working directory, solving described problem.
Related
I am creating an app with Electron and Vue (using js not ts).
When I run the app using npm run electron:serve the app runs fine.
I now want to build a Windows exe so I can distribute my app. I have tried using electron-builder, electron-packager and electron-forge. Whenever I can get the build to finish, running the exe throws the cannot use import statement outside a module error (referring to the first import statement it finds, i.e. import { app, protocol, BrowserWindow } from 'electron').
I've tried adding "type":"module" to my package.json but (due a bug in Vue, according to this question), that throws Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]
I've also tried changing all my import statements to require but this doesn't work because some of the node modules I'm using use import and the error just throws for those instead.
I'm tearing my hair out over this. Where do I go from here?
UPDATE:
I have found a workaround for the Vue bug and posted my findings on the linked question. I can now add "type":"module" to my package.json.
However, I now get an error thrown when I run npm run electron:serve and from my built exe:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: <my_project_root>\dist_electron\index.js
require() of ES modules is not supported.
require() of <my_project_root>\dist_electron\index.js from <my_project_root>\node_modules\electron\dist\resources\default_app.asar\main.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
To be clear, I'm not using require in any of my source code, but the compiled(?) version does?
What's going on here?
UPDATE 2:
As requested, here is a minimal reproducible example that maintains original folder structure, configs and package.json
I am getting an error when trying to import a local file, though no problem when using npm packages.
server.js
import express from 'express'
import next from 'next'
import apis from './src/server/api'
api.js
export default {
ello: 'bye',
jamie: 'hey'
}
Starting app
node --experimental-modules --inspect server.js
Error
For help, see: https://nodejs.org/en/docs/inspector
(node:20153) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/default_resolve.js:59
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find module '/var/www/goldendemon.hutber.com/src/server/api' imported from /var/www/goldendemon.hutber.com/server.js
at Loader.resolve [as _resolve] (internal/modules/esm/default_resolve.js:59:13)
at Loader.resolve (internal/modules/esm/loader.js:70:33)
at Loader.getModuleJob (internal/modules/esm/loader.js:143:40)
at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:43:40)
at link (internal/modules/esm/module_job.js:42:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
I'm answering my own question if anybody else has this problem.
It turns out in experimental mode you need to define the full path with extension. So I am trying to import index.js thinking it will know.
To fix it:
import express from 'express'
import next from 'next'
import api from './src/server/api/index.js'
on node 12 you have 2 options:
use type="module" on package.json, experimental modules and specify extensions with specidier-resolution like this:
node --experimental-modules --es-module-specifier-resolution=node src/index
or not using specifier-resolution. Keep in mind you'll have to specify the extension of your files every where.
It should also work if you name your module file with a .mjs extension. Also, other ways to enable ESM are mentioned here.
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 .mjs.
Files ending in .js, or extensionless files, when the nearest parent package.json file contains a top-level field "type" with a value of "module".
Strings passed in as an argument to --eval or --print, or piped to node via STDIN, with the flag --input-type=module.
Or do like I did and just use transpilation the minute your source code deals with ES style module imports or some other non-standard JavaScript code, (E.g. TypeScript) on Node. For reference see this quick bash script I wrote, saved as .script/run-it.sh inside of my Node project:
#!/bin/bash
script=$1
out_path==/tmp/$script-out.js
npx esbuild --platform=node --bundle --outfile=$out_path $script
node $out_path
I added it as a run script in my package.json:
"scripts": {
"test": "sst test",
"start": "sst start",
"build": "sst build",
"deploy": "sst deploy",
"remove": "sst remove",
"run-it": "./.script/run-it.sh"
},
And my target script (import-test.js), what I want to emit/transpile as JavaScript code:
import { default as myImport } from './lib/index.js'
console.log(myImport)
And now I run it:
$ npm run run-it ./import-test.js
> my-nop#0.1.0 run-it /Users/jmquij0106/git/a-rebalancing-act
> ./.script/run-it.sh "./import-test.js"
[Function: main]
Bottomline is spare yourself the pain and just emit CommonJS compliant code whenever dealing with ES Modules on Node.js, see this comment/issue.
Although I am able to start the npm project using npm start without any issues with webpack or babel, once I run npm test, I find the following error related to testing App.js using App.test.js (where App.js imports ApolloClient):
TypeError: Cannot assign to read only property '__esModule' of object '[object Object]'
| import ApolloClient from 'apollo-boost';
| ^
at node_modules/apollo-boost/lib/bundle.cjs.js:127:74
at Array.forEach (<anonymous>)
at Object.<anonymous> (node_modules/apollo-boost/lib/bundle.cjs.js:127:36)
Essentially, I'm confused as to why I get an error when running the test but not when starting the project.
I've tried adding in a number of babel plugins to both .babelrc and in my webpack config file:
#babel/plugin-transform-object-assign
#babel/plugin-transform-modules-commonjs
babel-plugin-transform-es2015-modules-commonjs
However, I haven't been able to resolve the issue. My thinking was that this is related to the fact that the file that fails to compile was originally CommonJS.
I was only able to find something relatively similar here, https://github.com/ReactTraining/react-router/pull/6758, but I didn't find a solution.
Is there something that I'm missing specifically related to running tests? I should also mention I've tried frameworks other than Jest and ran into the same issue.
EDIT:
I removed everything from App.test.js except the imports to isolate the issue so it just contains the following:
import React from 'react';
import { shallow } from 'enzyme/build';
import App from './App';
UPDATE:
I was able to resolve the initial error by upgrading apollo-boost from version 0.3.1 to 0.4.2. However, I now have a different error that is similarly frustrating. I am using Babel 7 and have added the plugin #babel/plugin-syntax-dynamic-import to both my .babelrc and to my webpack.config.js files. Despite this, I get the following error related to the use of a dynamic import in App.js when running the Jest to test App.test.js:
SyntaxError: Support for the experimental syntax 'dynamicImport' isn't currently enabled
Add #babel/plugin-syntax-dynamic-import (https://git.io/vb4Sv) to the 'plugins' section of your Babel config to enable parsing.
I'm not sure if there is a parsing error or something else, but I've tried numerous things that have not worked. The closest discussion I could find related to this problem is, https://github.com/facebook/jest/issues/5920, however, the proposed solutions don't work for me.
UPDATE:
One thing that I'm trying is to avoid duplication of the babel options as right now they're both in .babelrc and in the babel-loader options within webpack.config.js. From what I found online (Whats the difference when configuring webpack babel-loader vs configuring it within package.json?), the way to make webpack use the settings in .babelrc is to not specify options. However, doing so results in the same error described above showing up only this time when running npm start. I will add that the project that was originally created using create-react-app, however, in order to support multiple pages, I needed to customize webpack's configuration and so ejected from it. I'm not sure why this is so convoluted.
its probably a babel configuration issue, I'm pretty sure jest needs to be compiled to work with create-react-app...
did you specify a setup file in package.json:
"jest": {
"setupFiles": [
"/setupTests.js"
]
}
and in setupTests.js:
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
It turns out that one of the components in the project's src directory had its own local package.json file even though it wasn't being used and was not installed as a local dependency in the top level package.json (instead imports were done using relative urls). For some reason, the existence of this file changed the behavior of webpack and other tools when starting and testing the project such that none of the top level configurations were used for files within directories with separate package.json files. Once I removed these local package.json files from the components sub-directory, all the prior issues were resolved. One hallmark of this problem is that compilation errors were not showing up for JavaScript files that weren't nested under an alternate package.json file.
Hopefully this is useful for anyone that encounters similar errors as I don't think the cause can be directly determined from the compiler messages alone.
Our project is using the webpack resolve.root option to import modules with absolute paths. (avoiding something like ../../../module)
In its current state the project is using babel-loader which works perfectly fine.
My task is to migrate the app to Angular 2.
Therefor I am currently in the process of transitioning to TypeScript.
Somehow it seems like the ts-loader does not work in combination with the resolve.root option of the webpack config.
Example of the webpack.config.js
resolve: {
root: [
path.resolve('./node_modules'),
path.resolve('./app'),
path.resolve('./app/lib'),
]
},
Example of a module import
import AbstractListState from 'states/abstract_list_state';
The states directory is inside the app/lib directory.
Error when executing webpack
ERROR in ./app/mainViews/panel/panel.controller.ts
Module not found: Error: Cannot resolve module 'states/abstract_list_state' in C:\Users\...\Project\app\mainViews\panel
# ./app/mainViews/panel/panel.controller.ts 4:28-65
Pre version 2.0 TypeScript will try to load modules with an absolute path from the node_modules directory. This is because TypeScript's module resultion is per default set to "node". Which means it works like node's require method. So, even if you're using webpack to build your app, TypeScript (and its compiler) will still want to load the files.
In order to let webpack import your modules with absolute path you have to go back and use the require method. This way TypeScript will let webpack import stuff. But of course you will not get any type-inference, autocomplete, ...
Or, you update to the TypeScript 2.0 beta and give this a try: https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#module-resolution-enhancements-baseurl-path-mapping-rootdirs-and-tracing
I'm writing some mocha tests that load code that have paths like this:
import MyStore from "stores/MyStore"
This works fine in the web browser because we are using the webpack-dev-server which in turn reads this entry from webpack.config.js: config.resolve.root: [path.resolve(__dirname, "./app")] so it knows to find ./app/stores/MyStore.
This path does not work when running it from mocha --compilers js:babel/register. I'm trying to locate a package or configuration that I may use for this. It would save us from having to change may code references and of course keep our imports more portable.
Not sure if it matters, we use iojs. If this really can't be done it would be fine just to update the paths. Thank you...
How about including your app directory in $NODE_PATH:
env NODE_PATH=$NODE_PATH:$PWD/app mocha ...
Here's a cross-platform method. First install cross-env:
npm install cross-env --save-dev
then in your package.json:
"scripts": {
...
"test": "cross-env NODE_PATH=./app mocha ..."
}
In windows, I had to do this:
set NODE_PATH=%CD%/app&& mocha...
for some reason, adding a space after 'app' would cause it not to work