Skip to content

Commit 241bc03

Browse files
committed
feat: add module exports for preload scripts
1 parent 3d6ba80 commit 241bc03

File tree

16 files changed

+163
-48
lines changed

16 files changed

+163
-48
lines changed

build/esbuild/esbuild.config.base.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const EXTERNAL_BASE = [
3636
'node:crypto',
3737
'node:events',
3838
'node:fs',
39+
'node:module',
3940
'node:os',
4041
'node:path',
4142
'node:stream',
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
dist
2-
*.map
2+
*.map
3+
*.preload.js

packages/electron-chrome-extensions/README.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Multi-tab browser with full support for Chrome extension APIs.
4848
4949
```js
5050
const { app, session, BrowserWindow } = require('electron')
51-
const { ElectronChromeExtensions } = require('electron-chrome-extensions')(
51+
const { ElectronChromeExtensions } = require('electron-chrome-extensions')
5252

5353
app.whenReady().then(() => {
5454
const browserSession = session.fromPartition('persist:custom')
@@ -93,6 +93,34 @@ app.whenReady().then(() => {
9393
})
9494
```
9595

96+
### Packaging the preload script
97+
98+
This module uses a [preload script](https://www.electronjs.org/docs/latest/tutorial/tutorial-preload#what-is-a-preload-script).
99+
When packaging your application, it's required that the preload script is included. This can be
100+
handled in two ways:
101+
102+
1. Include `node_modules` in your packaged app. This allows `electron-chrome-extensions/preload` to
103+
be resolved.
104+
2. In the case of using JavaScript bundlers, you may need to copy the preload script next to your
105+
app's entry point script. You can try using
106+
[copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin),
107+
[vite-plugin-static-copy](https://github.com/sapphi-red/vite-plugin-static-copy),
108+
or [rollup-plugin-copy](https://github.com/vladshcherbin/rollup-plugin-copy) depending on your app's
109+
configuration.
110+
111+
Here's an example for webpack configurations:
112+
113+
```js
114+
module.exports = {
115+
entry: './index.js',
116+
plugins: [
117+
new CopyWebpackPlugin({
118+
patterns: [require.resolve('electron-chrome-extensions/preload')],
119+
}),
120+
],
121+
}
122+
```
123+
96124
## API
97125

98126
### Class: ElectronChromeExtensions
@@ -104,7 +132,6 @@ app.whenReady().then(() => {
104132
- `options` Object
105133
- `license` String - Distribution license compatible with your application. See LICENSE.md for more details. \
106134
Valid options include `GPL-3.0`, `Patron-License-2020-11-19`
107-
- `modulePath` String (optional) - Path to electron-chrome-extensions module files. Might be needed if JavaScript bundlers like Webpack are used in your build process.
108135
- `session` Electron.Session (optional) - Session which should support
109136
Chrome extension APIs. `session.defaultSession` is used by default.
110137
- `createTab(details) => Promise<[Electron.WebContents, Electron.BrowserWindow]>` (optional) -
@@ -224,7 +251,7 @@ To enable the element on a webpage, you must define a preload script which injec
224251
Inject the browserAction API to make the `<browser-action-list>` element accessible in your application.
225252

226253
```js
227-
import { injectBrowserAction } from 'electron-chrome-extensions/dist/browser-action'
254+
import { injectBrowserAction } from 'electron-chrome-extensions/browser-action'
228255

229256
// Inject <browser-action-list> element into our page
230257
if (location.href === 'webui://browser-chrome.html') {

packages/electron-chrome-extensions/esbuild.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const { createConfig, build, EXTERNAL_BASE } = require('../../build/esbuild/esbu
33

44
console.log(`building ${packageJson.name}`)
55

6-
const external = [...EXTERNAL_BASE]
6+
const external = [...EXTERNAL_BASE, 'electron-chrome-extensions/preload']
77

88
const browserConfig = createConfig({
99
entryPoints: ['src/index.ts'],
@@ -25,7 +25,7 @@ build(browserESMConfig)
2525

2626
const preloadConfig = createConfig({
2727
entryPoints: ['src/preload.ts'],
28-
outfile: 'dist/preload.js',
28+
outfile: 'dist/chrome-extension-api.preload.js',
2929
platform: 'browser',
3030
external,
3131
sourcemap: false,

packages/electron-chrome-extensions/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@
1111
"import": "./dist/esm/index.mjs",
1212
"require": "./dist/cjs/index.js"
1313
},
14+
"./browser-action": {
15+
"types": "./dist/types/browser-action.d.ts",
16+
"import": "./dist/esm/browser-action.mjs",
17+
"require": "./dist/cjs/browser-action.js"
18+
},
1419
"./dist/browser-action": {
1520
"types": "./dist/types/browser-action.d.ts",
1621
"import": "./dist/esm/browser-action.mjs",
1722
"require": "./dist/cjs/browser-action.js"
18-
}
23+
},
24+
"./preload": "./dist/chrome-extension-api.preload.js"
1925
},
2026
"scripts": {
2127
"build": "yarn clean && tsc && node esbuild.config.js",

packages/electron-chrome-extensions/src/browser/index.ts

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { session as electronSession } from 'electron'
22
import { EventEmitter } from 'node:events'
33
import path from 'node:path'
4-
import { promises as fs } from 'node:fs'
4+
import { existsSync } from 'node:fs'
5+
import { createRequire } from 'node:module'
56

67
import { BrowserActionAPI } from './api/browser-action'
78
import { TabsAPI } from './api/tabs'
@@ -27,6 +28,31 @@ function checkVersion() {
2728
}
2829
}
2930

31+
function resolvePreloadPath(modulePath?: string) {
32+
// Attempt to resolve preload path from module exports
33+
try {
34+
return createRequire(__dirname).resolve('electron-chrome-extensions/preload')
35+
} catch (error) {
36+
if (process.env.NODE_ENV !== 'production') {
37+
console.error(error)
38+
}
39+
}
40+
41+
const preloadFilename = 'chrome-extension-api.preload.js'
42+
43+
// Deprecated: use modulePath if provided
44+
if (modulePath) {
45+
process.emitWarning(
46+
'electron-chrome-extensions: "modulePath" is deprecated and will be removed in future versions.',
47+
{ type: 'DeprecationWarning' },
48+
)
49+
return path.join(modulePath, 'dist', preloadFilename)
50+
}
51+
52+
// Fallback to preload relative to entrypoint directory
53+
return path.join(__dirname, preloadFilename)
54+
}
55+
3056
export interface ChromeExtensionOptions extends ChromeExtensionImpl {
3157
/**
3258
* License used to distribute electron-chrome-extensions.
@@ -44,6 +70,8 @@ export interface ChromeExtensionOptions extends ChromeExtensionImpl {
4470
/**
4571
* Path to electron-chrome-extensions module files. Might be needed if
4672
* JavaScript bundlers like Webpack are used in your build process.
73+
*
74+
* @deprecated See "Packaging the preload script" in the readme.
4775
*/
4876
modulePath?: string
4977
}
@@ -60,7 +88,6 @@ export class ElectronChromeExtensions extends EventEmitter {
6088
}
6189

6290
private ctx: ExtensionContext
63-
private modulePath: string
6491

6592
private api: {
6693
browserAction: BrowserActionAPI
@@ -78,7 +105,7 @@ export class ElectronChromeExtensions extends EventEmitter {
78105
constructor(opts: ChromeExtensionOptions) {
79106
super()
80107

81-
const { license, session = electronSession.defaultSession, modulePath, ...impl } = opts || {}
108+
const { license, session = electronSession.defaultSession, ...impl } = opts || {}
82109

83110
checkVersion()
84111
checkLicense(license)
@@ -99,8 +126,6 @@ export class ElectronChromeExtensions extends EventEmitter {
99126
store,
100127
}
101128

102-
this.modulePath = modulePath || path.join(__dirname, '../..')
103-
104129
this.api = {
105130
browserAction: new BrowserActionAPI(this.ctx),
106131
contextMenus: new ContextMenusAPI(this.ctx),
@@ -115,7 +140,7 @@ export class ElectronChromeExtensions extends EventEmitter {
115140
}
116141

117142
this.listenForExtensions()
118-
this.prependPreload()
143+
this.prependPreload(opts.modulePath)
119144

120145
// Register crx:// protocol in default session for convenience
121146
if (this.ctx.session !== electronSession.defaultSession) {
@@ -129,10 +154,10 @@ export class ElectronChromeExtensions extends EventEmitter {
129154
})
130155
}
131156

132-
private async prependPreload() {
157+
private async prependPreload(modulePath?: string) {
133158
const { session } = this.ctx
134159

135-
const preloadPath = path.join(this.modulePath, 'dist/preload.js')
160+
const preloadPath = resolvePreloadPath(modulePath)
136161

137162
if ('registerPreloadScript' in session) {
138163
session.registerPreloadScript({
@@ -150,15 +175,12 @@ export class ElectronChromeExtensions extends EventEmitter {
150175
session.setPreloads([...session.getPreloads(), preloadPath])
151176
}
152177

153-
let preloadExists = false
154-
try {
155-
const stat = await fs.stat(preloadPath)
156-
preloadExists = stat.isFile()
157-
} catch {}
158-
159-
if (!preloadExists) {
178+
if (!existsSync(preloadPath)) {
160179
console.error(
161-
`Unable to access electron-chrome-extensions preload file (${preloadPath}). Consider configuring the 'modulePath' constructor option.`,
180+
new Error(
181+
`electron-chrome-extensions: Preload file not found at "${preloadPath}". ` +
182+
'See "Packaging the preload script" in the readme.',
183+
),
162184
)
163185
}
164186
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
dist
1+
dist
2+
*.preload.js

packages/electron-chrome-web-store/README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ app.whenReady().then(async () => {
5757
})
5858
```
5959

60+
### Packaging the preload script
61+
62+
This module uses a [preload script](https://www.electronjs.org/docs/latest/tutorial/tutorial-preload#what-is-a-preload-script).
63+
When packaging your application, it's required that the preload script is included. This can be
64+
handled in two ways:
65+
66+
1. Include `node_modules` in your packaged app. This allows `electron-chrome-web-store/preload` to
67+
be resolved.
68+
2. In the case of using JavaScript bundlers, you may need to copy the preload script next to your
69+
app's entry point script. You can try using
70+
[copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin),
71+
[vite-plugin-static-copy](https://github.com/sapphi-red/vite-plugin-static-copy),
72+
or [rollup-plugin-copy](https://github.com/vladshcherbin/rollup-plugin-copy) depending on your app's
73+
configuration.
74+
75+
Here's an example for webpack configurations:
76+
77+
```js
78+
module.exports = {
79+
entry: './index.js',
80+
plugins: [
81+
new CopyWebpackPlugin({
82+
patterns: [require.resolve('electron-chrome-web-store/preload')],
83+
}),
84+
],
85+
}
86+
```
87+
6088
## API
6189

6290
### `installChromeWebStore`
@@ -65,7 +93,6 @@ Installs Chrome Web Store support in the specified session.
6593

6694
- `options`
6795
- `session`: The Electron session to enable the Chrome Web Store in. Defaults to `session.defaultSession`.
68-
- `modulePath`: The path to the 'electron-chrome-web-store' module.
6996
- `extensionsPath`: The path to the extensions directory. Defaults to 'Extensions/' in the app's userData path.
7097
- `autoUpdate`: Whether to auto-update web store extensions at startup and once every 5 hours. Defaults to true.
7198
- `loadExtensions`: A boolean indicating whether to load extensions installed by Chrome Web Store. Defaults to true.

packages/electron-chrome-web-store/esbuild.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const { createConfig, build, EXTERNAL_BASE } = require('../../build/esbuild/esbu
33

44
console.log(`building ${packageJson.name}`)
55

6-
const external = [...EXTERNAL_BASE, 'adm-zip', 'pbf']
6+
const external = [...EXTERNAL_BASE, 'adm-zip', 'pbf', 'electron-chrome-web-store/preload']
77

88
const esmOnlyModules = ['pbf']
99

@@ -26,8 +26,8 @@ build(browserConfig)
2626
build(browserESMConfig)
2727

2828
const preloadConfig = createConfig({
29-
entryPoints: ['src/renderer/web-store-preload.ts'],
30-
outfile: 'dist/renderer/web-store-preload.js',
29+
entryPoints: ['src/renderer/chrome-web-store.preload.ts'],
30+
outfile: 'dist/chrome-web-store.preload.js',
3131
platform: 'browser',
3232
external,
3333
sourcemap: false,

packages/electron-chrome-web-store/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"types": "./dist/types/browser/index.d.ts",
1111
"import": "./dist/esm/browser/index.mjs",
1212
"require": "./dist/cjs/browser/index.js"
13-
}
13+
},
14+
"./preload": "./dist/chrome-web-store.preload.js"
1415
},
1516
"scripts": {
1617
"build": "yarn clean && tsc && node esbuild.config.js",

0 commit comments

Comments
 (0)