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

boilerplate for using fs-watcher #57

Merged
merged 6 commits into from
Dec 27, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ coverage
node_modules
dist
dump
deno.lock
854 changes: 0 additions & 854 deletions deno.lock

This file was deleted.

38 changes: 34 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"test": "vitest",
"watch": "tsc -b --watch --noEmit",
"rts-watch-with-deno": "deno --watch --unstable-sloppy-imports --allow-all ./rtsc/watch.ts",
"compile-all": "deno --watch --unstable-sloppy-imports --allow-all ./rtsc/compile-all.ts",
"rts-watch": "vite-node --watch ./rtsc/watch.ts",
"coverage": "vitest run --coverage",
"build": "tsc -b && vite build",
Expand All @@ -22,6 +23,7 @@
"@types/node": "^22.10.2",
"@uiw/codemirror-theme-abcdef": "^4.23.7",
"@uiw/react-codemirror": "^4.23.7",
"ignore": "^6.0.2",
"index-to-position": "^1.0.0",
"json-stable-stringify": "^1.2.1",
"json-stringify-pretty-compact": "^4.0.0",
Expand Down
124 changes: 124 additions & 0 deletions rtsc/compile-all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { watch } from "node:fs";
import fs from "node:fs/promises";
import process from "node:process";
import path, { basename, dirname, join } from "node:path";
import ignore from "ignore";

import { LibraryManager } from "../src/library-manager.ts";
import { get_globals } from "../src/global-module.ts";
import { core_patterns } from "../src/syntax-core-patterns.ts";
import { parse } from "../src/parse.ts";
import { assert } from "../src/assert.ts";

class DirWatcher {
private dir: string;
private callbacks: { [filename: string]: (path: string) => void } = {};
public onEvent: ((file: string) => void) | undefined = undefined;
constructor(dir: string) {
this.dir = dir;
}
watchFile(path: string, callback: (path: string) => void): { close(): void } {
assert(dirname(path) === this.dir);
const filename = basename(path);
assert(!this.callbacks[filename], `multiple watches for same path '${path}'`);
this.callbacks[filename] = callback;
const close = () => {
assert(this.callbacks[filename] !== undefined, `no callback registered`);
assert(this.callbacks[filename] === callback, `closing wrong watch`);
delete this.callbacks[filename];
};
return { close };
}
processEvent(file: string) {
this.onEvent?.(file);
const callback = this.callbacks[file];
if (callback) callback(join(this.dir, file));
}
}

class WatchFS {
dir_watchers: { [path: string]: DirWatcher } = {};
onRTSFile: (path: string) => void;
constructor(onRTSFile: (path: string) => void) {
this.onRTSFile = onRTSFile;
this.init();
}

watchFile(path: string, callback: (path: string) => void): { close(): void } {
const dir = dirname(path);
const watcher = this.get_or_create_watcher(dir);
return watcher.watchFile(path, callback);
}

private get_or_create_watcher(abspath: string): DirWatcher {
const existing = this.dir_watchers[abspath];
if (existing) return existing;
const watcher = new DirWatcher(abspath);
this.dir_watchers[abspath] = watcher;
watch(abspath, { encoding: "utf8", recursive: false }, (_event, file) => {
assert(file !== null);
watcher.processEvent(file);
});
return watcher;
}

private async init() {
const gitignore_content = await fs.readFile(".gitignore", { encoding: "utf8" });
const ig = ignore().add(gitignore_content).add("/.git");

async function getstat(absp: string) {
try {
return await fs.stat(absp);
} catch (err: any) {
if (err.code === "ENOENT") {
return null;
} else {
throw err;
}
}
}

const check_file = async (file: string, relpath: string, abspath: string) => {
const relp = path.join(relpath, file);
const absp = path.join(abspath, file);
if (this.dir_watchers[absp]) return;
if (ig.ignores(relp)) return;
const stat = await getstat(absp);
if (stat) {
if (stat.isDirectory()) {
seed_dir(relp, absp);
} else if (file.endsWith(".rts")) {
this.onRTSFile(absp);
}
}
};

const seed_dir = async (relpath: string, abspath: string) => {
const watcher = this.get_or_create_watcher(abspath);
assert(!watcher.onEvent);
watcher.onEvent = (file) => check_file(file, relpath, abspath);
fs.readdir(relpath).then((files) =>
files.forEach((file) => check_file(file, relpath, abspath)),
);
};

seed_dir(".", process.cwd());
}
}

const globals = get_globals("es2024.full");
const patterns = core_patterns(parse);
const library_manager = new LibraryManager(patterns, globals, ["es2024.full"], {
watchFile(path, callback) {
return FS.watchFile(path, callback);
},
});

function check_path(rts_file: string) {
assert(rts_file.endsWith(".rts"));
library_manager
.findPackage(rts_file)
.then(([pkg, rel]) => library_manager.ensureUpToDate(pkg, rel, rts_file));
}

const FS = new WatchFS(check_path);
6 changes: 3 additions & 3 deletions src/library-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ abstract class Module implements imported_module {
(x) => (x.dependant_modules = x.dependant_modules.filter((x) => x !== this)),
);
this.imported_modules = [];
dependant_modules.forEach((x) => x.force_recompile());
this.ensureUpToDate();
await Promise.all(dependant_modules.map((x) => x.force_recompile()));
await this.ensureUpToDate();
}

async file_changed(): Promise<void> {
Expand All @@ -202,7 +202,7 @@ abstract class Module implements imported_module {
switch (state.type) {
case "fresh": {
if (t && t > state.mtime) {
this.force_recompile();
await this.force_recompile();
}
return;
}
Expand Down
1 change: 0 additions & 1 deletion src/parse-dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ function parse_statements(statements: TS.NodeArray<TS.Statement>, path: string):
}

export function parse_dts(code: string, my_path: string, full_path: string): ts_exports {
console.log(`parsing ${full_path}`);
const options: TS.CreateSourceFileOptions = {
languageVersion: TS.ScriptTarget.ESNext,
jsDocParsingMode: TS.JSDocParsingMode.ParseNone,
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"root":["./files.test.ts","./generate-stdlibs.test.ts","./vite.config.ts","./vitest.config.ts","./rtsc/watch.ts","./src/assert.ts","./src/ast.ts","./src/data.ts","./src/expander.ts","./src/fs-helpers.ts","./src/generate-stdlibs.ts","./src/generated-stdlibs.ts","./src/global-module.ts","./src/library-manager.ts","./src/llhelpers.ts","./src/parse-dts.ts","./src/parse.ts","./src/pprint.ts","./src/preexpand-handlers.ts","./src/preexpand-helpers.ts","./src/proxy-code.ts","./src/serialize.ts","./src/stx-error.ts","./src/stx.ts","./src/syntax-core-patterns.ts","./src/syntax-structures.ts","./src/tags.ts","./src/zipper.ts","./test-project/index.ts","./test-project/main.rts.ts","./test-project/mod.rts.ts","./ui/astvis.tsx","./ui/app.tsx","./ui/editor.tsx","./ui/main.tsx","./ui/vite-env.d.ts"],"version":"5.7.2"}
{"root":["./files.test.ts","./generate-stdlibs.test.ts","./vite.config.ts","./vitest.config.ts","./rtsc/compile-all.ts","./rtsc/watch.ts","./src/assert.ts","./src/ast.ts","./src/data.ts","./src/expander.ts","./src/fs-helpers.ts","./src/generate-stdlibs.ts","./src/generated-stdlibs.ts","./src/global-module.ts","./src/library-manager.ts","./src/llhelpers.ts","./src/parse-dts.ts","./src/parse.ts","./src/pprint.ts","./src/preexpand-handlers.ts","./src/preexpand-helpers.ts","./src/proxy-code.ts","./src/serialize.ts","./src/stx-error.ts","./src/stx.ts","./src/syntax-core-patterns.ts","./src/syntax-structures.ts","./src/tags.ts","./src/zipper.ts","./test-project/index.ts","./test-project/main.rts.ts","./test-project/mod.rts.ts","./ui/astvis.tsx","./ui/app.tsx","./ui/editor.tsx","./ui/main.tsx","./ui/vite-env.d.ts"],"version":"5.7.2"}
Loading