Search by

jacerider / neo_alchemist

jacerider

Component creation and management.

Package info

github.com/jacerider/neo_alchemist

Type:drupal-module

pkg:composer/jacerider/neo_alchemist

Statistics

Installs: 5 113

Dependents: 3

Suggesters: 0

Stars: 0

Open Issues: 0

1.3.4 2026-10-15 20:08 UTC

README

Provides component creation and management utilizing Drupal single directory components.

For a developer's map of the module internals — the neo_component entity, the ComponentShape/prop-def plugin system, the render pipeline, services, and extension points — see ARCHITECTURE.md.

For the automated tests, how to set a host site up to run them, and the fixture module they rely on, see TESTING.md.

REQUIREMENTS

This module requires the Neo suite of modules.

INSTALLATION

Install as you would normally install a contributed Drupal module. Visit https://www.drupal.org/node/1897420 for further information.

DRUSH COMMANDS

Alchemist ships introspection and verification commands to make authoring components easier — list what already exists, look up the available shapes, icons and color schemes, and confirm a component renders before shipping it.

In every command below, <id> is the SDC component id in provider:machine_name form (e.g. front:cards_test). Run drush neo:alchemist:components to see the valid ids. All tabular commands accept --format=json (and the other standard Drush formats) for machine parsing.

Discover & inspect

neo:alchemist:components (alias neoa-components) — list every Neo component (SDC with neo: true) with its provider, status, and prop/slot counts. Use it to check whether a machine name is already taken.

drush neo:alchemist:components
drush neo:alchemist:components --theme=front

neo:alchemist:info <id> (alias neoa-info) — dump one component's resolved definition: props (authored type, title, required, examples), slots, libraries, and status. Defaults to YAML output.

drush neo:alchemist:info front:cards_test

neo:alchemist:shapes [name] (alias neoa-shapes) — with no argument, list every prop-def shape (heading, image, link, scheme, spacing, …). With a name, dump that shape's schema, a paste-ready .component.yml prop snippet, and its Twig render pattern.

drush neo:alchemist:shapes
drush neo:alchemist:shapes heading

Two related lookups live in the modules that own the data:

  • Icons — drush neo:icon:list [search] (Neo Icon) searches the icon names for the icon() Twig function.
  • Color schemes — drush neo:color:schemes (Neo Color) lists the enabled schemes (id, label, selector, dark/colorized) to verify components against, and supplies the id for neo:alchemist:render --scheme.

Verify

neo:alchemist:validate <id> (alias neoa-validate) — statically lint a component. Flags missing neo: true, props with no examples, unknown prop types, {% if/for %} references to props that aren't declared, and Tailwind class names assembled dynamically (which never compile). Exits non-zero on hard errors.

drush neo:alchemist:validate front:cards_test

neo:alchemist:render <id> (alias neoa-render) — render a component headlessly from its examples and report PASS/FAIL, surfacing Twig/render errors as a message instead of a broken page. --html prints the rendered markup; --scheme=<id> wraps the render in a color scheme selector; --live renders the runtime path (neoIsPreview false) instead of the editor preview.

drush neo:alchemist:render front:cards_test
drush neo:alchemist:render front:cards_test --html
drush neo:alchemist:render front:cards_test --scheme=dark
drush neo:alchemist:render front:header --live --html

neo:alchemist:audit <id> (alias neoa-audit) — render a component and check its markup for what search engines and screen readers trip over: headings that skip a level or start below h2 (a component sits under the page's H1), an h1 of its own, images without alt text (or a file name for one), and headings, links and buttons with no accessible name. It renders twice — from its examples, and again with every optional heading prop left out, the way an editor often places it — and names the render each issue came from. --all audits every component (--theme=<name> narrows it). Exits non-zero when anything is found. The component's preview page runs the same audit: an "N issues" badge in its top bar lists them, and Show outlines the element in the preview.

drush neo:alchemist:audit front:cards_test
drush neoa-audit --all --theme=front

Checks are services tagged neo_alchemist.component_audit_check, implementing ComponentAuditCheckInterface, so another module can add its own. A check sees the component as well as its markup, and may add a report — what the component produces, say — which the preview page folds under the issues (the badge then reads "No issues" rather than hiding). Likewise neo:alchemist:validate runs every service tagged neo_alchemist.component_linter (ComponentLinterInterface), for modules that read a key of their own from .component.yml; neo_seo checks a component's seo declaration this way.

neo:alchemist:h1-report (alias neoa-h1) — list each Alchemist page's H1s and the component each sits in (page title when no heading in the layout is the H1 and the hidden page title is). It checks every published page whose layout the page owns (custom or hybrid), and the most recently changed few of each locked layout, which stand for the rest — they share the layout and differ only in their data; the summary names what was sampled. --all checks every page, --sample=N (default 5) sizes the samples, and --entity-type, --bundle and --limit narrow the sweep. Each page's entity is rendered in its full view mode under a request for its page — the layout without the header, menus, footer and blocks around it — so a run takes seconds rather than minutes; an <h1> a block or the theme prints outside the layout is not seen. Progress and each problem page go to stderr as the run goes; --problems lists only pages without exactly one H1. Save an --all report before migrating components to heading.tag and compare it with one taken after.

drush neo:alchemist:h1-report --problems
drush neoa-h1 --all --format=csv > h1-before.csv

Capture a component thumbnail

The component listings show a thumbnail per component, and core reads it from a thumbnail.png sitting in the component's own directory. You can produce one from the browser instead of exporting it by hand:

  1. Be on a local development environment — see the gate below.
  2. Open the component's preview workspace at /admin/config/neo/alchemist/preview/<id>.
  3. Click Capture thumbnail, frame it with the Align and Width controls, then click Capture.

The PNG is written straight to <component-dir>/thumbnail.png, so it lands next to the .component.yml, shows up in git status, and travels with the component. No cache rebuild is needed — core picks it up on the next request. It also becomes the fallback thumbnail for any saved component wrapping that SDC which has no thumbnail of its own.

Because this writes into the codebase, it is offered only on a local development environment. Either signal is enough:

  • the Neo dev server is running (npm start), or
  • the dev config split is enabled — $config['config_split.config_split.dev']['status'] = TRUE; in the gitignored settings.local.php.

The split is the steadier of the two: it holds for the whole environment, whereas the dev-server signal comes and goes with npm start.

Where neither holds the button is absent and the endpoint refuses the write. Where one holds but the component directory is not writable by the web server, the button renders disabled and names the directory — chmod it.

TWIG EVENTS

Sometimes a component needs to allow mouse events to be able to be triggered while in the management interface (tabs, accordions, etc.). To tell Alchemist about this, the following can be added in twig to the element that needs to be exposed:

Basic event

A simple event that will just allow mouse interfaction with an element.

<div
{% if neoIsPreview %}
  data-event
{% endif %}
>

Toggle event

For elements that can be toggled. This is ideal for an accordion that allows multiple elements to be visible at once and each one can be shown or hidden.

<div
{% if neoIsPreview %}
  data-event='{"action": "toggle"}'
{% endif %}
>

Grouped event

For elements that belong together. This is ideal for tabs or accordion elements that only allow a single visible element. The group name can be any string but should be unique per grouping.

<div
{% if neoIsPreview %}
  data-event='{"group": "tabs"}'
{% endif %}
>