Webpack, babelrc dynamic import not working - ecmascript-6

I spent quite some time trying to figure this out myself but here I am, with no more options to consider than to reach out to the community for some guidance.
I am trying to do something very simple in principle, dynamically import a component with WebPack, using ES6 modules and babelrc.
I have the following app architecture:
-root
-- root/.webpack.dev.js
-- root/.webpack.prod.js
-- root/.babelrc
-- root/package.json
-- root/node_modules/
-- root/dist/
-- root/src/
--- root/src/index.js
--- root/src/modules/
--- root/src/modules/module1.js
--- root/src/modules/module2.js
--- root/src/modules/module3.js
--- root/src/modules/module4.js
--- root/src/modules/module5.js
In my module1.js (not the real name) I am using the following code to dynamically import module2.js:
async function load(configObject) {
const {
init,
requestPermissions
} = await import( /* webpackChunkName: "chunkname" */ `./module2.js`)
init(configObject)
_namespace.requestPermissions = requestPermissions;
}
My .babelrc file:
{
"presets": [
["#babel/preset-env", {
"targets": "> 0.25%, not dead"
}]
],
"plugins": ["#babel/plugin-syntax-dynamic-import",
["#babel/plugin-transform-runtime",
{
"regenerator": true
}
],
],
"comments": true
}
// "#babel/preset-env"
My Webpack config:
const path = require('path');
const webpack = require('webpack')
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin
const WorkboxPlugin = require('workbox-webpack-plugin');
const {
InjectManifest
} = require('workbox-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry: {
lib: "./src/index.js"
},
mode: 'development',
module: {
rules: [{
test: /\.js$/,
use: [{
loader: "babel-loader"
}],
exclude: /node_modules/
}]
},
optimization: {
minimizer: [new TerserPlugin({
test: /\.js(\?.*)?$/i,
parallel: true,
cache: true,
terserOptions: {
ecma: 8,
warnings: false,
parse: {
ecma: 8,
},
compress: {
warnings: false,
comparisons: false,
},
mangle: {
safari10: true,
},
module: false,
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
toplevel: false,
nameCache: null,
ie8: false,
keep_classnames: undefined,
keep_fnames: false,
safari10: false,
},
})],
},
output: {
filename: '[name].js',
chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: "/"
},
devServer: {
contentBase: "dist",
compress: true,
stats: {
colors: true
},
overlay: true
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development'),
'API_URL': JSON.stringify('ENDPOINT')
}
}),
new BundleAnalyzerPlugin({
generateStatsFile: true
}),
new WorkboxPlugin.GenerateSW({
"swDest": "firebase-messaging-sw.js",
}),
new InjectManifest({
"swSrc": path.join('src', 'firebase-messaging-sw.js')
})
]
};
My package.json:
{
"name": "refactor",
"version": "1.0.0",
"description": "",
"main": "backuprefacto.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "NODE_ENV=production webpack --config=webpack.prod.js",
"build:dev": "webpack --config=webpack.dev.js",
"start": "webpack-dev-server --config=webpack.dev.js"
},
"keywords": [],
"private": true,
"license": "ISC",
"devDependencies": {
"#babel/plugin-syntax-dynamic-import": "^7.2.0",
"#babel/preset-env": "^7.5.5",
"babel-loader": "^8.0.6",
"babel-minify": "^0.5.1",
"babel-minify-webpack-plugin": "^0.3.1",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"terser-webpack-plugin": "^1.4.1",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.39.2",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0",
"workbox-webpack-plugin": "^4.3.1"
},
"dependencies": {
"#babel/core": "^7.5.5",
"#babel/plugin-transform-runtime": "^7.5.5",
"#babel/runtime": "^7.5.5",
"firebase": "^6.4.0",
"save": "^2.4.0"
}
}
I have checked all my modules, none of them expect for module1.js are calling module2.js.
I have also explored the option of webpack comments being deleted by babel and therefore added a comments: true to make sure the webpackChunkName is not being deleted but in the end, the only thing that gets built is my lib.js, not the lib.bundle.js that I expect.
I have also tried to remove all the TerserPlugin bit to check if that could have the same impact but nothing changed there.
In the need, what I am looking for is simply to have the module2.js loaded whenever it is invoked, and I therefore expect a new network request to materialise this.

Well, it turns out that if you want to use dynamic imports you need to make sure first that you are not importing at all the module at the top....
In module1.js I was importing twice, once at the top, the "regular way", once the dynamic way which was obviously leading to module2.js being consistently loaded.

I resolve my problem by modify .babelrc, modules: false
["#babel/preset-env", {
"loose": true,
"useBuiltIns": "usage",
"corejs": 3,
"modules": false
}],

Related

Webpack 5, How to make live reload when I update html file after the service run

I want the server reload the page automatic when I update the html file but I keep getting error.
[webpack-dev-middleware] HookWebpackError: Path variable [id] not implemented in this context: [id].[fullhash].hot-update.js
I am not sure if I set the dev-server correctly or the plugin I use make this error.
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
mode: "development",
devtool: "source-map",
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.html$/,
use: ["html-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
title: "Bootstrap 2",
template: "src/index.html",
filename: "index.html",
}),
],
optimization: {
chunkIds: false,
},
devServer: {
port: 9000,
historyApiFallback: true,
hot: true,
static: path.resolve(__dirname,"dist")
},
};

Webpack images path in build

webpack break my image paths
This is my code
and it is error
Refused to apply style from 'http://localhost:8080/style.css' because
its MIME type ('text/html') is not a supported stylesheet MIME type,
and strict MIME checking is enabled.
webpack.config.js
i use file-loader but this not help me
const path = require('path');
const HtmlWebpackPlugin = require('*html-webpack-plugin*');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devServer = (isDev) => !isDev ? {} : {
devServer: {
open: true,
port: 8080,
static: {
watch: true,
directory: '**/*.html',
},
},
};
module.exports = ({development}) => ({
mode: development ? 'development' : 'production',
devtool: development ? 'inline-source-map' : false,
entry: {
app:'./src/index.js',
main: './src/shelter/pages/main/index.js',
pets: './src/shelter/pages/pets/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
assetModuleFilename: 'assets/images/[hash][ext]',
},
module: {
rules: [
{
test: /\.(?:ico|gif|png|jpg|jpeg|svg|html|text)$/i,
use: ['file-loader']
},
{
test: /\.(woff(2)?|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
},
{
loader: 'file-loader',
options: {
publicPath: '/../../image',
name: `assets/images/[hash].[ext]`,
}
},
]
},
resolve: {
extensions: ['.ts', '.js']
},
plugins: [
new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' }),
new HtmlWebpackPlugin({ template: './src/shelter/pages/main/index.html' }),
],
...devServer(development)
});
package.json
it is my package.json
{
"name": "webpack",
"scripts": {
"build": "webpack",
"dev": "webpack server --env development"
},
"devDependencies": {
"css-loader": "^6.7.1",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.6.0",
"sass": "^1.50.0",
"sass-loader": "^12.6.0",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"file-loader": "^6.2.0",
"resolve-url-loader": "^5.0.0",
"webpack-dev-server": "^4.8.1"
}
}
My structure fails enter image description here

What is best way to go about replacing 'deployUrl' in angular.json for v13?

I'm currently in the process of upgrading my app from v12 to v13 and noticed this warning pop up:
Option "deployUrl" is deprecated: Use "baseHref" option, "APP_BASE_HREF" DI token or a combination of both instead. For more information, see https://angular.io/guide/deployment#the-deploy-url.
After digging into it a little more, none of the 'baseHref' or APP_BASE_REF options really work for my setup so I'm wondering if I'm using them incorrectly or if there isn't a good way to go about replacing it
Here's a snippet of the app config from angular.json:
"dashboard": {
"projectType": "application",
"root": "apps/dashboard",
"sourceRoot": "apps/dashboard/src",
"architect": {
"build": {
"builder": "#angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [],
"outputPath": "../dist/dashboard/",
"deployUrl": "/dist/dashboard/",
"index": "apps/dashboard/src/index.html",
"main": "apps/dashboard/src/main.ts",
"tsConfig": "apps/dashboard/tsconfig.app.json",
"polyfills": "apps/dashboard/src/polyfills.ts",
"styles": [
"apps/dashboard/src/styles.scss"
],
"scripts": [],
"stylePreprocessorOptions": {
"includePaths": [
"libs/assets/styles"
]
},
"aot": false,
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"aot": true,
"buildOptimizer": true,
"extractLicenses": true,
"fileReplacements": [
{
"replace": "apps/dashboard/src/environments/environment.ts",
"with": "apps/dashboard/src/environments/environment.prod.ts"
}
],
"namedChunks": false,
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"vendorChunk": false
},
"es5": {
"tsConfig": "apps/dashboard/tsconfig.es5.json"
}
},
"defaultConfiguration": ""
}
}
}
Snippet of routing file:
export const DashboardRoutes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: '/dashboard' },
{
path: 'dashboard',
data: {
label: 'Dashboard',
appBase: true
},
children: [
// this is a child so we can load the component in same router-outlet
{
path: '',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule),
data: {
authorizedRoles: ['member'],
}
},
// ...other children
]
}
]
I've tried changing deployUrl to baseHref and that works, kind of - It changes the main page from localhost/dashboard to localhost/dist/dashboard/dashboard (obviously not right) and just putting an empty string or "/" doesn't load the app correctly (looks at dist/ vs dist/dashboard like it should)
Worth noting that my index.html does use <base href="/" /> and APP_BASE_HREF is not overridden in the app module providers
Ended up finding something to replace this with:
Replaced "deployUrl" with "baseHref" in angular.json
Added the APP_BASE_HREF override in app.module
i.e { provide: APP_BASE_HREF, useValue: '/' }
Replaced any hrefs in index.html that referenced the dist (output) folder
e.g replaced 'dist/assets/{some_asset}' with '../assets/{some_asset'}'
index.html still uses <base href="/">
This is a problem with Angular 13+
Found a solution here:
https://github.com/angular/angular-cli/issues/22113#issuecomment-1004279867
You should put following code in your main.ts file:
declare var __webpack_public_path__: string;
__webpack_public_path__ = 'valueFormerlyAssignedUsing_deployUrl';
Replace valueFormerlyAssignedUsing_deployUrl with your path to folder containing chunks.

awesome-typescript-loader does not pick up changes in JSON

Assume I have some JSON file (let's name it template.json)
{
"myField1": "",
"myField2": ""
}
I also have a kind of generic class
export default GenericClass<T> {
// Creating an empty constuctor with passed type.
// to allow define type automatically.
// This allow us not to manually set generic type for class
// and also allows Webpack to pick up changes.
constructor(template?: T) {}
// ...some fields and methods
get typedField(): T {
return /* something slightly calculated */
}
}
I'm using it like a type in my Typescript project:
import GenericClass from "path/to/GenericClass"
import template from "template.json"
export type TemplateType = typeof template
export default new GenericClass(template)
// we can also write
// export default new GenericClass<TemplateType>()
// but in this case the changes in template.json
// won't be picked up by Webpack.
// However, this does not affects the problem,
// it occurs in both cases.
I'm running the webpack dev-server, and use it somewhere:
import * as React from "react"
import GenericInstance from "path/to/GenericInstance"
export default MyComponent extends React.Component {
render() {
var { typedField } = GenericInstance
return (
<main>
<p>{typedField.myField1} {/* ok */}</p>
<p>{typedField.myField2} {/* ok */}</p>
</main>
)
}
}
After that I'm adding a new field into my template.json:
{
"myField1": "",
"myField2": "",
"myField3": ""
}
Saving it. webpack dev-server picks up this change in template.json. Allright. One important thing is that autocomplete of VSCode works (it shows this myField3 in list of available fields). Fine.
At this moment, when I'm trying to use myField3 in MyComponent (like <p>{typedField.myField3}</p>), awesome-typescript-loader sends an error during compilation:
Property 'myField3' does not exist on type '{ "myField1": string; "myField2": string; }'
Obviously, awesome-typescript-loader did not pick up changes in template.json which is used as type in my GenericClass.
How can I beat it? After restart of the dev-server it works fine until I make changes in template.json.
Partial webpack.config.js, package.json and tsconfig.json
config = {
rules: {
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader",
exclude: /node_modules/
},
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
},
}
}
{
"devDependencies": {
"awesome-typescript-loader": "^5.2.1",
"source-map-loader": "^0.2.4",
"typescript": "^3.3.3",
"webpack": "^4.29.3",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^3.1.14"
}
}
{
"compilerOptions": {
"module": "esnext",
"target": "es5",
"moduleResolution": "node",
"baseUrl": "src",
"allowSyntheticDefaultImports": true,
"noImplicitAny": true,
"strict": false,
"sourceMap": true,
"outDir": "dist/",
"jsx": "react",
"traceResolution": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowJs": true,
"declaration": false,
"removeComments": true,
"noLib": false,
"preserveConstEnums": true,
"suppressImplicitAnyIndexErrors": true,
"types": [ "node" ],
"lib": ["es6", "dom", "dom.iterable"],
"downlevelIteration": true,
"resolveJsonModule": true,
"typeRoots": [
"./node_modules/#types"
]
},
"include": [
"src/**/*"
]
}
Update
I can confirm that this occurs only with imported *.json. Probably, the problem can be in touch with resolveJsonModule setting for TypeScript, but not sure. Setting useCache and usePrecompiledFiles to false explicitly for awesome-typescript-loader in webpack.config.js does not help. I mean, changed webpack.config.js now looks like:
{
test: /\.(t|j)sx?$/,
loader: "awesome-typescript-loader",
options: {
useCache: false,
usePrecompiledFiles: false
},
exclude: /node_modules\/(?!superagent)/,
},
This is a bug in awesome-typescript-loader. As of v5.2.1, here's a quick fix:
// node_modules/awesome-typescript-loader/dist/instance.js
// ln: 214:
- var EXTENSIONS = /\.tsx?$|\.jsx?$/;
+ var EXTENSIONS = /\.tsx?$|\.jsx?|\.json$/;
Apparently the author forget to include .json extension as a valid target.

How can I configure the webpack or typescript that changes are made immediately?

I've implemented webpack so that it generates one file from my angular application. webpack.js
Now the problem is whenever I change the TypeScript file, I'd have to re-run the webpack to see the effect.
That's slowed the development dramatically.
How can I configure the webpack or typescript that changes are made immediately?
webpack.config.js:
const webpack = require('webpack');
module.exports = {
entry: {
app: './app/main.js',
vendor: './app/vendor.js'
},
output: {
//path: './bin',
filename: 'webpack/webpack-[name].js'
},
resolve: {
extensions: ['', '.js', '.ts']
},
devtool: 'source-map',
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' },
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test: /\.css$/, loaders: ['style', 'css'] },
{ test: /\.json/, loaders: ['json-loader'] },
{ test: /\.html/, loaders: ['raw-loader'] },
{ test: /\.(jpg|png|gif)$/, loaders: ['file-loader'] }
]}
// Add minification
, plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
comments: false
}
})
]
};
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
How can I configure the webpack or typescript that changes are made immediately
Actually configure how you run webpack. If you use webpack --watch it automatically updates the bundle as soon as you make a change, but then you still need to refresh the browser page. Even better if you use webpack-dev-server as webpack-dev-server --hot --inline will even reload your browser page immediately.