Add ESLint and Prettier, and close the node test gaps
Tooling: - Prettier as the single formatting authority (110 columns, single quotes, measured against the existing style); Markdown excluded because table-cell padding turns the README glossary into 1000-character lines. - Type-aware ESLint ending in eslint-config-prettier, so a rule always means "this is a bug", never "this looks different". - .editorconfig and an `npm run check` that runs typecheck, lint, format and tests in one go. Findings fixed on the first run: three more duplicate imports, four String(unknown) calls (parameter types narrowed instead of casts), two dead initialisers, two redundant casts, a console.log for a real error. Two warnings were false here and are disabled with a reason in place. Tests 132 -> 269: - node-catalog.test.ts covers every node with a run(), with a guard that fails when a new node has no row. - behavior-events.test.ts covers the ten event nodes through a running World, checking timing rather than existence. - Input's state machine (press/release/releaseAll) is reachable without DOM events now; the DOM handlers are thin adapters. Compare fell back to == without a configured operator although the schema default is <; aligned. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f140c2fd77
commit
d54a620ebd
113 files changed
+5049
-666
No files matched your search
@@ -1,4 +1,11 @@
|
||||
import { edgesInto, edgesOfNode, findNode, type GraphEdge, type GraphNode, portsCompatible } from '../graph/Graph';
|
||||
import {
|
||||
edgesInto,
|
||||
edgesOfNode,
|
||||
findNode,
|
||||
type GraphEdge,
|
||||
type GraphNode,
|
||||
portsCompatible,
|
||||
} from '../graph/Graph';
|
||||
import { nodeDef } from '../graph/NodeRegistry';
|
||||
import { newId } from '../ids';
|
||||
import type { PropertyBag, PropertyValue } from '../model/Properties';
|
||||
@@ -14,11 +21,23 @@ export class AddNodeCommand implements Command {
|
||||
readonly label: string;
|
||||
readonly node: GraphNode;
|
||||
|
||||
constructor(private readonly assetId: string, type: string, x: number, y: number, params: PropertyBag = {}) {
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
type: string,
|
||||
x: number,
|
||||
y: number,
|
||||
params: PropertyBag = {},
|
||||
) {
|
||||
// An unknown type is rejected in `execute`, not here: constructors run outside the
|
||||
// editor's error handling, so throwing here would crash instead of showing a notice.
|
||||
const def = nodeDef(type);
|
||||
this.node = { id: newId(), type, x: Math.round(x), y: Math.round(y), params: { ...defaultsOf(def?.params ?? []), ...params } };
|
||||
this.node = {
|
||||
id: newId(),
|
||||
type,
|
||||
x: Math.round(x),
|
||||
y: Math.round(y),
|
||||
params: { ...defaultsOf(def?.params ?? []), ...params },
|
||||
};
|
||||
this.label = def ? `Add ${def.label}` : `Add ${type}`;
|
||||
}
|
||||
|
||||
@@ -38,7 +57,10 @@ export class RemoveNodeCommand implements Command {
|
||||
readonly label = 'Remove node';
|
||||
private removed?: { node: Removed<GraphNode>; edges: Removed<GraphEdge>[] };
|
||||
|
||||
constructor(private readonly assetId: string, private readonly nodeId: string) {}
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
private readonly nodeId: string,
|
||||
) {}
|
||||
|
||||
execute(project: Project): void {
|
||||
const graph = requireBehavior(project, this.assetId).graph;
|
||||
@@ -65,7 +87,12 @@ export class MoveNodeCommand implements Command {
|
||||
readonly label = 'Move node';
|
||||
private previous = { x: 0, y: 0 };
|
||||
|
||||
constructor(private readonly assetId: string, private readonly nodeId: string, private readonly x: number, private readonly y: number) {}
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
private readonly nodeId: string,
|
||||
private readonly x: number,
|
||||
private readonly y: number,
|
||||
) {}
|
||||
|
||||
execute(project: Project): void {
|
||||
const node = requireNode(project, this.assetId, this.nodeId);
|
||||
@@ -83,7 +110,12 @@ export class MoveNodeCommand implements Command {
|
||||
|
||||
/** Sets a node param or the inline value of an unconnected data input. */
|
||||
export class SetNodeParamCommand extends SetBagValueCommand<GraphNode> {
|
||||
constructor(private readonly assetId: string, private readonly nodeId: string, key: string, private readonly value: PropertyValue) {
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
private readonly nodeId: string,
|
||||
key: string,
|
||||
private readonly value: PropertyValue,
|
||||
) {
|
||||
super(key, `Set ${key}`);
|
||||
}
|
||||
|
||||
@@ -111,7 +143,11 @@ export class ConnectCommand implements Command {
|
||||
readonly edge: GraphEdge;
|
||||
private replaced: Removed<GraphEdge>[] = [];
|
||||
|
||||
constructor(private readonly assetId: string, from: { node: string; port: string }, to: { node: string; port: string }) {
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
from: { node: string; port: string },
|
||||
to: { node: string; port: string },
|
||||
) {
|
||||
this.edge = { id: newId(), from: { ...from }, to: { ...to } };
|
||||
}
|
||||
|
||||
@@ -124,7 +160,8 @@ export class ConnectCommand implements Command {
|
||||
const out = requireNodeDef(fromNode.type).outputs.find((p) => p.key === this.edge.from.port);
|
||||
const inp = requireNodeDef(toNode.type).inputs.find((p) => p.key === this.edge.to.port);
|
||||
if (!out || !inp) throw new CommandError('Port not found');
|
||||
if (!portsCompatible(out.type, inp.type)) throw new CommandError(`Cannot connect ${out.type} to ${inp.type}`);
|
||||
if (!portsCompatible(out.type, inp.type))
|
||||
throw new CommandError(`Cannot connect ${out.type} to ${inp.type}`);
|
||||
if (graph.edges.some((e) => sameEnds(e, this.edge))) throw new CommandError('Already connected');
|
||||
const occupied = inp.type === 'exec' ? [] : edgesInto(graph, this.edge.to.node, this.edge.to.port);
|
||||
this.replaced = removeWhere(graph.edges, (e) => occupied.includes(e));
|
||||
@@ -143,7 +180,10 @@ export class DisconnectCommand implements Command {
|
||||
readonly label = 'Disconnect';
|
||||
private removed: Removed<GraphEdge>[] = [];
|
||||
|
||||
constructor(private readonly assetId: string, private readonly edgeId: string) {}
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
private readonly edgeId: string,
|
||||
) {}
|
||||
|
||||
execute(project: Project): void {
|
||||
const graph = requireBehavior(project, this.assetId).graph;
|
||||
@@ -157,5 +197,10 @@ export class DisconnectCommand implements Command {
|
||||
}
|
||||
|
||||
function sameEnds(a: GraphEdge, b: GraphEdge): boolean {
|
||||
return a.from.node === b.from.node && a.from.port === b.from.port && a.to.node === b.to.node && a.to.port === b.to.port;
|
||||
return (
|
||||
a.from.node === b.from.node &&
|
||||
a.from.port === b.from.port &&
|
||||
a.to.node === b.to.node &&
|
||||
a.to.port === b.to.port
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user