openpencil/tools/lint
Danila Poyarkov e532f616ba
refactor!: move shared primitives below dom-css and core (#771)
* refactor!: move shared primitives below dom-css and core

dom-css depended on core for color conversion, base64 helpers, text
direction, and web-font assets, so core could not use dom-css and every
caller special-cased HTML and Tailwind output.

Color conversion and management, base64 helpers, and text/layout
direction now live in scene-graph under `color`, `bytes`, and
`text-direction`. dom-css takes web-font resolution as an injected
`fonts` option and owns the font face types, so it depends only on
scene-graph and core can depend on it.

BREAKING CHANGE: `@open-pencil/core/color` and `@open-pencil/core/bytes`
are removed, and the direction helpers are no longer exported from
`@open-pencil/core/text`; import them from `@open-pencil/scene-graph`
subpaths. `exportHTMLBundle` takes a font resolver in `fonts` instead of
`'assets'`.

* fix(tools): import color parsing from scene-graph in visual bisect

* fix(mcp): declare the scene-graph dependency

MCP imports `@open-pencil/scene-graph/bytes` since base64 helpers moved
there, but only reached scene-graph through core, so isolated installs
and package checks depended on transitive resolution.

* refactor!: use js-base64 directly instead of a base64 wrapper

Base64 helpers had moved into scene-graph only to sit below dom-css,
but they are a thin wrapper over js-base64 and unrelated to the graph;
fig already called js-base64 directly.

Callers use js-base64 and check `isValid` where input comes from outside
(clipboard, imported HTML, tool arguments, the plugin API). A new
`open-pencil/no-hand-rolled-base64` lint rule rejects atob, btoa, and
Buffer Base64 conversions, and AGENTS.md records the convention.

BREAKING CHANGE: `@open-pencil/core/bytes` is removed; use `js-base64`.

* fix(dom-css): keep images with invalid Base64 inline in HTML export

`exportHTMLBundle` accepts documents parsed from outside HTML, and
js-base64 drops characters it cannot decode, so extracting an invalid
image data URL wrote different bytes. Such images now stay inline.
2026-09-26 11:39:11 +04:00
..
src refactor!: move shared primitives below dom-css and core (#771) 2026-09-26 11:39:11 +04:00
tests refactor!: move shared primitives below dom-css and core (#771) 2026-09-26 11:39:11 +04:00
NOTICE refactor: narrow lint policies and consolidate their support 2026-09-14 10:10:27 +03:00
package.json refactor: narrow lint policies and consolidate their support 2026-09-14 10:10:27 +03:00
README.md refactor: narrow lint policies and consolidate their support 2026-09-14 10:10:27 +03:00
tsconfig.json refactor: replace complex conditional object spreads 2026-09-01 19:49:57 +03:00

OpenPencil lint rules

src/plugin.ts registers locally owned rules; the workspace oxlint.json selects policy. Run bun run --cwd tools/lint test for rule tests and bun run lint for repository enforcement.

Test and type policies

  • no-module-mocking rejects Bun, Vitest, and Jest module registry mocking, including Bun's mock and jest exports and Vitest's vi and vitest exports. Scoped spies and injected dependencies remain supported. Detection covers direct framework calls and renamed named imports, not arbitrary alias propagation or destructuring.
  • no-reduce-accumulator-copy detects unbounded accumulator copies through Object.assign, Array.from, and array copy methods. It complements oxc/no-accumulating-spread. Literal-bounded slices such as slice(0, 2) and slice(-2) are allowed; slice(2) and slice(0, -1) still copy a potentially growing range. This is a conservative syntactic check, not a proof of quadratic runtime for every reducer.
  • no-widen-then-assert detects immutable local bindings widened to broad types and then asserted narrower in the same function. It intentionally avoids inferring arbitrary type aliases, imported return types, or cross-function flows.

The experimental known-value-widening audit is not included: its broad policy produced too many intentional-contract diagnostics to justify maintaining a separate type resolver.

Maintenance

Upstream attribution and the full license are in NOTICE. These are adapted, locally owned rules, not an automatically synchronized vendor directory. Compare any upstream updates against the recorded commit, preserve local behavior, and add positive and negative regression cases before changing enforcement.

Tests share tests/helpers/lint.ts, which invokes the real Oxlint plugin, rejects parser/configuration failures, and cleans each fixture in finally. Keep test infrastructure out of runtime source helpers.