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, 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)); }