Files
hyperframes/scripts/animejs-v4-guidance.test.mjs
focsuerandMiguel Ángel 67edb01bf4 docs(skills): fix anime.js v3 syntax in v4 adapter guidance (#3064)
* docs(skills): fix anime.js v3 syntax in v4 adapter guidance

The animejs adapter docs teach v3 syntax against a v4 build, so the examples
cannot run as written: every v4 bundle assigns a *namespace object* to the
global `anime`, making v3's `anime({ targets })` call form a TypeError. `easing:`
is now `ease:`, ease names lost their `ease` prefix, and `timeline.add()` takes
`(targets, parameters, position)`.

- Rewrite `skills/hyperframes-animation/adapters/animejs.md` for v4:
  `anime.animate()` / `anime.createTimeline()`, `ease:` names, targets-first
  `add()`, position shorthands, and a note that the producer fixtures pin
  4.0.2 (`lib/`) while 4.1+ moved bundles to `dist/bundles/`.
- Fix the same v3 `anime.timeline({ targets })` snippet in
  `skills/hyperframes-keyframes/references/keyframe-patterns.md`.
- Stop advertising `anime.running` auto-discovery as a safety net. No v4 build
  exports `running` (checked 4.0.2 and 4.5.0), so `discover()` returns
  immediately and any instance a composition forgets to push onto
  `window.__hfAnime` is silently never seeked. Marked v3-only/inert in the
  skill page and the adapter docstring; explicit registration is now stated
  as mandatory.
- Add render-safety notes the page lacked: `createSeededRandom()` as the
  deterministic replacement for `Math.random()`, and why
  `autoplay: onScroll(...)`, `createDraggable`, and pointer-driven
  `createAnimatable` cannot work under headless seek rendering.
- Regenerate skills-manifest.json.

Runtime behaviour is unchanged: the `packages/core` edit is comment-only, and
the seek path already works on v4 (registered instances expose
seek/pause/play). The now-dead `anime.running` branch in `discover()` is left
in place — it is guarded and try/caught, and removing it is a behaviour change
that belongs in its own PR.

* fix(core): align Anime.js globals with v4

---------

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-08-16 14:01:38 -04:00

64 lines
2.3 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
const guidance = [
{
path: "packages/core/src/runtime/adapters/animejs.ts",
required: ["anime.animate", "anime.createTimeline"],
},
{
path: "skills/hyperframes-animation/adapters/animejs.md",
required: ["anime.animate", "anime.createTimeline"],
},
{
path: "skills/hyperframes-keyframes/references/keyframe-patterns.md",
required: ["anime.createTimeline"],
},
];
function read(path) {
return readFileSync(join(repoRoot, path), "utf8");
}
function fencedCode(source) {
const normalized = source.replace(/^\s*\* ?/gm, "");
return [...normalized.matchAll(/```[^\n]*\n([\s\S]*?)```/g)].map((match) => match[1]).join("\n");
}
for (const { path, required } of guidance) {
test(`${path} uses Anime.js v4 call sites in executable examples`, () => {
const snippets = fencedCode(read(path));
for (const call of required) {
assert.match(snippets, new RegExp(`\\b${call.replace(".", "\\.")}\\s*\\(`));
}
assert.doesNotMatch(snippets, /\banime\s*\(\s*\{/);
assert.doesNotMatch(snippets, /\banime\.timeline\s*\(/);
});
}
test("the window anime global advertises the v4 namespace API", () => {
const source = read("packages/core/src/runtime/window.d.ts");
const global = source.match(/\n\s*anime\?: \{([\s\S]*?)\n\s*\};/);
assert.ok(global, "found Window.anime declaration");
assert.match(global[1], /\banimate\??:\s*\(/);
assert.match(global[1], /\bcreateTimeline\??:\s*\(/);
assert.doesNotMatch(global[1], /^\s*\(params:/m);
assert.doesNotMatch(global[1], /\btimeline\??:/);
});
test("the adapter's local anime global shape matches v4 while retaining legacy discovery", () => {
const source = read("packages/core/src/runtime/adapters/animejs.ts");
const global = source.match(/interface AnimeGlobal \{([\s\S]*?)\n\}/);
assert.ok(global, "found AnimeGlobal declaration");
assert.match(global[1], /\banimate\??:\s*\(/);
assert.match(global[1], /\bcreateTimeline\??:\s*\(/);
assert.match(global[1], /\brunning\??:/);
assert.doesNotMatch(global[1], /^\s*\(params:/m);
assert.doesNotMatch(global[1], /\btimeline\??:/);
});