Error: No "exports" main defined in graphql-upload/package.json - javascript

Have installed graphql-upload, do
import { graphqlUploadExpress } from 'graphql-upload';
And getting this error:
Error: No "exports" main defined in graphql-upload/package.json
Dependencies:
"graphql-upload": "^14.0.0",
"graphql": "15.8.0",
"graphql-request": "^4.2.0",
"graphql-tools": "^8.2.0",
"#nestjs/axios": "^0.0.7",
"#nestjs/common": "^8.4.1",
"#nestjs/config": "^1.1.5",
"#nestjs/core": "^8.4.1",
"#nestjs/graphql": "^9.1.2",
"#nestjs/platform-express": "^8.0.0",
The version of node: v16.10.0

I have personally put a ts-ignore for ignore errors.
// #ts-ignore
import GraphQLUpload from 'graphql-upload/GraphQLUpload.js';
// #ts-ignore
import Upload from 'graphql-upload/Upload.js';
And imports like that. I hope it can help even if it's dirty!

graphql-upload library doesn't have any main index.js re-export for all of its functions. It has direct file exports for all the specific functionalities. It is specified in its package.json file under exports key like so:
"exports": {
"./GraphQLUpload.js": "./GraphQLUpload.js",
"./graphqlUploadExpress.js": "./graphqlUploadExpress.js",
"./graphqlUploadKoa.js": "./graphqlUploadKoa.js",
"./package.json": "./package.json",
"./processRequest.js": "./processRequest.js",
"./Upload.js": "./Upload.js"
},
So instead of directly importing from package root you need to specify a sub-module path like this:
import graphqlUploadKoa from "graphql-upload/graphqlUploadKoa.js";
Reference: package.json of graphql-upload

So the problem was in the .default build settings.
You can remove it, but when we removed it we saw the problem with other modules, so we resolved this issue via this:
import Upload = require('graphql-upload/Upload.js');
It looks very dirty, but it works.
You can check conversation about this module in issues on GitHub: https://github.com/jaydenseric/graphql-upload/issues/305#issuecomment-1136574019

The owner of the package just chooses to restrict importing everything from the index file, so to run the new version (> 13.0.0) you have to change the way you are importing the package:
const graphqlUploadExpress = require('graphql-upload/graphqlUploadExpress.js');
or
const GraphQLUpload = require('graphql-upload/GraphQLUpload.js');
Alternatively, you can download grade your package to version 13.0.0

Just like every answer, the maintainer of the package has chosen to restrict the imports for this package. I run into the same issue and have decided to stick with version 13 moving forward.
You can use the old imports with version 13
import { graphqlUploadExpress } from 'graphql-upload';

Just ran into this problem.
Apparently, the the new version i.e. ^16 ,has a major update
now you need to do
const Upload = require('graphql-upload/Upload.mjs');
or
import { default as Upload } from 'graphql-upload/Upload.mjs';
Instead of .js, all the imports needs to be from .mjs.
Hope this helps!

use version v15.0.1 for graphql-upload
import like this:
import GraphQLUpload from 'graphql-upload/GraphQLUpload.js';
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.js';
https://github.com/jaydenseric/graphql-upload/issues/314#issuecomment-1140441744
https://github.com/jaydenseric/graphql-upload/releases/tag/v15.0.1
Supported runtime environments:
Node.js versions ^14.17.0 || ^16.0.0 || >= 18.0.0.
Projects must configure TypeScript to use types from the ECMAScript modules that have a // #ts-check comment:
compilerOptions.allowJs should be true.
compilerOptions.maxNodeModuleJsDepth should be reasonably large, e.g. 10.
compilerOptions.module should be "node16" or "nodenext".
https://www.npmjs.com/package/graphql-upload

I faced the same issue and I fixed it using an alternate package graphql-upload-minimal
Here is the old code
const {
graphqlUploadExpress, // A Koa implementation is also exported.
} = require("graphql-upload");
const { GraphQLUpload } = require("graphql-upload");
Here is the new code
const {
graphqlUploadExpress, // A Koa implementation is also exported.
} = require("graphql-upload-minimal");
const { GraphQLUpload } = require("graphql-upload-minimal");
This fixed it.

Related

Renaming file by using Node Package Version [duplicate]

Is there a way to get the version set in package.json in a nodejs app? I would want something like this
var port = process.env.PORT || 3000
app.listen port
console.log "Express server listening on port %d in %s mode %s", app.address().port, app.settings.env, app.VERSION
I found that the following code fragment worked best for me. Since it uses require to load the package.json, it works regardless of the current working directory.
var pjson = require('./package.json');
console.log(pjson.version);
A warning, courtesy of #Pathogen:
Doing this with Browserify has security implications.
Be careful not to expose your package.json to the client, as it means that all your dependency version numbers, build and test commands and more are sent to the client.
If you're building server and client in the same project, you expose your server-side version numbers too.
Such specific data can be used by an attacker to better fit the attack on your server.
If your application is launched with npm start, you can simply use:
process.env.npm_package_version
See package.json vars for more details.
Using ES6 modules you can do the following:
import {version} from './package.json';
Or in plain old shell:
$ node -e "console.log(require('./package.json').version);"
This can be shortened to
$ node -p "require('./package.json').version"
There are two ways of retrieving the version:
Requiring package.json and getting the version:
const { version } = require('./package.json');
Using the environment variables:
const version = process.env.npm_package_version;
Please don't use JSON.parse, fs.readFile, fs.readFileSync and don't use another npm modules it's not necessary for this question.
For those who look for a safe client-side solution that also works on server-side, there is genversion. It is a command-line tool that reads the version from the nearest package.json and generates an importable CommonJS module file that exports the version. Disclaimer: I'm a maintainer.
$ genversion lib/version.js
I acknowledge the client-side safety was not OP's primary intention, but as discussed in answers by Mark Wallace and aug, it is highly relevant and also the reason I found this Q&A.
Here is how to read the version out of package.json:
fs = require('fs')
json = JSON.parse(fs.readFileSync('package.json', 'utf8'))
version = json.version
EDIT: Wow, this answer was originally from 2012! There are several better answers now. Probably the cleanest is:
const { version } = require('./package.json');
There is another way of fetching certain information from your package.json file namely using pkginfo module.
Usage of this module is very simple. You can get all package variables using:
require('pkginfo')(module);
Or only certain details (version in this case)
require('pkginfo')(module, 'version');
And your package variables will be set to module.exports (so version number will be accessible via module.exports.version).
You could use the following code snippet:
require('pkginfo')(module, 'version');
console.log "Express server listening on port %d in %s mode %s", app.address().port, app.settings.env, module.exports.version
This module has very nice feature - it can be used in any file in your project (e.g. in subfolders) and it will automatically fetch information from your package.json. So you do not have to worry where you package.json is.
I hope that will help.
NPM one liner:
From npm v7.20.0:
npm pkg get version
Prior to npm v7.20.0:
npm -s run env echo '$npm_package_version'
Note the output is slightly different between these two methods: the former outputs the version number surrounded by quotes (i.e. "1.0.0"), the latter without (i.e. 1.0.0).
One solution to remove the quotes in Unix is using xargs
npm pkg get version | xargs echo
Option 1
Best practice is to version from package.json using npm environment variables.
process.env.npm_package_version
more information on: https://docs.npmjs.com/using-npm/config.html
This will work only when you start your service using NPM command.
Quick Info: you can read any values in pacakge.json using process.env.npm_package_[keyname]
Option 2
Setting version in environment variable using https://www.npmjs.com/package/dotenv as .env file and reading it as process.env.version
Just adding an answer because I came to this question to see the best way to include the version from package.json in my web application.
I know this question is targetted for Node.js however, if you are using Webpack to bundle your app just a reminder the recommended way is to use the DefinePlugin to declare a global version in the config and reference that. So you could do in your webpack.config.json
const pkg = require('../../package.json');
...
plugins : [
new webpack.DefinePlugin({
AppVersion: JSON.stringify(pkg.version),
...
And then AppVersion is now a global that is available for you to use. Also make sure in your .eslintrc you ignore this via the globals prop
If you are looking for module (package.json: "type": "module") (ES6 import) support, e.g. coming from refactoring commonJS, you should (at the time of writing) do either:
import { readFile } from 'fs/promises';
const pkg = JSON.parse(await readFile(new URL('./package.json', import.meta.url)));
console.log(pkg.version)
or, run the node process with node --experimental-json-modules index.js to do:
import pkg from './package.json'
console.log(pkg.version)
You will however get a warning, until json modules will become generally available.
If you get Syntax or (top level) async errors, you are likely in a an older node version. Update to at least node#14.
You can use ES6 to import package.json to retrieve version number and output the version on console.
import {name as app_name, version as app_version} from './path/to/package.json';
console.log(`App ---- ${app_name}\nVersion ---- ${app_version}`);
A safe option is to add an npm script that generates a separate version file:
"scripts": {
"build": "yarn version:output && blitz build",
"version:output": "echo 'export const Version = { version: \"'$npm_package_version.$(date +%s)'\" }' > version.js"
}
This outputs version.js with the contents:
export const Version = { version: "1.0.1.1622225484" }
To determine the package version in node code, you can use the following:
const version = require('./package.json').version; for < ES6 versions
import {version} from './package.json'; for ES6 version
const version = process.env.npm_package_version;
if application has been started using npm start, all npm_* environment variables become available.
You can use following npm packages as well - root-require, pkginfo, project-version.
we can read the version or other keys from package.json in two ways
1- using require and import the key required e.g:
const version = require('./package.json')
2 - using package_vars as mentioned in doc
process.env.npm_package_version
You can use the project-version package.
$ npm install --save project-version
Then
const version = require('project-version');
console.log(version);
//=> '1.0.0'
It uses process.env.npm_package_version but fallback on the version written in the package.json in case the env var is missing for some reason.
Why don't use the require resolve...
const packageJson = path.dirname(require.resolve('package-name')) + '/package.json';
const { version } = require(packageJson);
console.log('version', version)
With this approach work for all sub paths :)
In case you want to get version of the target package.
import { version } from 'TARGET_PACKAGE/package.json';
Example:
import { version } from 'react/package.json';
I know this isn't the intent of the OP, but I just had to do this, so hope it helps the next person.
If you're using docker-compose for your CI/CD process, you can get it this way!
version:
image: node:7-alpine
volumes:
- .:/usr/src/service/
working_dir: /usr/src/service/
command: ash -c "node -p \"require('./package.json').version.replace('\n', '')\""
for the image, you can use any node image. I use alpine because it is the smallest.
The leanest way I found:
const { version } = JSON.parse(fs.readFileSync('./package.json'))
I've actually been through most of the solutions here and they either did not work on both Windows and Linux/OSX, or didn't work at all, or relied on Unix shell tools like grep/awk/sed.
The accepted answer works technically, but it sucks your whole package.json into your build and that's a Bad Thing that only the desperate should use temporarily to get unblocked, and in general should be avoided, at least for production code. The alternative is to use that method only to write the version to a single constant that can be used instead of the whole file.
So for anyone else looking for a cross-platform solution (not reliant on Unix shell commands) and local (without external dependencies):
Since it can be assumed that Node.js is installed, and it's already cross-platform for this, I just created a make_version.js file with:
const PACKAGE_VERSION = require("./package.json").version;
console.log(`export const PACKAGE_VERSION = "${PACKAGE_VERSION}";`);
console.error("package.json version:", PACKAGE_VERSION);
and added a version command to package.json:
scripts: {
"version": "node make_version.js > src/version.js",
and then added:
"prebuild": "npm run version",
"prestart": "npm run version",
and it creates a new src/versions.js on every start or build. Of course this can be easily tuned in the version script to be a different location, or in the make_version.js file to output different syntax and constant name, etc.
I do this with findup-sync:
var findup = require('findup-sync');
var packagejson = require(findup('package.json'));
console.log(packagejson.version); // => '0.0.1'
I am using gitlab ci and want to automatically use the different versions to tag my docker images and push them. Now their default docker image does not include node so my version to do this in shell only is this
scripts/getCurrentVersion.sh
BASEDIR=$(dirname $0)
cat $BASEDIR/../package.json | grep '"version"' | head -n 1 | awk '{print $2}' | sed 's/"//g; s/,//g'
Now what this does is
Print your package json
Search for the lines with "version"
Take only the first result
Replace " and ,
Please not that i have my scripts in a subfolder with the according name in my repository. So if you don't change $BASEDIR/../package.json to $BASEDIR/package.json
Or if you want to be able to get major, minor and patch version I use this
scripts/getCurrentVersion.sh
VERSION_TYPE=$1
BASEDIR=$(dirname $0)
VERSION=$(cat $BASEDIR/../package.json | grep '"version"' | head -n 1 | awk '{print $2}' | sed 's/"//g; s/,//g')
if [ $VERSION_TYPE = "major" ]; then
echo $(echo $VERSION | awk -F "." '{print $1}' )
elif [ $VERSION_TYPE = "minor" ]; then
echo $(echo $VERSION | awk -F "." '{print $1"."$2}' )
else
echo $VERSION
fi
this way if your version was 1.2.3. Your output would look like this
$ > sh ./getCurrentVersion.sh major
1
$> sh ./getCurrentVersion.sh minor
1.2
$> sh ./getCurrentVersion.sh
1.2.3
Now the only thing you will have to make sure is that your package version will be the first time in package.json that key is used otherwise you'll end up with the wrong version
I'm using create-react-app and I don't have process.env.npm_package_version available when executing my React-app.
I did not want to reference package.json in my client-code (because of exposing dangerous info to client, like package-versions), neither I wanted to install an another dependency (genversion).
I found out that I can reference version within package.json, by using $npm_package_version in my package.json:
"scripts": {
"my_build_script": "REACT_APP_VERSION=$npm_package_version react-scripts start"
}
Now the version is always following the one in package.json.
I made a useful code to get the parent module's package.json
function loadParentPackageJson() {
if (!module.parent || !module.parent.filename) return null
let dir = path.dirname(module.parent.filename)
let maxDepth = 5
let packageJson = null
while (maxDepth > 0) {
const packageJsonPath = `${dir}/package.json`
const exists = existsSync(packageJsonPath)
if (exists) {
packageJson = require(packageJsonPath)
break
}
dir = path.resolve(dir, '../')
maxDepth--
}
return packageJson
}
If using rollup, the rollup-plugin-replace plugin can be used to add the version without exposing package.json to the client.
// rollup.config.js
import pkg from './package.json';
import { terser } from "rollup-plugin-terser";
import resolve from 'rollup-plugin-node-resolve';
import commonJS from 'rollup-plugin-commonjs'
import replace from 'rollup-plugin-replace';
export default {
plugins: [
replace({
exclude: 'node_modules/**',
'MY_PACKAGE_JSON_VERSION': pkg.version, // will replace 'MY_PACKAGE_JSON_VERSION' with package.json version throughout source code
}),
]
};
Then, in the source code, anywhere where you want to have the package.json version, you would use the string 'MY_PACKAGE_JSON_VERSION'.
// src/index.js
export const packageVersion = 'MY_PACKAGE_JSON_VERSION' // replaced with actual version number in rollup.config.js
const { version } = require("./package.json");
console.log(version);
const v = require("./package.json").version;
console.log(v);
Import your package.json file into your server.js or app.js and then access package.json properties into server file.
var package = require('./package.json');
package variable contains all the data in package.json.
Used to version web-components like this:
const { version } = require('../package.json')
class Widget extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
public connectedCallback(): void {
this.renderWidget()
}
public renderWidget = (): void => {
this.shadowRoot?.appendChild(this.setPageTemplate())
this.setAttribute('version', version)
}
}

Node.js: SyntaxError: Cannot use import statement outside a module

I am getting this error SyntaxError: Cannot use import statement outside a module when trying to import from another javascript file. This is the first time I'm trying something like this. The main file is main.js and the module file is mod.js.
main.js:
import * as myModule from "mod";
myModule.func();
mod.js:
export function func(){
console.log("Hello World");
}
How can I fix this? Thanks
In order to use the import syntax (ESModules), you need to add the following to your package.json at the top level:
{
// ...
"type": "module"
}
If you are using a version of Node earlier than 13, you additionally need to use the --experimental-modules flag when you run the program:
node --experimental-modules program.js
Use commonjs syntax instead of es module syntax:
module.exports.func = function (){
console.log("Hello World");
}
and
const myMod = require("./mod")
myMod.func()
Otherwise, if you want to use es modules you have to do as the answer by Achraf Ghellach suggests
I recently encountered this problem. This solution is similar to the top rated answer but with some ways I found worked for me.
In the same directory as your modules create a package.json file and add "type":"module". Then use import {func} from "./myscript.js";. The import style works when run using node.
In addition to the answers above, note by default(if the "type" is omitted) the "type" is "commonjs". So, you have explicitly specify the type when it's "module". You cannot use an import statement outside a module.
If you are in the browser (instead of a Node environment), make sure you specify the type="module" attribute in your script tag. If you want to use Babel, then it must be type="text/babel" data-plugins="transform-es2015-modules-umd" data-type="module".
For browser(front end):
add type = "module" inside your script tag i.e
<script src="main.js" type="module"></script>
For nodejs:
add "type": "module", in your package.json file
I had this issue trying to run mocha tests with typescript. This isn't directly related to the answer but may help some.
This article is quite interesting. He's using a trick involving cross-env, that allows him to run tests as commonjs module type. That worked for me.
// package.json
{
...
"scripts": {
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{ \"module\": \"commonjs\" }' mocha -r ts-node/register -r src/**/*.spec.ts"
}
}
I got the same issue but in another module (python-shell).
I replaced the code as follows:
import {PythonShell} from 'python-shell'; (original code)
let {PythonShell} = require('python-shell')
That solved the issue.

react-testing-library why is toBeInTheDocument() not a function

Here is my code for a tooltip that toggles the CSS property display: block on MouseOver and on Mouse Out display: none.
it('should show and hide the message using onMouseOver and onMouseOut events respectively', () => {
const { queryByTestId, queryByText } = render(
<Tooltip id="test" message="test" />,
)
fireEvent.mouseOver(queryByTestId('tooltip'))
expect(queryByText('test')).toBeInTheDocument()
fireEvent.mouseOut(queryByTestId('tooltip'))
expect(queryByText('test')).not.toBeInTheDocument()
cleanup()
})
I keep getting the error TypeError: expect(...).toBeInTheDocument is not a function
Has anyone got any ideas why this is happening? My other tests to render and snapshot the component all work as expected. As do the queryByText and queryByTestId.
toBeInTheDocument is not part of RTL. You need to install jest-dom to enable it.
And then import it in your test files by:
import '#testing-library/jest-dom'
As mentioned by Giorgio, you need to install jest-dom. Here is what worked for me:
(I was using typescript)
npm i --save-dev #testing-library/jest-dom
Then add an import to your setupTests.ts
import '#testing-library/jest-dom/extend-expect';
Then in your jest.config.js you can load it via:
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.ts"
]
When you do npm i #testing-library/react make sure there is a setupTests.js file with the following statement in it
import '#testing-library/jest-dom/extend-expect';
Having tried all of the advice in this post and it still not working for me, I'd like to offer an alternative solution:
Install jest-dom:
npm i --save-dev #testing-library/jest-dom
Then create a setupTests.js file in the src directory (this bit is important! I had it in the root dir and this did not work...). In here, put:
import '#testing-library/jest-dom'
(or require(...) if that's your preference).
This worked for me :)
Some of the accepted answers were basically right but some may be slightly outdated:
Some references that are good for now:
https://github.com/testing-library/jest-dom
https://jestjs.io/docs/configuration
Here are the full things you need:
in the project's <rootDir> (aka where package.json and jest.config.js are), make sure you have a file called jest.config.js so that Jest can automatically pick it up for configuration. The file is in JS but is structured similarly to a package.json.
Make sure you input the following:
module.exports = {
testPathIgnorePatterns: ['<rootDir>/node_modules', '<rootDir>/dist'], // might want?
moduleNameMapper: {
'#components(.*)': '<rootDir>/src/components$1' // might want?
},
moduleDirectories: ['<rootDir>/node_modules', '<rootDir>/src'],
setupFilesAfterEnv: ['<rootDir>/src/jest-setup.ts'] // this is the KEY
// note it should be in the top level of the exported object.
};
Also, note that if you're using typescript you will need to make sure your jest-setup.ts file is compiled (so add it to src or to the list of items to compile in your tsconfig.json.
At the top of jest-setup.ts/js (or whatever you want to name this entrypoint) file: add import '#testing-library/jest-dom';.
You may also want to make sure it actually runs so put a console.log('hello, world!');. You also have the opportunity to add any global functions you'd like to have available in jest such as (global.fetch = jest.fn()).
Now you actually have to install #testing-library/jest-dom: npm i -D #testing-library/jest-dom in the console.
With those steps you should be ready to use jest-dom:
Without TS: you still need:
npm i -D #testing-library/jest-dom
Creating a jest.config.js and adding to it a minimum of: module.exports = { setupFilesAfterEnv: ['<rootDir>/[path-to-file]/jest-setup.js'] }.
Creating a [path-to-file]/jest-setup.js and adding to it: import '#testing-library/jest-dom';.
The jest-setup file is also a great place to configure tests like creating a special renderWithProvider( function or setting up global window functions.
None of the answers worked for me because I made the silly mistake of typing toBeInDocument() instead of toBeInTheDocument(). Maybe someone else did the same mistake :)
I had a hard time solving that problem so I believe it's important to note the followings if you're using CREATE REACT APP for your project:
You DO NOT need a jest.config.js file to solve this, so if you have that you can delete it.
You DO NOT need to change anything in package.json.
You HAVE TO name your jest setup file setupTests.js and have it under the src folder. It WILL NOT work if your setup file is called jest.setup.js or jest-setup.js.
install required packages
npm install --save-dev #testing-library/jest-dom eslint-plugin-jest-dom
create jest-setup.js in the root folder of your project and add
import '#testing-library/jest-dom'
in jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest-setup.js']
TypeScript only, add the following to the tsconfig.json file. Also, change .js extension to .ts.
"include": ["./jest-setup.ts"]
toBeInTheDocument() and many similar functions are not part of the React-testing-library. It requires installing an additional package.
For anyone out there that like is trying to run tests in Typescript with jest and is still getting the same error even after installing #testing-library/jest-dom and following all the other answers: you probably need to install the type definitions for jest-dom (here) with:
npm i #types/testing-library__jest-dom
or
yarn add #types/testing-library__jest-dom
You need to install them as real dependencies and not as devDependency.
I was having this issue but for #testing-library/jasmine-dom rather than #testing-library/jest-dom.
The process of setup is just a tiny bit different with jasmine. You need to set up the environment in a before function in order for the matchers to be added. I think jest-dom will go ahead and add the matchers when you first import but Jasmine does not.
import { render, screen } from '#testing-library/react';
import MyComponent from './myComponent';
import JasmineDOM from '#testing-library/jasmine-dom';
describe("My Suite", function () {
beforeAll(() => {
jasmine.getEnv().addMatchers(JasmineDOM);
})
it('render my stuff', () => {
const { getByText } = render(<MyComponent />);
const ele = screen.getByText(/something/i);
expect(ele).toBeInTheDocument();
});
});
If you are using react-script then follow the below steps
Install #testing-library/jest-dom library if not done already using
npm i #testing-library/jest-dom.
Put import "#testing-library/jest-dom/extend-expect" in setUpTest.js
If you are using jest then import the library in jest.setup.js file.
the problem already was solved, but i will comment a little tip here, you don't need to create a single file called setup just for this, you just need to specify the module of the jest-dom on the setupFilesAfterEnv option in your jest configuration file.
Like this:
setupFilesAfterEnv: ['#testing-library/jest-dom/extend-expect'],
If you're using TS
You could also add a test.d.ts file to your test directory and use a triple slash directive:
///<reference types='#testing-library/jest-dom'>
Instead of doing:
expect(queryByText('test')).toBeInTheDocument()
you can find and test that it is in the document with just one line by using
let element = getByText('test');
The test will fail if the element isn't found with the getBy call.

How to trick Node.js to load .js files as ES6 modules?

Node.JS 10 added experimental support for loading ES6 modules, which already work in browsers. That would mean that we could finally use exactly the same files for Node.JS and browsers without any transpiling or polyfills.
Except we can't. Node.js requires .mjs extension for files to be loaded as modules. I tried tricking node by using a symlink, but node got around it:
D:\web\lines>node --experimental-modules ES6test.mjs
(node:7464) ExperimentalWarning: The ESM module loader is experimental.
D:\web\lines\ES6test.js:6
import myLibrary from "./MyFile.mjs";
^^^^^^^^^^^^^^^
I can't think of any other workaround to make this work - which really renders the whole ES6 module support useless.
Can anybody else think of some trick to make Node.js ignore the extension?
You can now import .js file in node v12.x, in 2 steps:
Add the following line in your package.json file:
// package.json
{
"type": "module"
}
Add --experimental-modules flag before the script:
node --experimental-modules index.js
Reference: https://nodejs.org/api/esm.html
Node.js requires all ES modules should have .mjs extension. Since Node.js support of ES modules is experimental, this is subject to change. A proposal and open pull request are expected to address this problem with package.json esm flag and --mode option.
Currently this can be solved with custom ES module loader that hooks into default module resolver and changes module type for some modules:
custom-loader.mjs
import path from 'path';
const ESM_WITH_JS_EXT = './MyFile.js'; // relative to loader path
const ESM_WITH_JS_EXT_URL = new URL(path.dirname(import.meta.url) + `/${ESM_WITH_JS_EXT}`).href;
export function resolve(specifier, parentModuleURL, defaultResolver) {
const resolvedModule = defaultResolver(specifier, parentModuleURL);
if (resolvedModule.url === ESM_WITH_JS_EXT_URL)
resolvedModule.format = 'esm';
return resolvedModule;
}
It is used as:
node --experimental-modules --loader ./custom-loader.mjs ./index.mjs
Since there are fundamental differences in how ES and CommonJS modules are evaluated, the changes should be limited to modules that need them.
I solved exactly this problem with the fabulous esm package. You can enable dynamic (smart) esm module loading package wide, or per run with a flag like this:
node -r esm your/es6/module.js
It also has options to treat every file as a es6 module, or only those ending in '.mjs'. There are other packages out there, but this one just worked.
Import and export modules using ES6 that work with Node.js
Name files with .mjs extension instead of .js
Create files
touch main.mjs lib.mjs
main.js
import { add } from './lib.mjs';
console.log(add(40, 2));
lib.mjs
export let add = (x,y) => {
return x + y
}
Run
node --experimental-modules main.js
Here is a module that does what you need esmjs.mjs
import { readFileSync } from 'fs'
import { fileURLToPath, pathToFileURL } from 'url'
import { dirname, join } from 'path'
export const jsmodule = (test_url_or_path, module_path) => {
const __filename = test_url_or_path.toLowerCase().startsWith('file:')
? fileURLToPath(test_url_or_path)
: test_url_or_path
const __dirname = dirname(__filename)
const abs_path = join(__dirname, module_path)
const file_url = pathToFileURL(abs_path)
const file_buf = readFileSync(file_url)
const b64 = file_buf.toString('base64')
const moduleData = "data:text/javascript;base64," + b64
return import(moduleData)
}
Usage from .mjs module:
const { hey } = await jsmodule(import.meta.url, '../../test-data/mjs.js')
Usage, from .js file:
const { hey } = await jsmodule(__filename, '../../test-data/mjs.js')
Reference & tests on Github
You can do it in this way:
Create a file module2.mjs (the name is up to you)
'use strict';
export function foo() {
return 'foo';
}
Create index.mjs file:
'use strict';
import { foo } from './module2.mjs';
console.log(foo());
Using node 8.11.1 (or 10) you can run it as (command and output provided):
node --experimental-modules index.mjs
(node:60428) ExperimentalWarning: The ESM module loader is experimental.
foo

The create-react-app imports restriction outside of src directory

I am using create-react-app. I am trying to call an image from my public folder from a file inside my src/components. I am receiving this error message.
./src/components/website_index.js Module not found: You attempted to
import ../../public/images/logo/WC-BlackonWhite.jpg which falls
outside of the project src/ directory. Relative imports outside of
src/ are not supported. You can either move it inside src/, or add a
symlink to it from project's node_modules/.
import logo from '../../public/images/logo_2016.png';
<img className="Header-logo" src={logo} alt="Logo" />
I have read many things saying you can do an import to the path but that is still not working for me. Any help would be greatly appreciated. I know there are many questions like this but they are all telling me to import logo or image so clearly I am missing something in the big picture.
This is special restriction added by developers of create-react-app. It is implemented in ModuleScopePlugin to ensure files reside in src/. That plugin ensures that relative imports from app's source directory don't reach outside of it.
There is no official way to disable this feature except using eject and modify webpack config.
But, most features and its updates are hidden into the internals of create-react-app system. If you make eject you will have no more new features and its update. So if you are not ready to manage and configure application included to configure webpack and so on - do not do eject operation.
Play by the existing rules - move assets to src or use based on public folder url without import.
However instead of eject there are much unofficial solutions, based on
rewire which allows you to programmatically modify the webpack config without eject. But removing the ModuleScopePlugin plugin is not good - this loses some protection and does not adds some features available in src. ModuleScopePlugin is designed to support multiple folders.
The better way is to add fully working additional directories similar to src also protected by ModuleScopePlugin. This can be done using react-app-alias
Anyway do not import from public folder - that will be duplicated in the build folder and will be available by two different url (and with different ways to load), which ultimately worsen the package download size.
Importing from the src folder is preferable and has advantages. Everything will be packed by webpack to the bundle with chunks optimal size and for best loading efficiency.
The package react-app-rewired can be used to remove the plugin. This way you do not have to eject.
Follow the steps on the npm package page (install the package and flip the calls in the package.json file) and use a config-overrides.js file similar to this one:
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
module.exports = function override(config, env) {
config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin));
return config;
};
This will remove the ModuleScopePlugin from the used WebPack plugins, but leave the rest as it was and removes the necessity to eject.
Remove it using Craco:
module.exports = {
webpack: {
configure: webpackConfig => {
const scopePluginIndex = webpackConfig.resolve.plugins.findIndex(
({ constructor }) => constructor && constructor.name === 'ModuleScopePlugin'
);
webpackConfig.resolve.plugins.splice(scopePluginIndex, 1);
return webpackConfig;
}
}
};
If your images are in the public folder then you should use
"/images/logo_2016.png"
in your <img> src instead of importing
'../../public/images/logo_2016.png';
This will work
<img className="Header-logo" src="/images/logo_2016.png" alt="Logo" />
To offer a little bit more information to other's answers. You have two options regarding how to deliver the .png file to the user. The file structure should conform to the method you choose. The two options are:
Use the module system (import x from y) provided with react-create-app and bundle it with your JS. Place the image inside the src folder.
Serve it from the public folder and let Node serve the file. create-react-app also apparently comes with an environment variable e.g. <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;. This means you can reference it in your React app but still have it served through Node, with your browser asking for it separately in a normal GET request.
Source: create-react-app
There are a few answers that provide solutions with react-app-rewired, but customize-cra includes a removeModuleScopePlugin() API which is a bit more elegant. (It's the same solution, but abstracted away by the customize-cra package.)
npm i --save-dev react-app-rewired customize-cra
package.json
"scripts": {
- "start": "react-scripts start"
+ "start": "react-app-rewired start",
...
},
config-overrides.js
const { removeModuleScopePlugin } = require('customize-cra')
module.exports = removeModuleScopePlugin()
I was able to import files outside of src/ by "copying" the outside files with file: as local dependency.
"dependencies": {
"#my-project/outside-dist": "file:./../../../../dist".
}
then
import {FooComponent} from "#my-project/outside-dist/components";
No eject or react-app-rewired or other 3rd-party solution was needed.
You need to move WC-BlackonWhite.jpg into your src directory. The public directory is for static files that's going to be linked directly in the HTML (such as the favicon), not stuff that you're going to import directly into your bundle.
install these two packages
npm i --save-dev react-app-rewired customize-cra
package.json
"scripts": {
- "start": "react-scripts start"
+ "start": "react-app-rewired start"
},
config-overrides.js
const { removeModuleScopePlugin } = require('customize-cra');
module.exports = function override(config, env) {
if (!config.plugins) {
config.plugins = [];
}
removeModuleScopePlugin()(config);
return config;
};
I think Lukas Bach solution to use react-app-rewired in order to modify webpack config is a good way to go, however, I wouldn't exclude the whole ModuleScopePlugin but instead whitelist the specific file that can be imported outside of src:
config-overrides.js
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");
module.exports = function override(config) {
config.resolve.plugins.forEach(plugin => {
if (plugin instanceof ModuleScopePlugin) {
plugin.allowedFiles.add(path.resolve("./config.json"));
}
});
return config;
};
Copy-Paste Typescript solution
(e.g. this will work for a CRA/TS stack, which requires an additional step compared to CRA/JS. The solution itself is not typed.)
Adds the required paths to the ModuleScopePlugin instead of bluntly removing the plugin.
This code below is using craco, but should be easily usable for react-app-rewired or similar solutions. You just need to find the spot where you have a webpackConfig object (react-app-rewired: module.exports.webpack inside your config-overrides.js), and pass it to the provided functions.
craco.config.js
const path = require("path");
const enableImportsFromExternalPaths = require("./src/helpers/craco/enableImportsFromExternalPaths");
// Paths to the code you want to use
const sharedLibOne = path.resolve(__dirname, "../shared-lib-1/src");
const sharedLibTwo = path.resolve(__dirname, "../shared-lib-2/src");
module.exports = {
plugins: [
{
plugin: {
overrideWebpackConfig: ({ webpackConfig }) => {
enableImportsFromExternalPaths(webpackConfig, [
// Add the paths here
sharedLibOne,
sharedLibTwo,
]);
return webpackConfig;
},
},
},
],
};
helpers/craco/enableImportsFromExternalPaths.js
const findWebpackPlugin = (webpackConfig, pluginName) =>
webpackConfig.resolve.plugins.find(
({ constructor }) => constructor && constructor.name === pluginName
);
const enableTypescriptImportsFromExternalPaths = (
webpackConfig,
newIncludePaths
) => {
const oneOfRule = webpackConfig.module.rules.find((rule) => rule.oneOf);
if (oneOfRule) {
const tsxRule = oneOfRule.oneOf.find(
(rule) => rule.test && rule.test.toString().includes("tsx")
);
if (tsxRule) {
tsxRule.include = Array.isArray(tsxRule.include)
? [...tsxRule.include, ...newIncludePaths]
: [tsxRule.include, ...newIncludePaths];
}
}
};
const addPathsToModuleScopePlugin = (webpackConfig, paths) => {
const moduleScopePlugin = findWebpackPlugin(
webpackConfig,
"ModuleScopePlugin"
);
if (!moduleScopePlugin) {
throw new Error(
`Expected to find plugin "ModuleScopePlugin", but didn't.`
);
}
moduleScopePlugin.appSrcs = [...moduleScopePlugin.appSrcs, ...paths];
};
const enableImportsFromExternalPaths = (webpackConfig, paths) => {
enableTypescriptImportsFromExternalPaths(webpackConfig, paths);
addPathsToModuleScopePlugin(webpackConfig, paths);
};
module.exports = enableImportsFromExternalPaths;
Taken from here and here 🙏
Image inside public folder
use image inside html extension
<img src="%PUBLIC_URL%/resumepic.png"/>
use image inside js extension
<img src={process.env.PUBLIC_URL+"/resumepic.png"}/>
use image inside js Extension
This restriction makes sure all files or modules (exports) are inside src/ directory, the implementation is in ./node_modules/react-dev-utils/ModuleScopePlugin.js, in following lines of code.
// Resolve the issuer from our appSrc and make sure it's one of our files
// Maybe an indexOf === 0 would be better?
const relative = path.relative(appSrc, request.context.issuer);
// If it's not in src/ or a subdirectory, not our request!
if (relative.startsWith('../') || relative.startsWith('..\\')) {
return callback();
}
You can remove this restriction by
either changing this piece of code (not recommended)
or do eject then remove ModuleScopePlugin.js from the directory.
or comment/remove const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); from ./node_modules/react-scripts/config/webpack.config.dev.js
PS: beware of the consequences of eject.
Adding to Bartek Maciejiczek's answer, this is how it looks with Craco:
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");
module.exports = {
webpack: {
configure: webpackConfig => {
webpackConfig.resolve.plugins.forEach(plugin => {
if (plugin instanceof ModuleScopePlugin) {
plugin.allowedFiles.add(path.resolve("./config.json"));
}
});
return webpackConfig;
}
}
};
My previous workaround worked with Webpack 4, but not with 5. After skimming through the accumulated workarounds since then, I found the following one really easy (and seemingly scalable).
import { CracoAliasPlugin } from 'react-app-alias';
const cracoConfig = {
plugins: [
{
plugin: CracoAliasPlugin,
options: {
alias: { '~': './' },
},
},
],
}
Then import like so:
import whatever from '~/<path-to-file>';
I have had to overcome this same issue in Truffle. The solution was as follows:
ince Create-React-App's default behavior disallows importing files from outside of the src folder, we need to bring the contracts in our build folder inside src. We can copy and paste them every time we compile our contracts, but a better way is to simply configure Truffle to put the files there.
In the truffle-config.js file, replace the contents with the following:
const path = require("path");
module.exports = {
contracts_build_directory: path.join(__dirname, "client/src/contracts")
};
I don't know if this helps you, but I know I found your question when I had the same issue in Truffle, and this might help someone else.
This can be done directly without using the path to the public folder.
You can do it like
<img src="/images/image-name" alt=""/>
This happens because we do not use App.js in the browser. Since index.html is executed in the browser itself and the path to images is already in the public folder containing index.html file
You don't need to eject, you can modify the react-scripts config with the rescripts library
This would work then:
module.exports = config => {
const scopePluginIndex = config.resolve.plugins.findIndex(
({ constructor }) => constructor && constructor.name === "ModuleScopePlugin"
);
config.resolve.plugins.splice(scopePluginIndex, 1);
return config;
};
Came to the same issue in my project, and found this in the official create-react-app docs: https://create-react-app.dev/docs/using-the-public-folder/
There is an "escape hatch" to add an asset outside the module system:
If you put a file into the public folder, it will not be processed by
webpack. Instead it will be copied into the build folder untouched. To
reference assets in the public folder, you need to use an environment
variable called PUBLIC_URL.
Here's an example they provide:
render() {
// Note: this is an escape hatch and should be used sparingly!
// Normally we recommend using `import` for getting asset URLs
// as described in “Adding Images and Fonts” above this section.
return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}
This worked for me without installing/ changing anything
Context: I got this error when I tried to generate a build using yarn run build
Things I have done between the working and failing of yarn run build
I updated my ant-design to the latest stable version (v4.23.5).
Note: I highly believe that there is nothing to do with this version. I am just mentioning it to add more details.
This answer solved my issue. But I have changed no imports that access something outside the src directory.
The changes include updated package.json, yarn.lock, new Antd implementations (change in props mainly).
It made no sense why the build command broke/ why the answer is working.
Solution here
As all the changes are related to package.json, yarn.lock. I deleted node_modules and clean installed all the packages.
Run
yarn
or
npm install
If you only need to import a single file, such as README.md or package.json, then this can be explicitly added to ModuleScopePlugin()
config/paths.js
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
appPackageJson: resolveApp('package.json'),
appReadmeMD: resolveApp('README.md'),
};
config/webpack.config.dev.js + config/webpack.config.prod.js
module.exports = {
resolve: {
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [
paths.appPackageJson,
paths.appReadmeMD // README.md lives outside of ./src/ so needs to be explicitly included in ModuleScopePlugin()
]),
]
}
}
the best solution is to fork react-scripts, this is actually mentioned in the official documentation, see: Alternatives to Ejecting
If you need multiple modifications, like when using ant design, you can combine multiple functions like this:
const {
override,
removeModuleScopePlugin,
fixBabelImports,
} = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css',
}),
removeModuleScopePlugin(),
);
You can try using simlinks, but in reverse.
React won't follow simlinks, but you can move something to the source directory, and create a simlink to it.
In the root of my project, I had a node server directory that had several schema files in it. I wanted to use them on the frontend, so I:
moved the files /src
in the termal, I cd'ed into where the schema files belonged in server
ln -s SRC_PATH_OF_SCHEMA_FILE
This gave react what it was looking for, and node was perfectly happy including files through simlinks.
If you want to access CSS files from the public, you might face an error OUTSIDE OF SOURCE DIRECTORY
Alternatively, you can link this file in index.html which also resides in the public directory.
<link rel="stylesheet" href="App.css">
Here's an alternative that works well in simple cases (using fs and ncp). While developing, keep a script running that watches for changes to your shared folder(s) outside of /src. When changes are made, the script can automatically copy the shared folder(s) to your project. Here's an example that watches a single directory recursively:
// This should be run from the root of your project
const fs = require('fs')
const ncp = require('ncp').ncp;
ncp.limit = 16
// Watch for file changes to your shared directory outside of /src
fs.watch('../shared', { recursive: true }, (eventType, filename) => {
console.log(`${eventType}: ${filename}`)
// Copy the shared folder straight to your project /src
// You could be smarter here and only copy the changed file
ncp('../shared', './src/shared', function(err) {
if (err) {
return console.error(err);
}
console.log('finished syncing!');
});
})
This is an issue with the relative import, which might have caused because we've used "create-react-app project" command which forms a directory named project with node_modules folder and several other files in public and src folders inside it.
The create-react-app command puts a limitation that we can't import anything from outside src.
My Problem:
I had to import react-bootstrap css files which are created in node_modules folder outside the src folder.
I used import "../node_modules/bootstrap/dist/css/bootstrap.min.css"; but I got the error on terminal.
I found out that I can create a new react app and follow solution steps from A to G, in order to fix this issue.
Solution:
A) Create a new react app, using create-react-app new
B) cd new
C) run this command: "npm install react-bootstrap bootstrap#4.6.0" (without the "" double quotes )
D) in your react file put this to import bootstrap:
D.1) import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
or
D.2)import Button from "react-bootstrap/Button";
E) create a bootstrap element like a Button or anything in your react file, for D.1) < button className="btn btn-success" > Bootstrap < /button>
or
for D.2) < Button variant="primary"> Bootstrap < /Button>
F) in terminal: cd src
G) in terminal: npm start,
this time it will be compiled successfully.
Reasoning:
I could see react-bootstrap working finally once I followed steps A to G in order, and this time I didn't get any error.
(I thought of this solution because:
I've used npm install "#material-ui/icons" and that got installed in the node_modules folder outside the src.
In my react file I've used import Add from "#material-ui/icons/Add"
and Material-ui icons were working fine for me,
but here also we are importing from outside src, from node_modules.. and everything works fine. Why there is no error of importing from outside src this time)
That's why I just created a new react app, and followed solution steps A to G.
If you want to set a background image using CSS. So you have to set the image using the URL of your's localhost and add the path of your image. Just see the example below.
.banner {
width: 100%;
height: 100vh;
background-image: url("http://localhost:3000/img/bg.jpg");
background-size: cover;
background-repeat: no-repeat;
}
Posting here what #Flaom wrote as a comment in the marked as reply answer and that actually saves lives:
"How is this the accepted answer? This bogus restriction is trivially eliminated by simply setting NODE_PATH=./src/.. in the .env file. By doing so, you can import from outside of the src folder without going through the pain associated with ejecting your app. "
Flaom
EDIT Added some more info as #cigien requested.
All the answers above describe very well why we cannot use an image from the public folder when we create our react app with the create-react-app. Having the issue myself and reading all these answers I realized that, what the answers say is to "hack" the app in order to remove the module that restricts us. Some of the answers don't even have an undo option. For a "training" application that is ok.
Personally I would not want to add a solution that alters the concept of the app to my own project, specially in a commercial one. #Flaom solution is the simplest and if anything change in the future it can be replaced with another solution. It has no risk, it can be removed anytime and is the simplest.
This was my code:
import React from 'react';
import './Navbar.scss';
import {images} from '../../constants';
const Navbar = () => {
return (
<nav>
<div>
< img src = {images.logo} alt = "logo" />
</div>
</nav>
);
}
export default Navbar;
Changed it too:
import React from 'react';
import './Navbar.scss';
import {images} from '././constants';
const Navbar = () => {
return (
<nav>
<div>
< img src = {images.logo} alt = "logo" />
</div>
</nav>
);
}
export default Navbar;
And it worked! Im getting better at fixing bugs haha.
If you file reside in public folder and if you want to import it without eject or without using react-app-rewired then in that case you can access file via domains name and the path of the file and using axios.
Example: There is a font file called favico.ico located inside public folder. You want to import it in one the file located in src. You
can access the font using following logic.
axios.get('example.com/favico.ico').then(() => {
// here you can access this file.
})
In above example example.com is domain. If you have different environment like localhost, staging, production then in that case the domain name is different.
So, to get the favico.ico you can use following logic.
axios.get(`${window.location.origin}/favico.ico`).then(() => {
// here you can access this file.
})
In above example you window.location.origin give you current domain meaning if you run your code locally then, it will give you http://localhost:{portnumber},
If your code run on production and production domain is example.com then, it will give you "example.com". So using this pattern you can access assets located in public folder.

Categories