I'm writing a React-Redux app now. And a question appeared in my mind. I often have to write such strings like:
import * as mainActions from '../../../main/actions/main-actions';
import {wysiwygComponent} from '../../../../components.jsx';
So I'm kind of sick of these "../../..". Is there any way to make components having global names or paths?
This is when module loaders come in handy.
If you use Webpack 2.x, you can modify the Path Resolver Configuration like this
resolve: {
modules: [
path.resolve('./client'),
path.resolve('./node_modules')
]
},
You specify a number of directories in modules which will turn
import Header from '../../components/Header';
import TransactionForm from '../TransactionForm';
into
import Header from 'components/Header';
import TransactionForm from 'TransactionForm';
Reference: No Relative Path
Related
It's a common thing to create a index.js file in an React application with the only purpose to export several modules, in order to avoid having too many import statements on other components. This can be done by:
index.js
export { Credits } from './Credits.js';
export { SocialMedia } from './SocialMedia.js';
any module that might use those exports:
import * as var_name from index.js
And this is very nice. It wraps exports into a single file. However, when I changed my project to React with typescript, I found that .tsx files cannot be exported like that. The image below is the error I got after changing the project to typescript and the extensions became .tsx
Is there a way of 'bundle' export React .tsx files with the structure shown above? If not, what is the simplest way of centralizing .tsx files export?
My webpack.config.js:
module.exports = {
module: {
rules: [{
test: /\.scss$/,
use: ["sass-loader"]
}]
}
};
You can definitely use the same style of having an index file to group up exports for a whole folder. The simplest way around your problem would be to omit the file extension (assuming you only have one "index" file in the folder).
For example, let's say you have a component in 'common/Example.tsx':
import React from 'react'
export const Example = () => (<div>I'm an example component</div>)
You can then export it in an index file 'common/index.tsx':
export { Example } from './Example'
And import it from somewhere else, e.g. 'App.tsx':
import { Example } from './common'
Structure of the project generally looks like this:
components
- my-component
- - my-component.ts
- - index.ts
Where index.(ts/js) always consists of
import MyComponent from './my-component';
export default MyComponent;
I want to remove index.(js/ts) and still import/require my components using the path
import MyComponent from './components/my-component' // not having the index file!
// Please, do not suggest importing them like this
import MyComponent from './components/my-component/my-component'
I use Webpack 5. I know there was a module for Webpack 4, but it does not work with 5.
I want NodeJS/Webpack to look for a custom filename rather than index.
Some other solutions:
You could move components/my-component/my-component.ts to components/my-component.ts. I assume you have a reason not to do this (presumably, other files in that component directory).
You could use resolve.alias and list all mappings you want:
module.exports = {
//...
resolve: {
alias: {
"./components/my-component": path.resolve(__dirname, 'components/my-component/my-component.js'),
//...
},
},
};
You could set resolve.mainFiles to list all of your .js filenames. This seems pretty ugly though, as it's a global setting.
Are you sure that Webpack 5 doesn't support DirectoryNamedWebpackPlugin? It's explicitly mentioned in the documentation.
I created an NPM package that uses Webpack and Babel for transpiling/bundling.
In my package.json, I've got main set to "main": "build/index.js". And in my Webpack config, I have entry set to entry: { app: './src/index.js' }. My entry file is shown below.
Everything works fine when the package is installed. However, with this setup, two import paths are exposed for every helper:
This is a problem for editors that support auto imports, since they will sometimes auto import from 'my-package/build/utils/helper1' rather than the preferred path of 'my-package'.
So, two questions:
Is there any way to prevent the longer import path from being exposed?
What is considered best practice when creating NPM packages. Is my setup acceptable, or should I be doing something different?
Entry File:
import helper1 from './utils/helper1';
import helper2 from './utils/helper2';
export {
helper1,
helper2,
};
const myPackage = {
helper1,
helper2,
};
export default myPackage;
you can utilize Webpack resolve
I often use the first way:
export {
helper1,
helper2,
};
Recently, I found that we can use Object.freeze() to export. This is a good article.
I would suggest probably merge your helper1 and helper2 in one file and name it helpers, then you can put them in the class myPackage so that you then export them as a module like this
import myPackage from './utils/helper';
// OR import {helper1, helper2} from './utils/helpers';
export default class myPackage {
helper1,
helper2,
};
OR
import {myPackage} from './utils/helpers';
// OR import {helper1, helper2} from './utils/helpers';
module.exports.myPackage = (helper1, helper2) => {
this.helper1 = helper1;
this.helper2 = helper2;
};
I hope this helps.
Our application uses TypeScript's namespaces and do not use any kind of modules. We want to use react-datepicker, which is written using modules.
For example, it contains the next code:
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import onClickOutside from 'react-onclickoutside';
import moment from 'moment';
import { Manager, Popper, Target } from 'react-popper';
...
export default DatePicker;
Or transpiled:
var React = _interopDefault(require('react'));
var PropTypes = _interopDefault(require('prop-types'));
var classnames = _interopDefault(require('classnames'));
var onClickOutside = _interopDefault(require('react-onclickoutside'));
var moment = _interopDefault(require('moment'));
var reactPopper = require('react-popper');
...
exports['default'] = DatePicker;
It is required to build some js-bundle for this package, that will contain react-datepicker itself as global variable DatePicker, and all its dependecies, besides react, moment, classnames.
These libraries are already added to the application as global variables (React, moment, classNames), so it should use these global variables.
Is there some plugins and techniques for rollup, webpack, etc. that can help to build such bundle?
You can achieve this with rollup, you would need to use its external and globals option like this:
external: ['react', 'react-dom', 'moment', 'classnames'],
globals: {
'react': 'React',
'react-dom': 'ReactDOM',
'classnames': 'classNames',
'moment': 'moment'
},
Depending on the source file you're using, you might need to use these 2 plugins:
import commonjs from "rollup-plugin-commonjs";
import resolve from 'rollup-plugin-node-resolve';
Also, you might need to use rollup-plugin-replace because react-datepicker seems to include process.env.NODE_ENV in its code, and you will need to remove that.
In case you would like to see a full working example, check this repo I created:
https://github.com/mxcoder/rollup-iife-react-datepicker
I have an App.js file like this:
import './bootstrap';
if (document.getElementById('vue')) {
new Vue({
});
}
It imports a bootstrap javascript file which holds the Vue npm package(node module).
In my bootstrap file I import it like so:
import Vue from 'vue';
When I run eslint with this setup though I get told:
'Vue' is not defined.
If the eslinter only checks per file this seems really obvious since the actually Vue variable is defined in a file that is imported. Can this be fixed cleanly though or do I have to edit my .eslintrc.js for a case like this?
I believe ES6 imports only apply to the current file (which is the main benefit of a module system – to avoid global contamination). Importing a module without bindings won't also make that module's imports available; they remain scoped to that module only.
You have a few options:
You can explicitly import it everywhere you need it (the intended way with modules).
import Vue from 'vue';
You can export Vue (and anything else) from your bootstrap file and import everything:
In bootstrap.js:
import Vue from 'vue';
export { Vue };
In App.js:
import * as bootstrap from './bootstrap';
const Vue = bootstrap.Vue;
You can make Vue global from in your bootstrap file, but it defeats the benefit of modules:
window.Vue = Vue;
The import and export articles at MDN give a good overview of different possible ways of importing and exporting.
You can try few configuration of eslint in .eslintrc to get this working. This error is coming with es6-modules, you can try playing with following config:
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"semi": 2
}
}