Webpack how to cache bust angular-translate with $translatePartialLoader? - javascript

"webpack": "^2.7.0"
I'm trying to add a hash to our translation files in order to cache bust when deploying. I've managed to extract the json and add a hash and output it to a folder and is good with the world.
But, my unhashed json is still under there original folders after building. I understand that we don't need to add a loader for json as it already has means of handling importing, so my question would be how do I clean out the json that's already been processed?
my folder structure is as follows
src/
app/
module-name/
/translations
en.json
fn.json
module-name/
/translations
en.json
fn.json
//ect...
I used the CopyWebpackPlugin to get the json and hash is there maybe an option ive missed that cleans out the process'd files? or maybe i'm approaching this the incorrect way.
const webpack = require('webpack');
const conf = require('./gulp.conf');
const path = require('path');
const VersionFile = require('webpack-version-file');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const pkg = require('../package.json');
const autoprefixer = require('autoprefixer');
module.exports = {
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
enforce: 'pre'
},
{
test: /\.(css|scss)$/,
loaders: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader?minimize!resolve-url-loader!sass-loader?sourceMap!postcss-loader'
})
},
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file-loader',
options: {
regExp: /\/([a-z0-9]+)\/[a-z0-9]+\.json$/,
name: '[name]-[hash].[ext]'
}
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate-loader',
'babel-loader'
]
},
{
test: /\.html$/,
loaders: [
'html-loader'
]
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
template: conf.path.src('index.html')
}),
new webpack.optimize.UglifyJsPlugin({
output: {comments: false},
compress: {unused: true, dead_code: true, warnings: false} // eslint-disable-line camelcase
}),
new ExtractTextPlugin('index-[contenthash].css'),
new webpack.optimize.CommonsChunkPlugin({name: 'vendor'}),
new webpack.LoaderOptionsPlugin({
options: {
postcss: () => [autoprefixer]
}
}),
new webpack.HashedModuleIdsPlugin(),
new CopyWebpackPlugin([{
from: 'src/app/**/*.json',
to: 'translations/[name]-[hash].[ext]'
}]),
new VersionFile({
output: `${conf.paths.dist}/version.txt`,
verbose: true
})
],
output: {
path: path.join(process.cwd(), conf.paths.dist),
filename: '[name]-[hash].js'
},
entry: {
app: [`./${conf.path.src('app/app.module.js')}`],
vendor: Object.keys(pkg.dependencies)
},
node: {
fs: 'empty',
/* eslint-disable camelcase */
child_process: 'empty'
}
};
Or to similfy the question, how can i add a hash to json files? and the following code doesn't seem to do anything.
{
test: /\.json$/,
loader: 'file-loader',
options: {
name: '[name]-[hash].[ext]'
}
}
EDIT:
so it seems like my json loader doesnt pick up the translation files as they're dynamicly imported like so how:
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'app/{part}/translations/{lang}.json'
});
do you handle cases like this?

The main goal you're trying to do here is telling the browser its a new file when releasing a new version and we can do this fairly easily without having to force webpack to know what files are being used.
in your webpack config add this
const pkg = require('../package.json');
//...
new webpack.DefinePlugin({
__VERSION__: JSON.stringify(pkg.version)
})
and where you add your translation files this allows for the browser to know where has been a new release and should update translation files.
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: `app/{part}/translations/{lang}.json?v=${__VERSION__}`
});

Related

Webpack4 how to load images and custom javascript directly from HTML file?

I want to load images directly from HTML with Webpack 4 and add custom Javascript files to my HTML file but both files inspected at console show Not found 404.
How to properly load images and Javascipt files with Webpack 4?
My Webpack 4 config file:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: {
main: "./src/index.js"
},
output: {
path: path.join(__dirname, "../build"),
filename: "[name].bundle.js"
},
mode: "development",
devServer: {
contentBase: path.join(__dirname, "../build"),
compress: true,
port: 3000,
overlay: true
},
devtool: "cheap-module-eval-source-map",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader" // transpiling our JavaScript files using Babel and webpack
}
},
{
test: /\.(sa|sc|c)ss$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"postcss-loader", // Loader for webpack to process CSS with PostCSS
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: "file-loader", // This will resolves import/require() on a file into a url and emits the file into the output directory.
options: {
name: "[name].[ext]",
outputPath: "assets",
}
},
]
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: {
attrs: ["img:src", ":data-src"],
minimize: true
}
}
}
]
},
plugins: [
// CleanWebpackPlugin will do some clean up/remove folder before build
// In this case, this plugin will remove 'dist' and 'build' folder before re-build again
new CleanWebpackPlugin(),
// The plugin will generate an HTML5 file for you that includes all your webpack bundles in the body using script tags
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
]
My webpack.prod.js file:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const TerserJSPlugin = require("terser-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-
plugin");
const BrotliPlugin = require("brotli-webpack-plugin");
const PurgecssPlugin = require('purgecss-webpack-plugin');
const glob = require("glob");
module.exports = {
entry: {
main: "./src/index.js"
},
output: {
path: path.join(__dirname, "../build"),
filename: "[name].[chunkhash:8].bundle.js",
chunkFilename: "[name].[chunkhash:8].chunk.js"
},
mode: "production",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader" // transpiling our JavaScript files using
Babel and webpack
}
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // translates CSS into CommonJS
"postcss-loader", // Loader for webpack to process CSS with
PostCSS
"sass-loader" // compiles Sass to CSS, using Node Sass by
default
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: "file-loader", // This will resolves import/require()
on a file into a url and emits the file into the output directory.
options: {
name: "[name].[ext]",
outputPath: "assets/"
}
},
]
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: {
attrs: ["img:src", ":data-src"],
minimize: true
}
}
}
]
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
}
},
chunks: "all"
},
runtimeChunk: {
name: "runtime"
}
},
plugins: [
// CleanWebpackPlugin will do some clean up/remove folder before build
// In this case, this plugin will remove 'dist' and 'build' folder
before re-build again
new CleanWebpackPlugin(),
// PurgecssPlugin will remove unused CSS
new PurgecssPlugin({
paths: glob.sync(path.resolve(__dirname, '../src/**/*'), { nodir:
true })
}),
// This plugin will extract all css to one file
new MiniCssExtractPlugin({
filename: "[name].[chunkhash:8].bundle.css",
chunkFilename: "[name].[chunkhash:8].chunk.css",
}),
// The plugin will generate an HTML5 file for you that includes all
your webpack bundles in the body using script tags
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
My project nesting:
--Build
--src
----html
----js
----styles
----assets
------images
I want that files would load simply:
<img src="assets/images/myimage.jpg">
<srcipt src="js/custom.js"></script>
Any help would be appreciated.
Ironically this is my second project with Webpack 4 and this time I can't fix this issue, the first time there was no problem.
This is how img work in Webpack:
<img src=require("./assets/images/myimage.jpg")>
if you use "require" with file-loader, you need to add default.
<img src=require("./assets/images/myimage.jpg").default>
For script tag, src should be your output bundle.js file; because Webpack writes all the code into the bundle.js with the help of Babel. So when browser request html, browser will have only one Javascript file which is the bundle to download. That is the whole point of bundle, smaller bundle.js is better for performance.
// According to your Webpack config, it is main.bundle.js
<srcipt src="main.bundle.js"></script>
but in order to browser to use this main.bundle.js, it has to be publicly available. So you need express to define the public folder, when browser looks for main.bundle.js, you app will be looking into the public folder and if that file exists, it will ship it to the browser.
const express = require("express");
const server = express();
const staticMiddleware = express.static("build");
server.use(staticMiddleware);

Webpack bundles tests (enzyme / jest) in the production env

I have issue with Webpack building production bundle with test files + test libs included.
In this case it is Enzyme and Jest which we use.
Webpack version 3.10.0
Webpack.build.js
const path = require('path');
const webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = function() {
return {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, '../build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: [
/node_modules/,
/__snapshots__/,
/test-util/,
],
loader: 'babel-loader'
},
{
test: /\.scss$/,
use: [
{
loader: 'isomorphic-style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
]
},
plugins: [
new UglifyJsPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}
}),
]
}
};
Here is the file structure
I have tried to workaround the issue by placing Enzyme as external resource in webpack.
externals: {
'enzyme': 'enzyme',
'enzyme-adapter-react-16': 'enzyme-adapter-react-16',
}
That does exclude it from build but (it is workaround) it still builds the snapshot files
I have tried to make "test" regex of babel-loader more specific and exclude test files but it failed.
This is new project and I spent whole day trying to make it bundle only what is necesary. Drained all of my know-how, my google know-how and know-how of the poeple I know or I work with. Hope that SO will be smarter :)

Webpack throws TypeError: Super expression must either be null or a function, not undefined when importing LESS file

I have the following in a file initialize.js:
import App from './components/App';
import './styles/application.less';
document.addEventListener('DOMContentLoaded', () => {
const app = new App();
app.start();
});
In webpack.config.js I have:
'use strict';
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const ProvidePlugin = webpack.ProvidePlugin;
const ModuleConcatenationPlugin = webpack.optimize.ModuleConcatenationPlugin;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const extractLess = new ExtractTextPlugin({
filename: 'app.css',
});
const webpackCommon = {
entry: {
app: ['./app/initialize']
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader?presets[]=es2015'
}]
}, {
test: /\.hbs$/,
use: {
loader: 'handlebars-loader'
}
}, {
test: /\.less$/,
exclude: /node_modules/,
use: extractLess.extract({
use: [{
loader: 'css-loader'
}, {
loader: 'less-loader'
}],
// use style-loader in development
fallback: 'style-loader'
}),
}]
},
output: {
filename: 'app.js',
path: path.join(__dirname, './public'),
publicPath: '/'
},
plugins: [
extractLess,
new CopyWebpackPlugin([{
from: './app/assets/index.html',
to: './index.html'
}]),
new ProvidePlugin({
$: 'jquery',
_: 'underscore'
}),
new ModuleConcatenationPlugin(),
],
};
module.exports = merge(webpackCommon, {
devtool: '#inline-source-map',
devServer: {
contentBase: path.join(__dirname, 'public'),
compress: true,
port: 9000
}
});
I tried removing the the plugins and the contents of application.less, but I keep getting this error:
ERROR in ./node_modules/css-loader!./node_modules/less-loader/dist/cjs.js!./app/styles/application.less
Module build failed: TypeError: Super expression must either be null or a function, not undefined
at ...
# ./app/styles/application.less 4:14-127
# ./app/initialize.js
If I replace that LESS file with a CSS one and update the config it works fine, so I guess the problem has to do with less-loader.
I'm using Webpack 3.4.1, Style Loader 0.18.2, LESS Loader 4.0.5, Extract Text Webpack Plugin 3.0.0 and CSS Loader css-loader.
My bad, I didn't notice I was using an old less version. That was the culprit. Just updated it to 2.7.2 and the problem is gone.

How to include this node_module's .html asset in webpack?

I am using the package electron-notifications and it relies on a .html and .css file in its assets folder. This assets folder is not included in webpack (1.14.0) though.
I know I should not add a module as an entry point. I have come across a concept called code splitting, but I'm not clear on how that works and if that is what I need to be looking into further. Any advice you can give would be greatly appreciated.
webpack.config.production.js
import path from 'path';
import webpack from 'webpack';
import validate from 'webpack-validator';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import merge from 'webpack-merge';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import BabiliPlugin from 'babili-webpack-plugin';
import baseConfig from './webpack.config.base';
export default validate(merge(baseConfig, {
devtool: 'inline-source-map',
entry: [
'babel-polyfill',
'./app/index'
],
output: {
path: path.join(__dirname, 'app/dist'),
publicPath: '../dist/'
},
module: {
loaders: [
// Extract all .global.css to style.css as is
{
test: /\.global\.css$/,
// loaders: [
loader: ExtractTextPlugin.extract(
'style-loader',
'css-loader?sourceMap'
)
// ]
},
// Pipe other styles through css modules and append to style.css
{
test: /^((?!\.global).)*\.css$/,
// loaders: [
loader: ExtractTextPlugin.extract(
'style-loader',
'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
)
},
// Fonts
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' },
// Images
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/,
loader: 'url-loader'
}
]
},
plugins: [
// https://webpack.github.io/docs/list-of-plugins.html#occurrenceorderplugin
// https://github.com/webpack/webpack/issues/864
new webpack.optimize.OccurrenceOrderPlugin(),
// NODE_ENV should be production so that modules do not perform certain development checks
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new BabiliPlugin(),
new ExtractTextPlugin('style.css', { allChunks: true }),
new HtmlWebpackPlugin({
filename: '../app.html',
template: 'app/app.html',
inject: false
})
],
// https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
target: 'electron-renderer'
}));
If you want that packages' CSS to be recognised by the webpack, you just add it to the style's(CSS's) loader block, as an include attribute along with "test" and "loader". In the include attribute point it to the node_modules/electron_notification path.
HTML of that package need not be included, since your Single Page Application, has it's own HTML, if needed try to replicate the class names there. But I doubt if you need to do that.

Still getting 'Symbol' is undefined' error even after adding babel-polyfill in my webpack

https://babeljs.io/docs/usage/polyfill/#usage-in-browser
I did not understand the lines on the documentation page under:
Usage in Browser heading
can someone help me with what else is required:
Below are my code snippets:
I'm using storybook as a boilerplate:
webpack.config.js file:
entry: [
'babel-polyfill',
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs
]
index.js file:
import 'babel-polyfill';
import React from 'react';
Is there some other files also where I need to add babel-polyfill related code.
require('babel-polyfill');
var path = require('path');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
var getClientEnvironment = require('./env');
var paths = require('./paths');
var publicPath = '/';
var publicUrl = '';
var env = getClientEnvironment(publicUrl);
module.exports = {
devtool: 'cheap-module-source-map',
entry: ['babel-polyfill',
require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
paths.appIndexJs
],
output: {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/bundle.js',
publicPath: publicPath
},
resolve: {
fallback: paths.nodePaths,
extensions: ['.js', '.json', '.jsx', ''],
alias: {
'react-native': 'react-native-web'
}
},
module: {
// First, run the linter.
// It's important to do this before Babel processes the JS.
preLoaders: [{
test: /\.(js|jsx)$/,
loader: 'eslint',
include: paths.appSrc,
}],
loaders: [{
exclude: [/\.html$/, /\.(js|jsx)$/, /\.css$/, /\.json$/],
loader: 'url',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
},
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: 'babel',
query: {
cacheDirectory: true
}
}, {
test: /\.css$/,
loader: 'style!css?importLoaders=1!postcss'
}, {
test: /\.json$/,
loader: 'json'
}
]
},
// We use PostCSS for autoprefixing only.
postcss: function() {
return [
autoprefixer({
browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway
]
}),
];
},
plugins: [
new InterpolateHtmlPlugin({
PUBLIC_URL: publicUrl
}),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
new webpack.DefinePlugin(env),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
There are two ways to get this code into your browser.
1 - Include the babel-polyfill module in the webpack bundle
2 - Load it as an external script in your html
Webpack - adding bundle dependencies with entry arrays
Put an array as the entry point to make the babel-polyfill module available to your bundle as an export.
With webpack.config.js, add babel-polyfill to your entry array.
The webpack docs explain how an entry array is handled:
What happens when you pass an array to entry? Passing an array of file
paths to the entry property creates what is known as a "multi-main
entry". This is useful when you would like to inject multiple
dependent files together and graph their dependencies into one
"chunk".
Webpack.config.js
require("babel-polyfill");
var config = {
devtool: 'cheap-module-eval-source-map',
entry: {
main: [
// configuration for babel6
['babel-polyfill', './src/js/main.js']
]
},
}
Alternative to Webpack - load babel-polyfill as an external script in the browser html
The alternative to using webpack would mean including the module as an external script in your html. It will then be available to code in the browser but the webpack bundle won't be directly aware of it.
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.22.0/polyfill.js"></script>

Categories