The druxtjs.org documentation site, with a Drupal 11 backend and a Nuxt frontend. It follows the Druxt repository standard for a Nuxt application.
- This repository is public. Nothing that resolves only on a private
network may reach a tracked file: no internal URLs, hostnames, repository
names or issue links, in any file, comments, patch descriptions and lock
files included.
npm run lint:privateenforces the URL-shaped half of this and runs in the pipeline. It cannot catch an internal name written as prose, so that part is on you. - Conventional Commits, and the same for merge request and pull request titles. A squash merge makes the title the commit subject, so a prose title breaks the next push to the target branch.
- No AI tool is credited. No co-author trailer naming an assistant, no
generated-with footer, no session link, in commits, merge request
descriptions or tracked files. The commit-msg hook rejects it locally, and
npm run lint:attributionand the pipeline check the rest. - Prose is linted with Vale. The ai-tells style covers the markdown a
change touches, its commit messages and the merge request description. Run
npm run lint:prose:installonce, thennpm run lint:prose. - Export configuration with the change that needed it. Run
vendor/bin/drush config:exportindrupal/and commitdrupal/config/sync/. - The database holds the content. Markdown is never imported. A
non-production environment takes a sanitised copy of production's database
on every rollout and applies the branch's updates to it, so it holds real
content on new code. An environment with no database to copy starts empty.
DOCS_SKIP_SYNC=1keeps the database an environment already has. - A maintainer keeps their login through the sanitise. An address at
DOCS_MAINTAINER_DOMAINsurvives, andDOCS_MAINTAINER_NAMEgets the password inDOCS_MAINTAINER_PASSWORDback, both set as Lagoon variables. A password that reaches a non-production environment this way is not one to reuse anywhere else. docs/hosting.md covers it. - The pin is the API reference's version, not the site's content. docgen
reads the pinned
druxt.jsto generate theapi/,components/and module pages, which have no Drupal representation. The authored pages come from the database, and the sitemap and thellmsfiles describe both. The footer badge namesdruxtat that pin, never the installed package, which follows npm'sdevtag until 0.25.0 is released. github-sluggerstays at 1.5.0.druxt_docscomputes the table of contents with a PHP port of that version. Renovate is configured not to offer it.- Patches are public upstream diffs.
drupal/composer.jsonpoints each one at a drupal.org merge request or GitHub pull request.diffURL, and installs the patched package from source (preferred-install), so a diff that touches files the dist archive leaves out still applies. Commitdrupal/patches.lock.jsonwith it. composer-patches prints every patch description duringcomposer install, so a description is public text too. A core patch is the exception: it is vendored intodrupal/patches/with the merge request and commit in its name, because the upstream branch moves and a core change needs review before it changes under the site.docs/frontend-cache.mdrecords what each one is for, andlagoon/cli.dockerfilecopiesdrupal/patchesin beforecomposer install. - Adding or changing a patch needs
composer patches-relockandcomposer patches-repatch.composer installapplies nothing to a package it already has. Grep the patched file for something the patch adds before committing. - The coverage floor goes up, never down.
npm run test:coveragefails below 88% of lines, 87% of branches and 87% of functions inscripts/. It needs Node 22.8 or later, so CI runs it.npm testis the same suite on Node 16, without coverage. - The frontend's audit is report-only, and the root's blocks.
npm run lint:audit:nuxtauditsnuxt/with Yarn 4 in a scratch copy, because Yarn 3.8.7's audit endpoint now answers 400. Nuxt 2's frozen tree cannot pass it. - PHP 8.3 is the floor. The Composer lock is resolved against
config.platform.php8.3.0, and CI runs on 8.3.
Drupal renders only administration. Gin, from contrib, is both the default
theme and the admin theme, with Gin Login for the sign-in page and core
Navigation for the sidebar. druxtjsorg adds a small stylesheet for phones
(css/admin.css). Druxt renders the public site.
The druxtjs theme holds only the frontend's regions and its 10 blocks, and
Drupal renders it only in the block region demo. The frontend reads the
theme's regions and settings from
/jsonapi/decoupled/settings?consumerId=druxtjs_org. decoupled_settings is
patched to expose the regions and to let a consumer select its theme, and
drush druxtjsorg:oauth-client creates the druxtjs_org consumer with the
druxtjs theme when a site has none.
content_moderation runs an editorial workflow on doc_page, so a page can
carry a draft while its published revision stays live. A signed-in editor
(the editor or contributor role, over druxt-auth PKCE against the
druxtjs_org consumer) sees the draft on the page's own URL. JSON:API writes
are enabled (jsonapi.settings read_only: false), so a signed-in client can
write a page and its paragraphs as a draft on live or into a workspace. The
site is a JSON:API client and doesn't carry a writer of its own. Content goes
through a Druxt client. Workspaces stage a set of changes off live, chosen per
request with the X-Druxt-Workspace header, which druxtjsorg honours only
for a signed-in account that may view that workspace: the editor role has
view any workspace, contributor does not, and a write naming a workspace
the account cannot see is refused rather than sent to live.
docs/backend.md covers both.
| Path | Purpose |
|---|---|
nuxt/ |
The Nuxt 2 frontend on Yarn 3.8.7, copied from the docs app in druxt.js |
drupal/ |
The Drupal codebase and configuration |
drupal/.devtools/ |
Provisioning scripts on PHP and SQLite, with no Docker |
drupal/config/sync/ |
Exported site configuration |
drupal/web/modules/custom/druxt_docs/ |
The table of contents, previews and hooks for the content model |
drupal/web/themes/custom/druxtjs/ |
The frontend's regions and blocks |
docs-source.json |
The druxt.js commit docgen generates the API reference from |
scripts/ |
The docs fetch and generation, the JS tables generator, the host lint |
tests/ |
Node tests, and bash guardrail tests for .devtools and the scripts |
docs/ |
Notes on the backend: previews, configuration and page history |
.devcontainer/ |
The dev container, for VS Code, Codespaces and DevPod |
.githooks/ |
Committed hooks, enabled by npm install |
.gitlab/scripts/ |
The attribution and prose checks, copied from the standard |
npm install # tooling, and enables the git hooks
npm run setup # assemble, provision, fetch the pinned druxt.js, start Drupal
npm run dev # Nuxt dev server against that Drupal
npm run login # one-time login link
npm run docs:generate # Modules, API and Components pages, built in .docs-source
npm run lint # every linter except prose
npm run lint:prose # Vale, after `npm run lint:prose:install`
npm test # node tests
bash tests/start-guardrails.sh # also post-rollout- and replacing-guardrails.sh
cd drupal && .devtools/assemble && vendor/bin/phpunitdrupal/.devtools/README.md covers provisioning and serving.
nuxt/ is copied from the docs app in druxt.js, and runs on Yarn 3.8.7 and
Node 16.20.1. nuxt.config.js reads only DRUXT_BASE_URL, so npm run dev
fills it from the BASE_URL that .devtools/start writes to .env.
nuxt/content is a gitignored link to the pinned documentation checkout in
.docs-source, and npm run setup creates it.
Its own lint setup comes with the frontend rework, so do not add Nuxt tooling
here ahead of it.
Until then ESLint, Prettier, markdownlint, cspell and Vale skip nuxt/. The
attribution and private-host checks still read every tracked file, nuxt/
included, because the repository is public.
.mise.toml and .nvmrc pin Node 16.20.1, the Node Nuxt 2 builds with, and
PHP 8.4. CI tests PHP on 8.3, the floor the Composer lock is resolved
against. The root lint tooling is held at the last versions that run on Node 16,
and Renovate is configured not to offer newer ones.