Skip to content

Commit

Permalink
upgrade esbuild (#149)
Browse files Browse the repository at this point in the history
- upgrades esbuild to latest version, as part of core dependencies review
- incorporate lint into build step, and prefer yarn run build to calling ./build.sh directly
- update README and some documentation (comments)
  • Loading branch information
cloverich authored Jan 29, 2024
1 parent 77a22ca commit 01371d9
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 228 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@

Electron based markdown journaling application, in the spirit of [incremental note taking][incr-notes].

**Status**: Hobby project, prototyping and re-working UX to try out various concepts with little regard for usability, stability, or appearances. Will remove this clause when I can guarantee your notes won't _poof_ and the experience isn't confusing. Reach out if you are interested in journaling concepts and I'll provide a tour.

Tech stack:

- Electron and esbuild
- Typescript
- React and mobx
- Slate and Plate (Notion style WSYIWYG)
**Status**: Hobby project, prototyping and re-working UX to try out various concepts with little regard for usability, stability, or appearances.

## Development

Expand All @@ -28,9 +21,16 @@ yarn run electron-rebuild

See scripts/dev.js for specifics on how the source files are compiled and re-loaded in development.

### Tech stack

- Electron and esbuild
- Typescript
- React and mobx
- Slate and Plate (Notion style WSYIWYG)

## Build and release

- Read and use the `build.sh` script
- Use `yarn build`
- Make a Github release

At a high level, the build is comprised of:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"lint:types:check": "tsc --noEmit --skipLibCheck",
"postinstall": "yarn run electron-rebuild",
"prestart": "tailwindcss -i ./src/index.css -o ./src/index-compiled.css",
"prebuild": "tailwindcss -i ./src/index.css -o ./src/index-compiled.css",
"prebuild": "yarn run lint && tailwindcss -i ./src/index.css -o ./src/index-compiled.css",
"start": "node ./scripts/dev.js",
"test": "mocha -r esm -r ts-node/register src/**/*.test.ts",
"test:one": "mocha -r ts-node/register -r esm"
Expand Down Expand Up @@ -50,7 +50,7 @@
"date-fns": "^3.3.1",
"electron": "^28.2.0",
"emotion": "^10.0.27",
"esbuild": "^0.13.8",
"esbuild": "^0.20.0",
"esm": "^3.2.25",
"evergreen-ui": "^7.1.9",
"klaw": "^3.0.0",
Expand Down
165 changes: 92 additions & 73 deletions scripts/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,90 +3,49 @@ const cp = require("child_process");
const electron = require("electron");
const lodash = require("lodash");

// After successful rebuild, log results
function afterRebuild(name, cb) {
return (error, result) => {
if (error) {
console.error(`${name} bundle completed with error`, error);
} else {
console.log(`${name} bundle completed`);
restartElectron();
}
};
}
// Builds and watches the application for development
// Bundles the renderer, preload, and main processes, and
// starts Electron, then watches for changes

// After successful build, log results
function afterBuild(name) {
return ({ errors, warnings, ...rest }) => {
if (errors.length) {
console.error(`${name} bundle completed with errors`, errors);
} else if (warnings.length) {
console.warn(`${name} bundle completed with warnings`, warnings);
} else {
console.log(`${name} bundle completed`);
}

// note: until I see errors or warnings, unsure if I should have any special behavior...
startElectron();
};
}
const startTracker = new Set();
let didStart = false;

// build renderer bundle
esbuild
.build({
entryPoints: ["src/index.tsx"],
outfile: "src/renderer.bundle.js",
bundle: true,
platform: "browser",
watch: {
onRebuild: afterRebuild("renderer"),
},
})
.then(afterBuild("renderer"), console.error);
// esbuild callbacks work as plugins; this one handles (re)starting the electron
// process after the application bundles are built
function startElectronPlugin(name) {
return {
name: "(Re)start Electron",
setup(build) {
build.onEnd((result) => {
// NOTE: This will not handle type checking; see type checking call below
if (result.errors.length) {
console.error(`${name} bundle completed with errors`, result.errors);
} else {
console.log(`${name} bundle completed`);
startTracker.add(name);
}

// build preload bundle
esbuild
.build({
entryPoints: ["src/preload/index.ts"],
outfile: "src/preload.bundle.js",
bundle: true,
platform: "node",
external: ["knex", "electron", "electron-store", "better-sqlite3"],
watch: {
onRebuild: afterRebuild("preload"),
},
})
.then(afterBuild("preload"), console.error);
if (startTracker.size !== 3) return;

// build electron main bundle
esbuild
.build({
entryPoints: ["src/electron/index.js"],
outfile: "src/main.bundle.js",
bundle: true,
platform: "node",
external: ["electron", "electron-store", "better-sqlite3"],
watch: {
onRebuild: afterRebuild("main"),
if (didStart) {
restartElectron();
} else {
didStart = true;
startElectron();
}
});
},
})
.then(afterBuild("main"), console.error);
};
}

// For holding the spawned Electron main process, so it can
// be .kill()'ed on re-start
let eprocess;

// Track build completions, see below
let startCounter = 0;

// Start the electron main process
// Start the electron main process (first time)
function startElectron() {
// Naive way to wait for all three bundles before starting the first time, since
// main bundle completes quickest
startCounter++;
if (startCounter < 3) return;

console.log("starting electron");
checkTypes();
eprocess = cp.spawn(`${electron}`, ["src/main.bundle.js"], {
stdio: "inherit",
});
Expand All @@ -98,10 +57,17 @@ function startElectron() {
}

// Re-start the main process
// todo: This works, but is heavy-handed since it fully re-starts the process each time.
// Ideally renderer code would hot-reload, or merely refresh and load from a dev-server
// instead or restarting electron
const restartElectron = lodash.debounce(function startElectron() {
if (eprocess) eprocess.kill("SIGTERM");

console.log("starting electron");
// todo: This was a quick hack to get type errors to show up in console. Re-write this as a
// plugin that checks the relevant types (renderer, preload) and fails the build (and, ideally,
// is incremental or something, rather than a fresh sub-process)
checkTypes();
console.log("restarting electron");
eprocess = cp.spawn(`${electron}`, ["src/main.bundle.js"], {
stdio: "inherit",
});
Expand All @@ -111,3 +77,56 @@ const restartElectron = lodash.debounce(function startElectron() {
process.exit(1);
});
}, 200);

const checkTypes = lodash.debounce(function checkTypes() {
const typesProcess = cp.spawn("yarn", ["tsc", "--noEmit"], {
stdio: "inherit",
});

typesProcess.on("error", (error) => {
console.error("types error", error, error.message);
process.exit(1);
});
}, 200);

async function watchRenderer() {
const ctxRenderer = await esbuild.context({
entryPoints: ["src/index.tsx"],
outfile: "src/renderer.bundle.js",
bundle: true,
platform: "browser",
plugins: [startElectronPlugin("renderer")],
});

await ctxRenderer.watch();
}

async function watchPreload() {
const ctxPreload = await esbuild.context({
entryPoints: ["src/preload/index.ts"],
outfile: "src/preload.bundle.js",
bundle: true,
platform: "node",
external: ["knex", "electron", "electron-store", "better-sqlite3"],
plugins: [startElectronPlugin("preload")],
});

await ctxPreload.watch();
}

async function watchMain() {
const ctxMain = await esbuild.context({
entryPoints: ["src/electron/index.js"],
outfile: "src/main.bundle.js",
bundle: true,
platform: "node",
external: ["electron", "electron-store", "better-sqlite3"],
plugins: [startElectronPlugin("main")],
});

await ctxMain.watch();
}

watchMain();
watchPreload();
watchRenderer();
73 changes: 35 additions & 38 deletions scripts/production.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,47 @@
const esbuild = require("esbuild");
const cp = require("child_process");
const electron = require("electron");

// After successful build, log results
function afterBuild(name) {
return ({ errors, warnings, ...rest }) => {
console.log(rest);
if (errors.length) {
console.error(`${name} bundle completed with errors`, errors);
process.exit(1);
} else if (warnings.length) {
console.warn(`${name} bundle completed with warnings`, warnings);
} else {
console.log(`${name} bundle completed`);
}
return {
name: `after-build-${name}`,
setup(build) {
build.onEnd((result) => {
if (result.errors.length) {
console.error(`${name} bundle completed with errors`, errors);
process.exit(1);
} else {
console.log(`${name} bundle completed`);
}
});
},
};
}

// build renderer bundle
esbuild
.build({
entryPoints: ["src/index.tsx"],
outfile: "src/renderer.bundle.js",
bundle: true,
platform: "browser",
})
.then(afterBuild("renderer"), console.error);
esbuild.build({
entryPoints: ["src/index.tsx"],
outfile: "src/renderer.bundle.js",
bundle: true,
platform: "browser",
plugins: [afterBuild("renderer")],
});

// build preload bundle
esbuild
.build({
entryPoints: ["src/preload/index.ts"],
outfile: "src/preload.bundle.js",
bundle: true,
platform: "node",
external: ["knex", "electron", "electron-store", "better-sqlite3"],
})
.then(afterBuild("preload"), console.error);
esbuild.build({
entryPoints: ["src/preload/index.ts"],
outfile: "src/preload.bundle.js",
bundle: true,
platform: "node",
external: ["knex", "electron", "electron-store", "better-sqlite3"],
plugins: [afterBuild("preload")],
});

// build electron main bundle
esbuild
.build({
entryPoints: ["src/electron/index.js"],
outfile: "src/main.bundle.js",
bundle: true,
platform: "node",
external: ["electron", "electron-store", "better-sqlite3"],
})
.then(afterBuild("main"), console.error);
esbuild.build({
entryPoints: ["src/electron/index.js"],
outfile: "src/main.bundle.js",
bundle: true,
platform: "node",
external: ["electron", "electron-store", "better-sqlite3"],
plugins: [afterBuild("main")],
});
Loading

0 comments on commit 01371d9

Please sign in to comment.