I am using webpack with HtmlWebpackPlugin, html-loader and file-loader. I have a simple project structure in which I use no frameworks, but only typescript. Thus, I write my HTML code directly to index.html. I also use this HTML file as my template in HtmlWebpackPlugin.
As all websites do I need to put an image which refers to a PNG in my assets folder. file-loader should load the file correctly put the new filename inside the src tag but that is not what is happening. Instead, as the value of src tag, I have [object Module]. I assume the file-loader emits some object and it is represented like this when its .toString() method is run. However, I can see that file-loader has processed the file successfully and emitted with new name to the output path. I get no errors. Here is my webpack configuration and index.html.
const projectRoot = path.resolve(__dirname, '..');
{
entry: path.resolve(projectRoot, 'src', 'app.ts'),
mode: 'production',
output: {
path: path.resolve(projectRoot, 'dist'),
filename: 'app.bundle.js'
},
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.html$/i,
use: 'html-loader'
},
{
test: /\.(eot|ttf|woff|woff2|svg|png)$/i,
use: 'file-loader'
},
{
test: /\.scss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: false
}
},
{
loader: 'css-loader',
options: {
sourceMap: false
}
},
{
loader: 'sass-loader',
options: {
sourceMap: false
}
}
]
},
{
exclude: /node_modules/,
test: /\.ts$/,
use: 'ts-loader'
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(projectRoot, 'src', 'index.html')
}),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css',
ignoreOrder: false
})
]
};
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body class="dark">
<header>
<nav class="navigation">
<div class="left">
<img src="assets/logo.png" class="logo"> <!-- This logo is output as [object Module] -->
</div>
<div class="right">
</div>
</nav>
</header>
</body>
</html>
Project structure:
config/
webpack.config.js
dist/
src/
styles/
assets/
logo.png
index.html
app.ts
Edit
My package.json dependencies:
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.2.0",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"ts-loader": "^6.2.1",
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
Per the file-loader docs:
By default, file-loader generates JS modules that use the ES modules syntax. There are some cases in which using ES modules is beneficial, like in the case of module concatenation and tree shaking.
It seems that webpack resolves ES module require() calls to an object that looks like this: {default: module}, instead of to the flattened module itself. This behavior is somewhat controversial and is discussed in this issue.
Therefore, to get your src attribute to resolve correctly, you need to be able to access the default property of the exported module. If you're using a framework, you should be able to do something like this:
<img src={require('assets/logo.png').default}/> <!-- React -->
<!-- OR -->
<img src="require('assets/logo.png').default"/> <!-- Vue -->
Alternatively, you can enable file-loader's CommonJS module syntax, which webpack will resolve directly to the module itself. Set esModule:false in your webpack config.
webpack.config.js:
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
esModule: false,
},
},
],
},
#stellr42's suggested fix of esModule: false in your file-loader configuration is the best workaround at the current time.
However, this is actually a bug in html-loader which is being tracked here: https://github.com/webpack-contrib/html-loader/issues/203
It looks like ES Module support was added to file-loader, css-loader, and other friends, but html-loader was missed.
Once this bug is fixed, it will be better to remove esModule: false and simply upgrade html-loader, as ES Modules offer some minor benefits (as mentioned in the docs)
Alternatively, if (like me), you found this issue because you were having trouble loading an image from CSS (instead of from HTML), then the fix is just to upgrade css-loader, no need to disable ES Modules.
Just updated my file-loader to ^5.0.2 minutes ago.
I know esModule: false was the suggested fix but that did not work for me.
My fix was <img src={require('assets/logo.png').default}/> which was weird. First time using .default but it worked.
This happens on file-loader version 5.0.2 , earlier version works fine without calling default property
Instead of this: <img src="require('assets/logo.png').default"/>
Use it like this: <img src={require('assets/logo.png').default}/>
Use "default" followed by require to display a dynamic image in react js
src={require('../images/'+image_name+'.png').default}
For Next.JS:
// require(...).default.src
<img src={require("../public/images/avatar.png").default.src} width={256} height={256} />
I have had the same problem recently after upgrading Laravel Mix v4 to v6.
This works fine with me in my Vue component.
<img :src="require('./assets/profile.png').default"/>
I had the same problem in vuejs and esModule:false did not work for me.
Instead, I used #kerubim solution and it fixed it, but only in production mode and in development mode I get some error.
So I wrote this function in util.js that solved my problem.
maybeDefault: (module) => {
if (typeof module === "object") {
module = module.default;
}
return module;
},
use example:
let logo = 'logo.svg';
util.maybeDefault(require(`img/svg/logos/${logo}`));
This is a weird issue still unsure how mine got fixed.
So I deleted my node_module and package-lock.json and ran npm install --force
and it worked fine after
Related
I would like to package a javascript project with webpack 4 and I'm struggling with the CSS file.
My bundled JS file will be used by other website using a script tag with an absolute URI.
So I want my bundle to inject the link tag for the stylesheet in order to manage the hash in the filename.
My style source is a scss file.
It works well when I use the style-loader but I would like to extract the CSS in an other file.
I tried the following webpack.config.js :
const miniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.scss$/,
use: [
miniCssExtractPlugin.loader,
'css-loader',
'sass-loader']
}
]
},
plugins: [
new miniCssExtractPlugin({
filename: '[name].[hash].css',
}),
]
};
It creates my CSS file in the output dir, but the loader doesn't inject it in the DOM like the style-loader does. And I can't use style-loader after the mini-css-extract-plugin.
I can't use html-webpack-plugin because there is no HTML output file.
Is there a way to do what I want with existing loaders ?
Is there a way to get the extracted file url in the JS to create the link tag by myself ?
My devDependencies :
{
// ...
"devDependencies": {
"css-loader": "^3.3.2",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0",
"sass-loader": "^8.0.0",
"webpack": "^4.41.3",
"webpack-cli": "^3.3.10"
}
}
Thank you.
In fact I was in the wrong direction trying to use mini-css-extract-plugin. It can be achieved simply with this rule :
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
options: {
injectType: 'linkTag'
}
},
{
loader: 'file-loader',
options: {
name: "css/[name].[hash].css",
publicPath: "dist/" // depends on your project architecture
}
},
'sass-loader'
]
}
I will have to add another loader in order to minify I think.
I'm playing with Vue CLI project. I have configured startup project, set some development changes like those:
package.json
"dependencies": {
"bootstrap": "^4.3.1",
"core-js": "^3.0.1",
"preload-it": "^1.2.2",
"register-service-worker": "^1.6.2",
"vue": "^2.6.10",
"vue-router": "^3.0.3",
"vuetify": "^1.5.14",
"vuex": "^3.1.1"
},
"devDependencies": {
"#vue/cli-plugin-babel": "^3.7.0",
"#vue/cli-plugin-pwa": "^3.7.0",
"#vue/cli-service": "^3.7.0",
"fontello-cli": "^0.4.0",
"node-sass": "^4.9.0",
"sass-loader": "^7.1.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"vue-cli-plugin-vuetify": "^0.5.0",
"vue-template-compiler": "^2.5.21",
"vuetify-loader": "^1.2.2"
}
vue.config.js
module.exports = {
configureWebpack: {
devtool: process.env.NODE_ENV === 'development'
? 'inline-source-map'
: false,
},
css: {
sourceMap: process.env.NODE_ENV === 'development'
}
}
babel.config.js
module.exports = {
presets: [
[
'#vue/app',
{ useBuiltIns: 'entry' }
]
]
}
But sourcemaps to vue files are still generated wrongly (to scss files works ok).
After clicking href to vue component
Note:
lot of versions of same file in webpack://./
only part that is in tag is visibile in source editor (maybe this is a cause)
file from mounted filesystem workspace is not used
And this is how original file looks like - it is possible to edit it via Chrome devtools
Is it possible to fix that so also element inspector tab (style) will provide proper source target?
EDIT 1
Simplest setup:
Install Vue CLI (3.7)
Add my vue.config.js (to enable sourcemaps)
Run npm run serve
EDIT 2
Same for Vue CLI 3.5
I also created repo with test project, but like I wrote it is just startup project with my config.
https://github.com/l00k/vue-sample
EDIT 3
Vue-cli github issue
https://github.com/vuejs/vue-cli/issues/4029
So far I did not found solution - at least using Vue CLI.
But I have found workaround.
But first of all - whole problem is not about Vue CLI but it is something with vue-loader-plugin IMO. I think so because while using clean setup with vue and webpack I also see that problem.
I have found out that it is related to wrong sourcemap generated for those parts of Vue file and
Source for those part is strip to only content of those tags. That is probably why browser could not map it to source. Also path to source file in sourcemap is wrong.
I have prepared additional loader for webpack which fixes those sourcemaps.
Check sm-fix-loader in repo below.
I dont know does it fix all issues, but at least in my cases it works awesome.
What works ok:
Build NODE_ENV=development webpack
SCSS inline (in vue file) and in separate file <style src="...">
TS / JS inline (in vue file) and in separate file <script src="...">
HRM NODE_ENV=development webpack-dev-server --hotOnly
SCSS inline (in vue file) and in separate file <style src="...">
It also reloads styles without reloading page itself :D
TS / JS inline (in vue file) and in separate file <script src="...">
Repo with working example:
https://github.com/l00k/starter-vue
Step by step solution:
Enable css sourcemaps in vue.config.js:
module.exports = {
css: {sourceMap: true},
Move all scss from components to separate files, collate them in index.scss and import index.scss via App.vue. This will solve lots of problems with vue-css-sourcemaps (caused by Webpack, Devtools and vue-cli), and somewhat simplify your workflow. If you need scoping, scope manually via #selectors (Importing SCSS file in Vue SFC components without duplication with Webpack)
To go further, you may need to set up CSS extraction for node_modules only, as another mysterious bug ruins styling as soon as you touch any css in devtools:
devtool: 'cheap-source-map',
plugins: process.env.NODE_ENV === 'development' ?
([new MiniCssExtractPlugin()]) : [],
module: {
rules: [
process.env.NODE_ENV === 'development' ?
(
{
// test: /node_modules/,
test: /node_modules\/.+\.scss/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [require('autoprefixer')],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {sourceMap: true}
}
]
}) : {}
],
}
If you extract all css, you'll loose hmr (hot module reloading = reload on edit), but since you don't really edit scss in your node_modules, you extact only them.
All in all, this fixed all vue css-related sourcemap issues with Devtools and enabled hot-editing right in browser.
I have a simple project set up using TypeScript, ReactJS, and SASS, and would like to bundle it all using Webpack. There's plenty of documentation on how to achieve this with JavaScript and regular old CSS. However, I can't find any documentation that combines the loaders I need and uses Webpack 2 syntax (rather than the original Webpack syntax for loaders). Thus, I'm unsure of how to create the correct configuration.
You can find my webpack.config.js file here. How would I modify the configuration so that TypeScript accepts my SCSS modules, and so that Webpack properly bundles my SCSS with my TypeScript?
This may also be helpful: when I run Webpack at the moment, I get the following error:
ERROR in ./node_modules/css-loader!./node_modules/typings-for-css-modules-loader/lib?{"namedExport":true,"camelCase":true}!./node_modules/sass-loader/lib/loader.js!./src/raw/components/styles.scss
Module build failed: Unknown word (1:1)
> 1 | exports = module.exports = require("../../../node_modules/css-loader/lib/css-base.js")(undefined);
| ^
2 | // imports
3 |
4 |
# ./src/raw/components/styles.scss 4:14-206
# ./src/raw/components/greetings/greetings.tsx
# ./src/raw/index.tsx
# multi ./src/raw/index.tsx
ERROR in [at-loader] ./src/raw/components/greetings/greetings.tsx:3:25
TS2307: Cannot find module '../styles.scss'.
Note that ./src/raw/index.tsx is the entry point of my application, ./src/raw/components/greetings/greeting.tsx is my only React component, and ./src/raw/components/styles.scss is my only SCSS file.
The typings-for-css-modules-loader is a drop-in replacement for css-loader (technically it uses css-loader under the hood) and that means it takes CSS and transforms it to JavaScript. You're also using the css-loader, and that fails because it receives JavaScript, but expected CSS (as JavaScript is not valid CSS, it fails to parse).
Additionally, you are not using CSS modules, because you're not setting the modules: true option on the CSS loader (or typings-for-css-modules-loader, which passes it on to css-loader).
Your .scss rule should be:
{
test: /\.scss$/,
include: [
path.resolve(__dirname, "src/raw")
],
use: [
{ loader: "style-loader" },
{
loader: "typings-for-css-modules-loader",
options: {
namedexport: true,
camelcase: true,
modules: true
}
},
{ loader: "sass-loader" }
]
}
Here is a little extended version (since the above did somehow not work for me), using another package (css-modules-typescript-loader) derived from the stale typings-for-css-modules-loader.
In case anybody runs into the same problems - this is a configuration that works for me:
TypeScript + WebPack + Sass
webpack.config.js
module.exports = {
//mode: "production",
mode: "development", devtool: "inline-source-map",
entry: [ "./src/app.tsx"/*main*/ ],
output: {
filename: "./bundle.js" // in /dist
},
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: [".ts", ".tsx", ".js", ".css", ".scss"]
},
module: {
rules: [
{ test: /\.tsx?$/, loader: "ts-loader" },
{ test: /\.scss$/, use: [
{ loader: "style-loader" }, // to inject the result into the DOM as a style block
{ loader: "css-modules-typescript-loader"}, // to generate a .d.ts module next to the .scss file (also requires a declaration.d.ts with "declare modules '*.scss';" in it to tell TypeScript that "import styles from './styles.scss';" means to load the module "./styles.scss.d.td")
{ loader: "css-loader", options: { modules: true } }, // to convert the resulting CSS to Javascript to be bundled (modules:true to rename CSS classes in output to cryptic identifiers, except if wrapped in a :global(...) pseudo class)
{ loader: "sass-loader" }, // to convert SASS to CSS
// NOTE: The first build after adding/removing/renaming CSS classes fails, since the newly generated .d.ts typescript module is picked up only later
] },
]
}
};
Also put a declarations.d.ts in your project:
// We need to tell TypeScript that when we write "import styles from './styles.scss' we mean to load a module (to look for a './styles.scss.d.ts').
declare module '*.scss';
And you will need all these in your package.json's dev-dependencies:
"devDependencies": {
"#types/node-sass": "^4.11.0",
"node-sass": "^4.12.0",
"css-loader": "^1.0.0",
"css-modules-typescript-loader": "^2.0.1",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"ts-loader": "^5.3.3",
"typescript": "^3.4.4",
"webpack": "^4.30.0",
"webpack-cli": "^3.3.0"
}
Then you should get a mystyle.d.ts next to your mystyle.scss containing the CSS classes you defined, which you can import as a Typescript module and use like this:
import * as styles from './mystyles.scss';
const foo = <div className={styles.myClass}>FOO</div>;
The CSS will automatically be loaded (injected as a style element into the DOM) and contain cryptic identifiers instead of your CSS classes in the .scss, to isolate your styles in the page (unless you use :global(.a-global-class) { ... }).
Note that the first compile will fail whenever you add CSS classes or remove them or rename them, since the imported mystyles.d.ts is the old version and not the new version just generated during compilation. Just compile again.
Enjoy.
Good afternoon,
This is the same issue I reported at webpack's github, but I suspect I might be the one doing something wrong, thus opening a question here.
I'm trying to configure webpack 2 with Babel, and one of the requirements is to transpile built-ins such as Symbol.
Despite that now working fine, when I try to use webpack and babel's transform-runtime, I'm unable to use exports *.
Input file (src/index.js):
export * from './secondFile'
secondFile.js:
export let TESTSYMBOL = Symbol('test');
export let TESTSYMBOL2 = Symbol('test2');
webpack.config.js (only copied the relevant part):
module: {
rules: [
{
test: /\.jsx?$/,
// Skip any files outside of `src` directory
include:path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: ["es2015", "stage-3"],
plugins: ['transform-runtime']
}
}
}
]
}
script:
"webpack -d --config config/webpack.config.js"
Output file: gist
Exception:
Uncaught ReferenceError: exports is not defined - at Object.defineProperty(exports, "__esModule", {
Dev Dependencies:
"webpack": "2.6.1",
"webpack-dev-server": "2.4.5",
"webpack-notifier": "1.5.0"
"babel-cli": "6.24.1",
"babel-loader": "7.0.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-3": "6.24.1"
Dependencies:
- "babel-runtime": "6.23.0"
Thanks for any help!
It seems that the problem is with the include. For some reason, I was unable to use path.resolve or path.join. The webpack documentation has such example.
If the webconfig is as follows, it works just fine:
module: {
rules: [
{
test: /\.js$/,
include: [
/src/
],
// or exclude: [/node_modules/],
use:
{
loader: 'babel-loader',
options: {
plugins: ['transform-runtime'],
presets: ['es2015', 'stage-3']
}
}
}
]
}
Either way, now there's a problem with exports not defined, which can be solved by setting modules to false in es2015 preset (thanks to Vanuan at Github for that suggestion):
presets: [['es2015', { modules: false }], 'stage-3'],
For IE or older browsers, I need to use es-shims - libraries which ports the ECMAScript specs to legacy JS engines.
These libs below may resolve your problem if added as the first imports on your index.html (or equivalent). Here's one for example:
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script>
See this link for every lib you may need:
ES-Shims
Overview
I am trying to use Babel and Webpack to build a React app. I know I could use create-react-app but I'd like to learn how these technologies can work together for myself.
When I run yarn run start or yarn run build (see package.json below), Webpack reports to have built the bundle fine. When I run the application in the browser, I get the Uncaught ReferenceError: React is not defined error.
There are a number of questions on SO regarding this same error, but none of the solutions have solved my problem yet.
Question
What piece am I missing to get React, Babel, and Webpack to play together nicely?
Code
package.json
{
"private": true,
"scripts": {
"build": "webpack",
"start": "webpack-dev-server"
},
"dependencies": {
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-redux": "^5.0.1",
"redux": "^3.6.0"
},
"devDependencies": {
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"redux-devtools": "^3.3.1",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2"
}
}
.babelrc
{
"presets": ["react", "es2015"]
}
webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: './dist'
},
devtool: 'source-map',
debug: true,
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<div id="root"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
src/index.js
import ReactDom from 'react-dom';
import React from 'react';
import App from './App';
ReactDom.render(
<App />,
document.getElementById('root'),
);
src/App.js
import React from 'react';
import { Component } from 'react';
export default class App extends Component {
render() {
return (
<h1>hello</h1>
);
}
}
Observations
It seems that Webpack/Babel is expecting React to be available on the global scope, or is ignoring my import React from 'react'; statements.
Also, I have yet to find the proper incantation of devtools and debug properties in my Webpack config to actually get source maps working. I don't yet see them in the compiled output.
EDIT
The broken bundle.js is too large for SO (21,000+ lines), so here is a link: http://chopapp.com/#1a8udqpj — it takes several seconds to load and display.
Since you're trying to learn the in's and out's of webpack et al... Your original problem was that you needed to specify the output.publicPath in your webpack.config.js:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: './dist',
publicPath: '/dist' // <- was missing
},
devtool: 'source-map',
debug: true,
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
}
"path" is the physical path to the folder that contains your bundle.js. "publicPath" is the virtual path (URL) to that folder. So, you don't even have to use "dist". For example, if you used...
output: {
filename: 'bundle.js',
path: './dist',
publicPath: '/assets'
},
Your HTML would then point to:
<script src="assets/bundle.js"></script>
I wish to make the following observations. First to your question regarding devTools and debug. The debug flag for webpack (according to their official documentation) switches the loaders to debug mode. Then, from what I understand, webpack, when running in a dev environment, does not actually compile the code to hard files, but keeps it in memory. Thus the source maps are also kept in memory and linked to the browser. You see their effect when you open the browser dev tools and view source.
Now assuming you have the dev server configured correctly, I also assume your index.html is located in your source directory. If this is the case, then your script reference should simply point to '/bundle.js' and not 'dist/bundle.js' since there may not be a physical "dist" folder.
I would also suggest dropping the ".js" from your entry point in the webpack.config.js
You will need to add a query to your module within the webpack.config.js file:
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
};