How do I compile a preload script w/ webpack in Electron? - javascript

Electron 1.6.5, Webpack 2.4.1
I'm using electron-react-boilerplate with a webview component. I inject a preload script into the webview that does something like this:
const { ipcRenderer } = require('electron');
const doSomething = require('./utils/do-some-thing.js');
document.addEventListener('DOMContentLoaded', event => {
doSomeThing()
// tell scraper to get started
ipcRenderer.sendToHost('THING IS DONE', [{ url: document.URL }]);
});
webview needs this script passed as a file:// path like so:
<webview
preload={'./some/folder/preload.js''}
{...props}
/>
The problem is that my webpack setup doesn't transpile preload.js because it isn't explicitly called via require(). Then, when I build the app, the path ./some/folder/ doesn't exist.
I've tried setting webpack to create a second compiled script like so:
entry: [
'babel-polyfill',
'./app/index',
'./some/folder/preload.js'
],
output: {
path: path.join(__dirname, 'app/dist'),
publicPath: '../dist/'
},
But this leads to a JavaScript heap out of memory error, which leads me to believe this isn't correct.
Also: wouldn't this approach duplicate electron in the ./dist folder since it's require()'d by both preload.js and index.js ?

You could use the electron-main and electron-preload configurations of webpack:
const path = require('path');
module.exports = [
{
entry: './src/index.js',
target: 'electron-main',
output: {
path: path.join(__dirname, 'dist'),
filename: 'index.bundled.js'
},
node: {
__dirname: false,
}
},
{
entry: './src/preload.js',
target: 'electron-preload',
output: {
path: path.join(__dirname, 'dist'),
filename: 'preload.bundled.js'
}
},
]
You get two bundles after building, but Electron is not in either of them, so no duplicates.
Notice the __dirname: false, which is required since otherwise webpack replaces __dirname always replaced by / by webpack, leading to an unexpected behaviour in almost all cases (see here for further information, should be false by default, but wasn't for me).

We had a similar issue where we had several preload scripts instead of one. Our solution was to use the CopyPlugin. So for us, the config looks like this:
const CopyPlugin = require("copy-webpack-plugin");
plugins.push(new CopyPlugin([{ from: "src/container-with-scripts/", to: "preloadScripts/" }]));
module.exports = {
module: { rules }, // imported from elsewhere
target: "electron-renderer",
node: { global: true },
plugins,
resolve: {
extensions: [".js", ".ts", ".jsx", ".tsx", ".css", ".scss"]
}
};
So we just take the folder containing all our scripts and copy them to the autogenerated .webpack folder.

I was having the same problem and here's solution that worked for me:
Electron-webpack documentation now contains example how to add additional entries.
"electronWebpack": {
"main": {
"extraEntries": ["#/preload.js"]
}
}
Note that you can use the # alias to reference your main.sourceDirectory
in this case it's path to your main process script which will be src/main by default
This adds preload script from src/main/preload.js into webpack entries.
Then you need to add preload script into window with
new BrowserWindow({
webPreferences: {
nodeIntegration: true,
preload: path.resolve(path.join(__dirname, "preload.js")),
}
})
That's all. __dirname was giving me relative path and electron requires absolute path for preload script so it was failing. path.resolve will get you absolute path needed to make it work.

Electron provides an option to load a script before any other execution in your DOM.
You have to provide your pre-load script file path while creating a browser window and file path of the script should be the absolute.
You can find reference here

Related

React import images from public folder

I'm trying to get static images fro my public directory but is not being found. I'm not using CRA, so maybe is some configuration with Webpack that I'm missing. Using file-loader module and importing the image works on Dev Mode, but doesn't work in for my production server specification
My Project structure:
\public
\static
\images
image.png
\src
\component
component.js
...
package.json
webpack.common.js
webpack.dev.js
webpack.prod.js
On component.js, I want to get image.jpg on static/images folder like this:
<img src='/static/images/image.png'></img>
But I'm getting a 404 not found.
My webpack.commom.js:
const CleanWebPackPlugin = require('clean-webpack-plugin')
const HtmlWebPackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
entry: {
main: './src/index.js'
},
output: {
filename: '[name].[hash].js',
path: path.resolve('./dist'),
publicPath: "/"
},
module:{
rules:[
{
test: /\.js$/,
exclude: ['/node_modules'],
use: [{ loader: 'babel-loader'}],
},
{
test: /\.s?(a|c)ss$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
},{
loader: 'sass-loader'
}]
},
{
test: /\.(png|jpe?g|gif)$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
]
},
plugins: [
new HtmlWebPackPlugin({
template: 'index.html'
}),
new CleanWebPackPlugin(),
],
}
And the Dev version:
module.exports = merge(common, {
mode: 'development',
devServer: {
host: 'localhost',
port: 3000,
open: true,
historyApiFallback: true,
publicPath: "/",
}
})
Thank you in advance.
I assume you want to display such image more than once. In that case, is annoying to keep writing something like '%PUBLIC_URL%/img/static/images/image.png' or {process.env.PUBLIC_URL + '/img/static/images/image.png'} two, three or more times within your jsx code. Basically, without a plugin is imposible to import images from the public folder if your app is rooted in the src folder. However I did find a solution for me and was quite simple in fact. It was something like this:
import React from 'react'
var path = process.env.PUBLIC_URL;
var image = "/img/static/images/image.png";
and then, within jsx code:
<img src={path + image}/>
it worked for me, hope this is helpful for anyone! :D
You are using file-loader as webpack plugin, which does not work in the way of "just mirroring the directory structure of public to dist". You can find the documentation for that plugin here: https://webpack.js.org/loaders/file-loader/
Basically, what the plugin does is, if you import an image file (actually programatically importing it, not just using a string reference to its path), the file is copied to your dist directory, potentially renamed and than in your compiled source code the proper file name is inserted.
So in your case, if you want to solve your problem using file-loader, you would reference the image file like
// Relative path to image file from js file
import imageFile from './assets/image.png';
// ...
const component = props => <img src={imageFile}></img>;
If you want to use the approach of actually just mirroring a public-directory to the dist directory, you need to use an additional webpack-plugin for that (e.g. https://stackoverflow.com/a/33374807/2692307)
By the way, I assume it works in dev-mode because you are setting '/' as public path, so the development server just serves everything in the root directory as well. That is something different than copying the files to dist however as you are trying to achieve.
Not sure if this help but I'm will give it a try
Try to add public path to your dist folder also something like this
output: {
path: path.resolve(__dirname, "/dist"),
filename: "[name].js",
publicPath: "/dist/"
},

Purpose of Webpack-Manifest-Plugin in Webpack

In official doc says when applying Code Splitting and generating chunk files, if chunk code changes, then the filename of it will change. However index.html which uses the chunk code files can't change the filename in its <script> tag, so in this case the manifest.json which is generated by webpack-manifest-plugin will help mapping [name].js to [name].[hash].js.
But opposing to what the doc says, it seems that every time I run webpack to build my codes, new codes are generated with new hash value in its file(in case something in code changed), and HTML-Webpack-Plugin will autometically inject <script> tag with new name of code's file. This seems that there is no need to use webpack-manifest-plugin, I don't even see where the manifest.json is used.
In case if you are looking for my webpack.config.js:
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
module.exports = {
entry: ["core-js/stable", "regenerator-runtime/runtime", "./src/index.jsx"],
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(`${__dirname}/build`)
},
mode: "none",
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: "/node_modules",
use: ['babel-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HTMLWebpackPlugin({
template: './public/index.html'
}),
new ManifestPlugin({
fileName: 'manifest.json'
})
// need this plugin for SSR?
]
}
What is the detailed usage of webpack-manifest-plugin and manifest.json?
HtmlWebpackPlugin "knows" that your asset bundle.js maps to bundle.some-hash.js because it uses the Webpack's Manifest. This manifest is not emitted though. It's just data that Webpack uses to keep track of how all the modules map to the output bundles.
WebpackManifestPlugin uses Webpack's manifest data data to emit a JSON file (that you can call manifest.json or whatever you want).
Since you are using HtmlWebpackPlugin with the inject: true option (it's the default one), HtmlWebpackPlugin injects your bundle bundle.some-hash.js into your template. So you probably have no need to use WebpackManifestPlugin for your application.
If you did not use HtmlWebpackPlugin, or if you used it with inject: false, then you would need to find a way to "manually" inject the assets generated by Webpack.
So, the manifest.json is not for Webpack. It's for you.
Let's say for example that you have a Python Flask web application and your web pages are built with Jinja templates. You could use Webpack to generate all of your static assets, and use the manifest.json to resolve the asset generated by Webpack. This Flask extension does just that. This means that in your jinja template you can write this:
<img src="{{ asset_for('images/hamburger.svg') }}" alt="Hamburger">
and get this:
<img src="images/hamburger.d2cb0dda3e8313b990e8dcf5e25d2d0f.svg" alt="Hamburger">
Another use case would be if you want fine control where the bundles are injected into your templates. For that, have a look at this example in the html-webpack-plugin repo.

Pack HTML with html-webpack-plugin without packing any JavaScript

In another question, an accepted answer with 11 upvotes suggests using webpack's html-webpack-plugin to merge HTML+JS+CSS.
Not a single word on how though, so that's my question. I have a documentation file, in HTML. It includes two external CSS files and one javascript file (syntax highlighter). I would like to bundle it all into one HTML file for easy distribution.
I tried to use webpack without packing any JS with following webpack.config.js:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: 'Documentation',
template: "index.html",
filename: 'index_bundle.html'
})
]
};
But that caused an error because webpack requires input and output script path:
Error: 'output.filename' is required, either in config file or as --output-filename
So how do I do this. Is webpack even the right choice?
What's tricky here is that webpack and HtmlWebpackPlugin each have their own entry-output pipeline. You don't care about webpack's entry point and output; you care about HtmlWebpackPlugin's, but in order for HtmlWebpackPlugin to run at all, you have to let webpack do its thing.
There's a clever trick that I stole from https://stackoverflow.com/a/43556756/152891 that you could do here -- tell webpack to output its bundle at index.html, and then tell HtmlWebpackPlugin to output to the same location. Webpack will run first, and then HtmlWebpackPlugin will overwrite its output. You still need to define an entry point for webpack, but this can be a blank index.js.
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'none',
entry: './src/index.js', // Default value, can omit
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html', // Default value, can omit
inject: false,
minify: false
})
],
output: {
filename: "index.html",
path: path.resolve(__dirname, 'dist')
}
};
You can either keep a blank ./src/index.js file in your repo, or you could define a little inline plugin in your webpack.config.js to create the file on run, and add it to your .gitignore.
plugins: [
{
apply: (compiler) => {
compiler.hooks.beforeRun.tap('NoOpPlugin', () => {
fs.writeFileSync('./src/index.js');
});
}
}
]

Webpack: load js modules dynamically from a URL at runtime

I am currently working on a library which should dynamically load a JavaScript from a remote host and instantiate that.
I am writing the library in TypeScript and my plan is to use Webpack as a bundler.
On another host (remote system) runs a provider which should serve JavaScript code (see here: https://stubs.d-koppenhagen.de/stubs/SimpleStub.js).
The library will dynamically resolve "Identitys" via Webfinger. These Identitys represented by an object and they having a property pointing to a "Stub Provider" which will serve JavaScript code (the link I mentioned before). My library should load this script during runtime (s the library don't know the target for this stubs before) and should use it.
currently my webpack.config.js looks like the following:
var path = require('path');
var webpack = require('webpack');
var WebpackBuildNotifierPlugin = require('webpack-build-notifier');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const PATHS = {
src: path.join(__dirname, './src'),
build: path.join(__dirname, './dist')
};
module.exports = {
entry: {
wonder: PATHS.src + '/wonder'
},
output: {
path: PATHS.build,
filename: '[name].js',
library: 'Wonder',
libraryTarget: 'umd'
},
devtool: 'source-map',
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader'
}
]
},
resolve: {
extensions: ['.ts', '.js']
},
plugins: [
new WebpackBuildNotifierPlugin()
]
};
And here is a part of the library code:
require.ensure([], function() {
require(localMsgStubUrl);
});
When I am now including the bundled library in an example app, I will get the following error:
Error: loading chunk failed
So is there a way to tell webpack not to bundle the code which is required from a external resource loaded from a URL so that I can use that code like it is?
I don't want to tell webpack the URL statically in a config as maybe other stubs I am loading are located on a different target.
Thanks in advance for your help!

WebPack configuration- what is the proper code and what does it mean

I'm learning React and want to understand how web pack is configured for a project.
It would be great if someone can tell me what the following lines of code are doing.
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
function isDirectory(dir) {
return fs.lstatSync(dir).isDirectory()
}
const SubjectsDir = path.join(__dirname, 'subjects')
const SubjectDirs = fs.readdirSync(SubjectsDir).filter(function (dir) {
return isDirectory(path.join(SubjectsDir, dir))
})
module.exports = {
devtool: 'source-map',
entry: SubjectDirs.reduce(function (entries, dir) {
if (fs.existsSync(path.join(SubjectsDir, dir, 'exercise.js')))
entries[dir + '-exercise'] = path.join(SubjectsDir, dir, 'exercise.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'solution.js')))
entries[dir + '-solution'] = path.join(SubjectsDir, dir, 'solution.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'lecture.js')))
entries[dir + '-lecture'] = path.join(SubjectsDir, dir, 'lecture.js')
return entries
}, {
shared: [ 'react', 'react-dom' ]
}),
output: {
path: '__build__',
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '__build__'
},
resolve: {
extensions: [ '', '.js', '.css' ]
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style!css' },
{ test: /\.js$/, exclude: /node_modules|mocha-browser\.js/, loader: 'babel' },
{ test: /\.woff(2)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
{ test: /\.ttf$/, loader: 'file' },
{ test: /\.eot$/, loader: 'file' },
{ test: /\.svg$/, loader: 'file' },
{ test: require.resolve('jquery'), loader: 'expose?jQuery' }
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({ name: 'shared' })
],
devServer: {
quiet: false,
noInfo: false,
historyApiFallback: {
rewrites: [
{ from: /ReduxDataFlow\/exercise.html/,
to: 'ReduxDataFlow\/exercise.html'
}
]
},
stats: {
// Config for minimal console.log mess.
assets: true,
colors: true,
version: true,
hash: true,
timings: true,
chunks: false,
chunkModules: false
}
}
}
This informaiton is coming from a training course, but they do not explain what the lines are doing.
Webpack is what we call a module bundler for JavaScript applications. You can do a whole slew of things with it that help a client browser download and run your code. In the case of React, it helps convert JSX code into plain JS so that the browser can understand it. JSX itself will not run in the browser. We can even use plugins to help minify code, inject HTML, bundle various groups of code together, etc. Now that the introduction to Webpack is out of the way, let's take a look at the code. I will be starting from the very top. Feel free to skip down to #3 if you are only interested in the Webpack configuration object.
The following code will require the modules that are needed in this file. fs is short for "filesystem" and is a module that gives you functions to run that can access the project's filesystem. path is a common module used to resolve or create pathnames to files and is very easy to use! And then we have the webpack module through which we can access webpack specific functions (ie: webpack plugins like webpack.optimize.UglifyJsPlugin).
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
These next few lines first set up a helper function to determine whether or not something in the filesystem being read is a directory.
function isDirectory(dir) {
return fs.lstatSync(dir).isDirectory()
}
const SubjectsDir = path.join(__dirname, 'subjects')
const SubjectDirs = fs.readdirSync(SubjectsDir).filter(function (dir) {
return isDirectory(path.join(SubjectsDir, dir))
})
devtool specifies which developer tool you want to use to help with debugging. Options are listed here : https://webpack.github.io/docs/configuration.html#devtool. This can be very useful in helping you determine exactly which files and lines and columns errors are coming from.
devtool: 'source-map'
These next few lines tell Webpack where to begin bundling your files. These initial files are called entry points. The entry property in a Webpack configuration object should be an object whose keys determine the name of a bundle and values point to a relative path to the entry file or the name of a node_module. You can also pass in an array of files to each entry point. This will cause each of those files to be bundled together into one file under the name specified by the key - ie: react and react-dom will each be parsed and have their outputs bundled under the name shared.
entry: SubjectDirs.reduce(function (entries, dir) {
if (fs.existsSync(path.join(SubjectsDir, dir, 'exercise.js')))
entries[dir + '-exercise'] = path.join(SubjectsDir, dir, 'exercise.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'solution.js')))
entries[dir + '-solution'] = path.join(SubjectsDir, dir, 'solution.js')
if (fs.existsSync(path.join(SubjectsDir, dir, 'lecture.js')))
entries[dir + '-lecture'] = path.join(SubjectsDir, dir, 'lecture.js')
return entries
}, {
shared: [ 'react', 'react-dom' ]
}),
In the reduce function we simply read through the SubjectsDir, determine whether files exercise.js, lecture.js & solution.js exist, and then provide the path to those files as values associated with the key names identified by dir + '-' + filename (ie: myDir-exercise). This may end up looking like the following if only exercise.js exists:
entry : {
'myDir-exercise': 'subjectDir/myDir/exercise.js',
share: ['react', 'react-dom']
}
After we provide entry points to the Webpack configuration object, we must specify where we want Webpack to output the result of bundling those files. This can be specified in the output property.
output: {
path: '__build__',
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '__build__'
},
The path property defines the absolute path to the output directory. In this case we call it __build__.
The filename property defines the output name of each entry point file. Webpack understands that by specifying '[name]' you are referring to the key you assigned to each entry point in the entry property (ie: shared or myDir-exercise).
The chunkFilename property is similar to the filename property but for non-entry chunk files which can be specified by the CommonChunksPlugin (see below). The use of [id] is similar to the use of [name].
The publicPath property defines the public URL to where your files are located, as in the URL from which to access your files through a browser.
The resolve property tells Webpack what how to resolve your files if it can not find them for some reason. There are several properties we can pass here with extensions being one of them. The extensions property tells Webpack which file extensions to try on a file if one is not specified in your code.
resolve: {
extensions: [ '', '.js', '.css' ]
},
For example, let's say we have the following code
const exercise = require('./exercise');
We can leave out the .js because we have provided that string in the resolve property of the webpack configuration and Webpack will try and append .js to this at bundling time to find your file. As of Webpack 2 we also no longer need to specify an empty string as the first element of the resolve property.
The module property tells Webpack how modules within our project will be treated. There are several properties we can add here and I suggest taking a look at the documentation for more details. loaders is a common property to use and with that we can tell Webpack how to parse particular file types within our project. The test property in each loader is simply a Regex that tells Webpack which files to run this loader on. This /\.js$/ for example will run the specified loader on files that end with .js. babel-loader is a widely used JavaScript + ES6 loader. The exclude property tells Webpack which files to not run with the specified loader. The loader property is the name of the loader. As of Webpack 2 we are no longer able to drop the -loader from the string as we see here.
Plugins have a wide range of functions. As mentioned earlier we can use plugins to help minify code or to build chunks that are used across our entire application, like react and react-dom. Here we see the CommonChunksPlugin being used which will bundle the files under the entry name shared together as a chunk so that they can be separated from the rest of the application.
Finally we have the devServer property which specifies certain configurations for the behavior of webpack-dev-server, which is a separate module from webpack. This module can be useful for development in that you can opt out of building your own web server and allow webpack-dev-server to serve your static files. It also does not write the outputs to your filesystem and serves the bundle from a location in memory at the path specified by the publicPath property in the output property of the Webpack configuration object (see #5). This helps make development faster. To use it you would simply run webpack-dev-server in place of webpack. Take a look at the documentation online for more details.
The configuration object that we've taken a look at follows the Webpack 1 standard. There are many changes between Webpack 1 and 2 in terms of configuration syntax that would be important to take a look at. The concepts are still the same, however. Take a look at the documentation for information on migrating to Webpack 2 and for further Webpack 2 details.

Categories

Resources