Setting up webpack/HMR for Shopify development - javascript

I am trying to setup webpack's dev server and HMR to work with Shopify theme development. When running the server and opening the local IP, I get this error from Shopify's DNS provider, CloudFlare.
How can I properly setup webpack to inject hot changes (css/JS) to my proxied Shopify store (the mystore.myshopify.com url)?
My webpack config as follows:
const path = require("path");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
mode: "development",
devServer: {
contentBase: false,
hot: true,
https: true,
proxy: {
"**": {
target: "http://mystore.myshopify.com",
secure: false
}
},
},
entry: "./src/scripts/index.js",
output: {
filename: "./app.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
],
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
//postcss here (autoprefixer, babel etc)
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
},
],
},
};

My proposed solution for hot reloading with webpack and shopify.
In your package.json you will want the following scripts
A webpack build script that watches for changes. I have also added a progress parameter so I can see when it updates. This will be watching your /src folder for changes.
"webpack:build": "cross-env NODE_ENV=development webpack --watch --progress",
A shopify theme serve script that uploads the theme files from a /dist folder that is the output of a webpack build.
"shopify:serve": "cd dist && shopify theme serve"
You can link these two scripts together into one using the following.
"deploy:serve": "run-p -sr webpack:build shopify:serve"
So what happens is the webpack build script listens for changes in your /src code. When a change is main it re-builds the output to the /dist folder that shopify theme serve is listening on. When that updates, shopify uploads the changes.
Voila.
Here is the repo for this implementation if you want to play around - https://github.com/Sambuxc/9119-shopify-theme/
As of writing this, my dev versions are:
Node 18.7.0
Npm 8.15.0
Shopify Cli 2.21.0
Good luck and I hope this helps future developers.
Please reach out to me with any further questions if you get stuck.

Related

Webpack - Bundle multiple/different versions of .css files

I would like to make my bundled .css file being generated by Webpack more configurable, so I can output different 'versions' - based on the same .css file - to make the life of developers working on my project in the future easier.
I would like to have the following steps:
Concat of all SCSS into CSS (bundle.css)
Minimize output of step 1 (bundle.min.css)
Embed all images from step 2 (bundle.b64.min.css)
Embed all fonts from step 3 (bundle.bs64.fonts.min.css)
In the end - after my build process -, I would have 4 distinct files in my dist folder. Would that me possible?
The way I'm currently doing it, I run a different script for each step - deletes dist folder, goes through project, produces the output. I would like to have a single script that does all of it at once without having to go through my project 4 times.
I kind of found a solution for it here:
Webpack Extract-Text-Plugin Output Multiple CSS Files (Both Minified and Not Minified)
But, for my specific case, I would have to return 4 different configurations in a array instead of a single object.
Ok so based on our comment conversation i'm gonna give you a workflow of steps 1-4, but with regular assets handling, not a bundling of assets (which i haven't heard of but maybe someone else can elaborate there).
So the steps:
bundle all scss files into 1 bundle.css
make sure this bundle is minified
add assets management to build for images
add assets management to build for fonts
The important things:
This workflow is basically a built by configuration. configuring the npm scripts with the package.json file, and configuring webpack with config.webpack.js. This will allow you to simply run 1 command to build your project: npm run build. note: For simplicity's sake i am going to ignore production/development/etc environments and focus on a single environment.
package.json:
This is used to set up the command that will actually run when you input npm run build in the terminal (from the project dir of course).
since we are avoiding different environments for now and as you are not using Typescript this is a very simple configuraton:
"scripts": {
"build": "webpack",
},
that's all you have to add. It sound's stupid now but when the project will get more complex you are going to like those scripts so better start off making them already.
webpack.config.js:
The major lifting will be made in this configuration file. This basically tells webpack what to do when you run it (which is what npm run build is doing).
first off let's install some plugins:
npm install --save-dev file-loader
npm install --save-dev html-webpack-plugin
npm install --save-dev mini-css-extract-plugin
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map'
entry: './client/src/app.jsx',
output: {
path: path.join(__dirname, 'client/dist/public'),
filename: 'bundle.[hash].js'
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: false
}
},
'css-loader',
'sass-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
]
},
resolve: {
extensions: ['.js', '.json', '.jsx']
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './client/src/index_template.html'
}),
new MiniCssExtractPlugin({
filename: 'style.[hash].css',
chunkFilename: '[id].[hash].css'
}),
]
};
Notice i've added the htmlWebpackPlugin because it makes it easier to reference the correct hashed bundles automatically. Also I've assumed the app is a react app but you can just change the entry point to where your app loads from.
This is quite hard to do of the fly without testing things out, but i hope this gives you enough reference as to what you should change and do to get going with it.
Again i strognly recommend the webpack.js guides and documentation, they are very thorough and once you start getting the hang of it things start working smoothly.

Flask React App not Hot Reloading

I am trying to use Python Flask with a React Frontend. Here is my webpack file:
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: [
"./js/app.jsx",
"./scss/main.scss"
],
output: {
path: __dirname + '/static',
filename: "bundle.js"
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
},
exclude: /node_modules/
},
{ // sass / scss loader for webpack
test: /\.(sass|scss)$/,
loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
}
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
plugins: [
new ExtractTextPlugin({ // define where to save the file
filename: '[name].bundle.css',
allChunks: true,
})
]
};
I am not sure if this file is the problem, but for some reason, my app is not hot-reloading. Here is a simple repo that I setup for which hot reloading does not work: https://github.com/rishub/Flask_React
I am using
webpack version: 3.8.1,
node version: 6.11.5,
npm version: 3.10.10
I cannot seem to figure out why it is not reloading.
I run python app.py in one terminal and webpack --watch in the other.
The terminal with webpack seems to detect the changes to the React jsx files, however, the app does not reload on the browser unless I force refresh, even a regular refresh does not work.
If someone would be able to point out the problem or fork/make a PR to the repo, that would be great
With watch, you're only compiling the updates themselves & not interfacing with the application.
--watch is not a flag for hot reloading in your browser. It regulates registered files for changes and recompiles the Webpack output whenever there is a change within those files. However, it does not necessarily forward that output to the browser.
Your force refresh was syncing up with the new version served up by Webpack, and also disrupting the browser cache.
Hot Module Reloading needs to be done with some kind of additional tool like Webpack HMR and a configured Webpack Dev Server.
From the docs, Hot Reloading entails all of the following:
The application asks the HMR runtime to check for updates.
The runtime asynchronously downloads the updates and notifies the application.
The application then asks the runtime to apply the updates.
The runtime synchronously applies the updates.

Mixing React with Express?

I have a React based application, running off a NodeJS based server, and would like to allow it to download a non-static file, without the use of xhr.
I am thinking of using Express, but I am not sure how I would make it co-exist with React in the same container? Can anyone suggest how I would go about this? I am still learning webpack and React, and I having trouble finding examples of this?
The React server is started via webpack:
node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --host 0.0.0.0 --port 8081 --hot --inline
With the webpack-dev-server.js, looking as follows:
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: __dirname,
publicPath: '/',
filename: 'bundle.js'
},
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-1']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
The './src/index.js' is React type code.
Disclaimer: I've never used webpack-dev-server extensively myself (at least not directly), but I can think of two solutions.
One is to use setup in the webpack-dev-server configuration. As stated here:
setup: function(app) {
// Here you can access the Express app object and add your own custom middleware to it.
// For example, to define custom handlers for some paths:
// app.get('/some/path', function(req, res) {
// res.json({ custom: 'response' });
// });
}
Alternatively, which is a method I use myself, is to use webpack-dev-middleware, which is a middleware for Express. So basically, you create your own Express server that has the same functionality as webpack-dev-server.
Here's an excerpt from one of my projects (where app is the Express app instance):
// Webpack (only when not running in `production` mode):
if (process.env.NODE_ENV !== 'production') {
debug('setting up webpack middleware');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('./webpack.dev');
const compiler = require('webpack')(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
noInfo : true,
publicPath : webpackConfig.output.publicPath
}));
app.use(webpackHotMiddleware(compiler));
}
In addition to #robertklep's answer, based on some further research, you can 'compile' your React project and then just make use of those resources without nodejs.
Use node & webpack to 'compile' your project:
node node_modules/.bin/webpack
This will create a bundle.js file which contains the compiled project. Other files, such as the images, external Javascript and stylesheets will need to be copied, for example (assuming there is a dist folder):
cp bundle.js dist/
cp index.html dist/
cp -rf img css frameworks dist/
Everything in the dist folder then can be served via a traditional web server, such as Nginx or Apache, with NodeJS.

How to get browser reload when editing html/jade files in webpack

This is part of the code I have in my webpack.config.js
const common = {
entry: {
style: PATHS.style,
app: PATHS.jsComp
},
output: {
path: PATHS.build,
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.jade$/, loader: "jade" }
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack demo',
template: PATHS.template, // Load a custom template (ejs by default but can be changed)
inject: 'body' // Inject all scripts into the body (this is the default so you can skip it)
})
]
};
I'm showing you the section where I think the problem is, I don't think you needed the entire code, but it's basically a slightly modified version of this git file
When I run webpack-dev-server everything works. JavaScript and SASS changes do reload the browser but when I change by jade file, the browser doesn't reload. In the terminal I have tried running these variations of webpack-dev-server
webpack-dev-server
webpack-dev-server --inline --hot
webpack-dev-server --inline --hot --colors
webpack-dev-server --inline --hot --colors --content-base app
webpack-dev-server --inline --hot --colors --content-base app --host 0.0.0.0
webpack-dev-server --inline --hot --colors --host 0.0.0.0
None of the above 6 variations get the browser reloading when I made changes to the jade file.
Also, if I remember correctly, after every tutorial I have went through (before finding this setup), the browser never reloaded on html changes.
Is there anything else that I need to install (globaly or otherwise)
According to the documentation, you should do 3 things:
add an entry point to the webpack configuration: webpack/hot/dev-server.
add the new webpack.HotModuleReplacementPlugin() to the webpack configuration.
add hot: true to the webpack-dev-server configuration to enable HMR on the server.
As far as I see, I think the only thing of those 3 that you already did is using the hot: true. I use to run the webpack-dev-server via Gulp, so my configurations stay in a JS object, not in command line arguments.
Try this:
import webpack from 'webpack';
const common = {
entry: {
style: PATHS.style,
app: [
PATHS.jsComp,
'webpack/hot/dev-server', `webpack-dev-server/client?http://localhost:3000`
]
},
output: {
path: PATHS.build,
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.jade$/, loader: "jade" }
]
},
devServer: {
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: 'Webpack demo',
template: PATHS.template, // Load a custom template (ejs by default but can be changed)
inject: 'body' // Inject all scripts into the body (this is the default so you can skip it)
})
]
};
Edit:
After discussing in the comments, I think that my answer does not address your case. I thought you were trying to reload the application after some Jade module (say, some AngularJS component were using import with jade-loader to load a Javascript template string from a Jade file) were changed. But as it turns out, you are trying to reload when your base HTML (well, Jade, but the base of your page) file is changed.
If you are using some modern Javascript framework (React, Angular etc), chances are your base HTML will be very small, and the real thing happens in Javascript.
Anyway, I don't think that your need is currently supported neither by webpack-dev-server or html-webpack-plugin.

Webpack-dev-server not bundling even after showing bundle valid message

I've set up a basic react application with webpack but I couldn't get the webpack-dev-server running properly.
I've installed webpack-dev-server globally and tried running the command sudo webpack-dev-server --hot as hot reloading was required.
The project seems to be working fine with just webpack cmd. It builds into my build folder and I can get it working via some server but it wont work with webpack-dev-server. From terminal its clear that the build process has completed with no error being thrown [webpack: bundle is now VALID.] and its in fact watching properly because on any change it does trigger the build process but it doesn't really gets built [it doesn't serve my bundle.js]. I tried changing the entire config and still couldn't get the issue resolved.
It would be much appreciated if someone can help.
Following is my webpack.config.js file.
var path = require('path');
module.exports = {
devtool: '#inline-source-map"',
watch: true,
colors: true,
progress: true,
module: {
loaders: [{
loader: "babel",
include: [
path.resolve(__dirname, "src"),
],
test: /\.jsx?$/,
query: {
plugins: ['transform-runtime'],
presets: ['es2015', 'react', 'stage-0'],
}
}, {
loader: 'style!css!sass',
include: path.join(__dirname, 'src'),
test: /\.scss$/
}]
},
plugins: [],
output: {
path: path.join(__dirname, 'build/js'),
publicPath: '/build/',
filename: '[name].js'
},
entry: {
bundle: [
'./src/index.js'
]
},
devServer: {
contentBase: "./",
inline: true,
port: 8080
},
};
I've got the issue resolved by myself. Silly as it sounds but the issue was with the publicPath under the output object. It should match the path property instead of just /build/, i.e.,
output: {
path: path.join(__dirname, 'build/js'),
publicPath: '/build/js', // instead of publicPath: '/build/'
filename: '[name].js'
},
In my case I had to check where the webpack is serving the file.
You can see it:
http://localhost:8080/webpack-dev-server
Then I could see my bundle.js path > http://localhost:8080/dist/bundle.js
After that I used that /dist/bundle.js in my index.html <script src="/dist/bundle.js"></script>
Now it refreshes any file changes.
webpack-dev-server serves your bundle.js from memory. It won't generate the file when you run it. So bundle.js is not present as a file in this scenario.
If you wan't to use bundle.js, for example to optimize it's size or test your production deployment, generate it with webpack using the webpack command and serve it in production mode.
By the way, started from Webpack v4 it is possible to write in-memory assets to disk:
devServer: {
contentBase: "./",
port: 8080,
writeToDisk: true
}
See doc.
The fix to this for me was:
devServer: {
devMiddleware: {
writeToDisk: true,
}
}
https://webpack.js.org/configuration/dev-server/#devserverwritetodisk-
Dev-server keeps the compiled file in memory, and I can't access it directly...
BUT! THE SOLUTION is that you have no need to access it, in development(even when you run your project on node server or localhost) use localhost:8080 to access your page, and that's where webpack-dev-server is serving your page and you can feel all advantages of using it(live reload!), BUT! it doesn't compile your bundle.js, SO! before production, you need to manually run webpack build command, and that's it! there is no way for dev-server to compile your bundle.js file! Good Luck!
Please refer to this - https://github.com/webpack/webpack-dev-server/issues/24
According to this, webpack dev server no longer writes to the disk. So, you wont be able to see the build file. Instead, it will be served from the memory. To get it working you have to set the proper path.
Example: in index.html loads the build file from '/dist/main.js' so in webpack config file set the output.publicPath to '/dist/'
In my case I was using VS Code and I had to restart it. I have noticed that vs code bugs up sometimes. The changes you make in configuration files (package.json, webpack.config.js etc.) do not take effect sometimes. So, in case you encounter a situation where something doesn't work even with the correct settings, just restart vs code.
I can't see any bug reports related to this. So that's strange. I'm thinking this bug is triggered if you change one of the configuration files some time later after you have already built the project multiple times.
If this is actually what's happening then it's a huge bug. I'll try switching to Visual Studio instead of Code and see if this still happens. If it does then it's probably an issue with webpack.

Categories