Writing principles
Reading project files
A detector can read other files of the project with node:fs, starting from the file of the event.
That is how a Dart detector checks the pubspec, or a test detector looks for the test that already exists.
Find the kind of project#
Most detectors want to know one thing first: what kind of project is this?
The answer lies in a file somewhere above, and findUp from the SDK finds it:
import path from "node:path";
import { findUp } from "@wellactually/sdk/v4";
findUp("pubspec.yaml", path.dirname(event.file.path)); // for a file
findUp("pubspec.yaml", event.cwd); // for a prompt, a command or an outputIt returns the absolute path of the nearest file of that name, in that folder or above, or null.
Read a file#
import fs from "node:fs";
import path from "node:path";
import { findUp, onWrite } from "@wellactually/sdk/v4";
onWrite({
globs: ["**/*.dart"],
*detect(event) {
const pubspec = findUp("pubspec.yaml", path.dirname(event.file.path));
// Only Flutter apps: a pure Dart package has no business with widgets.
if (pubspec === null || !fs.readFileSync(pubspec, "utf8").includes("sdk: flutter")) {
return;
}
for (const written of event.written) {
if (written.text.includes("setState(")) {
yield { line: written.line, match: "setState(" };
}
}
},
});What node:fs can do#
| Call | Returns |
|---|---|
readFileSync(path) | The file as text. The encoding is always UTF-8. |
existsSync(path) | Whether a file or folder exists. |
statSync(path) | isFile(), isDirectory() and size. |
readdirSync(path) | The names in a folder, or with { withFileTypes: true } entries with name, isFile() and isDirectory(). |
- It is read-only, and only the synchronous calls exist, because a detector is synchronous.
- A file that is binary, larger than 512 KB or unreadable looks like a file that does not exist.
node:pathhasjoin,dirname,basename,extname,relative,resolve,normalize,isAbsoluteandparse, always with forward slashes.
What you read stays in the detector#
A finding names a line of the event, and a match has to be text from the event.
So what a detector read is for deciding, not for quoting: use the pubspec to tell a Flutter app from a package, not to show the agent what is in it.
Without a network there is nowhere else to send it.
Do not rely on event.project#
event.project is the nearest folder upwards with a .wellactually.json or a .git, and null when that is your home folder or there is none.
A file outside a repository, a monorepo, or a Codex session in a subfolder all make it surprising.
Walk up from event.file.path or event.cwd instead.
Test it#
A test sees only the file of its event, unless you give it a project.
Put a small one into the principle's fixtures folder and pass it to detect:
expect(detect(write("lib/counter.dart", code), { project: "fixtures/flutter_app" })).toEqual([{ line: 4, match: "setState(" }]);Testing explains how fixtures work.