webpack 'import' only at the top level - javascript

I'm trying to implement async routing in my react-project using react-router-async-routing.
The problem is that I get this error during webpack compiling:
'import' and 'export' may only appear at the top level
It happens in my router.js which is equal to the one from the link above.
Replacing the import with System.import compiles without errors and the browser loads the chunk (visible in the network-traffic-view), but the page remains blank (I guess it is not being executed!).
This is my webpack-config:
var { path, resolve } = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
app: "./src/js/user/main.js",
vendor: ["react", "react-dom"]
},
output: {
path: __dirname + "/resources/user/js",
publicPath: "/resources/user/js/",
filename: "[name].bundle.js",
chunkFilename: "[name].bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
filename: "vendor.bundle.js"
})
],
module: {
loaders: [{
test: /.(js|jsx)?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['env', 'react']
}
},
{
test: /\.(less)$/,
loaders: ["style-loader", "css-loader", "less-loader"]
},
{
test: /\.(css)$/,
loaders: ["style-loader", "css-loader"]
},
{
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
loader: "file-loader"
},
{
test: /\.(png|jpg|gif)$/,
loader: "url-loader"
}
]
},
resolve: {
alias: {
"global": __dirname + "/src/js/user/module/global"
}
}
};
How can I solve this problem?
EDIT1:
File with error:
import AsyncSetup from "react-router-async-routing";
import routes from "./routes";
const {Route, Link, Preload} = AsyncSetup(routes, path => {
import(`./routes/${path}.jsx`);
});
export {Link, Route, Preload};
EDIT2:
I solved the problem with the import by installing
babel-plugin-syntax-dynamic-import
and adding it to babel :
test : /.(js|jsx)?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: [['es2015'], 'react'],
plugins: ["syntax-dynamic-import"]
}
Now everything is working!

The new Import API is still a little confusing, because of different Node versions, and specially when using transpiling tools such as Babel and Webpack. I strongly recommend you to use require() instead of import() in your dynamic imports (where the file path is not a constant) and let import for constant dependencies, that's what I do in my projects, and I have no issues.
Something like this:
import AsyncSetup from "react-router-async-routing";
import routes from "./routes";
const {Route, Link, Preload} = AsyncSetup(routes, path => {
return require(`./routes/${path}.jsx`);
});
export {Link, Route, Preload};
You can also use absolute paths, this might be safer depending on your environment and code.
import path from 'path';
import AsyncSetup from "react-router-async-routing";
import routes from "./routes";
const {Route, Link, Preload} = AsyncSetup(routes, path => {
return require(path.join(__dirname, `./routes/${path}.jsx`));
});
export {Link, Route, Preload};
If this don't solve your issue, maybe you'll need to do some tweaking in the babel/webpack modules resolver.

Related

Importing a component library using react in Storybook

I have two projects: one is a component library called simply '#loonslanding/components'. The other is a storybook project. Inside of the components library, I have the following in src/index.js:
import React, { Component } from 'react';
class Button extends Component {
render () {
return (
<button>{this.props.label}</button>
);
}
}
export default Button;
I build this using webpack and babel with the following webpack.config.js:
const path = require('path');
module.exports = {
entry: './src/index.js',
mode: 'development',
output: {
filename: 'index.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
}
]
}
};
I then link my js module using yarn link in the components directory and run yarn link #loonslanding/components in my storybook directory. I have a single story in storybook, called button.stories.js:
import Button from "#loonslanding/components";
import { action } from '#storybook/addon-actions';
import React from 'react';
export default {
component: Button,
title: `Atoms/Button`
};
export const Standard = () => <Button onClick={action('clicked')} label={"Standard Button"}></Button>;
When I run yarn storybook, I get the following error message:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `storyFn`.
This seems like a pretty common error, and I've researched it over the past few hours, but I don't think I've actually mixed up named and default exports/imports. When I attempt to import the components project into a different project (e.g. a react application other than Storybook), it gives me a similar exception. This leads me to believe that something is wonky with how Babel is transpiling the components module. Thoughts on what I might be doing incorrectly?
This was due to having incorrect parameters in the Webpack configuration file. I learned from this answer that Webpack is designed to package applications by default, and if you want it to package a library, you need to add the library name, as well as the library target type. This looks like the following when using the commonjs2 library target type:
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
library: 'loonslandingstorybook',
libraryTarget: 'commonjs2'
},
plugins: [new CleanWebpackPlugin()],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
plugins: ['#babel/plugin-transform-react-jsx']
}
}
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, './src')
}
]
}
}

WebPack importing react custom packages in another folder

I am enhancing a website with ReactJS.
My folder structure looks something like this:
_npm
node_modules
package.json
package-lock.json
webpack.config.js
_resources
js
react
reactapp1.js
reactapp2.js
components
FormDisplay.js
I want to import a custom reactjs package into the FormDisplay component.
When I enter:
import PlacesAutocomplete from 'react-places-autocomplete'
This doesn't work. But if I enter:
import PlacesAutocomplete from "./../../../_npm/node_modules/react-places-autocomplete";
This works. I understand why this is the case. I was wondering if there was a way that I can just enter:
import PlacesAutocomplete from 'react-places-autocomplete';
How do I make it work with just that line of code, without having to find the path to node_modules folder?
My webpack config:
const path = require("path");
const webpack = require("webpack");
const PATHS = {
app: path.join(__dirname, "../_resources/react/"),
build: path.join(__dirname, '../wordpress_site/wp-content/themes/custom_theme/assets/js/'),
};
module.exports = {
entry: {
reactapp1: path.join(PATHS.app, "reactapp1.js"),
reactapp2: path.join(PATHS.app, "reactapp2.js")
},
output: {
filename: "[name].bundle.js",
//path: path.join(__dirname, "dist")
path: PATHS.build
},
module:{
rules: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: {
presets: ["env", "react"],
plugins: ["transform-class-properties"]
}
}
}
]// rules array
}, // module
}
Have you tried using webpack's resolve-modules?
resolve: {
modules: ['_npm/node_modules']
}
Might work

Webpack output is empty object

I want to build a react component library as a node module to then import it into different projects. But if I try to import a component it just returns an empty object.
button.jsx:
import React, {Component} from 'react'
export class Button extends Component {
render() {
return <button className='btn'>Hello Button comp</button>
}
}
export default Button
index.js
var Button = require('./button/button').default;
module.exports = {
Button: Button
}
webpack.config.js
const Path = require('path');
module.exports = {
resolve: {
extensions: ['.js', '.jsx']
},
entry: {
app: './src/components/index.js'
},
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx$/,
loader: 'babel-loader',
query: {
presets: [
'es2015',
'react'
]
},
exclude: /node_modules/,
include: [
Path.resolve(__dirname, 'src')
]
},
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: [
'es2015',
'react'
]
},
exclude: /node_modules/,
include: [
Path.resolve(__dirname, 'src')
]
}
]
}
}
Main property in package.json is bundle.js
I figured out that when I import Button in a project it is just an empty object. It seems to me as if webpack doesn't bundle the index file properly. Any ideas what could be wrong here?
A webpack bundle does not expose your exports by default, as it assumes that you're building an app and not a library (which is the far more common use of webpack). You can create a library by configuring output.library and output.libraryTarget.
output: {
path: __dirname,
filename: 'bundle.js',
library: 'yourLibName',
libraryTarget: 'commonjs2'
},
output.libraryTarget is the format of the module, which would also allow you to expose the library as a global variable. commonjs2 is the module format that Node uses. See What is commonjs2? for the difference between commonjs and commonjs2.
Since you're using React, you'll expect that the consumer of the library will have React present as a dependency and therefore you don't want to include it in your bundle. To do that you can define it as an External. This is shown in Authoring Libraries, which walks you through a small example.

webpack: 'import' not working whereas 'require' works okay

I have a strange issue using webpack.
This my webpack.config.js:
import webpack from "webpack";
import path from "path";
//not working: import ExtractTextPlugin from "extract-text-webpack-plugin";
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const GLOBALS = {
"process.env.NODE_ENV": JSON.stringify("production"),
__DEV__: false
};
export default {
debug: true,
devtool: "source-map",
noInfo: true,
entry: "./src/bootstrap",
target: "web",
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "bundle.js"
},
resolve: {
root: path.resolve(__dirname),
alias: {
"~": "src"
},
extensions: ["", ".js", ".jsx"]
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin(GLOBALS),
new ExtractTextPlugin("styles.css"),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin()
],
module: {
loaders: [
{ test: /\.jsx?$/, include: path.join(__dirname, "src"), loaders: ["babel"] },
{ test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: "file" },
{ test: /\.(woff|woff2)$/, loader: "file-loader?prefix=font/&limit=5000" },
{ test: /\.ttf(\?v=\d+.\d+.\d+)?$/, loader: "file-loader?limit=10000&mimetype=application/octet-stream" },
{ test: /\.svg(\?v=\d+.\d+.\d+)?$/, loader: "file-loader?limit=10000&mimetype=image/svg+xml" },
{ test: /\.(jpe?g|png|gif)$/i, loaders: ["file"] },
{ test: /\.ico$/, loader: "file-loader?name=[name].[ext]" },
{
test: /(\.css|\.scss)$/,
loader: ExtractTextPlugin.extract("css?sourceMap!sass?sourceMap")
}
]
}
};
As you can see: I set up an alias "~" pointing to my "src" directory.
According to webpack documentation I should be able to import modules this way:
import { ServiceStub } from "~/utilities/service-stub";
HINT: File service-stub.js sits here: [__dirname]/src/utilities/service-stub.js.
However, this does not work since webpack is throwing an error ("Path not found.").
When I userequire instead of import, everything works fine:
const { ServiceStub } = require("~/utilities/service-stub");
The same issue is in webpack.config.js itself:
import webpack from "webpack";
import path from "path";
//not working: import ExtractTextPlugin from "extract-text-webpack-plugin";
const ExtractTextPlugin = require("extract-text-webpack-plugin");
Here some modules import well with import (modules webpack and path), some do not (module extract-text-webpack-plugin).
I worked through dozens of forums, but found no solution yet.
The problem is ESLint - not webpack.
When you are using aliases in webpack like this
resolve: {
root: path.resolve(__dirname),
alias: {
"~": "src"
},
extensions: ["", ".js", ".jsx"]
}
and you are importing this way
import { ServiceStub } from "~/services/service-stub";
ESLint cannot resolve the alias and reports an error.
To get it work you must tell ESLint to ignore some rule with "import/no-unresolved": 0. This seems to be okay because if an imported file is actually missing, webpack reports an error itself.

import syntax not working with webpack

I got Illegal import declaration error. when I tried to integrated a react js repo with webpack
I migrated the original source code from https://github.com/dptoot/react-event-calendar/blob/master/example/src/example.js
How could I fix Illegal import declaration error ?
I think the import syntax only works in some js lib ?
Error
ERROR in ./app/main.js
Module build failed: Error: Parse Error: Line 2: Illegal import declaration
at throwError (/Users/poc/sandbox/ha/node_modules/jsx-loader/node_modules/jstransform/node_modules/esprima-fb/esprima.js:2823:21)
main.js
var React = require('react');
const EventCalendar = require('react-event-calendar');
import moment from 'moment';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button';
import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar';
import Popover from 'react-bootstrap/lib/PopOver';
import Overlay from 'react-bootstrap/lib/Overlay';
webpack.config.js
var path = require('path');
var webpack = require('webpack');
var config = module.exports = {
// the base path which will be used to resolve entry points
context: __dirname,
// the main entry point for our application's frontend JS
entry: './app/main.js',
output: {
filename: 'main.js'
},
resolve: {
extensions: ['', '.js', '.jsx', '.ts']
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'jsx-loader?insertPragma=React.DOM&harmony' }
]
}
};
Use Babel via babel-loader to transform import declarations (and other ES2015 if you want). http://babeljs.io/docs/setup/#webpack
As #JMM answered, it seems you need babel-loader. in addition, I was still facing the same problem, and finally get resolved by editing webpack.config.js such like
module: {
loaders: [
- {test: /\.jsx?$/, loader: 'babel-loader'},
- {test: /\.jsx$/, loader: 'jsx-loader'}
+ {test: /\.jsx$/, loader: 'jsx-loader'},
+ {test: /\.jsx?$/, loader: 'babel-loader'}
]
},
or because jsx-loader looks no longer working with this config, it can be deleted.
I hope it would help

Categories