I'm new to webpack. I have been using gulp until now and trying to migrate to webpack.
In my .gulpfile I have:
var preProcess = require('gulp-preprocess');
gulp.src('app/config/app.constants.js')
.pipe(preProcess({
context: {
NODE_ENV: options.env
}
}))
I have the following lines in app/config/app.constants.js that need to be removed in production:
//#if NODE_ENV='development'
AppConstants.api = 'https://localhost:333/api';
AppConstants.webRoot = 'http://localhost:222';
//#endif
I am trying to accomplish this in wepack.config.js:
if (!isDev) {
config.module.rules.push([
{
test: ???, // Can't figure out what to put here
exclude: /(node_modules|bower_components|\.spec\.js)/,
use: [
{
loader: 'webpack-strip-block',
options: {
start: '#if NODE_ENV='development'',
end: '#endif'
}
}]
}
])
}
Two questions: How do I test for a single file? Is this the right way to replace gulp-preprocess?
To apply a loader to a single file, use include instead of exclude. This is whitelisting instead of blacklisting. You don't need to use test at all. For example...
{
include: 'path/to/your/file',
use: [{
loader: 'webpack-strip-block',
options: {
start: '#if NODE_ENV='development'',
end: '#endif'
}
}]
}
Related
I recently upgraded to Webpack 5 and my html-loader no longer loads svg files and inlines them.
Here's my svg rule in webpack
{
test: /\.svg$/,
use: [
{
loader: 'html-loader',
options: {
minimize: true,
},
},
],
},
No matter how I try to import it, it seems to just create a file and not give me a string of HTML.
import mySvg from "../path/to/my.svg"
let mySvg = require("../path/to/my.svg").default;
// output = "/build/path/my.svg"
// output I want = "<svg>...."
It used to not give me several build files instead it inlined them in my JS.
Help would be appreciated.
svg-inline-loader can achieve the same (confirmed to work).
I have listed other options for loading SVGs at https://survivejs.com/webpack/loading/images/#loading-svgs.
I use posthtml and posthtml-inline-svg plugin.
const postHtml = require('posthtml');
const postHtmlInlineSvg = require('posthtml-inline-svg');
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
esModule: false,
preprocessor: async (content, loaderContext) => {
try {
return await postHtml(postHtmlInlineSvg({ cwd: loaderContext.context, tag: 'icon', attr: 'src' }),).process(content)).html;
} catch (error) {
loaderContext.emitError(error);
return content;
}
},
},
},
},
If you're using HtmlWebpackPlugin, the HtmlWebpackInlineSVGPlugin can be used to inline svgs.
Here are the relevant parts of the webpack config to achieve the same:
{
// ...
module: {
rules: [
{
test: /\.html/,
loader: "html-loader",
},
// ...
],
},
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackInlineSVGPlugin({
inlineAll: true,
}),
// ...
]
}
Is it possible to get the CSS for a Vue component, and attach it as a string to a component when building a library for use at runtime?
vue-cli-service build --mode production --target lib --name components src/index.ts
I currently achieve this for some custom js using a custom block:
vue.config.js:
...
rules: [
{
resourceQuery: /blockType=client-script/,
use: './client-script-block',
},
],
},
...
client-script-block.js:
module.exports = async function () {
return `export default function (Component) {
Component.options.__client_script = ${JSON.stringify(this.resourcePath)};
}`;
};
which then exposed the string in the Vue app that uses the library. But achieving the same thing with CSS doesn't seem to play ball.
You could take a look at this CSS Extraction modules from VueLoader, that extracts the CSS from specific file or files, and stores it in a custom file, that you could then load dynamically in runtime, like:
Install:
npm install -D mini-css-extract-plugin
// webpack.config.js
var MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// other options...
module: {
rules: [
// ... other rules omitted
{
test: /\.css$/,
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
// ... Vue Loader plugin omitted
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
Reference: https://vue-loader.vuejs.org/guide/extract-css.html#webpack-4:
Another approach:
// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")
module.exports = {
// other options...
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
extractCSS: true
}
}
]
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
Reference: https://vue-loader-v14.vuejs.org/en/configurations/extract-css.html
Also here you have a complete guide for extracting the CSS from a SSR (Server Side Rendered) apps: https://ssr.vuejs.org/guide/css.html#enabling-css-extraction
My current configuration is as follows:
output: {
filename: 'bundle.js',
path: OUT_DIR
},
However I need bundles.js to go to two directories?
Can I accomplish this by simply passing an array of directories to path?
Or do I need to do something more complex?
Currently I have a bash script cpll which I have to type in manually after each build and it is tedious.
Hopefully web pack has a configuration option to send the output to two or more locations.
Research
google search
This SO question is 4 years old and does not have what I am lookin for - so
The documentation does not mention a way to do it here - webpack.
If there is not a configuration option how can I have it run a bash command automatically?
I tried passing it an array of strings instead of a string and it crashed with the obvious error:
Invalid configuration object. Webpack has been initialised using a
configuration object that does not match the API schema.
- configuration.output.path should be a string.
Passing an array will not work. Hmmm.
Trying another approach starting with a google - search
Brings up a possible solution - so
per request - complete exportFunc
const exportFunc = ( env ) => {
console.log('webpack.config.js-exportFunc', OUT_DIR);
return {
entry: `${IN_DIR}/index.jsx`,
output: {
filename: 'bundle.js',
path: '/Users/c/_top/ll-front/dist'
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.jsx?/,
include: IN_DIR,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: ['#babel/plugin-proposal-object-rest-spread', '#babel/plugin-proposal-class-properties'],
}
}
}
]
}
};
};
module.exports = exportFunc;
You can use webpack's multi-compiler mode by exporting an array of configs.
As in the docs:
Instead of exporting a single configuration object/function, you may export multiple configurations (multiple functions are supported since webpack 3.1.0). When running webpack, all configurations are built.
For example:
const config = {
// your webpack config
}
const outputPaths = ["path/one", "path/two"]
module.exports = outputPaths.map(outputPath => {
return {
...config,
name: outputPath,
output: {
...config.output,
path: path.resolve(__dirname, outputPath)
}
}
})
As you're using a function config you can do something like this:
const outputPaths = ["path/one", "path/two"]
module.exports = outputPaths.map(outputPath => {
return env => {
return {
entry: `${IN_DIR}/index.jsx`,
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, outputPath)
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.jsx?/,
include: IN_DIR,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: ['#babel/plugin-proposal-object-rest-spread', '#babel/plugin-proposal-class-properties'],
}
}
}
]
}
};
}
})
You could use multiple configurations instead. Here's the webpack documentation link.
To avoid code duplicaion consider your current config as an object. Then duplicate that object and override the output section of the duplicated object. Finally put both objects into an array. Use that array as your new config.
var yourCurrentConfig = {...};
var secondaryDestinationConfig = Object.assign(yourCurrentConfig, {
output: {
path: {
filename: 'bundle.js',
path: SECONDARY_DIR
}
}
});
var newConfig = [
yourCurrentConfig, secondaryDestinationConfig
];
I'm developing an application that use php 5.6 and laravel 5.4. I'm using laravel mix for build my assets. I need to know how to use mix.webpackConfig({}) method to use another webpack configurations like use babel-loader, riot-tag-loader etc. Is there any way to use this method to do that with entry point and output files? For an example, I need to do following thing inside my mix.webpackConfig({}).
module.exports = {
entry: {
admin: ['./resources/assets/admin/js/app.js'],
'manuals/parent/child/js': ['./resources/views/manuals/parent/child/js/app.js']
},
output: {
filename: '[name]/app.js',
path: path.resolve(__dirname + '/public')
},
module: {
rules: [
{
test: /\.tag$/,
exclude: /node_modules/,
loader: 'riot-tag-loader',
query: {
type: 'es6',
hot: true
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
};
Is that possible? Is that so, please let me know how to do that. Thanks
I've hardly found the laravel-mix mix.webpackConfig({}) successfully initiated, and even working samples are rear. I do not know what framework you are trying to manage but this sample works; this is a config for less-loader, hope you can tune it to suit your purpose.
const path = require('path');
mix.webpackConfig({
module: {
rules: [
{
test: /\.less$/,
loader: "style-loader!css-loader!less-loader",
exclude: [
path.resolve(__dirname, "node-modules"),
path.resolve(__dirname, "resources/assets/less"),
],
},
]}
})
Mix is a configuration layer on top of Webpack, so to run your Mix tasks you only need to execute one of the NPM scripts that is included with the default Laravel package.json file: more details in official site
https://laravel.com/docs/5.7/mix
I am currently working on TypeScript. I want to replace JS with TS, but I have a lot of JS-Files, so that I only want to create new classes in TS and want to use these in my old JS files atm. Later on I want to replace all JS with TS.
I not 100% familiar with webpack and bundled js files, so that I could use some help.
Is there any opportunity to compile TS into JS with gulp (already done - it works) AND use these TS classes in different (old) JS files?
TypeScript file:
class Car {
seats: number;
passengers: string[];
add(passenger: string) {
if(this.passengers.indexOf(passenger) === -1) {
this.passengers.push(passenger);
}
}
}
JavaScript file:
var car = new Car();
car.seats = 5;
car.add("Pete");
As you can mention, Car is undefined.
How can I use my Car class in other JS files? (after ts compilation and webpack bundling)
This is my old gulp task:
gulp.task('bundle-ts-webpack', ['compile-ts-webpack'], function() {
var tsFiles = glob.sync(config.js.tsSource);
return webpackStream({
/* configuration options */
entry: tsFiles,
module: {
rules: [
{
//test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
// use ts in js - begin
alias: {
'TS': path.resolve(config.js.tsDest, 'ts-bundle'),
'ExpertFilterHelper': path.resolve('./static/ts/', 'car'),
'test': path.resolve('.static/ts/', 'test-globals')
}
// use ts in js - end
},
// use ts in js - begin
plugins: [
new webpack.ProvidePlugin({
'TS': 'TS',
identifier: 'car',
test: 'test'
})
],
// use ts in js - end
output: {
filename: 'ts-bundle.js',
path: path.resolve(config.js.tsDest, 'dist')
}
})
.pipe(gulp.dest(config.js.tsDest));
});
I already tried to get this running as you can see in the gulp.task, but I still "dont know what I am doing here".
Can someone give me a hint?
I already thought, whether I should compile TS to JS first and then bundle it via webpack, but I dont know whether this solve my problem.
Edit/Updated:
As Joshua Barnett mentioned, you can use the expose-loader, but I still have a question to the expose-loader:
Can I only add one typescript file to the globals?
I have three typescript files for exmaple:
A.ts
B.ts
Car.ts
This works fine: I can now use:
Library.Car()
BUT:
When I have another typescript file/class like so:
A.ts
B.ts
Car.ts
Delivery.ts
I can only use Library.Delivery() and cannot use Library.Car() anymore.
Any hints for that?
My current gulp.task:
gulp.task('compile-ts-webpack', function() {
var tsFiles = glob.sync('./static/ts/*.ts');
console.log(require.resolve('./static/ts/z-car-test.ts'));
return webpackStream(
{
entry: tsFiles,//path.resolve('./static/ts/z-car-test.ts'),
output: {
filename: 'ts-bundle.js',
path: path.resolve('./static/js/modules')
},
devtool: "source-map",
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
module: {
rules: [{
test: /\.ts|\.tsx$/, //require.resolve('./static/ts/z-car-test.ts'),
use: [{
loader: 'expose-loader',
options: 'TSGlobals'
}, {
loader: 'ts-loader'
}]
//exclude: /node_modules/
}]
}
}
).pipe(gulp.dest('./static/js/modules'));
Edit/Update 2:
If you want to have more than one class added to the TSGlobals you can do that with a little hack (I guess it's a hack, im not sure)
I created a exporter.ts which re-exports all classes I need to be global to use these in any js-file as Joshua Barnett mentioned in a comment:
//exporter.ts
export { Car } from "./car.ts";
export { Delivery } from "./delivery.ts";
I also need to tell webpack to bundle the exporter typescript file at the end of the files-array. (Im not 100% sure why)
//gulp.task
gulp.task('compile-ts', ['clean-ts'], function() {
var tsFiles = glob.sync('./ts/*.ts', {"ignore":['./exporter.ts']}); // Ignore exporter.ts
//add the exporter.ts to the end of the array to provide the global TSGlobals Library.
tsFiles.push(config.js.tsExporterFile);
[...]
}
Now I can use TSGlobals in any JS-file! Works perfectly.
//whatever.js
var car = new TSGlobals.Car();
car.seats = 5;
car.add('Pete');
var delivery = new TSGlobals.Delivery();
delivery.doSomething("123");
You need to make use of the expose-loader in order to make your module available through the global scope.
// File: webpack.config.js
const path = require('path')
module.exports = {
context: path.resolve('src'),
entry: './main.ts',
module: {
rules: [
{
test: require.resolve('./src/main.ts'),
use: [{
loader: 'expose-loader',
options: 'Library'
}, {
loader: 'ts-loader'
}],
exclude: /node_modules/
}
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
// File: main.ts
export class Car {
seats: number;
passengers: string[];
add(passenger: string) {
if(this.passengers.indexOf(passenger) === -1) {
this.passengers.push(passenger);
}
}
}
// File: main.js
var car = new Library.Car()
car.seats = 5
car.add('Pete')
Make sure the code calling the module is included after the module bundle.
<!-- File: index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script type="text/javascript" src="bundle.js"></script>
<script src="main.js"></script>
</body>
</html>