Open jega-ms opened 4 years ago
my file-loader ended up looking like
{
test: /\.(png|svg|jpe?g|gif|webm)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[hash]-[name].[ext]',
outputPath: 'static',
publicPath: '../static',
},
},
],
}
After landing on this issue and doing some of my own trial and error, I've come up with a solution that works both in dev and packaged, and combines various observations from this and linked issues.
If you're using webpack5 (and you should be if you used the template), you no longer need file-loader
or copy-webpack-plugin
. Instead, you should rely on webpack's native asset management.
// webpack.rules.js
// Or in both your webpack.renderer.js and webpack.main.js if you keep them completely separate
module.exports = [
// ... your other rules
{
// Note: I dont have `svg` here because I run my .svg through the `@svgr/webpack` loader,
// but you can add it if you have no special requirements
test: /\.(gif|icns|ico|jpg|png|otf|eot|woff|woff2|ttf)$/,
type: 'asset/resource',
},
]
Because Electron does magical things to an image based on the filename of the image, you should keep the original source file name in the output file name by configuring how webpack outputs asset names. Note that you can also do this for the renderer config if you prefer, but it's not a requirement since Chromium doesn't do anything magical based on the file name.
// `webpack.main.js`
module.exports = {
entry: './src/main/index.ts',
// ...other configs
output: {
// [file] is the key thing here. [query] and [fragment] are optional
assetModuleFilename: '[file][query][fragment]',
},
}
When using bundled files in the main process, make sure to resolve the bundled path:
// main/index.ts
import path from 'path';
import dockImagePath from './path/to/dockImage.png';
import trayImagePath from './path/to/trayImage.png';
// For example setting the dock icon
app.dock.setIcon(path.resolve(__dirname, dockImagePath));
// Or setting the tray icon
const tray = new Tray(
path.resolve(
__dirname,
trayImagePath,
),
);
Because you can have many entrypoints, the webpack output of each entry point is nested under a renderer
folder. By default, webpack outputs assets in the root output folder (which will be this same renderer
folder). So your output file structure (inside of the .webpack
folder) looks something like
> main
- index.js
> renderer
> entryPointOne
- index.js
> entryPointTwo
- index.js
- imageHash1.png
- imageHash2.png
This means that in the outputted renderer/entryPointOne/index.js
, we need to rewrite the image paths to one level above the index.js: ../
. Note that this is essentially the same solve as the publicPath: '..'
setting in file-loader
from this and other related issues
// webpack.renderer.config.js
module.exports = {
// ...other configs
output: {
publicPath: '../',
},
}
Note that, like the main webpack config, you may change where webpack outputs its assets, but you still have to set the public path since it's outside of folder containing the index.js
of each renderer entry point.
// webpack.renderer.config.js
module.exports = {
// ...other configs
output: {
publicPath: '../myAssetFolder',
// Note that here you are also keeping the original file name, but for the renderer you don't have to
// and can simply rely on the default value of [hash][ext][query]
assetModuleFilename: 'myAssetFolder/[file][query]',
},
}
Here's a caveman solution, for my fellow Neanderthals.
Base64-encode the image. Here's a handy tool for it: https://github.com/veler/DevToys
Create a file like images.ts
/images.js
:
// TODO: Figure out how to use file-loader or Webpack Asset Modules later. (If you're reading this two years later, congrats! Your temporary solution became permanent.)
export const iconImage512x512Base64 = "data:image/png;base64,iVBORw0KGgoAAAA(etc. etc.)=";
Then in your TSX
/JSX
:
{/* Don't worry. This is fine. */}
<img src={iconImage512x512Base64} />
I was able to to copy my assets into the development build with the help of copy-webpack-plugin
.
Here's steps for my webpack forge .ts based project
To set up copy-webpack-plugin
in your webpack.renderer.config.ts
for copying assets to your app root folder, follow these steps:
copy-webpack-plugin
npm install copy-webpack-plugin --save-dev
yarn add copy-webpack-plugin --dev
copy-webpack-plugin
in Your Webpack Configuration: At the top of your webpack.renderer.config.ts
, add an import.
import CopyPlugin from 'copy-webpack-plugin';
CopyPlugin
to your Plugins Array at webpack.plugins.ts
. Adjust the from path according to where your assets are stored relative to the context of your webpack configuration.new CopyPlugin({
patterns: [
{ from: 'src/assets/', to: 'assets/' },
],
}),
Also don't forget to install file-loader
npm install file-loader --save-dev
Then add to index.html
or to whatever your interface is called
<img src="./assets/images/logo.png" alt="Logo" class="logo-img">
Content of my webpack config files:
// webpack.plugins.ts
import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import CopyPlugin from 'copy-webpack-plugin';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
export const plugins = [
new ForkTsCheckerWebpackPlugin({
logger: 'webpack-infrastructure',
}),
new CopyPlugin({
patterns: [
{ from: 'src/assets/', to: 'assets/' },
],
}),
];
//webpack.main.config.ts
import type { Configuration } from 'webpack';
import { rules } from './webpack.rules';
import { plugins } from './webpack.plugins';
export const mainConfig: Configuration = {
entry: './src/index.ts',
module: {
rules,
},
plugins,
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'],
},
output: {
assetModuleFilename: '[file][query][fragment]',
},
};
//webpack.renderer.config.ts
import type { Configuration } from 'webpack';
import { rules } from './webpack.rules';
import { plugins } from './webpack.plugins';
rules.push({
test: /\.css$/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});
rules.push({
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
// options: {
// name: 'assets/images/[name].[ext]',
// },
},
],
});
export const rendererConfig: Configuration = {
module: {
rules,
},
plugins,
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'],
},
output: {
publicPath: '../',
},
};
// webpack.rules.ts
import type { ModuleOptions } from 'webpack';
export const rules: Required<ModuleOptions>['rules'] = [
// Add support for native node modules
{
// We're specifying native_modules in the test because the asset relocator loader generates a
// "fake" .node file which is really a cjs file.
test: /native_modules[/\\].+\.node$/,
use: 'node-loader',
},
{
test: /[/\\]node_modules[/\\].+\.(m?js|node)$/,
parser: { amd: false },
use: {
loader: '@vercel/webpack-asset-relocator-loader',
options: {
outputAssetBase: 'native_modules',
},
},
},
{
test: /\.tsx?$/,
exclude: /(node_modules|\.webpack)/,
use: {
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
},
{
test: /\.(gif|icns|ico|jpg|png|otf|eot|woff|woff2|ttf)$/,
type: 'asset/resource',
},
];
Hope this helps someone related
Assets are not getting loaded after making the binary using "yarn make" on all platform (windows/Mac/Linux). We are getting error "net::ERR_FILE_NOT_FOUND". Looks like Js files are pointing to the proper location as per the package.json configuration. but assets and source files are located in different location.
And we are seeing the same issue for component source which uses Lazy import statements
E.g on Mac
package.json
Sample Project https://github.com/jega-ms/electron-forge-react-typescript-webpack
File Listing