Skip to content

Commit 568f990

Browse files
committed
build: copy static assets in dev
Signed-off-by: Adam Setch <adam.setch@outlook.com>
1 parent e4f3e33 commit 568f990

9 files changed

Lines changed: 93 additions & 52 deletions

File tree

.github/actions/setup-node/action.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,28 @@
44
name: Setup Node, pnpm and install dependencies
55
description: Setup pnpm and Node.js with caching
66

7+
inputs:
8+
run-install:
9+
description: 'Whether to run `pnpm install`'
10+
required: false
11+
default: 'true'
12+
713
runs:
814
using: composite
915
steps:
1016
- name: Setup pnpm
1117
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
18+
with:
19+
run_install: false
1220

1321
- name: Setup Node
1422
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
1523
with:
1624
node-version-file: .nvmrc
1725
cache: pnpm
26+
cache-dependency-path: pnpm-lock.yaml
1827

1928
- name: Install dependencies
29+
if: ${{ inputs.run-install == 'true' }}
2030
run: pnpm install --frozen-lockfile
2131
shell: bash

.github/workflows/publish.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737

3838
release:
3939
name: Publish ${{ matrix.platform }} (electron-builder)
40+
needs: prepare
4041
strategy:
4142
matrix:
4243
include:
@@ -67,7 +68,7 @@ jobs:
6768
- name: Build application
6869
run: pnpm build
6970
env:
70-
OAUTH_CLIENT_ID: ${{ secrets.OAUTH_CLIENT_ID }}
71+
VITE_OAUTH_CLIENT_ID: ${{ secrets.OAUTH_CLIENT_ID }}
7172

7273
- name: Package and publish for ${{ matrix.platform }}
7374
run: ${{ matrix.package-cmd }}

.github/workflows/renovate.yml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,9 @@ jobs:
2121
- name: Checkout
2222
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2323

24-
- name: Setup pnpm
25-
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
24+
- uses: ./.github/actions/setup-node
2625
with:
27-
run_install: false
28-
29-
- name: Setup Node
30-
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
31-
with:
32-
node-version-file: .nvmrc
33-
cache: pnpm
26+
run-install: "false"
3427

3528
- name: Install Renovate
3629
run: pnpm install --global renovate

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ jobs:
1515
- name: Checkout
1616
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1717

18-
- uses: ./.github/actions/setup-node
18+
- name: Setup Node.js
19+
uses: ./.github/actions/setup-node
1920

2021
- name: Check TypeScript
2122
run: pnpm tsc --noEmit

CONTRIBUTING.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ We also suggest you read the [Project Philosophy](#project-philosophy) in our do
1010
> [!TIP]
1111
> _Optional: If you prefer to use your own OAuth credentials, you can do so by passing them as environment variables when bundling the app. This is optional as the app has some default "development" keys (use at your own discretion)._
1212
> ```shell
13-
> OAUTH_CLIENT_ID="123" pnpm build
13+
> VITE_OAUTH_CLIENT_ID="123" pnpm build
1414
> ```
1515
1616
To get started:
@@ -25,11 +25,6 @@ Copy the `.env.template` to `.env` and add update `GITHUB_TOKEN` with a GitHub P
2525
GITHUB_TOKEN=<some personal access token>
2626
```
2727

28-
Build static resources (tray icons, twemojis, etc). You only need to rebuild if you change static assets:
29-
```shell
30-
pnpm build
31-
```
32-
3328
Start development mode (includes GraphQL codegen and hot module reload):
3429
```shell
3530
pnpm dev

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@
125125
"vite-plugin-checker": "0.12.0",
126126
"vite-plugin-electron": "0.29.1",
127127
"vite-plugin-electron-renderer": "0.14.6",
128-
"vite-plugin-static-copy": "3.3.0",
129128
"vitest": "4.1.0",
130129
"zustand": "5.0.12"
131130
},

src/renderer/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const Constants = {
5757

5858
GITHUB_HOSTNAME: 'github.com' as Hostname,
5959

60-
OAUTH_DEVICE_FLOW_CLIENT_ID: process.env.OAUTH_CLIENT_ID as ClientID,
60+
OAUTH_DEVICE_FLOW_CLIENT_ID: import.meta.env.VITE_OAUTH_CLIENT_ID as ClientID,
6161

6262
// GitHub Enterprise Cloud with Data Residency uses *.ghe.com domains
6363
GITHUB_ENTERPRISE_CLOUD_DATA_RESIDENCY_HOSTNAME: 'ghe.com',

src/vite-env.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference types="vite/client" />
2+
3+
interface ViteTypeOptions {
4+
strictImportMetaEnv: unknown;
5+
}
6+
7+
interface ImportMetaEnv {
8+
readonly VITE_OAUTH_CLIENT_ID: string;
9+
}
10+
11+
interface ImportMeta {
12+
readonly env: ImportMetaEnv;
13+
}

vite.config.ts

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
13
import { fileURLToPath } from 'node:url';
24

35
import twemoji from '@discordapp/twemoji';
@@ -7,15 +9,15 @@ import type { Plugin } from 'vite';
79
import { defineConfig } from 'vite';
810
import checker from 'vite-plugin-checker';
911
import electron from 'vite-plugin-electron/simple';
10-
import { viteStaticCopy } from 'vite-plugin-static-copy';
1112

1213
import { Constants } from './src/renderer/constants';
1314

1415
/**
15-
* Helper to generate Twemoji static copy targets.
16-
* Extracts all emojis from Constants.EMOJIS and maps them to their SVG filenames.
16+
* Vite plugin that copies static assets to disk on startup.
17+
* Runs in both dev and build modes so the Electron main process can resolve
18+
* asset file URLs without requiring a prior `pnpm build`.
1719
*/
18-
const getTwemojiCopyTargets = () => {
20+
const copyStaticAssetsPlugin = (): Plugin => {
1921
const flatten = (obj: object): string[] =>
2022
Object.values(obj).flatMap((v) =>
2123
Array.isArray(v) ? v : flatten(v as object),
@@ -27,15 +29,59 @@ const getTwemojiCopyTargets = () => {
2729
.split('/')
2830
.pop();
2931

30-
const allEmojis = flatten(Constants.EMOJIS);
31-
const emojiFilenames = allEmojis.map((emoji) =>
32-
extractSvgFilename(twemoji.parse(emoji, { folder: 'svg', ext: '.svg' })),
33-
);
32+
let isBuild = false;
3433

35-
return emojiFilenames.map((filename) => ({
36-
src: `../../node_modules/@discordapp/twemoji/dist/svg/${filename}`,
37-
dest: 'assets/images/twemoji',
38-
}));
34+
const copyAssets = () => {
35+
// Copy the root assets/ directory (images, sounds, etc.) into build/
36+
fs.cpSync(
37+
fileURLToPath(new URL('assets', import.meta.url)),
38+
fileURLToPath(new URL('build/assets', import.meta.url)),
39+
{ recursive: true },
40+
);
41+
42+
// Copy individual Twemoji SVGs needed by the app into build/assets/images/twemoji/
43+
const twemojiSrcDir = fileURLToPath(
44+
new URL('node_modules/@discordapp/twemoji/dist/svg', import.meta.url),
45+
);
46+
const twemojiDestDir = fileURLToPath(
47+
new URL('build/assets/images/twemoji', import.meta.url),
48+
);
49+
50+
fs.mkdirSync(twemojiDestDir, { recursive: true });
51+
52+
const allEmojis = flatten(Constants.EMOJIS);
53+
for (const emoji of allEmojis) {
54+
const filename = extractSvgFilename(
55+
twemoji.parse(emoji, { folder: 'svg', ext: '.svg' }),
56+
);
57+
if (filename) {
58+
fs.copyFileSync(
59+
path.join(twemojiSrcDir, filename),
60+
path.join(twemojiDestDir, filename),
61+
);
62+
}
63+
}
64+
};
65+
66+
return {
67+
name: 'copy-static-assets',
68+
configResolved(config) {
69+
isBuild = config.command === 'build';
70+
},
71+
// In serve/dev mode, copy before the build starts (emptyOutDir doesn't run).
72+
// In build mode, copy after all output is written — buildStart runs before
73+
// Vite's emptyOutDir wipe, so assets copied there would be deleted.
74+
buildStart() {
75+
if (!isBuild) {
76+
copyAssets();
77+
}
78+
},
79+
closeBundle() {
80+
if (isBuild) {
81+
copyAssets();
82+
}
83+
},
84+
};
3985
};
4086

4187
/**
@@ -90,6 +136,9 @@ export default defineConfig(({ command }) => {
90136
main: {
91137
entry: fileURLToPath(new URL('src/main/index.ts', import.meta.url)),
92138
vite: {
139+
// The outer Vite config sets root:'src/renderer', so we must
140+
// explicitly tell the main-process sub-build where to find .env files.
141+
envDir: fileURLToPath(new URL('.', import.meta.url)),
93142
build: {
94143
outDir: fileURLToPath(new URL('build', import.meta.url)),
95144
rollupOptions: {
@@ -118,15 +167,7 @@ export default defineConfig(({ command }) => {
118167
},
119168
},
120169
}),
121-
viteStaticCopy({
122-
targets: [
123-
...getTwemojiCopyTargets(),
124-
{
125-
src: '../../assets',
126-
dest: '.',
127-
},
128-
],
129-
}),
170+
copyStaticAssetsPlugin(),
130171
],
131172
root: 'src/renderer',
132173
publicDir: false as const,
@@ -135,17 +176,5 @@ export default defineConfig(({ command }) => {
135176
outDir: fileURLToPath(new URL('build', import.meta.url)),
136177
emptyOutDir: true,
137178
},
138-
// Define build-time replacements for the main process bundle.
139-
// During CI builds `process.env.OAUTH_CLIENT_ID` will be injected via the environment.
140-
define: isBuild
141-
? {
142-
'process.env.OAUTH_CLIENT_ID': JSON.stringify(
143-
process.env.OAUTH_CLIENT_ID ?? '',
144-
),
145-
}
146-
: {
147-
// Development Keys - See CONTRIBUTING.md
148-
'process.env.OAUTH_CLIENT_ID': JSON.stringify('Ov23liQIkFs5ehQLNzHF'),
149-
},
150179
};
151180
});

0 commit comments

Comments
 (0)