92 lines
2.9 KiB
TypeScript
92 lines
2.9 KiB
TypeScript
import { Bytes, Observe, Ref, Turn } from "@syndicate-lang/core";
|
|
import { QuasiValue as Q } from "@syndicate-lang/core";
|
|
import { service } from '@syndicate-lang/service';
|
|
import { Contents, File, asConfig, Config, Encoding, toEncoding } from './gen/fs.js';
|
|
import chokidar from 'chokidar';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
|
|
export function main(_argv: string[]) {
|
|
service(args => {
|
|
const config = asConfig(args);
|
|
at config.dataspace {
|
|
during Observe({
|
|
"pattern": :pattern File({
|
|
"label": config.label,
|
|
"path": \Q.lit($relativePath: string),
|
|
"encoding": \Q.lit($encoding0),
|
|
"contents": \_,
|
|
}),
|
|
}) => {
|
|
const encoding = toEncoding(encoding0);
|
|
if (encoding !== void 0 && !path.isAbsolute(relativePath)) {
|
|
trackFile(config, relativePath, encoding);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function trackFile(config: Config<Ref>, relativePath: string, encoding: Encoding) {
|
|
const facet = Turn.activeFacet;
|
|
const absolutePath = path.resolve(config.path, relativePath);
|
|
|
|
field contents: Contents | undefined = void 0;
|
|
at config.dataspace {
|
|
assert File({
|
|
"label": config.label,
|
|
"path": relativePath,
|
|
"encoding": encoding,
|
|
"contents": contents.value!,
|
|
}) when (contents.value !== void 0);
|
|
}
|
|
|
|
let watcher: chokidar.FSWatcher | undefined = void 0;
|
|
|
|
on stop {
|
|
if (watcher !== void 0) {
|
|
watcher.close();
|
|
watcher = void 0;
|
|
}
|
|
}
|
|
|
|
watcher = chokidar.watch(absolutePath, {
|
|
cwd: config.path,
|
|
disableGlobbing: true,
|
|
depth: 0,
|
|
});
|
|
|
|
function readContents() {
|
|
const bytes = fs.readFileSync(absolutePath);
|
|
switch (encoding._variant) {
|
|
case 'utf8':
|
|
try {
|
|
const text = new TextDecoder('utf8', { fatal: true }).decode(bytes);
|
|
contents.value = Contents.text(text);
|
|
} catch {
|
|
contents.value = Contents.invalidText(Bytes.from(bytes));
|
|
}
|
|
break;
|
|
case 'binary':
|
|
contents.value = Contents.binary(Bytes.from(bytes));
|
|
break;
|
|
}
|
|
}
|
|
|
|
function markAbsent() {
|
|
contents.value = Contents.absent();
|
|
}
|
|
|
|
watcher
|
|
.on('ready', () => facet.turn(() => {
|
|
if (contents.value === void 0) {
|
|
markAbsent();
|
|
}
|
|
}))
|
|
.on('add', () => facet.turn(readContents))
|
|
.on('change', () => facet.turn(readContents))
|
|
.on('unlink', () => facet.turn(markAbsent))
|
|
.on('addDir', () => facet.turn(() => contents.value = Contents.directory()))
|
|
.on('unlinkDir', () => facet.turn(markAbsent));
|
|
}
|