Webpack configuration to allow import - javascript

I am creating a basic structure where the controller.js would need some Views to be imported from view.js and hence this is what I am using in the controller.js
import View from './View'
However there was an issue with the bundler with webpack and I ended up visiting this page
https://webpack.js.org/loaders/imports-loader/
and below is webpack.config.js.. I've added the lines at the end to the RULES as metioned in the documentation
I ran npm install npm install imports-loader --save-dev
// SOURCE OF TUTORIAL
// https://www.youtube.com/watch?v=MpGLUVbqoYQ&t=1205s
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
mode: "development",
devtool: false,
entry: {
main: "./src/js/controller.js",
model: "./src/js/model.js",
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/main.html",
}),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
"style-loader", // injects STYLES into DOM
"css-loader", // turns CSS into commonjs
"sass-loader", // turns SASS into CSS
],
},
{
test: /\.html$/,
use: ["html-loader"],
},
{
test: /\.(png|gif|svg|jpe?g)$/,
type: "asset/resource",
},
{
test: require.resolve("./path/to/example.js"),
loader: "imports-loader",
options: {
type: "module",
imports: "default lib myName",
},
},
],
},
};
The last section of the rule is what I have added.. it's a basic copy paste without really understanding what it does and it didn't work.. I am obviously doing somethibg wrong but I could not figure it out even after reading the section at the link above..
Could someone explain how the last section should be configured so that I am able to use import? I've recently created this webpack configuration based on a youtube tutorial so my knowledge on webpack is very basic..
Any help would be appreciated.

Related

How to stop the Vue compiler from handling static assets

My Vue 3 app references a number of static assets. The static assets are already set up with their own file structure, which happens to be outside of the app source directory. My web server has been configured for this and happily serves files from both the Vue build directory and the static assets directory.
The Vue compiler, however, is not so happy. When it sees a path, it tries to resolve it and copy the asset to the build directory. If I use an absolute path it cannot find the asset, and crashes and dies.
Requirement
I would like the Vue compiler to only output css and js files. I do not need the static files in the build directory. If the Vue compiler sees a reference to a static file - eg:
<style lang="scss">
.hero {
background-image: url('/images/background.jpg');
}
</style>
it should not try to resolve /images/background.jpg and copy it to the build directory; it should assume that I have already put the file where it needs to be.
Current setup
I'm using Vue 3 and Webpack 5. My webpack.config.js is
const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' )
const { VueLoaderPlugin } = require( 'vue-loader' )
const path = require( 'path' )
module.exports = {
entry: './src/index.ts',
mode: 'production',
module: {
rules: [ {
test: /\.vue$/,
loader: 'vue-loader'
}, {
test: /\.ts$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [ /\.vue$/ ]
}
}, {
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: "sass-loader",
options: {
additionalData: '#import "#/util/sass/_index.scss";' // import _index.scss to all Vue components
}
}
]
} ]
},
plugins: [
new MiniCssExtractPlugin( {
filename: 'style.css'
} ),
new VueLoaderPlugin(),
],
resolve: {
alias: {
'#': path.resolve( __dirname, 'src' )
},
extensions: [ '.ts', '.scss', '.vue' ],
},
output: {
filename: 'app.js',
path: path.resolve( __dirname, 'build' ),
},
}
You need to tell Webpack to disable asset emission.
By default, asset/resource modules are emitting with [hash][ext][query] filename into output directory.
Since you haven't specified anything concerning asset resources in your webpack config, it will emit a file for each ressource.
As a side note, your vue-loader will not modify absolute paths :
If your static asset URL is specified with an absolute path, Vue will preserve as-is
So, for your concern, make sure to add a rule in your webpack config targeting your asset types. Make sure this rule is the first one run (the last one in your rules array).
//...
module: {
rules: [
//...
{
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
type: 'asset/resource',
generator: {
emit: false,
},
},
],
},
//...
Here is the webpack reference for more information

Get list of webpack output files and use in another module for PWA

I'm trying to build a progressive web app, with support for offline usage.
According to MDN, the way to make PWAs work offline is to add the required resources to a cache in the service worker. This requires that the service worker code knows each of the output files. Ideally, this shouldn't be harcoded, and should be generated by webpack, since it knows what files it generates.
I'm struggling to actually generate this list. From my search, there are two plugins that can generate a json file containing a list of the files - webpack-assets-manifest and webpack-manifest-plugin. I can use these in combination with separate targets to generate a manifest with the page files. But I can't import the manifest, since webpack doesn't actually write the manifest until everything is done.
How can I import a list of files that one entry point generates and use them in another entry point/module?
webpack.config.js:
const path = require('path');
const WebpackAssetsManifest = require('webpack-assets-manifest');
const frontend = {
mode: "development",
entry: {
page:"./src/page/page.tsx",
},
devtool: 'inline-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.html?$|\.png$/,
type: "asset/resource",
generator: {
filename: "[name][ext]",
},
},
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.json$/,
type: "asset/resource",
exclude: /node_modules/,
}
],
},
resolve: {
extensions: [".html", ".tsx", ".ts", ".js"],
},
plugins: [
new WebpackAssetsManifest({
output: "page-files.json",
writeToDisk: true,
}),
]
};
const serviceworker = {
mode: "development",
entry: {
serviceworker: "./src/serviceworker/serviceworker.ts",
},
devtool: 'inline-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.html?$|\.png$/,
type: "asset/resource",
generator: {
filename: "[name][ext]",
},
},
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.json$/,
resourceQuery: /link/,
type: "asset/resource",
exclude: /node_modules/,
},
{
test: /\.json$/,
resourceQuery: /str/,
type: "asset/source",
exclude: /node_modules/,
}
],
},
resolve: {
extensions: [".html", ".tsx", ".ts", ".js"],
},
};
module.exports = [frontend, serviceworker];
serviceworker.ts:
import files from "../../dist/page-files.json?str";
console.log(files);
Error is:
Module not found: Error: Can't resolve '../../dist/page-files.json?str' in '<REDACTED>/src/serviceworker'
(When I build it again, it will find the file from the previous build)
Rather than relying on pre-existing webpack plugins to generate assets, I think you're going to need to write your own plugin for this use case. And if you want that plugin to write the manifest to an entry that itself needs to be bundled/compiled, creating a child compilation in that plugin would be the way to do it.
This is unfortunately not a straightforward task, but you can refer to the source code for the workbox-webpack-plugin's InjectManifest plugin, which more or does what you describe, as inspiration.
Alternatively... you can just use InjectManifest directly, if that meets your use case. While it's part of the Workbox family of libraries, InjectManifest will only actually do two things: process the entry file you pass in as swSrc via a child compilation, and replace the symbol self.__WB_MANIFEST anywhere in that swSrc file with an array of {url: '...', revision: '...'} entries generated based on the assets in the main configuration, filtered by any include/exclude parameters.
So if you don't plan on using Workbox, you can just make use of that self.__WB_MANIFEST value from your own code.
// service-worker.ts
const manifest = self.__WB_MANIFEST || [];
self.addEventListener('install', (event) => {
// Your code to cache the contents of manifest goes here.
});
// webpack.config.js
const {InjectManifest} = require('workbox-webpack-plugin');
module.exports = {
// ...other webpack config...
plugins: [
new InjectManifest({
swSrc: 'src/service-worker.ts',
swDest: 'service-worker.js',
// ...exclude/include config here...
}),
],
};
The reason behind this behavior is that webpack runs these 2 configurations parallelly. By forcing webpack to run sequentially we can fix the problem.
To do serial processing, add module.exports.parallelism = 1; at end of your webpack config.
module.exports = [frontend, serviceworker];
module.exports.parallelism = 1;
Here is the documentation from webpack, https://webpack.js.org/configuration/configuration-types/#parallelism

Webpack 5 - Uncaught ReferenceError: process is not defined

*** Edit - Ignore if you want answer only ***
Seeing as this question is still receiving views and upvotes I feel responsible to share some knowledge after going through the webpack rabbithole and coming out the other end.
If you:
are building a greenfield/early-stage modern javascript project
are considering migrating from create-react-app
don't have much experience with bundling
do not need advanced features like module federation or server side rendering (which doesn't need webpack anymore)
Consider using the next generaton bundlers such as vite/parcel (easy setup), esbuild/rollup (more setup required)
Webpack was/is a fantastic contribution to the frontend world and I'm glad I learned all its intricacies, however, the new bundlers are much faster during development and easier to mantain. It's great when it works but for those inexperienced with it; despite fantastic docs the learning curve can make it a horrible pain to debug.
To clarify, I'm not a maintainer on any of these projects - just a dev who enjoys good tooling. In today's landscape, webpack is comparable to using a sledgehammer to crack a nut.
*** End of Edit ***
Webpack newbie here, I was told by webpack cli that I needed to provide an alias for crypto as webpack no longer includes default node libraries. Now I'm getting this error, other answers haven't helped so much. crypto-browserify is trying to access process.browser. Can anyone shed more light? I was told by cli to install stream-browserify too so i did.
React v17, Babel 7.12.9, webpack 5.6.0
webpack.common.js
const paths = require('./paths');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const dotenv = require('dotenv-webpack');
module.exports = {
entry: [paths.src + '/index.js'],
output: {
path: paths.build,
filename: '[name].bundle.js',
publicPath: '/',
},
plugins: [
new dotenv(),
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: paths.public,
to: 'assets',
globOptions: {
ignore: ['*.DS_Store'],
},
},
],
}),
new HtmlWebpackPlugin({
title: 'Webpack Boilerplate',
// favicon: paths.src + '/images/favicon.png',
template: paths.src + '/template.html',
filename: 'index.html',
}),
],
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
},
},
module: {
rules: [
// javascript
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
// images
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
// Fonts and SVGs
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
// CSS, PostCSS, and Sass
{
test: /\.(scss|css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
esModule: true,
sourceMap: true,
importLoaders: 1,
modules: {
auto: true,
namedExport: true,
},
},
},
{ loader: 'postcss-loader', options: { sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
],
},
],
},
};
webpack.dev.js
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: 'development',
// Control how source maps are generated
devtool: 'inline-source-map',
// Spin up a server for quick development
devServer: {
historyApiFallback: true,
contentBase: paths.build,
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin(),
],
});
In webpack 5 automatic node.js polyfills are removed. In the migration docs it is mention that
Try to use frontend-compatible modules whenever possible.
It's possible to manually add a polyfill for a node.js core module.
An error message will give a hint on how to achieve that.
Package authors: Use the browser field in package.json to make a
package frontend-compatible. Provide alternative
implementations/dependencies for the browser.
See this issue.
Now you can refer this PR and check the libs that were removed and install them.
Next add alias for the lib in your webpack config.
For ex.
resolve: {
alias: {
process: "process/browser"
}
}
Update:
This can also be done using ProvidePlugin
package.json
"devDependencies": {
...
"process": "0.11.10",
}
webpack.config.js
module.exports = {
...
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
],
}
npm i process was all I needed.
Hope the correction I proposed will be accepted and released soon
I have this problem for HtmlWebpackPlugin, I added 'templateParameters' parameter to HtmlWebpackPlugin and it was fixed for me:
new HtmlWebpackPlugin({
baseUrl: '/',
template: 'app/index.html',
templateParameters(compilation, assets, options) {
return {
compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options,
},
process,
}
},
chunksSortMode: 'auto',
minify: {
collapseWhitespace: false,
},
cache: true,
}),
1. npm i dotenv-webpack
2. //Define dotenv in your webpack config
const Dotenv = require('dotenv-webpack');
plugins: [
new Dotenv({
path: './.env', // Path to .env file (this is the default)
safe: true, // load .env.example (defaults to "false" which does not use dotenv-safe)
})
],

Invalid or unexpected token loading css file using webpack in react project

I have a react project that uses styled components, and I'm trying to include a CSS file as part of react-image-gallery
I followed the steps to include css-loader and style-loader in my project and tried importing the file like this
import 'react-image-gallery/styles/css/image-gallery.css
and included the following in Webpack config rules
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
}
When running the server I'm getting the below error message
SyntaxError: Invalid or unexpected token
(function (exports, require, module, __filename, __dirname) { #charset "UTF-8";
in the above CSS file
From some googling, I understood that this is because the CSS file is included as a JS file by Webpack. But isn't that how it is supposed to be?
Addition info: I have a server side rendered app.
What am I doing wrong?
Update:
My rules look like this
A rules.ts file
import webpack from 'webpack'
const ts: webpack.RuleSetRule = {
test: /^(?!.*\.spec\.tsx?$).*\.tsx?$/,
exclude: /node_modules/,
use: ['babel-loader'],
}
const img: webpack.RuleSetRule = {
test: /\.(png|jpe?g|gif|svg)$/,
use: 'file-loader',
}
const css: webpack.RuleSetRule = {
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
}
export default {
client: {
ts,
img,
css,
},
server: {
ts,
css,
img: { ...img, use: [{ loader: 'file-loader', options: { emitFile: false } }] },
},
}
A config file that has the following
const config: webpack.Configuration = {
context: path.join(__dirname, '../../src/client'),
resolve: {
...resolve.client,
alias: { 'react-dom': '#hot-loader/react-dom' },
},
devtool: false,
entry: {
main: ['./index.tsx'],
},
mode: 'development',
module: {
rules: [rules.client.ts, rules.client.img, rules.client.css],
},
output: output.client,
plugins: [
...plugins,
...developmentPlugins,
new ForkTsCheckerWebpackPlugin({
tsconfig: path.join(__dirname, '../../tsconfig.fork-ts-checker-webpack.plugin.json'),
}),
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: ['!webpack.partial.html', '!favicon.ico'],
}),
],
}
We had 4 co-workers stuck on an issue like that.
Actually, we had a plugin "nodemon-webpack-plugin", which we configured for webpack.
This plugin tried to execute files like .css as java-script files.
We finally removed this plugin, because we had an up and running mon already.

Use fullcalendar with webpack

I use npm, webpack and FullCalendar, but I get the following error in the browser console when using fullcalendar:
main.js:37556 Uncaught TypeError: (0 , _jquery2.default)(...).fullCalendar is not a function
How do I fix this?
I use FullCalendar 3.0.0-beta and jquery 3.1.0. My code is below.
index.js:
import $ from 'jquery'
import jQueryUI from 'jquery-ui'
import moment from 'moment'
import fullCalendar from 'fullcalendar'
$('#timetable').fullCalendar({
editable: true,
firstDay: 1,
droppable: true,
})
webpack.config.js:
var path = require("path")
var webpack = require("webpack")
var BundleTracker = require("webpack-bundle-tracker")
module.exports = {
context: __dirname,
entry: [
'fullcalendar',
'./static/index',
],
output: {
path: path.resolve('./static/bundles/'),
filename: "[name].js",
},
plugins: [
new BundleTracker({filename: './webpack-stats.json'}),
],
resolve: {
modulesDirectories: ['node_modules'],
extensions: ['', '.js'],
},
module: {
loaders:[
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015'] } }
]
}
}
I know I am somewhat late to the party here, but I thought I'd answer anyway in case somebody hits this up on Google.
Whenever I run into a jQuery Plugin with Webpack (which FullCalendar is), I need to make sure that jQuery itself is exposed to the global namespace before the plugin will work through require/import.
My webpack.config.js:
var webpack = require("webpack")
var path = require("path")
var ExtractTextPlugin = require("extract-text-webpack-plugin")
var HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry: {
app: "./index.js",
vendor: [
"jquery",
"moment",
"fullcalendar"
]
},
output: {
path: path.join(__dirname, '../../public'),
publicPath: '/',
filename: "scripts/app.[chunkhash].js"
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract("style", ["css"]) },
{ test: require.resolve('jquery'), loader: 'expose?$!expose?jQuery' },
{ test: require.resolve('moment'), loader: 'expose?moment' }
]
},
resolve: {
alias: {
jquery: path.resolve(path.join(__dirname, '../..', 'node_modules', 'jquery')),
fullcalendar: 'fullcalendar/dist/fullcalendar'
}
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.CommonsChunkPlugin({ names: ["vendor"], filename: "scripts/[name].[chunkhash].js" }),
new ExtractTextPlugin("styles/[name].[chunkhash].css"),
new HtmlWebpackPlugin({
template: "index.html.handlebars"
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]
};
The relevant part is where jquery and moment are forced to be in the global namespace via the loader: 'expose?$!expose?jQuery' syntax.
Second, because fullcalendar is packaged in a way that the require can't automatically pick it up, I setup an alias so that I can have a clean package name. This is the alias: { fullcalendar: 'fullcalendar/dist/fullcalendar' } bit.
These two let me load fullcalendar via require/import and use it as I normally would.
The styles also need to be loaded. For this one I have not created aliases yet, so I just did a relative path to the css file:
#import "../../../node_modules/fullcalendar/dist/fullcalendar.css";
You can replace fullcalendar.js with fullcalendar.min.js to avoid recompressing, but for my use case because I was bundling all the vendor JS files together, I thought I would get better compression if I had more files concatenated. (Ditto for CSS fullcalendar.css with fullcalendar.min.css)
Disclaimer: I don't know if this is the "correct" way of doing this, but I know it took me a fair bit of trial and error with webpack to get jQuery plug ins like FullCalendar and Select2 to work, and this shell and method did make it easy.
For reference, links to the relevant files in a public repo where I use this pattern:
webpack.config.js: https://github.com/thegrandpoobah/mftk-back-office/blob/e531de0a94130d6e9634ba5ab547a3e4d41c5c5f/app/src/public/webpack.config.js
style scss: https://github.com/thegrandpoobah/mftk-back-office/blob/e531de0a94130d6e9634ba5ab547a3e4d41c5c5f/app/src/public/styles/main.scss
module where I use fullcalendar: https://github.com/thegrandpoobah/mftk-back-office/blob/e531de0a94130d6e9634ba5ab547a3e4d41c5c5f/app/src/public/students/index.js#L277
This is a step by step guide based on the data from above and other sources. First make sure you have moment.js installed:
npm install moment
Then make sure you have the fullcalendar version 3.10.2, which is the latest in version 3 which is optimized not bundling jQuery nor moment.js , and although it's not the latest version, it uses the old syntax, which won't break compatibility with legacy code:
npm install fullcalendar#3.10.2
Then install script-loader
npm install --save-dev script-loader
If you are using Laravel, then in resources/js/bootstrap.js add the following lines below bootstrap and jquery (note the use of script-lader!) :
window.moment = require('moment');
require('script-loader!fullcalendar/dist/fullcalendar');
require('script-loader!fullcalendar/dist/locale-all');
Then add the css style in resources/sass/app.scss:
#import '~fullcalendar/dist/fullcalendar.min.css';
Finally do:
npm run dev
Or, for production:
npm run prod
That's all
I think I found an even easier solution.
We're using fullcalendar and scheduler. We're converting from Rails sprockets to webpack. Adding fullcalendar to a lazyloaded chunk with webpack caused it to introduce two additional moments and jquerys (yep two) which, of course, didn't pick up our configuration changes as those where done on the original version in our chunked vendor file.
Ideally we just wanted fullcalendar included with no module processing (it does absolutely nothing and is totally unnecessary). Fortunately you can do this with webpack's script-loader.
require('script-loader!fullcalendar/dist/fullcalendar.js')
And you're done. Same with the scheduler. It loads it in isolation and unprocessed which is exactly what you want with a jquery plugin.
With webpack 5, below code solves the issue:
module: {
rules: [
{
test: require.resolve('jquery'),
loader: 'expose-loader',
options: {
exposes: ["$", "jQuery"]
}
},
{
test: require.resolve('moment'),
loader: 'expose-loader',
options: {
exposes: "moment"
}
},
{
test: require.resolve('fullcalendar'),
use: [
{
loader: 'script-loader',
options: 'fullcalendar/dist/fullcalendar.js'
}
]
},
{
test: require.resolve('fullcalendar-scheduler'),
use: [
{
loader: 'script-loader',
options: 'fullcalendar/dist/fullcalendar-scheduler.js'
}
]
},
]
},
I used fullCalendar for example:
$("#fullcalendar-activities").fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay'
},
events: events,
defaultView: 'month'
});

Categories

Resources