Files
Hankan/eslint.config.js
MechaCat02 b80deb2f6f chore: scaffold, CI, and the export bundle verbatim
The export bundle is the input to this port, not a sketch: the curriculum,
the tutor prompt and the five logic modules are finished and tested. They
land here byte-identical and stay that way.

  diff -r export/data data && diff -r export/lib lib
  diff -r export/prompt prompt && diff export/validate.mjs validate.mjs

data/, lib/, prompt/ and validate.mjs sit at the repo root so validate.mjs
runs verbatim with no path edits. All four are excluded from lint and
formatting — they are not ours to restyle. Types for lib/ live alongside in
types/ rather than as sibling .d.ts files, so the verbatim check stays a
plain directory diff.

CI runs the curriculum gate first, before anything else can pass:

  node validate.mjs   PASS — 0 blocking, 0 advisory

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:11:40 +02:00

76 lines
2.3 KiB
JavaScript

import js from "@eslint/js";
import tseslint from "typescript-eslint";
import reactHooks from "eslint-plugin-react-hooks";
/* The clock guard.
The artifact's sync bug was a fresh device stamping its own empty defaults
as newer than the server's real data. db/writes.ts owns the distinction
between a seed write (updated_at stays 0) and a user edit (stamped), so it
is the only file under src/db/ allowed to read the clock. Anywhere else,
reaching for Date.now() means someone is about to stamp a row without
deciding which kind of write it is. */
const CLOCK_BAN = [
{
selector: "CallExpression[callee.object.name='Date'][callee.property.name='now']",
message:
"Only db/writes.ts may read the clock. Use a seedX() helper (leaves updated_at 0) " +
"or an editX() helper (stamps it) — see the timestamp rule in db/writes.ts.",
},
{
selector: "NewExpression[callee.name='Date']",
message:
"Only db/writes.ts may read the clock. Use a seedX() or editX() helper from db/writes.ts.",
},
];
export default tseslint.config(
// lib/, data/ and validate.mjs are copied in from the export bundle
// unchanged and must stay byte-identical — they are not ours to restyle.
{
ignores: [
"**/dist/**",
"**/node_modules/**",
"export/**",
"vendor/**",
"app/android/**",
"lib/**",
"data/**",
"validate.mjs",
],
},
js.configs.recommended,
...tseslint.configs.recommended,
// Build scripts and the shared modules are plain Node ESM.
{
files: ["tools/**/*.mjs", "shared/**/*.mjs", "*.config.js"],
languageOptions: {
globals: {
console: "readonly",
process: "readonly",
URL: "readonly",
fetch: "readonly",
Buffer: "readonly",
},
},
},
/* The rules of hooks, and exhaustive deps in particular. A stale closure
in an effect is what let the tutor seed its opening turn three times. */
{
files: ["app/src/**/*.{ts,tsx}"],
plugins: { "react-hooks": reactHooks },
rules: {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
},
{
files: ["app/src/db/**/*.ts"],
rules: { "no-restricted-syntax": ["error", ...CLOCK_BAN] },
},
{
files: ["app/src/db/writes.ts"],
rules: { "no-restricted-syntax": "off" },
},
);