diff --git a/scripts/build.mjs b/scripts/build.mjs new file mode 100644 index 0000000..38bfcdb --- /dev/null +++ b/scripts/build.mjs @@ -0,0 +1,40 @@ +process.env.NODE_ENV = 'production' + +import { build as viteBuild } from 'vite' +import { build as electronBuild2 } from 'electron-builder' +import { config as builderConfig } from './electron-builder.config.mjs' +import chalk from 'chalk' + +const TAG = chalk.bgBlue('[build.mjs]') + +const viteConfigs = { + main: 'src/main/vite.config.ts', + preload: 'src/preload/vite.config.ts', + reactTs: 'src/react-ts/vite.config.ts', +} + +async function buildElectron() { + for (const [name, configPath] of Object.entries(viteConfigs)) { + console.group(TAG, name) + await viteBuild({ configFile: configPath, mode: process.env.NODE_ENV }) + console.groupEnd() + console.log() // for beautiful log. + } +} + +async function packElectron() { + return electronBuild2({ config: builderConfig }) + .then(result => { + console.log(TAG, chalk.green(`electron-builder.build result - ${result}`)) + }) +} + +; (async () => { + try { + await buildElectron() + await packElectron() + } catch (error) { + console.error(error) + process.exit(1) + } +})() diff --git a/scripts/build.ts b/scripts/build.ts deleted file mode 100644 index f16fb8e..0000000 --- a/scripts/build.ts +++ /dev/null @@ -1,76 +0,0 @@ -process.env.NODE_ENV = 'production' - -import { join, relative } from 'path' -import { build as viteBuild2 } from 'vite' -import { build as electronBuild2 } from 'electron-builder' -import { rollup, RollupOptions, OutputOptions, RollupOutput } from 'rollup' -import { config as builderConfig } from './electron-builder.config' -import chalk from 'chalk' -import { - mainOptions, - preloadOptions, - BuildResult, -} from './utils' - -const TAG = chalk.bgGray('[build.ts]') - -// build main、preload -async function rollupBuild(options: RollupOptions): Promise { - try { - const build = await rollup(options) - const optOutput = (options.output || {}) as OutputOptions - const output = await build.write(optOutput) - - output.output.forEach(out => { - const relativePath = relative(__dirname, optOutput.dir as string) - console.log(TAG, chalk.green(`Build successful - ${join(relativePath, out.fileName)}`)) - }) - - return [null, output] - } catch (error: any) { - console.error(TAG, chalk.red('Build failed:\n'), error) - return [error, null] - } -} - -// build react-ts -async function buildReactTs(): Promise { - try { - const output = await viteBuild2({ - root: join(__dirname, '../react-ts'), - configFile: join(__dirname, '../react-ts/vite.config.ts'), - }) as RollupOutput - - return [null, output] - } catch (error: any) { - return [error, null] - } -} - -// build electron -async function electronBuild() { - try { - const result = await electronBuild2({ config: builderConfig }) - - console.log(TAG, chalk.green(`electron-builder.build result - ${result}`)) - return [null, result] - } catch (error) { - return [error, null] - } -} - -; (async () => { - console.log(TAG, chalk.blue('Build with rollup.')) - try { - await Promise.all([ - // Avoid logs cleaned by vite - rollupBuild(mainOptions()), - rollupBuild(preloadOptions()), - ]) - await buildReactTs() - await electronBuild() - } catch (error) { - console.error(TAG, chalk.red(error)) - process.exit(1) - } -})(); diff --git a/scripts/dev.ts b/scripts/dev.ts deleted file mode 100644 index a908d46..0000000 --- a/scripts/dev.ts +++ /dev/null @@ -1,80 +0,0 @@ -process.env.NODE_ENV = 'development' - -import { join } from 'path' -import electron from 'electron' -import { spawn, ChildProcess } from 'child_process' -import { createServer as createViteServer } from 'vite' -import { RollupWatcher, RollupWatcherEvent, watch } from 'rollup' -import WebSocket from 'ws' -import chalk from 'chalk' -import pkg from '../package.json' -import { - mainOptions, - preloadOptions, -} from './utils' -import { createWsServer, formatWsSendData } from './ws' - -const TAG = chalk.bgGray('[dev.ts]') - -function eventHandle(ev: RollupWatcherEvent) { - if (ev.code === 'ERROR') { - console.error(TAG, chalk.red(ev.error)) - } else if (ev.code === 'BUNDLE_START') { - console.log(TAG, chalk.blue(`Rebuild - ${ev.output}`)) - } -} - -function watchMain(): RollupWatcher { - let electronProcess: ChildProcess | null = null - - return watch(mainOptions()) - .on('event', ev => { - if (ev.code === 'END') { - electronProcess && electronProcess.kill() - electronProcess = spawn( - electron as unknown as string, - [join(__dirname, '..', pkg.main)], - { stdio: 'inherit', env: Object.assign(process.env, pkg.env) }, - ) - } else if (ev.code === 'ERROR') { - electronProcess && electronProcess.kill() - electronProcess = null - } - - eventHandle(ev) - }) -} - -function watchPreload(): RollupWatcher { - const wssObj = createWsServer({ TAG }) - - return watch(preloadOptions()) - .on('event', ev => { - if (ev.code === 'END') { - // Hot reload renderer process !!! - if (wssObj.instance?.readyState === WebSocket.OPEN) { - console.log(TAG, chalk.yellow('Hot reload renderer process')) - wssObj.instance.send(formatWsSendData({ cmd: 'reload', data: Date.now() })) - } - } - - eventHandle(ev) - }) -} - -; (async () => { - try { - const server = await (await createViteServer({ - root: join(__dirname, '../react-ts'), - configFile: join(__dirname, '../react-ts/vite.config.ts'), - })).listen() - const { host = '127.0.0.1', port = 3000 } = server.config.server - - console.log(TAG, chalk.yellow(`Server run at - http://${host}:${port}`)) - - watchPreload() - watchMain() - } catch (error) { - console.error(TAG, chalk.red(error)) - } -})(); diff --git a/scripts/electron-builder.config.ts b/scripts/electron-builder.config.mjs similarity index 82% rename from scripts/electron-builder.config.ts rename to scripts/electron-builder.config.mjs index ca3aaa0..4fd5b9e 100644 --- a/scripts/electron-builder.config.ts +++ b/scripts/electron-builder.config.mjs @@ -1,14 +1,16 @@ -import { Configuration } from 'electron-builder' -const config: Configuration = { +/** + * @type {import('electron-builder').Configuration} + */ +const config = { appId: "308487730@qq.com", asar: true, directories: { output: "release/${version}" }, files: [ - "!node_modules", - "dist/**" + "dist", + "package.json" ], mac: { artifactName: "${productName}_${version}.${ext}", diff --git a/scripts/rollup.config.ts b/scripts/rollup.config.ts deleted file mode 100644 index 8240cb8..0000000 --- a/scripts/rollup.config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { builtinModules } from 'module' -import { RollupOptions } from 'rollup' -import { nodeResolve } from '@rollup/plugin-node-resolve' -import typescript from '@rollup/plugin-typescript' -import commonjs from '@rollup/plugin-commonjs' -import replace from '@rollup/plugin-replace' -// import swc from 'rollup-plugin-swc' - -function optionsFactory(options: RollupOptions): RollupOptions { - return { - input: options.input, - output: { - name: '[name].js', - format: 'cjs', - ...options.output, - }, - plugins: [ - commonjs(), - nodeResolve({ - extensions: ['.ts', '.js', 'json'], - }), - typescript(), - // swc(), Error: Cannot find module 'regenerator-runtime', - replace({ - ...Object - .entries({ NODE_ENV: process.env.NODE_ENV }) - .reduce( - (acc, [k, v]) => Object.assign(acc, { [`process.env.${k}`]: JSON.stringify(v) }), - {}, - ), - preventAssignment: true, - }), - ], - external: [ - 'electron', - ...builtinModules, - ], - } -} - -export { optionsFactory } diff --git a/scripts/utils.ts b/scripts/utils.ts deleted file mode 100644 index 4d1e5af..0000000 --- a/scripts/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { join } from 'path' -import { readdirSync } from 'fs' -import { OutputOptions, rollup, RollupOptions, RollupOutput, RollupError } from 'rollup' -import { optionsFactory } from './rollup.config' - -export type BuildResult = [RollupError | null, RollupOutput | null] - -function mainOptions(): RollupOptions { - return optionsFactory({ - input: join(__dirname, '../main/index.ts'), - output: { - dir: 'dist/main', - }, - }) -} - -function preloadOptions(): RollupOptions { - const dirs = readdirSync(join(__dirname, '../preload')) - const inputs = dirs.filter(name => /^index\..+\.ts$/.test(name)) - - return optionsFactory({ - input: inputs.map(input => join(__dirname, '../preload', input)), - output: { - dir: 'dist/preload', - }, - }) -} - -export { - mainOptions, - preloadOptions, -} - diff --git a/scripts/watch.mjs b/scripts/watch.mjs new file mode 100644 index 0000000..6273268 --- /dev/null +++ b/scripts/watch.mjs @@ -0,0 +1,80 @@ +process.env.NODE_ENV = 'production' + +import { readFileSync } from 'fs' +import { join } from 'path' +import electron from 'electron' +import { spawn } from 'child_process' +import { createServer, build as viteBuild } from 'vite' +import chalk from 'chalk' + +const TAG = chalk.bgGreen('[dev.mjs]') +const pkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) + +/** + * @param {{ name: string; configFile: string; writeBundle: import('rollup').OutputPlugin['writeBundle'] }} param0 + * @returns {import('rollup').RollupWatcher} + */ +function getWatcher({ name, configFile, writeBundle }) { + return viteBuild({ + // Options here precedence over configFile + build: { + watch: {}, + }, + configFile, + plugins: [ + { name, writeBundle }, + ], + }) +} + +/** + * @returns {Promise} + */ +async function watchMain() { + /** + * @type {import('child_process').ChildProcessWithoutNullStreams | null} + */ + let electronProcess = null + + /** + * @type {import('rollup').RollupWatcher} + */ + const watcher = await getWatcher({ + name: 'electron-main-watcher', + configFile: 'src/main/vite.config.ts', + writeBundle() { + electronProcess && electronProcess.kill() + electronProcess = spawn(electron, ['.'], { + stdio: 'inherit', + env: Object.assign(process.env, pkg.env), // Why don't work? + }) + }, + }) + + return watcher +} + +/** + * @param {import('vite').ViteDevServer} viteDevServer + * @returns {Promise} + */ +async function watchPreload(viteDevServer) { + return getWatcher({ + name: 'electron-preload-watcher', + configFile: 'src/preload/vite.config.ts', + writeBundle() { + viteDevServer.ws.send({ + type: 'full-reload', + }) + }, + }) +} + +; (async () => { + const viteDevServer = await createServer({ configFile: 'src/react-ts/vite.config.ts' }) + + await viteDevServer.listen() + + await watchPreload(viteDevServer) + await watchMain() +})() diff --git a/scripts/ws.ts b/scripts/ws.ts deleted file mode 100644 index c00e6f9..0000000 --- a/scripts/ws.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Hot reload from preload script during development - */ -import WebSocket from 'ws' -import chalk from 'chalk' -import pkg from '../package.json' - -export interface CreateWsServerOptions { - TAG: string -} - -export function createWsServer(options: CreateWsServerOptions) { - const { TAG } = options - const port = pkg.env.PORT_WS - const host = pkg.env.HOST || '127.0.0.1' - const wss = new WebSocket.Server({ host, port }) - const wssObj: { wss: WebSocket.Server; instance: WebSocket | null } = { wss, instance: null } - - console.log(TAG, 'Wss run at - ' + chalk.yellow(`ws://${host}:${port}`)) - - wss.on('connection', ws => { - console.log(TAG, chalk.yellow(`wss.on('connection')`)) - - wssObj.instance = ws - ws.on('message', message => { - console.log(TAG, `ws.on('message'):`, message.toString()) - }) - - ws.send(formatWsSendData({ cmd: 'message', data: 'connected.' })) - }) - - wss.on('close', () => { - console.log(TAG, chalk.gray(`wss.on('close')`)) - - wssObj.instance = null - }) - - return wssObj -} - -export function formatWsSendData(json: { cmd: string, data?: any }) { - return JSON.stringify(json) -}