@witchcraft/nuxt-electron 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +219 -104
- package/dist/module.json +1 -1
- package/dist/module.mjs +42 -17
- package/dist/runtime/components/WindowControls/PinButton.d.vue.ts +1 -1
- package/dist/runtime/components/WindowControls/PinButton.vue.d.ts +1 -1
- package/dist/runtime/electron/createPriviledgedProtocolScheme.d.ts +25 -0
- package/dist/runtime/electron/createPriviledgedProtocolScheme.js +16 -0
- package/dist/runtime/electron/createPrivilegedProtocolScheme.d.ts +25 -0
- package/dist/runtime/electron/createPrivilegedProtocolScheme.js +16 -0
- package/dist/runtime/electron/createProxiedProtocolHandler.d.ts +31 -0
- package/dist/runtime/electron/createProxiedProtocolHandler.js +158 -0
- package/dist/runtime/electron/getPaths.d.ts +7 -15
- package/dist/runtime/electron/getPaths.js +18 -12
- package/dist/runtime/electron/index.d.ts +2 -1
- package/dist/runtime/electron/index.js +2 -1
- package/genDevDesktop.js +50 -3
- package/package.json +22 -20
- package/src/module.ts +51 -18
- package/src/runtime/electron/createPriviledgedProtocolScheme.ts +40 -0
- package/src/runtime/electron/createPrivilegedProtocolScheme.ts +40 -0
- package/src/runtime/electron/createProxiedProtocolHandler.ts +229 -0
- package/src/runtime/electron/getPaths.ts +32 -27
- package/src/runtime/electron/index.ts +2 -1
- package/dist/runtime/electron/createNuxtFileProtocolHandler.d.ts +0 -14
- package/dist/runtime/electron/createNuxtFileProtocolHandler.js +0 -20
- package/src/runtime/electron/createNuxtFileProtocolHandler.ts +0 -42
package/README.md
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
- :zap: Auto reloads/restarts electron on changes to the main/renderer code or nuxt server restarts.
|
|
13
13
|
- :rocket: Api calls are proxied to the server.
|
|
14
|
-
- :start:
|
|
14
|
+
- :start: Rendering strategy isn't changed. Does not require static builds or changing baseURL/buildAssetsDir.
|
|
15
15
|
- :scissors: Trims server and non-electron routes from the electron bundle.
|
|
16
16
|
- :open_file_folder: Modifies directory structure for easier multi-platform builds.
|
|
17
|
+
- :snowflake: Nix Support - Playground contains an example flake for reproducible development and builds.
|
|
18
|
+
- :closed_lock_with_key: Supports Auth - Integrates with my auth library [@witchcraft/nuxt-auth](https://github.com/witchcraftjs/nuxt-auth) for local-first auth.
|
|
17
19
|
- :hammer_and_wrench: Helpful Tools/Composables
|
|
18
20
|
- `isElectron`
|
|
19
21
|
- Electron Only
|
|
@@ -29,16 +31,47 @@
|
|
|
29
31
|
|
|
30
32
|
# Playground
|
|
31
33
|
|
|
32
|
-
There is a playground
|
|
34
|
+
There is a playground with a comprehensive example, but it only works locally due to electron.
|
|
35
|
+
|
|
36
|
+
To try it:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/witchcraftjs/nuxt-electron.git
|
|
40
|
+
pnpm i
|
|
41
|
+
cd nuxt-electron/playground
|
|
42
|
+
pnpm build && pnpm build:electron:no-pack
|
|
43
|
+
pnpm preview:electron:dev
|
|
44
|
+
```
|
|
45
|
+
You can also do:
|
|
46
|
+
```bash
|
|
47
|
+
pnpm build
|
|
48
|
+
pnpm build:electron:pack
|
|
49
|
+
# in one tab
|
|
50
|
+
pnpm preview
|
|
51
|
+
# in another tab - replace `linux-unpacked` with your platform
|
|
52
|
+
OVERRIDE_PUBLIC_SERVER_URL=http://localhost:3000 ./.dist/electron/release/linux-unpacked/your-app-name
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Nix
|
|
56
|
+
|
|
57
|
+
If you're using nix the first example should work in the dev shell. The second won't. You can do `nix run`, but warning, this is like building and doing `pnpm launch:electron` so it api will be pointed at production server (see below).
|
|
58
|
+
|
|
59
|
+
See [#Usage on Nix](#Usage-on-Nix) for more details.
|
|
33
60
|
|
|
34
61
|
## Install
|
|
35
62
|
```bash
|
|
36
63
|
pnpx nuxi module add @witchcraft/nuxt-electron
|
|
37
64
|
|
|
38
65
|
```
|
|
39
|
-
|
|
66
|
+
### Components
|
|
67
|
+
|
|
68
|
+
If using any of the provided components, they rely on the @witchcraft/ui module.
|
|
69
|
+
|
|
70
|
+
If not, ignore it.
|
|
71
|
+
|
|
72
|
+
This module installs it to use it's css config and twMerge.
|
|
40
73
|
|
|
41
|
-
|
|
74
|
+
All you need to do is add the electron module to your tailwind css as a source:
|
|
42
75
|
```css [assets/css/tailwind.css]
|
|
43
76
|
@source "../../../.nuxt/witchcraft-electron.css";
|
|
44
77
|
```
|
|
@@ -47,141 +80,164 @@ If using the components, you should add the library to your tailwind css as a so
|
|
|
47
80
|
|
|
48
81
|
A directory structure like the following is suggested:
|
|
49
82
|
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
app
|
|
83
|
+
[project root]
|
|
84
|
+
├── .dist/ (I prefer .dist over dist so it stays hidden and at the top)/
|
|
85
|
+
│ └── [platform]/
|
|
86
|
+
│ ├── .output/ (nuxt output)
|
|
87
|
+
│ ├── release/
|
|
88
|
+
│ │ └── ${productName}_${version}.${ext}
|
|
89
|
+
│ └── build/ (for any intermediate builds like electron's)
|
|
90
|
+
├── app/ - nuxt code
|
|
91
|
+
├── app-electron/ - contains all the main/renderer code
|
|
92
|
+
│ └── package.json - to control packaged dependencies
|
|
93
|
+
└── electron-builder-config.js
|
|
94
|
+
|
|
58
95
|
```
|
|
96
|
+
The module sets it up like this when building electron, but not for the regular build. You should set that to go elsewhere if you're using it (though it's not required).
|
|
97
|
+
|
|
98
|
+
```ts [nuxt.config.ts]
|
|
99
|
+
export default defineNuxtConfig({
|
|
100
|
+
nitro: {
|
|
101
|
+
output: {
|
|
102
|
+
dir: ".dist/web/.output",
|
|
103
|
+
serverDir: ".dist/web/.output/server",
|
|
104
|
+
publicDir: ".dist/web/.output/public"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
59
110
|
Usage of nuxt 4's new directory structure is recommended.
|
|
60
111
|
|
|
61
112
|
For whatever electron builder you want to use, you must point it at the correct directories.
|
|
62
113
|
|
|
63
|
-
For `electron-builder` with the default directories the module uses and to
|
|
114
|
+
For `electron-builder` with the default directories the module uses and to be able to control which dependencies are packaged with `app-electron/package.json` copy the following:
|
|
64
115
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
},
|
|
70
|
-
"files": [
|
|
71
|
-
".dist/electron/build/**/*",
|
|
72
|
-
".dist/electron/.output/public/**/*"
|
|
73
|
-
],
|
|
74
|
-
"linux": {
|
|
75
|
-
"artifactName": "${productName}_${version}.${ext}",
|
|
76
|
-
// ...
|
|
77
|
-
},
|
|
78
|
-
"mac": {
|
|
79
|
-
"artifactName": "${productName}_${version}.${ext}",
|
|
80
|
-
// ...
|
|
81
|
-
},
|
|
82
|
-
"win": {
|
|
83
|
-
"artifactName": "${productName}_${version}.${ext}"
|
|
84
|
-
// ...
|
|
85
|
-
},
|
|
86
|
-
}
|
|
87
|
-
```
|
|
116
|
+
[electron-builder-config.js](https://github.com/witchcraftjs/nuxt-electron/blob/master/playground/electron-builder-config.js)
|
|
117
|
+
[app-electron/package.json](https://github.com/witchcraftjs/nuxt-electron/blob/master/playground/app-electron/package.json)
|
|
118
|
+
|
|
119
|
+
They should work out of the box, with the package name as configured in your package.json.
|
|
88
120
|
|
|
89
121
|
Add the following to the package.json:
|
|
90
|
-
|
|
122
|
+
|
|
123
|
+
<details>
|
|
124
|
+
<summary>Package.json</summary>
|
|
125
|
+
|
|
126
|
+
```json
|
|
91
127
|
// package.json
|
|
92
128
|
{
|
|
129
|
+
// these first properties are required to package the app
|
|
130
|
+
"name": "your-app-name",
|
|
131
|
+
"version": "0.0.0",
|
|
132
|
+
"description": "Your app description",
|
|
133
|
+
"author": "Your Name",
|
|
134
|
+
"repository" :"...",
|
|
93
135
|
"main": ".dist/electron/build/main.cjs",
|
|
94
136
|
"scripts": {
|
|
95
137
|
"dev": "nuxi dev",
|
|
96
138
|
"build": "nuxi build",
|
|
97
|
-
"preview": "nuxt preview .dist/web",
|
|
139
|
+
"preview": "LOG_LEVEL=trace nuxt preview .dist/web",
|
|
98
140
|
"======= electron": "=======",
|
|
99
|
-
"dev:electron": "AUTO_OPEN=electron
|
|
100
|
-
// write a dev desktop file for linux, see below
|
|
101
|
-
"gen:dev:electron:desktop": "node node_modules/@witchcraft/nuxt-electron/genDevDesktop.js YOURAPPNAMEK",
|
|
102
|
-
// for the desktop file
|
|
141
|
+
"dev:electron": "AUTO_OPEN=electron pnpm dev",
|
|
103
142
|
"launch:electron": "electron .",
|
|
104
|
-
"
|
|
105
|
-
"build:electron
|
|
106
|
-
"build:electron:pack": "electron-builder",
|
|
107
|
-
"
|
|
143
|
+
"launch:electron:dev": "LOG_LEVEL=trace OVERRIDE_PUBLIC_SERVER_URL=http://localhost:3000 electron .",
|
|
144
|
+
"build:electron": "BUILD_ELECTRON=true pnpm build",
|
|
145
|
+
"build:electron:pack": "APP_VERSION=0.0.0 electron-builder --config electron-builder-config.js",
|
|
146
|
+
"build:electron:no-pack": "APP_VERSION=0.0.0 SKIP_ELECTRON_PACK=true BUILD_ELECTRON=true nuxi build",
|
|
147
|
+
// write a dev desktop file for linux, see below
|
|
148
|
+
"preview:electron:dev": "concurrently --kill-others \"pnpm preview\" \"sleep 2 && pnpm launch:electron:dev\"",
|
|
149
|
+
"gen:dev:electron:desktop": "node node_modules/@witchcraft/nuxt-electron/genDevDesktop.js YOURAPPNAME",
|
|
108
150
|
}
|
|
109
151
|
}
|
|
110
152
|
```
|
|
153
|
+
</details>
|
|
154
|
+
|
|
155
|
+
### To Develop
|
|
111
156
|
|
|
157
|
+
Run `pnpm dev:electron`. This will both launch nuxt and open electron.
|
|
158
|
+
|
|
159
|
+
Alternatively, run the server and electron seperately:
|
|
160
|
+
|
|
161
|
+
Run `pnpm dev` to start the nuxt dev server. The dev version of the app will be written to `.dist/electron/build/main.cjs`.
|
|
162
|
+
|
|
163
|
+
In a seperate terminal run `pnpm launch:electron` to start the electron app (this will do `electron .` which will run the configured `main` property, aka `.dist/electron/build/main.cjs`).
|
|
164
|
+
|
|
165
|
+
#### Notes
|
|
112
166
|
By default the module will not open electron. You must set `process.env.AUTO_OPEN` to include the string `electron` or set `autoOpen `in the options to true, hence the seperate `dev:electron` script.
|
|
113
167
|
|
|
114
168
|
The idea is if you use other platform modules as well, you'd do `AUTO_OPEN=electron,android`, etc. for each module you wanted to actually have auto open.
|
|
115
169
|
|
|
116
|
-
|
|
170
|
+
### To Build
|
|
117
171
|
|
|
118
|
-
|
|
119
|
-
{
|
|
120
|
-
"scripts": {
|
|
121
|
-
"preview:electron:dev": "concurrrently --kill-others \" npm run dev\" \"PUBLIC_SERVER_URL=http://localhost:3000 electron .\"",
|
|
122
|
-
"preview:electron:prod": "electron .\""
|
|
172
|
+
Build the regular nuxt app with `pnpm build` then build the electron app with `pnpm build:electron` or `pnpm build:electron:no-pack` (if you just want to test, you can skip the packing).
|
|
123
173
|
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
```
|
|
174
|
+
In this case, the build written to `.dist/electron/build/main.cjs` is the **production** build.
|
|
127
175
|
|
|
128
|
-
|
|
176
|
+
To run the built production server and the production app proxied to this server, use `pnpm preview:electron:dev`.
|
|
129
177
|
|
|
130
|
-
|
|
178
|
+
Alternatively...
|
|
131
179
|
|
|
132
|
-
|
|
180
|
+
You can run `pnpm launch:electron` to launch the production build in nearly exactly as a user would.
|
|
133
181
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
// main.ts
|
|
182
|
+
CAREFUL though, **this will proxy api requests to the real server**.
|
|
138
183
|
|
|
139
|
-
|
|
184
|
+
ef you want to test against the local server build, run it with `pnpm preview` then run the app with `pnpm launch:electron:dev`.
|
|
140
185
|
|
|
141
|
-
|
|
142
|
-
const userDataDir = useDevDataDir() ?? app.getPath("userData")
|
|
186
|
+
If you use the example code, it allows `OVERRIDE_PUBLIC_SERVER_URL` which allows the app to override which server the app proxies to and that's what the script is setting to allow this.
|
|
143
187
|
|
|
144
|
-
|
|
145
|
-
const win = new BrowserWindow({
|
|
146
|
-
title: "...",
|
|
147
|
-
webPreferences: {
|
|
148
|
-
preload: paths.preloadPath
|
|
149
|
-
}
|
|
150
|
-
})
|
|
188
|
+
You can handle this different if you want and *not* allow overriding the server url. Up to you. See below.
|
|
151
189
|
|
|
152
|
-
|
|
153
|
-
const proxies = {
|
|
154
|
-
"/api": paths.publicServerUrl,
|
|
155
|
-
}
|
|
190
|
+
### Files
|
|
156
191
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
192
|
+
In main to get the correct paths during build and dev, use the `getPaths` helper.
|
|
193
|
+
|
|
194
|
+
To get the dev user data dir, use the `useDevDataDir` helper.
|
|
195
|
+
|
|
196
|
+
We also need to create the nuxt `app://` protocol handler for every window and configure the proxies for server calls.
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
See full example in [main.ts](https://github.com/witchcraftjs/nuxt-electron/blob/master/playground/app-electron/main.ts).
|
|
200
|
+
|
|
201
|
+
For the nuxt config, here's the minimum you need, the one in the playground contains additional options for testing and debugging:
|
|
161
202
|
|
|
203
|
+
<details>
|
|
204
|
+
<summary>nuxt.config.ts</summary>
|
|
205
|
+
|
|
162
206
|
```ts [nuxt.config.ts]
|
|
163
207
|
export default defineNuxtConfig({
|
|
164
208
|
modules: [
|
|
165
209
|
"@witchcraft/nuxt-electron",
|
|
210
|
+
/** Optional */
|
|
211
|
+
"@witchcraft/ui",
|
|
212
|
+
/** Optional */
|
|
213
|
+
"@witchcraft/nuxt-logger",
|
|
166
214
|
],
|
|
215
|
+
dir: ".dist/web/.output",
|
|
216
|
+
serverDir: ".dist/web/.output/server",
|
|
217
|
+
publicDir: ".dist/web/.output/public"
|
|
218
|
+
},
|
|
219
|
+
},
|
|
167
220
|
electron: {
|
|
168
221
|
additionalElectronVariables: {
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
222
|
+
// this will hardcode `process.env.PUBLIC_SERVER_URL` to the server url in production
|
|
223
|
+
// this means getPaths().publicServerUrl will always return your site url (see getPaths) unless you allow getPaths an override (see it for details)
|
|
224
|
+
publicServerUrl: process.env.NODE_ENV === "production"
|
|
225
|
+
// note the quotes for strings! this is a literal replacement that happens
|
|
226
|
+
// you also cannot access process.env dynamically if you want this to work (e.g. process.env[name])
|
|
227
|
+
? `"https://yoursite.com"`
|
|
228
|
+
: `undefined`
|
|
229
|
+
},
|
|
230
|
+
// the module will set this to prerender: true
|
|
231
|
+
// you can override it in routeRules and create a spa without prerender if you need
|
|
232
|
+
// additionalRoutes: ["/other-page-prerendered"]
|
|
174
233
|
}
|
|
175
234
|
})
|
|
176
235
|
```
|
|
177
|
-
|
|
178
|
-
A full example, including usage with `@witchcraft/nuxt-logger` is available in the playground.
|
|
179
|
-
|
|
236
|
+
</details>
|
|
180
237
|
|
|
181
238
|
**NOTE: The proxies only work for api calls. They do not work for pages.**
|
|
182
239
|
|
|
183
|
-
Proxying server only page routes seems possible but complicated because each route's resource calls must also be proxied. I don't think it's worth the pain. It's easier to make sure the electron app never
|
|
184
|
-
|
|
240
|
+
Proxying server only page routes seems possible but complicated because each route's resource calls must also be proxied. I don't think it's worth the pain. It's easier to make sure the electron app never access server only routes.
|
|
185
241
|
|
|
186
242
|
### Runtime Config
|
|
187
243
|
|
|
@@ -212,27 +268,65 @@ An isomorphic logger is also available for electron, see [@witchcraft/nuxt-logge
|
|
|
212
268
|
|
|
213
269
|
A script is provided for use with electron-builder to generate a dev desktop file for linux.
|
|
214
270
|
|
|
271
|
+
This is useful for when registering deep links in the app as these require a desktop file to work on linux.
|
|
272
|
+
|
|
215
273
|
It will create a desktop file named `dev-YOURAPPNAME.desktop`, put it in `~/.local/share/applications/` and re-install it with `xdg-desktop-menu un/install`.
|
|
216
274
|
|
|
217
|
-
The desktop's exec is set to run bash, cd into the project dir and run `
|
|
275
|
+
The desktop's exec is set to run bash, cd into the project dir and run `pnpm launch:electron`.
|
|
218
276
|
|
|
219
|
-
|
|
277
|
+
You can pass a second parameter to the script to use a different package.json script.
|
|
278
|
+
|
|
279
|
+
And a third parameter pointing to your config if it's not in one of the searched locations:
|
|
280
|
+
|
|
281
|
+
- `electron-builder-config.js`
|
|
282
|
+
- `electron-builder.json5`
|
|
283
|
+
- `electron-builder.json`
|
|
284
|
+
|
|
285
|
+
### Usage on Nix
|
|
220
286
|
|
|
287
|
+
As mentioned, the second example script won't work out of the box with nix. You need something like [nix-alien](https://github.com/thiagokokada/nix-alien) if you want to test the electron-builder packaged version (**which you should**, it is packaged completely different than on nix where electron builder is not used).
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
DEBUG=true LOG_LEVEL=trace nix run "github:thiagokokada/nix-alien#nix-alien" -- .dist/electron/release/linux-unpacked/your-app-name
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Apart from that nix has some additional goodies.
|
|
294
|
+
|
|
295
|
+
First there flake with a devenv based shell and direnv support. If you have direnv run `direnv allow .` in the project root. Otherwise run `nix develop`.
|
|
296
|
+
|
|
297
|
+
Nearly everything should work, except the build electron-builder builds. You can do `nix run` instead.
|
|
298
|
+
|
|
299
|
+
The derivation for reference is [here](https://github.com/witchcraftjs/nuxt-electron/blob/master/playground/nix/derivation.nix).
|
|
300
|
+
|
|
301
|
+
There is also a debugging script `debugNixBuild` which drops you in a shell to run the derivation. See it for details. The shell should give you info about all scripts.
|
|
302
|
+
|
|
303
|
+
It uses a set of devenv flake utils I've created (see [here](https://github.com/alanscodelog/nix-devenv-utils)). They contain some good stuff like working support for electron (obviously), android, playwright, etc if you're interested.
|
|
221
304
|
|
|
222
305
|
## Misc Notes
|
|
223
306
|
|
|
224
|
-
Note that while nuxt's path aliases are passed to the electron vite config, you cannot use other nuxt paths (such as those added by modules, e.g. `#somemodule`) in electron. This is why a seperate `@witchcraft/nuxt-electron/electron` export is provided.
|
|
307
|
+
- Note that while nuxt's path aliases are passed to the electron vite config, you cannot use other nuxt paths (such as those added by modules, e.g. `#somemodule`) in electron. This is why a seperate `@witchcraft/nuxt-electron/electron` export is provided.
|
|
308
|
+
- In any electron main code, import.meta.url is always the built main.mjs file regardless of whether you're in dev or prod or what file you're in.
|
|
309
|
+
|
|
310
|
+
## Auth
|
|
311
|
+
|
|
312
|
+
While you might not want to use it (it is very beta) my auth library [@witchcraft/nuxt-auth](https://github.com/witchcraftjs/nuxt-auth) contains a whole section on auth flow on electron. It also contains electron specific code for doing local-first auth which you might find helpful (more code will be added as I figure more things out). It includes support for "semi-authed" local only users and handling "synced" users who can remain "logged in" past authentication expiration so long as they don't perform actions that require auth (e.g. sync).
|
|
313
|
+
|
|
314
|
+
I do not use an existing lib because I have not found any that support complex scenarios like these.
|
|
315
|
+
|
|
316
|
+
This takes a lot of careful consideration and planning to implement like this. I'm still working on some pain points.
|
|
317
|
+
|
|
318
|
+
I mention it because you will need to render your login page as a SPA to get it working. This makes middleware "soft" on that page, just so you're aware. You will also need to proxy auth requests.
|
|
225
319
|
|
|
226
320
|
|
|
227
321
|
## How it Works
|
|
228
322
|
|
|
229
|
-
|
|
323
|
+
### Development
|
|
230
324
|
|
|
231
325
|
Electron is pointed to the localhost server and sees a similar view to the web app except we must client side detect we're on electron and redirect to the `electronRoute`.
|
|
232
326
|
|
|
233
|
-
|
|
327
|
+
### Production
|
|
234
328
|
|
|
235
|
-
Normally nuxt
|
|
329
|
+
Normally nuxt would have had to be configured to output a SPA by setting `ssr: false` and you have to modify baseURL and buildAssetsDir for everything to work (among other changes, see nuxt-electron module for the typical changes).
|
|
236
330
|
|
|
237
331
|
But this module has gone a different route.
|
|
238
332
|
|
|
@@ -240,37 +334,51 @@ First for production only changes, we run the nuxt config with a different env v
|
|
|
240
334
|
|
|
241
335
|
Then we use a custom protocol to proxy requests and api calls.
|
|
242
336
|
|
|
243
|
-
|
|
337
|
+
#### Custom `app://` Handler
|
|
338
|
+
|
|
339
|
+
Electron uses the `file://` protocol by default to load all scripts/assets/etc.
|
|
340
|
+
|
|
341
|
+
Loading from files does not work well with the default nuxt config so we use use a custom protocol handler to intercept these requests and correctly route them so that we don't have to be changing nuxt's baseURL and buildAssetsDir.
|
|
342
|
+
|
|
343
|
+
It is recommended to use a custom protocol instead of `file://`, so the example uses `app://`.
|
|
244
344
|
|
|
245
|
-
|
|
345
|
+
Additionally to have as close to regular browser behavior, the host is set to `bundle`.
|
|
246
346
|
|
|
247
|
-
|
|
347
|
+
So `getPaths` returns `app://bundle/path/to/file` for `windowUrl` in production.
|
|
348
|
+
|
|
349
|
+
This is because using a `standard` protocol schema, paths like `/path/to/file` are resolved relative to the host as they are in the browser.
|
|
350
|
+
|
|
351
|
+
Internally the protocol handler does the following:
|
|
352
|
+
|
|
353
|
+
- Checks the request is safe (does not try to escape the path given).
|
|
354
|
+
- Reroutes proxy requests to the correct url.
|
|
355
|
+
- Checks if the request exists as a file, if not, attempts to find the nested `index.html` file (e.g. `/some/path` will correctly load `/some/path/index.html`).
|
|
248
356
|
|
|
249
357
|
##### Electron Route
|
|
250
358
|
In electron we want to point to a different route, `/{electronRoute}`, so we can trim other routes from the bundle.
|
|
251
359
|
|
|
252
360
|
To do this we prerender the `/{electronRoute}` route and point electron to `/{electronRoute}/index.html`.
|
|
253
|
-
- This used to require changing baseURL and buildAssetsDir to "./" and "/" respectively, but with the
|
|
361
|
+
- This used to require changing baseURL and buildAssetsDir to "./" and "/" respectively, but with the custom `app://` handler it should just work.
|
|
254
362
|
|
|
255
363
|
We then need to remove unwanted routes from the bundle which are included regardless of whether they're used. So for the web app, remove electron routes, and for the electron app, remove web app routes. This is done through a nuxt hook in the nuxt config on production builds only.
|
|
256
364
|
|
|
257
365
|
Note we would ideally also remove "/" from the prerender, but this requires manually splitting chunks by pages and I was having issues with this.
|
|
258
366
|
|
|
259
|
-
|
|
367
|
+
##### Why not use a redirect?
|
|
260
368
|
|
|
261
369
|
A `routeRules` redirect won't work, because it will trigger a request to electron's file protocol handler which we don't know what to do with.
|
|
262
370
|
|
|
263
371
|
A redirect from a page (e.i. `if (process.client && isElectron) { await navigateTo("/app") }`) works, but it takes a little big of time to navigate. This is still needed for development redirecting, but not in production.
|
|
264
372
|
|
|
265
|
-
|
|
373
|
+
### Build
|
|
266
374
|
|
|
267
375
|
Building for electron requires lots of changes to the config. We can't just build for web then copy. So this module reroutes the output when building the web app (and reroutes it differently when building for electron (see directory sturcture above).
|
|
268
376
|
|
|
269
|
-
The
|
|
377
|
+
The reason for the nested `.output` is so it doesn't overwrite the default one. We can also do `nuxt preview .dist/platform` though it's often not of much help.
|
|
270
378
|
|
|
271
379
|
To build for electron you must set `process.env.BUILD_ELECTRON` to true, to do the configuration required to make the final output actually work with electron.
|
|
272
380
|
|
|
273
|
-
|
|
381
|
+
#### Electron Build
|
|
274
382
|
|
|
275
383
|
The electron app itself is "built" in two parts, the "build" and the "packing".
|
|
276
384
|
|
|
@@ -284,6 +392,13 @@ Note that nuxt builds the server anyways, it's just not packed into the final ap
|
|
|
284
392
|
|
|
285
393
|
Your packer should then create the final executables (into `.dist/electron/release`).
|
|
286
394
|
|
|
395
|
+
### Debugging Tips
|
|
396
|
+
|
|
397
|
+
- To inspect the asar, run `npx @electron/asar list .dist/electron/release/linux-unpacked/resources/app.asar`.
|
|
398
|
+
- If routes aren't rendering:
|
|
399
|
+
- Check the final config in the ready hook. Other modules might be causing issues.
|
|
400
|
+
- Check your structure is correct (`pages/page.vue` with NuxtPage, `pages/page/index.vue`).
|
|
401
|
+
|
|
287
402
|
|
|
288
403
|
<!-- Badges -->
|
|
289
404
|
[npm-version-src]: https://img.shields.io/npm/v/@witchcraft/nuxt-electron/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { crop } from '@alanscodelog/utils/crop';
|
|
2
2
|
import { run } from '@alanscodelog/utils/run';
|
|
3
3
|
import { defineNuxtModule, useLogger, createResolver, addComponentsDir, addTemplate, extendRouteRules, addImportsDir } from '@nuxt/kit';
|
|
4
|
-
import { nuxtRemoveUneededPages,
|
|
4
|
+
import { nuxtRemoveUneededPages, nuxtRerouteOutputTo, createConstantCaseVariables } from '@witchcraft/nuxt-utils/utils';
|
|
5
5
|
import { defu } from 'defu';
|
|
6
6
|
import fs from 'node:fs/promises';
|
|
7
7
|
import path from 'node:path';
|
|
@@ -21,7 +21,7 @@ startup.exit = async () => {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
|
-
const module = defineNuxtModule({
|
|
24
|
+
const module$1 = defineNuxtModule({
|
|
25
25
|
meta: {
|
|
26
26
|
name: "electron",
|
|
27
27
|
configKey: "electron"
|
|
@@ -90,7 +90,7 @@ const module = defineNuxtModule({
|
|
|
90
90
|
logger.warn(`Missing electron scripts: ${[hasMainScript ? "" : "main.ts", hasPreloadScript ? "" : "preload.ts"].join(", ")}. Skipping electron build.`);
|
|
91
91
|
}
|
|
92
92
|
const isElectronBuild = process.env.BUILD_ELECTRON === "true" && hasScripts;
|
|
93
|
-
const skipElectronPack = process.env.SKIP_ELECTRON_PACK === "true";
|
|
93
|
+
const skipElectronPack = !isElectronBuild || process.env.SKIP_ELECTRON_PACK === "true";
|
|
94
94
|
const autoOpen = !!(options.autoOpen && hasScripts && isDev);
|
|
95
95
|
const useWatch = nuxt.options.dev;
|
|
96
96
|
const devUserDataDir = options.devUserDataDir && await resolvePath(options.devUserDataDir, nuxt.options.alias);
|
|
@@ -159,11 +159,11 @@ const module = defineNuxtModule({
|
|
|
159
159
|
);
|
|
160
160
|
const additionalElectronVariables = defu(
|
|
161
161
|
options.additionalElectronVariables,
|
|
162
|
-
nuxt.options.electron.additionalElectronVariables
|
|
162
|
+
nuxt.options.electron === false ? {} : nuxt.options.electron.additionalElectronVariables
|
|
163
163
|
);
|
|
164
164
|
const additionalViteDefinesToCopy = [
|
|
165
165
|
...options.additionalViteDefinesToCopy,
|
|
166
|
-
...nuxt.options.electron.additionalViteDefinesToCopy ?? []
|
|
166
|
+
...nuxt.options.electron === false ? [] : nuxt.options.electron.additionalViteDefinesToCopy ?? []
|
|
167
167
|
];
|
|
168
168
|
const copyFromVite = [
|
|
169
169
|
"__NUXT_VERSION__",
|
|
@@ -180,11 +180,13 @@ const module = defineNuxtModule({
|
|
|
180
180
|
copyFromVite.map((v) => [v, viteConfig.define[v]])
|
|
181
181
|
),
|
|
182
182
|
...createConstantCaseVariables({
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
electronBuildDir,
|
|
183
|
+
// on windows the backslashes in the paths must be double escaped
|
|
184
|
+
electronRoute: electronRoute.replaceAll("\\", "\\\\"),
|
|
185
|
+
electronProdUrl: electronProdUrl.replaceAll("\\", "\\\\"),
|
|
186
|
+
// all paths muxt be made relative to the build dir where electron will launch from in production
|
|
187
|
+
electronNuxtDir: path.relative(electronBuildDir, relativeElectronDir).replaceAll("\\", "\\\\"),
|
|
188
|
+
electronNuxtPublicDir: path.relative(electronBuildDir, electronNuxtPublicDir).replaceAll("\\", "\\\\"),
|
|
189
|
+
electronBuildDir: path.relative(electronBuildDir, electronBuildDir).replaceAll("\\", "\\\\"),
|
|
188
190
|
// ...options.additionalElectronVariables,
|
|
189
191
|
// nuxt's runtimeConfig cannot be used in electron's main since it's built seperately
|
|
190
192
|
// also we must stringify ourselves since escaped double quotes are not preserved in the final output :/
|
|
@@ -327,11 +329,25 @@ const module = defineNuxtModule({
|
|
|
327
329
|
}
|
|
328
330
|
});
|
|
329
331
|
nuxtRemoveUneededPages(nuxt, ["/", electronRoute, ...options.additionalRoutes]);
|
|
330
|
-
|
|
331
|
-
nuxt.options.router =
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
);
|
|
332
|
+
nuxt.options.router.options ??= {};
|
|
333
|
+
nuxt.options.router.options.hashMode = false;
|
|
334
|
+
extendRouteRules(electronRoute + "/", {
|
|
335
|
+
prerender: true
|
|
336
|
+
}, { override: true });
|
|
337
|
+
extendRouteRules(electronRoute + "/**", {
|
|
338
|
+
prerender: true
|
|
339
|
+
}, { override: true });
|
|
340
|
+
for (const route of options.additionalRoutes) {
|
|
341
|
+
extendRouteRules(route, {
|
|
342
|
+
prerender: true
|
|
343
|
+
// allow user to set something else and use a spa
|
|
344
|
+
}, { override: false });
|
|
345
|
+
}
|
|
346
|
+
nuxt.hook("prerender:routes", ({ routes }) => {
|
|
347
|
+
for (const route of ["/404.html"]) {
|
|
348
|
+
routes.add(route);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
335
351
|
nuxtRerouteOutputTo(nuxt, electronNuxtDir);
|
|
336
352
|
nuxt.hook("close", async () => {
|
|
337
353
|
logger.info(`Building Electron`);
|
|
@@ -360,10 +376,19 @@ const module = defineNuxtModule({
|
|
|
360
376
|
} else {
|
|
361
377
|
logger.info(`Skipping Electron Build`);
|
|
362
378
|
}
|
|
363
|
-
|
|
379
|
+
const nuxtOutputDir = nuxt.options.nitro?.output?.dir;
|
|
380
|
+
if (nuxtOutputDir === void 0 || nuxtOutputDir === ".output") {
|
|
381
|
+
logger.warn(crop`Nitro output dir is not set or set to the default, it's suggested you set it to the following when using nuxt-electron:
|
|
382
|
+
nitro: {
|
|
383
|
+
output: ".dist/web/.output",
|
|
384
|
+
serverDir: ".dist/web/.output/server"
|
|
385
|
+
publicDir: ".dist/web/.output/public"
|
|
386
|
+
}
|
|
387
|
+
.`);
|
|
388
|
+
}
|
|
364
389
|
}
|
|
365
390
|
addImportsDir(resolve("runtime/utils"));
|
|
366
391
|
}
|
|
367
392
|
});
|
|
368
393
|
|
|
369
|
-
export { module as default };
|
|
394
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a protocol scheme to register as privileged with the following permissions (you can override them with the second parameter).
|
|
3
|
+
*
|
|
4
|
+
* Note this must be called before the app's ready event:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* protocol.registerSchemesAsPrivileged([
|
|
8
|
+
* createPrivilegedProtocolScheme("app")
|
|
9
|
+
* })
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* ## Privileges
|
|
13
|
+
*
|
|
14
|
+
* Basically all except `bypassCSP` and `enableCORS`.
|
|
15
|
+
*
|
|
16
|
+
* - `standard`
|
|
17
|
+
* - `allowServiceWorkers`
|
|
18
|
+
* - `bypassCSP`
|
|
19
|
+
* - `corsEnabled`
|
|
20
|
+
* - `secure`
|
|
21
|
+
* - `supportFetchAPI`
|
|
22
|
+
* - `codeCache`
|
|
23
|
+
* - `stream`
|
|
24
|
+
*/
|
|
25
|
+
export declare function createPrivilegedProtocolScheme(protocolName: string, privileges?: Partial<Electron.CustomScheme["privileges"]>): Electron.CustomScheme;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function createPrivilegedProtocolScheme(protocolName, privileges = {}) {
|
|
2
|
+
return {
|
|
3
|
+
scheme: protocolName,
|
|
4
|
+
privileges: {
|
|
5
|
+
bypassCSP: false,
|
|
6
|
+
corsEnabled: false,
|
|
7
|
+
stream: false,
|
|
8
|
+
standard: true,
|
|
9
|
+
secure: true,
|
|
10
|
+
supportFetchAPI: true,
|
|
11
|
+
allowServiceWorkers: true,
|
|
12
|
+
codeCache: true,
|
|
13
|
+
...privileges
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|