Webpacker asset digest not consistent from deploy to deploy - ruby-on-rails

I'm using Rails with Webpacker. When I deploy, my CSS asset digest (the digest on the files in public/packs/css) is not the same from deploy to deploy.
Not only is the digest not the same from deploy to deploy, but sometimes (although not always) the digest is different from machine to machine.
How can I troubleshoot this issue? I thought I could maybe gain some insight by learning exactly how the digest is generated, but I haven't been able to find any good information on that so far.

Found the solution, thanks to these two GitHub issues.
Step 1: run yarn add webpack-merge.
Step 2: change config/webpack/environment.js to match the following:
const { environment } = require('#rails/webpacker')
const { merge } = require('webpack-merge');
const sassLoader = environment.loaders.get('sass')
const cssLoader = environment.loaders.get('css')
sassLoader.use.map(loader => {
if (loader.loader === 'css-loader') {
loader.options = merge(loader.options, { sourceMap: false })
}
});
cssLoader.use.map(loader => {
if (loader.loader === 'css-loader') {
loader.options = merge(loader.options, { sourceMap: false })
}
});
module.exports = environment
Afterward, the CSS digests should be deterministic.

Related

remoteEntry.js not found on Jenkins

We are deploying a remote app written in NextJS and Typescript; The host app is in React only.
Currently the host app gets a 404 not found error as the remote app runs into this error in the Build Snapshot on Jenkins
+ ls ./dist/static/chunks/remoteEntry.js
ls: cannot access './dist/static/chunks/remoteEntry.js': No such file or directory
script returned exit code 2
However, the file is generated locally and both apps are able to spin up in local environment.
Here is our next.config.js:
const NextFederationPlugin = require('#module-federation/nextjs-mf');
const { exposedModules } = require('./lib/routes');
const version = process.env.VERSION_OVERRIDE || require('./package.json').version;
const deps = require('./package.json').dependencies;
// Note: This path needs to match with what's specified in CIRRUS_FRONTEND_ENTRYPOINT for www.
const assetBasePath = process.env.CDN_PATH ? `${process.env.CDN_PATH}${version}` : process.env.ASSET_BASE_PATH;
// Note: Heavily references module federation example meant for omnidirectional federation between Next apps.
// Changes mostly around path resolution due to our current resolution pattern via cdn
// https://github.com/module-federation/module-federation-examples/blob/master/nextjs/home/next.config.js
module.exports = {
webpack(config, options) {
Object.assign(config.experiments, { topLevelAwait: true });
// Integrated mode calls `next build` which has minimization by default. For local development, this is unnecessary.
if (process.env.NEXT_PUBLIC_ENVIRONMENT === 'INTEGRATED') {
config.optimization.minimize = false;
}
if (!options.isServer) {
console.log("Not Server");
config.output.publicPath = 'auto';
config.plugins.push(
new NextFederationPlugin({
name: 'cirrus',
filename: 'static/chunks/remoteEntry.js',
exposes: {
'./FederatedRouter': './lib/FederatedRouter',
...exposedModules
},
remoteType: 'var',
remotes: {},
shared: {
'#transcriptic/amino': {
requiredVersion: deps['#transcriptic/amino'],
singleton: true
},
react: {
requiredVersion: deps.react,
singleton: true
},
'react-dom': {
requiredVersion: deps['react-dom'],
singleton: true
},
'#strateos/micro-apps-utils': {
requiredVersion: deps['#strateos/micro-apps-utils'],
singleton: true
}
},
extraOptions: {
// We need to override the default module sharing behavior of this plugin as that assumes a nextjs host
// and thus next modules will be provided by the parent application.
// However, web is currently NOT a nextjs application so this child application so that assumption is
// invalid. Note that this means we need to ensure we explicitly specify common modules such as `react`
// in the `shared` key above.
skipSharingNextInternals: true
}
})
);
} else {
console.log("Is Server");
}
return config;
},
// Note: Annoyingly, NextJS automatically automatically appends a `_next` directory for assetPrefix
// but NOT public path so we'll have to manually include it here.
publicPath: `${assetBasePath}/_next/`,
// Note: If serving assets via CDN, assetPrefix is required to help resolve static assets.
// Also, NextJS automatically appends and expects a `_next` directory to the assetPrefix path.
// See https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix
assetPrefix: process.env.CDN_PATH ? assetBasePath : undefined,
distDir: 'dist',
// Use index react-router as fallback for resolving any pages that are not directly specified
async rewrites() {
return {
fallback: [
{
source: '/:path*',
destination: '/'
}
]
};
}
};
Tried upgrade NextJS from 12.1.6 to 12.2.2
Tried upgrade Webpack from 5.74.0 to 5.75.0
Cleaned cache by sh 'yarn cache clean'
Tried clear env by sh 'env -i PATH=$PATH make build-snapshot'
Hash of node modules by tar -cf - node_modules | md5sum
Downgraded "#module-federation/nextjs-mf" from 5.12.9 to 5.10.5
Verified file writing permission

Workbox precache doesn't precache

I'm attempting to implement workbox to precache image and video assets on a website.
I've created a service worker file. It appears to be successfully referenced and used in my application. The service worker:
import { clientsClaim, setCacheNameDetails } from 'workbox-core';
import { precacheAndRoute, addRoute } from 'workbox-precaching';
const context = self; // eslint-disable-line no-restricted-globals
setCacheNameDetails({ precache: 'app' });
// eslint-disable-next-line no-restricted-globals, no-underscore-dangle
precacheAndRoute(self.__WB_MANIFEST);
context.addEventListener('install', (event) => {
event.waitUntil(
caches.open('dive').then((cache) => {
console.log(cache);
}),
);
});
context.addEventListener('activate', (event) => {
console.log('sw active');
});
context.addEventListener('fetch', async (event) => {
console.log(event.request.url);
});
context.addEventListener('message', ({ data }) => {
const { type, payload } = data;
if (type === 'cache') {
payload.forEach((url) => {
addRoute(url);
});
const manifest = payload.map((url) => (
{
url,
revision: null,
}
));
console.log('attempting to precache and route manifest', JSON.stringify(manifest));
precacheAndRoute(manifest);
}
});
context.skipWaiting();
clientsClaim();
The application uses workbox-window to load, reference and message the service worker. The app looks like:
import { Workbox } from 'workbox-window';
workbox = new Workbox('/sw.js');
workbox.register();
workbox.messageSW({
type: 'cache',
payload: [
{ url: 'https://media0.giphy.com/media/Ju7l5y9osyymQ/giphy.gif' }
],
});
This project is using vue with vue-cli. It has a webpack config which allows plugins to be sent added to webpack. The config looks like:
const { InjectManifest } = require('workbox-webpack-plugin');
const path = require('path');
module.exports = {
configureWebpack: {
plugins: [
new InjectManifest({
swSrc: path.join(__dirname, 'lib/services/Asset-Cache.serviceworker.js'),
swDest: 'Asset-Cache.serviceworker.js',
}),
],
},
};
I can see messages are successfully sent to the service worker and contain the correct payload. BUT, the assets never show up in Chrome dev tools cache storage. I also never see any workbox logging related to the assets sent via messageSW. I've also tested by disabling my internet, the assets don't appear to be loading into the cache. What am I doing wrong here?
I found the workbox docs to be a little unclear and have also tried to delete the message event handler from the service worker. Then, send messages to the service worker like this:
workbox.messageSW({
type: 'CACHE_URLS',
payload: { urlsToCache: [ 'https://media0.giphy.com/media/Ju7l5y9osyymQ/giphy.gif'] },
});
This also appears to have no effect on the cache.
The precache portion of precacheAndRoute() works by adding install and activate listeners to the current service worker, taking advantage of the service worker lifecycle. This will effectively be a no-op if the service worker has already finished installing and activating by the time it's called, which may be the case if you trigger it conditionally via a message handler.
We should probably warn folks about this ineffective usage of precacheAndRoute() in our Workbox development builds; I've filed an issue to track that.

How to configure webpack devServer port?

I'm trying to use webpack in my Symfony app in docker, but I'm still getting error:
GET http://localhost:8000/sockjs-node/info?t=1556798329924 404 (Not Found)
Everything works fine axcepts this error...
App is running on port 8000 and node on port 8081. The address with 8081 port is accessible, but how can I tell webpack to use port 8081 with devServer?
Here is my webpack.config.js:
const Encore = require('#symfony/webpack-encore');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const outputPath = './public/build/';
const publicPath = '/build';
Encore
.setOutputPath(outputPath)
.setPublicPath(publicPath)
// Clean output dir before build
.cleanupOutputBeforeBuild()
.splitEntryChunks()
.enableSingleRuntimeChunk()
// uncomment if you're having problems with a jQuery plugin
.autoProvidejQuery()
// Generate JS files
.addEntry('loader', './assets/javascript/loader.js')
.addEntry('admin-loader', './assets/javascript/admin.js')
// Generate CSS files
.addStyleEntry('forms', './assets/styles/forms.scss')
.addStyleEntry('grid', './assets/styles/grid.scss')
.addStyleEntry('reboot', './assets/styles/reboot.scss')
.addStyleEntry('styles', './assets/styles/styles.scss')
.addStyleEntry('utilities', './assets/styles/utilities.scss')
.addStyleEntry('admin', './assets/styles/admin.scss')
.enableEslintLoader()
.configureTerserPlugin((options) => {
options.cache = true;
options.parallel = true;
options.terserOptions = {
output: {
comments: false,
}
}
})
.configureSplitChunks((options) => {
options.chunks = 'all',
options.maxInitialRequests = Infinity,
options.cacheGroups = {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `pckg.${packageName.replace('#', '')}`;
},
}
}
})
// Enable SASS loader with PostCSS config
.enableSassLoader()
.enablePostCssLoader()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
// CSS Hot Loader for HMR in webpack dev server
.addLoader({
enforce: 'post',
test: /\.(s?c|sa)ss$/,
exclude: /node_modules/,
loader: 'css-hot-loader',
})
.addPlugin(new StyleLintPlugin({
lintDirtyModulesOnly: Encore.isProduction(),
context: './assets/styles/',
quiet: false,
}));
const config = Encore.getWebpackConfig();
// Export settings and generate files
module.exports = config;
Does anyone know?

Rails Webpacker internal assets aren't proxied to action_controller.asset_host

I am using Rails 5.1 and Webpacker. It all works fine. I am using images inside my react components that are in /app/javascripts/images, and I import them fine into my components and all is well on development.
However - when I deploy, the internal images are now served directly from my site url (with their webpacker compiled paths, but the problem is I have set up my production environment to use an asset_host - so Rails is correctly prefixing all my other usual Rails image assets (including my react JS packs) - so they are all being served correctly from my asset_host.
JUST the internal images that are in /app/javascripts/images that I imported into my react components - they AREN'T being proxied through the asset_host. (it works fine in development - as I don't use an asset_host there).
How can I let webpacker know, that I need the images proxied through an asset_host on production?
You can add an environment argument "process.env.WEBPACKER_ASSET_HOST" to /config/webpack/production.js file.
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
process.env.WEBPACKER_ASSET_HOST = process.env.WEBPACKER_ASSET_HOST || '/yourpath/'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
i am working in rails6. hope this can help someone
I managed top set up my config/webpack/environment.js file up like this:
const { environment } = require('#rails/webpacker')
const env = process.env.NODE_ENV || 'development'
const { resolve } = require('path')
const { safeLoad } = require('js-yaml')
const { readFileSync } = require('fs')
const filePath = resolve('config', 'webpacker.yml')
const appConfig = safeLoad(readFileSync(filePath), 'utf8')[env]
const config = appConfig
const removeOuterSlashes = string =>
string.replace(/^\/*/, '').replace(/\/*$/, '')
const formatPublicPath = (host = '', path = '') => {
let formattedHost = removeOuterSlashes(host)
if (formattedHost && !/^http/i.test(formattedHost)) {
formattedHost = `//${formattedHost}`
}
const formattedPath = removeOuterSlashes(path)
return `${formattedHost}/${formattedPath}/`
}
const fileLoader = environment.loaders.get('file')
fileLoader.use[0].options.publicPath = formatPublicPath(process.env.WEBPACKER_ASSET_HOST, config.public_output_path)
module.exports = environment
According to the following comment here: https://github.com/rails/webpacker/issues/1186#issuecomment-358110765

Webpack2 cannot resolve: config.context absolute path

I am trying to run my webpack config file (see below), but I am still getting certain type of errors that reffers to a paths I use in my webpack settings:
- config.context
- config.module.rules
- config.output
My idea was, that I set up my config.context path absolutely (as it is written in docs), otherwise my webpack.config files reffers to node_modules in parents directory. But still, when I run webpack -w --env.dev command, it throws following errors:
It seems to me, that config.context cant handle absolute path as it should. Any help how to set up paths correctly? Thank you!
My webpack.config.js:
var path = require('path');
var webpack = require('webpack');
var ExtractText = require('extract-text-webpack-plugin');
module.exports = function (env) {
var project = {
env: env.prod ? 'prod' : 'dev',
jsBase: './routesMap/',
cssBase: './src/css/'
}
var config = {
context: path.resolve(__dirname),
entry: {
'routesMap': project.jsBase + 'main.js'
},
output: {
path: path.join(__dirname, '/dist'),
filename: '[name].js'
},
plugins: [
new ExtractText({
filename: 'styles.min.css',
disable: false,
allChunks: true
})
],
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: path.join(__dirname, '/routesMap'),
exclude: /node_modules/,
query: {
cacheDirectory: true,
presets: ['es2015'],
plugins: ["transform-runtime"]
}
}
]
}
};
return config;
}
That's an issue with the latest webpack version. Try using uppercase drive letters in shell, e.g. C:/ instead c:/.
More info https://github.com/webpack/webpack/issues/4530.
I uninstalled latest webpack version (I had webpack 2.3.0) and installed version of 2.2.0, problem solved! As #zemirco stated in his answer, it has something to do with casesensitive letters in absolute path. Unfortunatelly changing small letter to big one doesnt help for me, so I just changed webpack version.

Resources