Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade esbuild #149

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading