I have the following jsconfig.json in the root of my react app:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"rmv": ["components/rmv/*"]
}
}
}
and there is a helper.jsx file located in ./src/components/rmv folder.
But my attempts to export it directly like that:
import Helper from 'rmv/helper'
fail with an error:
Failed to compile
Module not found: Can't resolve 'rmv/helper'
Only import Helper from 'components/rmv/helper' works.
Why?
PS: I also tried:
"paths": {
"rmv/*": ["components/rmv/*"]
}
Does not work either.
Here is the minimum reproducible example: github.com/chapkovski/trouble_with_jsconfig
Specifically these lines:
https://github.com/chapkovski/trouble_with_jsconfig/blob/e37c8c216eac0da7c70023f7fba47eea54973176/src/App.js#L4-L5
Paths are currently unavailable in apps made with create-react-app:
https://github.com/facebook/create-react-app/issues/5645
You may want to consider using rescripts to let you modify your configuration in CRA 2+ apps.
The paths need to be relative to the baseUrl. Your baseUrl has a value of ./src which is good. However, your paths listed in the array for rmv/* are NOT relative paths, as they don't start with a relative location (./ or ../).
I would encourage you to try prefixing ./ onto your paths.
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"rmv/*": ["./components/rmv/*"]
}
}
}
I found this documentation on the subject: Using webpack aliases
[Eject CRA] You could use webpack alias as alternative solution for the use case.
In webpack.config.js
module.exports = {
//...
resolve: {
alias: {
rmv: path.resolve(__dirname, 'src/components/rmv/')
}
}
};
Now, you could import Helper component as bellow:
import Helper from 'rmv/helper';
Related
I have an issue with my jsconfig.js not taking affect for my react application.
I have set the following in my jsconfig.js which is located at my project root.
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"#/img/*": ["img/*"]
}
}
}
I have also tried to move this to /src where my react application is stored and updated the baseUrl to "." but no luck.
In my react import I am importing my component like:
import logo from '#/img/logo.svg';
but receieve the following:
Module not found: Error: Can't resolve '#/img/logo.svg' in '/Users/ivyjack/Sites/hcl-fos-app/src/components'
Any help to resolve, I can get the image to load using relative paths however I would prefer to use alias?
I encountered the module resolving issue in my testcafe(1.17.1) test.
I have a test
//test.e2e.ts in <root>/tests folder
import {MyComponent} from '../src/MyComponent';
...
...
fixture('Custom domain tests');
test('test 1', async () => {
...
...
});
the <root>/src/MyComponent.tsx looks like this
//MyComponent.tsx in <root>/src folder
import React, { FunctionComponent } from 'react';
import styles from '../scss/MyComponent.module.scss';
export const MyComponent: FunctionComponent = () => {
...
...
}
When I run testcafe ./tests/test.e2e.ts, I always got the error
Error: TypeScript compilation failed. Cannot find module '../scss/MyComponent.module.scss' or its corresponding type declarations.
My testcafe tsconfig specified the path config, the <root>/scss-module-for-tests.ts just exports a empty string
// .testcaferc.json in <root>/
{
"compilerOptions": {
"typescript": {
"options": {
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"../scss/MyComponent.module.scss": ["scss-module-for-tests.ts"],
"*.scss": ["scss-module-for-tests.ts"]
}
}
}
}
}
However, seems typescript path config doesn't resolve relative path nor accept a regex, but My project has a lot of those ../scss/*module.scss imports. Is there anyway to resolve the scss file directly for testcafe?
Thanks in advance!
Update on 12/02/2021
I tried add compiler for testcafe according to this doc , I put a .testcaferc.json at the root folder
//.testcaferc.json at <root>
{
"compilerOptions": {
"typescript": {
"options": {
"esModuleInterop": "true"
}
}
}
}
But seems the esModuleInterop=true config didn't reach to testcafe.
Update 12/13/2021:
Not easy to configure it correctly, I removed the *.scss file reference in the test code. Problem solved.
It looks like your TestCafe tests' code references your testing application code. However, TestCafe tests do not require testing application code. In fact, the code of the TestCafe tests and the application can be divided into two separate projects. Please try to separate your web application code and your TestCafe tests' code.
I have a JSON file that looks like following:
{
"primaryBright": "#2DC6FB",
"primaryMain": "#05B4F0",
"primaryDarker": "#04A1D7",
"primaryDarkest": "#048FBE",
"secondaryBright": "#4CD2C0",
"secondaryMain": "#00BFA5",
"secondaryDarker": "#009884",
"secondaryDarkest": "#007F6E",
"tertiaryMain": "#FA555A",
"tertiaryDarker": "#F93C42",
"tertiaryDarkest": "#F9232A",
"darkGrey": "#333333",
"lightGrey": "#777777"
}
I'm trying to import it into a .tsx file. For this I added this to the type definition:
declare module "*.json" {
const value: any;
export default value;
}
And I'm importing it like this.
import colors = require('../colors.json')
And in the file, I use the color primaryMain as colors.primaryMain. However I get an error:
Property 'primaryMain' does not exist on type 'typeof "*.json"
With TypeScript 2.9.+ you can simply import JSON files with benefits like typesafety and intellisense by doing this:
import colorsJson from '../colors.json'; // This import style requires "esModuleInterop", see "side notes"
console.log(colorsJson.primaryBright);
Make sure to add these settings in the compilerOptions section of your tsconfig.json (documentation):
"resolveJsonModule": true,
"esModuleInterop": true,
Side notes:
Typescript 2.9.0 has a bug with this JSON feature, it was fixed with 2.9.2
The esModuleInterop is only necessary for the default import of the colorsJson. If you leave it set to false then you have to import it with import * as colorsJson from '../colors.json'
The import form and the module declaration need to agree about the shape of the module, about what it exports.
When you write (a suboptimal practice for importing JSON since TypeScript 2.9 when targeting compatible module formatssee note)
declare module "*.json" {
const value: any;
export default value;
}
You are stating that all modules that have a specifier ending in .json have a single export named default.
There are several ways you can correctly consume such a module including
import a from "a.json";
a.primaryMain
and
import * as a from "a.json";
a.default.primaryMain
and
import {default as a} from "a.json";
a.primaryMain
and
import a = require("a.json");
a.default.primaryMain
The first form is the best and the syntactic sugar it leverages is the very reason JavaScript has default exports.
However I mentioned the other forms to give you a hint about what's going wrong. Pay special attention to the last one. require gives you an object representing the module itself and not its exported bindings.
So why the error? Because you wrote
import a = require("a.json");
a.primaryMain
And yet there is no export named primaryMain declared by your "*.json".
All of this assumes that your module loader is providing the JSON as the default export as suggested by your original declaration.
Note: Since TypeScript 2.9, you can use the --resolveJsonModule compiler flag to have TypeScript analyze imported .json files and provide correct information regarding their shape obviating the need for a wildcard module declaration and validating the presence of the file. This is not supported for certain target module formats.
Here's how to import a json file at runtime
import fs from 'fs'
var dataArray = JSON.parse(fs.readFileSync('data.json', 'utf-8'))
This way you avoid issues with tsc slowing down or running out of memory when importing large files, which can happen when using resolveJsonModule.
It's easy to use typescript version 2.9+. So you can easily import JSON files as #kentor decribed.
But if you need to use older versions:
You can access JSON files in more TypeScript way. First, make sure your new typings.d.ts location is the same as with the include property in your tsconfig.json file.
If you don't have an include property in your tsconfig.json file. Then your folder structure should be like that:
- app.ts
+ node_modules/
- package.json
- tsconfig.json
- typings.d.ts
But if you have an include property in your tsconfig.json:
{
"compilerOptions": {
},
"exclude" : [
"node_modules",
"**/*spec.ts"
], "include" : [
"src/**/*"
]
}
Then your typings.d.ts should be in the src directory as described in include property
+ node_modules/
- package.json
- tsconfig.json
- src/
- app.ts
- typings.d.ts
As In many of the response, You can define a global declaration for all your JSON files.
declare module '*.json' {
const value: any;
export default value;
}
but I prefer a more typed version of this. For instance, let's say you have configuration file config.json like that:
{
"address": "127.0.0.1",
"port" : 8080
}
Then we can declare a specific type for it:
declare module 'config.json' {
export const address: string;
export const port: number;
}
It's easy to import in your typescript files:
import * as Config from 'config.json';
export class SomeClass {
public someMethod: void {
console.log(Config.address);
console.log(Config.port);
}
}
But in compilation phase, you should copy JSON files to your dist folder manually. I just add a script property to my package.json configuration:
{
"name" : "some project",
"scripts": {
"build": "rm -rf dist && tsc && cp src/config.json dist/"
}
}
In my case I needed to change tsconfig.node.json:
{
"compilerOptions": {
...
"resolveJsonModule": true
},
"include": [..., "colors.json"]
}
And to import like that:
import * as colors from './colors.json'
Or like that:
import colors from './colors.json'
with "esModuleInterop": true
You should add
"resolveJsonModule": true
as part of compilerOptions to tsconfig.json.
Often in Node.js applications a .json is needed. With TypeScript 2.9, --resolveJsonModule allows for importing, extracting types from and generating .json files.
Example #
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"resolveJsonModule": true,
"esModuleInterop": true
}
}
// .ts
import settings from "./settings.json";
settings.debug === true; // OK
settings.dry === 2; // Error: Operator '===' cannot be applied boolean and number
// settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
by: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html
Another way to go
const data: {[key: string]: any} = require('./data.json');
This was you still can define json type is you want and don't have to use wildcard.
For example, custom type json.
interface User {
firstName: string;
lastName: string;
birthday: Date;
}
const user: User = require('./user.json');
In an Angular (typescript) app, I needed to include a .json file in my environment.ts. To do so, I had to set two options in tsconfig:
{
"compilerOptions": {
"moduleResolution": "node",
"resolveJsonModule": true
}
}
Then, I could import my json file into the environment.ts:
import { default as someObjectName } from "../some-json-file.json";
You can import a JSON file without modifying tsconfig you tell explicitly that you are importing JSON
import mydata from './mydataonfile.json' assert { type: "json" };
I know this does not fully answer the question but many people come here to know how to load JSON directly from a file.
Enable "resolveJsonModule": true in tsconfig.json file and implement as below code, it's work for me:
const config = require('./config.json');
Note that if you using #kentor ways
Make sure to add these settings in the compilerOptions section of your tsconfig.json (documentation):
You need to add --resolveJsonModule and--esModuleInterop behind tsc command to compile your TypeScript file.
Example:
tsc --resolveJsonModule --esModuleInterop main.ts
require is a common way to load a JSON file in Node.js
in my case I had to change: "include": ["src"] to "include": ["."] in addition to "resolveJsonModule":true because I tried to import manifest.json from the root of the project and not from ./src
In the past, we had a problem importing files from the local working directory when using React Native. Then we found a way from React Native Github: https://github.com/facebook/react-native/issues/3099#issuecomment-221815006.
So for example, we had the folder structure like this:
src
- core/
- config/
- package.json
- file1.js
- index.js
- package.json
- package-lock.json
And we could declare our config/ folder as a custom npm module by setting this in config/package.json:
{
"name": "#config"
}
Then we could import the file1 from anywhere using:
import { something } from "#config/file1";
But the problem is that, in this way, VScode lost the ability to auto-complete and intellisense the import path import from "#somewhere", and VScode could not detect the actual content of the imported variables like something above from file1
So is there a way to configure our React Native project such that VScode could intellisense and detect this kind of custom import?
Here's a simple solution I've found from the VS docs here
Declare a jsconfig.json file at the same level as your index.js file. Declare all the custom modules in the paths object.
A config file I've used for one of my projects:
{
"compilerOptions": {
"target": "es6",
"baseUrl": "./",
"paths": {
"#assets/*": [
"./src/assets/*"
],
"#config/*": [
"./src/config/*"
],
"#screens/*": [
"./src/screens/*"
],
"#library/*": [
"./src/library/*"
],
}
}
}
Answear by Lohit is correct, but please note that you might want to use ** to match all paths recursively, I think it's safer to do that:
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"lib/**": [
"./lib/**"
]
}
}
}
This way when you can do import ... from "lib/nested/path/will/work"
You can check the difference between * and ** here:
https://stackoverflow.com/a/66744400/5155607
They just fixed it back in version 1.60.0. Rejoice!
I use webpack to develop a React component. Here is a simple version of it:
'use strict';
require('./MyComponent.less');
var React = require('react');
var MyComponent = React.createClass({
render() {
return (
<div className="my-component">
Hello World
</div>
);
}
});
module.exports = MyComponent;
Now, I would like to test this component using jest. Here is the relevant bit from my package.json:
"scripts": {
"test": "jest"
},
"jest": {
"rootDir": ".",
"testDirectoryName": "tests",
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"unmockedModulePathPatterns": [
"react"
]
}
When running npm test, I get the following error:
SyntaxError: /Users/mishamoroshko/react-component/src/tests/MyComponent.js: /Users/mishamoroshko/react-component/src/MyComponent.js: /Users/mishamoroshko/react-component/src/MyComponent.less: Unexpected token ILLEGAL
Looks like webpack needs to process require('./MyComponent.less') before jest can run the test.
I wonder if I need to use something like jest-webpack. If yes, is there a way to specify multiple scriptPreprocessors? (note that I already use babel-jest)
The cleanest solution I found for ignoring a required module is to use the moduleNameMapper config (works on the latest version 0.9.2)
The documentation is hard to follow. I hope the following will help.
Add moduleNameMapper key to your packages.json config. The key for an item should be a regex of the required string. Example with '.less' files:
"moduleNameMapper": { "^.*[.](less|LESS)$": "EmptyModule" },
Add a EmptyModule.js to your root folder:
/**
* #providesModule EmptyModule
*/
module.exports = '';
The comment is important since the moduleNameMapper use EmptyModule as alias to this module (read more about providesModule).
Now each require reference that matches the regex will be replaced with an empty string.
If you use the moduleFileExtensions configuration with a 'js' file, then make sure you also add the EmptyModule to your 'unmockedModulePathPatterns'.
Here is the jest configuration I ended up with:
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"moduleFileExtensions": ["js", "json","jsx" ],
"moduleNameMapper": {
"^.*[.](jpg|JPG|gif|GIF|png|PNG|less|LESS|css|CSS)$": "EmptyModule"
},
"preprocessorIgnorePatterns": [ "/node_modules/" ],
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-addons-test-utils",
"<rootDir>/EmptyModule.js"
]
}
I ended up with the following hack:
// package.json
"jest": {
"scriptPreprocessor": "<rootDir>/jest-script-preprocessor",
...
}
// jest-script-preprocessor.js
var babelJest = require("babel-jest");
module.exports = {
process: function(src, filename) {
return babelJest.process(src, filename)
.replace(/^require.*\.less.*;$/gm, '');
}
};
But, I'm still wondering what is the right solution to this problem.
I just found that it's even simpler with Jest's moduleNameMapper configuration.
// package.json
"jest": {
"moduleNameMapper": {
"^.+\\.scss$": "<rootDir>/scripts/mocks/style-mock.js"
}
}
// style-mock.js
module.exports = {};
More detail at Jest's tutorial page.
I recently released Jestpack which might help. It first builds your test files with Webpack so any custom module resolution/loaders/plugins etc. just work and you end up with JavaScript. It then provides a custom module loader for Jest which understands the Webpack module runtime.
From Jest docs:
// in terminal, add new dependency: identity-obj-proxy
npm install --save-dev identity-obj-proxy
// package.json (for CSS Modules)
{
"jest": {
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
}
}
}
The snippet above will route all .less files to the new dependency identity-obj-proxy, which will return a string with the classname when invoked, e.g. 'styleName' for styles.styleName.
I think a less hacky solution would be to wrap your preprocessor in a conditional on the filename matching a javascript file:
if (filename.match(/\.jsx?$/)) {
return babelJest.process(src, filename);
} else {
return '';
}
This works even if you don't explicitly set the extension in the require line and doesn't require a regex substitution on the source.
I have experienced similar issue with such pattern
import React, { PropTypes, Component } from 'react';
import styles from './ContactPage.css';
import withStyles from '../../decorators/withStyles';
#withStyles(styles)
class ContactPage extends Component {
see example at https://github.com/kriasoft/react-starter-kit/blob/9204f2661ebee15dcb0b2feed4ae1d2137a8d213/src/components/ContactPage/ContactPage.js#L4-L7
For running Jest I has 2 problems:
import of .css
applying decorator #withStyles (TypeError: <...> (0 , _appDecoratorsWithStyles2.default)(...) is not a function)
First one was solved by mocking .css itself in script preprocessor.
Second one was solved by excluding decorators from automocking using unmockedModulePathPatterns
module.exports = {
process: function (src, filename) {
...
if (filename.match(/\.css$/)) src = '';
...
babel.transform(src, ...
}
}
example based on https://github.com/babel/babel-jest/blob/77a24a71ae2291af64f51a237b2a9146fa38b136/index.js
Note also: when you working with jest preprocessor you should clean cache:
$ rm node_modules/jest-cli/.haste_cache -r
Taking inspiration from Misha's response, I created an NPM package that solves this problem while also handling a few more scenarios I came across:
webpack-babel-jest
Hopefully this can save the next person a few hours.
If you're using babel, you can strip unwanted imports during the babel transform using something like https://github.com/Shyp/babel-plugin-import-noop and configuring your .babelrc test env to use the plugin, like so:
{
"env": {
"development": {
...
},
"test": {
"presets": [ ... ],
"plugins": [
["import-noop", {
"extensions": ["scss", "css"]
}]
]
}
}
}
We had a similar problem with CSS files. As you mentioned before jest-webpack solves this problem fine. You won't have to mock or use any module mappers either. For us we replaced our npm test command from jest to jest-webpack and it just worked.
Webpack is a great tool, but I don't need to test it's behavior with my Jest unit tests, and adding a webpack build prior to running unit tests is only going to slow down the process. The text-book answer is to mock non-code dependencies using the "moduleNameMapper" option
https://facebook.github.io/jest/docs/webpack.html#handling-static-assets