0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-11 02:59:07 +08:00
discourse/frontend
Sérgio Saquetim 885ec52869
DEV: Replace dag-map with a locality-preserving topological sort (#39204)
The `dag-map` npm package (v2.0.2, last published 8 years ago) sorts by
walking from sink vertices and recursing over incoming edges. That makes
`before: X` land next to X for free, but an item declaring `after: X` is
only reached later through some unrelated sink, so it drifts away from
its anchor. On the post menu it drifts all the way to the end.

A button registered as `{ after: firstButtonKey }` renders last today:

```
dag-map:  read like copyLink flag edit bookmark delete admin showMore reply plugin-button
this PR:  read plugin-button like copyLink flag edit bookmark delete admin showMore reply
```

Three published plugins hit this, all asking to sit next to a core
button and all landing at the opposite end of the menu.

This replaces `dag-map` with a locality-preserving sort inlined into
`dag.ts`, the last remaining import of the package. Every vertex gets a
placement rank: a path of `(side, insertionIdx)` steps rooted at an
unanchored vertex. Kahn's algorithm then emits the lowest-ranked vertex
that is currently legal. Ranks only break ties among vertices whose
predecessors have all been placed, so the edges alone decide correctness
and the output is always a valid topological order. An anchored item
inherits its anchor's rank prefix, so registration order never outweighs
an anchor, and a key with a high in-degree no longer influences where
unrelated items land.

### Positions can be simpler

A quarter of the positioned `add()` calls across published plugins and
themes name both a `before` and an `after` anchor to pin an item into a
slot one anchor should have secured. Of those that can be tested against
their surface, every one works with a single anchor under this sort,
where only a third do under `dag-map`.

Arrays deserve a mention too: `before: [a, b]` means before *all* of the
listed keys, not whichever happens to exist. A defensive extra key can
drag an unrelated core item across the list, and dropping it to a single
anchor avoids that.

### Known limitation

When a customization claims the same anchor and the same side as a core
item, the core item keeps the adjacent slot, because core registers
first. Anchoring to that core item instead yields the intended
placement. This matches `dag-map`'s behaviour, so nothing regresses, and
it is pinned by a test.

### Typing and tests

The module is authored in TypeScript. `DAG` is generic over the value it
stores, with exported `DAGPosition`, `DAGOptions`, and
`DAGResolvedEntry` interfaces. `dag-test.ts` keeps the runtime behaviour
tests and adds coverage for hub-shaped graphs and for the before-side
guarantees, which the suite did not previously exercise. A compile-time
test at `type-tests/lib/dag-test.ts` asserts the generic contract with
expect-type.

Checked against every DAG `add()` call site in the published plugin and
theme corpus: no ordering regressions anywhere, and five customizations
get the placement they ask for and do not currently receive.

---------

Co-authored-by: David Taylor <david@taylorhq.com>
2026-08-03 10:29:58 -03:00
..
asset-processor DEV: Derive styleguide code samples from the rendered example (#42100) 2026-07-29 13:48:35 -03:00
deprecation-silencer DEV: Remove (again) the old non-rollup plugin compiler (#39419) 2026-04-21 21:07:46 +02:00
discourse DEV: Replace dag-map with a locality-preserving topological sort (#39204) 2026-08-03 10:29:58 -03:00
discourse-i18n DEV: add typescript support for core, themes and plugins (#41478) 2026-07-08 12:57:18 +01:00
discourse-markdown-it DEV: Bump eslint/lint-configs, and correct violations (#41321) 2026-07-01 10:39:57 +01:00
discourse-types DEV: Use bundler module resolution for @discourse/types consumers (#41661) 2026-07-13 21:43:39 -03:00
pretty-text FIX: clean up onebox loading decorations on fetch failure (#39277) 2026-07-23 19:50:41 -03:00
.npmrc DEV: Rename app/assets/javascripts/ -> frontend/ 2025-10-22 16:24:11 +01:00
polyfills.js DEV: Rename app/assets/javascripts/ -> frontend/ 2025-10-22 16:24:11 +01:00