Skip to content
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
6 changes: 2 additions & 4 deletions docs/src/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,11 @@ export default defineConfig({
mocha: {
timeout: 10000
},
watch: true,
abortOnFail: false,
exitOnFinish: true,
headless: false,
startupDelay: 10000,
waitForPlugin: `() => Zotero.MyPlugin.initialized`,
watch: false,
hooks: {}
}
});
Expand All @@ -99,8 +98,6 @@ To handle such cases, use the `test.waitForPlugin` configuration option. This op

## Watch Mode

This feature is still under development.

In watch mode, Scaffold automatically:

- Recompiles source code, reloads plugins, and reruns tests when the source changes.
Expand All @@ -119,6 +116,7 @@ Run tests
Options:
--abort-on-fail Abort the test suite on first failure
--exit-on-finish Exit the test suite after all tests have run
--no-watch Same with `exit-on-finish`
-h, --help display help for command
```

Expand Down
1 change: 1 addition & 0 deletions packages/scaffold/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"fs-extra": "^11.3.0",
"hookable": "^5.5.3",
"octokit": "^4.1.2",
"pathe": "^2.0.3",
"std-env": "^3.8.1",
"tiny-update-notifier": "^2.0.0",
"tinyglobby": "^0.2.12",
Expand Down
14 changes: 7 additions & 7 deletions packages/scaffold/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,16 @@ async function main() {
.description("Run tests")
.option("--abort-on-fail", "Abort the test suite on first failure")
.option("--exit-on-finish", "Exit the test suite after all tests have run")
.option("--no-watch", "Exit the test suite after all tests have run")
.action((options) => {
process.env.NODE_ENV = "test";

Config.loadConfig({}).then((ctx) => {
if (options.abortOnFail) {
ctx.test.abortOnFail = true;
}
if (options.exitOnFinish) {
ctx.test.exitOnFinish = true;
}
Config.loadConfig({
test: {
abortOnFail: options.abortOnFail,
watch: !options.exitOnFinish && options.watch,
},
}).then((ctx) => {
new Test(ctx).run();
});
});
Expand Down
3 changes: 1 addition & 2 deletions packages/scaffold/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,10 @@ const defaultConfig = {
timeout: 10000,
},
abortOnFail: false,
exitOnFinish: false,
headless: false,
startupDelay: 1000,
waitForPlugin: "() => true",
watch: false,
watch: true,
hooks: {},
},
logLevel: "INFO",
Expand Down
13 changes: 13 additions & 0 deletions packages/scaffold/src/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const BASE_DIR = ".scaffold";
export const CACHE_DIR = `${BASE_DIR}/cache`;
export const DEFAULT_PROFILE_DIR = "";
export const DEFAULT_DATA_DIR = "";

// Testser
export const TESTER_BASE_PATH = `${BASE_DIR}/test`;
export const TESTER_PROFILE_DIR = `${TESTER_BASE_PATH}/profile`;
export const TESTER_DATA_DIR = `${TESTER_BASE_PATH}/data`;
export const TESTER_PLUGIN_DIR = `${TESTER_BASE_PATH}/resource`;
export const TESTER_PLUGIN_TESTS_DIR = `${TESTER_PLUGIN_DIR}/content/units`;
export const TESTER_PLUGIN_REF = "zotero-plugin-scaffold-test-runner";
export const TESTER_PLUGIN_ID = "[email protected]";
10 changes: 5 additions & 5 deletions packages/scaffold/src/core/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,28 @@ export default class Build extends Base {
await emptyDir(dist);
await this.ctx.hooks.callHook("build:mkdir", this.ctx);

this.logger.tip("Preparing static assets");
this.logger.tip("Preparing static assets", { space: 1 });
await this.makeAssets();
await this.ctx.hooks.callHook("build:copyAssets", this.ctx);

this.logger.debug("Preparing manifest");
this.logger.debug("Preparing manifest", { space: 2 });
await this.makeManifest();
await this.ctx.hooks.callHook("build:makeManifest", this.ctx);

this.logger.debug("Preparing locale files");
this.logger.debug("Preparing locale files", { space: 2 });
await this.prepareLocaleFiles();
await this.ctx.hooks.callHook("build:fluent", this.ctx);

await this.preparePrefs();

this.logger.tip("Bundling scripts");
this.logger.tip("Bundling scripts", { space: 1 });
await this.esbuild();
await this.ctx.hooks.callHook("build:bundle", this.ctx);

/** ======== build resolved =========== */

if (process.env.NODE_ENV === "production") {
this.logger.tip("Packing plugin");
this.logger.tip("Packing plugin", { space: 1 });
await this.pack();
await this.ctx.hooks.callHook("build:pack", this.ctx);

Expand Down
64 changes: 21 additions & 43 deletions packages/scaffold/src/core/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import type { Context } from "../types/index.js";
import { existsSync } from "node:fs";
import { join } from "node:path";
import process from "node:process";
import chokidar from "chokidar";
import { debounce } from "es-toolkit";
import { watch } from "../utils/watcher.js";
import { ZoteroRunner } from "../utils/zotero-runner.js";
import { Base } from "./base.js";
import Build from "./builder.js";
Expand Down Expand Up @@ -69,47 +68,26 @@ export default class Serve extends Base {
async watch() {
const { source } = this.ctx;

const watcher = chokidar.watch(source, {
ignored: /(^|[/\\])\../, // ignore dotfiles
persistent: true,
});

const onChangeDebounced = debounce(async (path: string) => {
await this.onChange(path).catch((err) => {
// Do not abort the watcher when errors occur
// in builds triggered by the watcher.
this.logger.error(err);
});
}, 500);

watcher
.on("ready", async () => {
await this.ctx.hooks.callHook("serve:ready", this.ctx);
this.logger.clear();
this.logger.ready("Server Ready!");
})
.on("change", async (path) => {
this.logger.clear();
this.logger.info(`${path} changed`);
await onChangeDebounced(path);
})
.on("error", (err) => {
this.logger.fail("Server start failed!");
this.logger.error(err);
});
}

async onChange(path: string) {
await this.ctx.hooks.callHook("serve:onChanged", this.ctx, path);

if (path.endsWith(".ts") || path.endsWith(".tsx")) {
await this.builder.esbuild();
}
else {
await this.builder.run();
}

await this.reload();
watch(
source,
{
onReady: async () => {
await this.ctx.hooks.callHook("serve:ready", this.ctx);
},
onChange: async (path) => {
await this.ctx.hooks.callHook("serve:onChanged", this.ctx, path);

if (path.endsWith(".ts") || path.endsWith(".tsx")) {
await this.builder.esbuild();
}
else {
await this.builder.run();
}

await this.reload();
},
},
);
}

async reload() {
Expand Down
Loading