aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-30 00:26:01 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-30 02:24:28 +0100
commit61fd13c55f5e69a9d8b32dd0d74b08870783bcce (patch)
tree4f3f97b1629f3c262bea076b596bc7245ccbc0bd /scripts
parentRevert "refactor: Switch back to consola for prettyness" (diff)
downloadsophie-61fd13c55f5e69a9d8b32dd0d74b08870783bcce.tar.gz
sophie-61fd13c55f5e69a9d8b32dd0d74b08870783bcce.tar.zst
sophie-61fd13c55f5e69a9d8b32dd0d74b08870783bcce.zip
build: Switch to esbuild
We will build all packages except the frontend (where vite remains in use) with esbuild. For some reason, the @yarnpkg/esbuild-plugin-pnp doesn't allow esbuild to load esm modules and we fall back to commonjs for dependencies. Hence we had to switch back to node_modules (but still rely on yarn hardlinking for a more efficient install).
Diffstat (limited to 'scripts')
-rw-r--r--scripts/build.js37
-rw-r--r--scripts/watch.js259
2 files changed, 166 insertions, 130 deletions
diff --git a/scripts/build.js b/scripts/build.js
new file mode 100644
index 0000000..416530d
--- /dev/null
+++ b/scripts/build.js
@@ -0,0 +1,37 @@
1const esbuild = require('esbuild');
2const { join } = require('path');
3const vite = require('vite');
4
5/**
6 * @param {string} packageName
7 * @returns {Promise<import('esbuild').BuildResult>}
8 */
9function buildPackageEsbuild(packageName) {
10 const config = require(`../packages/${packageName}/esbuild.config.js`);
11 return esbuild.build(config);
12}
13
14function buildPackageVite(packageName) {
15 return vite.build({
16 configFile: join(__dirname, `../packages/${packageName}/vite.config.js`),
17 });
18}
19
20function buildAll() {
21 // Esbuild can natively load a typescript main file,
22 // so we don't need to bundle `service-shared`.
23 // We onyl bundle `shared` for vite.
24 const buildShared = buildPackageEsbuild('shared');
25 return Promise.all([
26 buildShared.then(() => buildPackageEsbuild('main')),
27 buildPackageEsbuild('service-inject'),
28 buildPackageEsbuild('service-preload'),
29 buildPackageEsbuild('preload'),
30 buildPackageVite('renderer'),
31 ]);
32}
33
34buildAll().catch((err) => {
35 console.error(err);
36 process.exit(1);
37});
diff --git a/scripts/watch.js b/scripts/watch.js
index 82dc01e..367ab88 100644
--- a/scripts/watch.js
+++ b/scripts/watch.js
@@ -1,32 +1,23 @@
1#!/usr/bin/env node
2
3// @ts-check 1// @ts-check
4 2
3const esbuild = require('esbuild');
5const { spawn } = require('child_process'); 4const { spawn } = require('child_process');
5const chokidar = require('chokidar');
6const electronPath = require('electron'); 6const electronPath = require('electron');
7const { build, createLogger, createServer } = require('vite'); 7const { join } = require('path');
8const { createServer } = require('vite');
8 9
9/** @type {string} */ 10process.env.MODE = 'development';
10const mode = process.env.MODE = process.env.MODE || 'development'; 11process.env.NODE_ENV = 'development';
11 12
12/** @type {import('vite').LogLevel} */ 13/** @type {string} */
13const LOG_LEVEL = 'info'; 14const sharedPackageSource = packageSource('shared');
14 15
15/** @type {import('vite').InlineConfig} */ 16/** @type {string} */
16const sharedConfig = { 17const serviceSharedPackageSource = packageSource('service-shared');
17 mode,
18 build: {
19 watch: {},
20 },
21 logLevel: LOG_LEVEL,
22};
23 18
24/** 19/** @type {RegExp[]} */
25 * Messages on stderr that match any of the contained patterns will be stripped from output 20const stderrIgnorePatterns = [
26 *
27 * @type {RegExp[]}
28 */
29const stderrFilterPatterns = [
30 // warning about devtools extension 21 // warning about devtools extension
31 // https://github.com/cawa-93/vite-electron-builder/issues/492 22 // https://github.com/cawa-93/vite-electron-builder/issues/492
32 // https://github.com/MarshallOfSound/electron-devtools-installer/issues/143 23 // https://github.com/MarshallOfSound/electron-devtools-installer/issues/143
@@ -37,27 +28,102 @@ const stderrFilterPatterns = [
37]; 28];
38 29
39/** 30/**
40 * @param {{name: string; configFile: string; writeBundle?: import('rollup').OutputPlugin['writeBundle'] }} config 31 * @param {string} packageName
41 * @returns {Promise<unknown>} 32 * @returns {string}
33 */
34function packageSource(packageName) {
35 return join(__dirname, `../packages/${packageName}/src`);
36}
37
38/**
39 * @param {string} packageName
40 * @param {string[]} [extraPaths]
41 * @param {() => void} [callback]
42 * @return {Promise<void>}
43 */
44async function setupEsbuildWatcher(packageName, extraPaths, callback) {
45 const config = require(`../packages/${packageName}/esbuild.config.js`);
46 config.logLevel = 'info';
47 config.incremental = true;
48 const incrementalBuild = await esbuild.build(config);
49 const paths = [
50 packageSource(packageName),
51 ...(extraPaths || []),
52 ];
53 const watcher = chokidar.watch(paths, {
54 ignored: /(^|[\/\\])\.|__(tests|mocks)__|\.(spec|test)\.[jt]sx?$/,
55 ignoreInitial: true,
56 persistent: true,
57 });
58 if (callback) {
59 callback();
60 }
61 watcher.on('change', () => {
62 incrementalBuild.rebuild().then(() => {
63 console.log(`\u26a1 Reloading package ${packageName}`);
64 if (callback) {
65 callback();
66 }
67 }).catch((err) => {
68 const errCount = err.errors.length;
69 console.error(
70 '\ud83d\udd25',
71 errCount,
72 errCount > 1 ? 'errors' : 'error',
73 'while rebuilding package',
74 packageName
75 );
76 });
77 });
78}
79
80/**
81 * @param {string} packageName
82 * @return {Promise<import('vite').ViteDevServer>}
42 */ 83 */
43function getWatcher({ name, configFile, writeBundle }) { 84async function setupDevServer(packageName) {
44 return build({ 85 const viteDevServer = await createServer({
45 ...sharedConfig, 86 build: {
46 configFile, 87 watch: {
47 plugins: [ 88 skipWrite: true,
48 { 89 clearScreen: false,
49 name,
50 writeBundle,
51 }, 90 },
52 ], 91 },
92 configFile: join(__dirname, `../packages/${packageName}/vite.config.js`),
93 });
94 await viteDevServer.listen();
95 return viteDevServer;
96}
97
98/**
99 * @param {(event: import('vite').HMRPayload) => void} sendEvent
100 * @return {Promise<void>}
101 */
102function setupPreloadPackageWatcher(sendEvent) {
103 return setupEsbuildWatcher('preload', [sharedPackageSource], () => {
104 sendEvent({
105 type: 'full-reload',
106 });
107 });
108}
109
110/**
111 * @param {string} packageName
112 * @param {(event: import('vite').HMRPayload) => void} sendEvent
113 * @return {Promise<void>}
114 */
115function setupServicePackageWatcher(packageName, sendEvent) {
116 return setupEsbuildWatcher(packageName, [serviceSharedPackageSource], () => {
117 sendEvent({
118 type: 'custom',
119 event: 'sophie:reload-services',
120 });
53 }); 121 });
54} 122}
55 123
56/** 124/**
57 * Start or restart App when source files are changed.
58 *
59 * @param {import('vite').ViteDevServer} viteDevServer 125 * @param {import('vite').ViteDevServer} viteDevServer
60 * @returns {Promise<unknown>} 126 * @return {Promise<void>}
61 */ 127 */
62function setupMainPackageWatcher(viteDevServer) { 128function setupMainPackageWatcher(viteDevServer) {
63 // Write a value to an environment variable to pass it to the main process. 129 // Write a value to an environment variable to pass it to the main process.
@@ -67,122 +133,55 @@ function setupMainPackageWatcher(viteDevServer) {
67 const path = '/'; 133 const path = '/';
68 process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}${path}`; 134 process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}${path}`;
69 135
70 const logger = createLogger(
71 LOG_LEVEL,
72 {
73 prefix: '[main]',
74 },
75 );
76
77 /** @type {import('child_process').ChildProcessWithoutNullStreams | null} */ 136 /** @type {import('child_process').ChildProcessWithoutNullStreams | null} */
78 let spawnProcess = null; 137 let spawnProcess = null;
79 138
80 return getWatcher({ 139 return setupEsbuildWatcher(
81 name: 'reload-app-on-main-package-change', 140 'main',
82 configFile: 'packages/main/vite.config.js', 141 [
83 writeBundle() { 142 serviceSharedPackageSource,
143 sharedPackageSource
144 ],
145 () => {
84 if (spawnProcess !== null) { 146 if (spawnProcess !== null) {
85 spawnProcess.kill('SIGINT'); 147 spawnProcess.kill('SIGINT');
86 spawnProcess = null; 148 spawnProcess = null;
87 } 149 }
88 150
89 spawnProcess = spawn(String(electronPath), ['.']); 151 spawnProcess = spawn(String(electronPath), ['.'], {
90 152 stdio: ['inherit', 'inherit', 'pipe'],
91 spawnProcess.stdout.on('data', (data) => {
92 if (data.toString().trim() !== '') {
93 logger.warn(data.toString(), {timestamp: true})
94 }
95 }); 153 });
96 154
97 spawnProcess.stderr.on('data', (data) => { 155 spawnProcess.stderr.on('data', (data) => {
98 const trimmedData = data.toString().trim(); 156 const stderrString = data.toString('utf-8').trimRight();
99 if (trimmedData === '') { 157 if (!stderrIgnorePatterns.some((r) => r.test(stderrString))) {
100 return; 158 console.error(stderrString);
101 } 159 }
102 const mayIgnore = stderrFilterPatterns.some((r) => r.test(data));
103 if (mayIgnore) {
104 return;
105 }
106 logger.error(data, { timestamp: true });
107 });
108 },
109 });
110}
111
112/**
113 * Reload App when source files are changed.
114 *
115 * @param {import('vite').ViteDevServer} viteDevServer
116 * @returns {Promise<unknown>}
117 */
118function setupPreloadPackageWatcher(viteDevServer) {
119 return getWatcher({
120 name: 'reload-page-on-preload-package-change',
121 configFile: 'packages/preload/vite.config.js',
122 writeBundle() {
123 viteDevServer.ws.send({
124 type: 'full-reload',
125 });
126 },
127 });
128}
129
130/**
131 * Reload services when source files are changed.
132 *
133 * @param {import('vite').ViteDevServer} viteDevServer
134 * @param {string} packageName The name of the package to watch.
135 * @returns {Promise<unknown>}
136 */
137function setupServicePreloadPackageWatcher(viteDevServer, packageName) {
138 return getWatcher({
139 name: `reload-services-on-${packageName}-package-change`,
140 configFile: `packages/${packageName}/vite.config.js`,
141 writeBundle() {
142 console.log('wrote');
143 viteDevServer.ws.send({
144 type: 'custom',
145 event: 'sophie:reload-services',
146 }); 160 });
147 }, 161 },
148 }); 162 );
149}
150
151/**
152 * Rebuild package when source files are changed.
153 *
154 * @param {string} packageName The name of the package to watch.
155 * @returns {Promise<unknown>}
156 */
157function setupSharedPackageWatcher(packageName) {
158 return getWatcher({
159 name: `rebuild-package-on-${packageName}-package-change`,
160 configFile: `packages/${packageName}/vite.config.js`,
161 });
162} 163}
163 164
164/** 165/**
165 * @returns {Promise<unknown>} 166 * @returns {Promise<void>}
166 */ 167 */
167async function setupDevEnvironment() { 168async function setupDevEnvironment() {
168 const serviceSharedWatcher = setupSharedPackageWatcher('service-shared'); 169 /** @type {import('vite').ViteDevServer | null} */
169 170 let viteDevServer = null;
170 await setupSharedPackageWatcher('shared'); 171 /** @type {(event: import('vite').HMRPayload) => void} */
171 172 const sendEvent = (event) => {
172 const viteDevServer = await createServer({ 173 if (viteDevServer !== null) {
173 ...sharedConfig, 174 viteDevServer.ws.send(event);
174 configFile: 'packages/renderer/vite.config.js', 175 }
175 }); 176 };
176 await viteDevServer.listen();
177
178 await Promise.all([ 177 await Promise.all([
179 setupPreloadPackageWatcher(viteDevServer), 178 setupEsbuildWatcher('shared'),
180 serviceSharedWatcher.then(() => Promise.all([ 179 setupPreloadPackageWatcher(sendEvent),
181 setupServicePreloadPackageWatcher(viteDevServer, 'service-inject'), 180 setupServicePackageWatcher('service-inject', sendEvent),
182 setupServicePreloadPackageWatcher(viteDevServer, 'service-preload'), 181 setupServicePackageWatcher('service-preload', sendEvent),
183 ])),
184 ]); 182 ]);
185 183 viteDevServer = await setupDevServer('renderer');
184 console.log('\ud83c\udf80 Sophie is starting up')
186 return setupMainPackageWatcher(viteDevServer); 185 return setupMainPackageWatcher(viteDevServer);
187} 186}
188 187