Rails Webpacker internal assets aren't proxied to action_controller.asset_host - ruby-on-rails

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

Related

"process is not defined" in gatsby-browser?

I'm trying to insert an environment variable "GATSBY_EX_ENV" inside a window property to make it available to external scripts.
and GATSBY_EX_ENV is defined in my environment files alongside numerous other env variables which are accessed at runtime within React components. So I know it's declared correctly and that our env variables are generally working.
Right now in my gatsby-browser.js I am doing this:
export const onClientEntry = () => {
window.onload = () => {
var EXT = location.host
.split('.')
.filter(x => x !== 'www' && x && x !== DOMAIN && x !== 'com')[0];
// additional parsing
window._external.properties = { env: EXT };
}
}
This does work, but it's clunky and brittle. I want to replace that URL parsing with:
window._external.properties = { env: process.env.GATSBY_EX_ENV }
But when I do, my local develop site fails to load with the error "process is not defined". Should I be placing this code in a different method?

Webpacker asset digest not consistent from deploy to deploy

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.

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?

How to use Material Components for the web with Webpacker

Rails 5.2.1 webpacker4.0
app/javascript/application.scss
#import "material-components-web/material-components-web";
I use webpacker with the default setting, but it does not work well.
What additional settings do I need?
I want to use material-components-web in webpacker
err
Module build failed:
#import "#material/base/mixins";
File to import not found or unreadable: #material/base/mixins.
webpack/environment.js
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const vue = require('./loaders/vue')
environment.plugins.append('VueLoaderPlugin', new VueLoaderPlugin())
environment.loaders.append('vue', vue)
module.exports = environment

RSpec + jasmine-node

I was wondering if anyone has already solved a problem I am experiencing.
I am currently working on a project with processes split between Rails and a Nodejs process. I use RSpec for Rails based testing and jasmine-node for the Node counterpart.
My situation is that both the Rails and Node apps access the same database. My problem is that RSpec makes it trivial in setting a TEST database, seeding it with some values and running tests against these but jasmine-node has no such mechanism.
Instead, my jasmine-node database related tests have no mechanism to setup a TEST database so my jasmine-node tests instead rely on my DEVELOPMENT database. To me this is both wrong and bad as these tests are not repeatable.
Currently my jasmine-node tests are in a separate directory and outside of Rails' RSpec spec folder (and run via the jasmine-node command line). Recently I learnt that RSpec can drive jasmine client javascript tests. Does a similar mechanism exist to drive jasmine-node tests from RPsec?
Has anyone else encountered this problem? If so, I am very eager to hear how you've approached and tackled this problem.
My thanks and gratitude in advance.
My solution was to use an environment variable TEST to specify whether to use the test or development database. This example, which uses Sequelize, works with test and dev databases on both the local machine and Heroku.
var config = {}
if (typeof (process.env.DATABASE_URL) != 'undefined') {
var url = require('url');
var dbUrl;
if (process.env.TEST == 'true') {
dbUrl = url.parse(process.env.HEROKU_POSTGRESQL_NAVY_URL);
}
else {
dbUrl = url.parse(process.env.DATABASE_URL);
}
var authArr = dbUrl.auth.split(':');
config.database = dbUrl.path.substring(1);
config.username = authArr[0];
config.password = authArr[1];
config.host = dbUrl.hostname;
config.port = dbUrl.port;
config.dialect = 'postgres';
config.protocol = 'postgres';
config.importBatchSize = 1000;
}
else {
if (process.env.TEST == 'true') {
console.log('DB: test');
config.database = 'test';
config.username = 'postgres';
config.password = 'postgres';
config.host = '127.0.0.1';
config.port = 5432;
config.dialect = 'postgres';
config.protocol = 'tcp';
config.importBatchSize = 1000;
}
else {
console.log('DB: db');
config.database = 'db';
config.username = 'postgres';
config.password = 'postgres';
config.host = '127.0.0.1';
config.port = 5432;
config.dialect = 'postgres';
config.protocol = 'tcp';
config.importBatchSize = 1000;
}
}
config.logging = false;
//config.logging = console.log;
module.exports = config;
Then, each jasmine-node test that accesses the database does a require on a file containing the following bootstrapping code:
process.env['TEST'] = 'true'; // Use test database
var config = require('../config/config.js');
process.env['TEST'] = 'false'; // Stop using test database after this
var sequelizeSingleton = require("../classes/model.js");
sequelizeSingleton.setup(path.normalize(__dirname + '/../models'), config.database, config.username, config.password,
{ dialect: config.dialect, protocol: config.protocol, host: config.host, port: config.port, omitNull: true, logging: config.logging });

Resources