Initial Drupal 11 with DDEV setup

This commit is contained in:
gluebox
2025-10-08 11:39:17 -04:00
commit 89ef74b305
25344 changed files with 2599172 additions and 0 deletions

View File

@ -0,0 +1,183 @@
/**
* @file
* Renders BigPipe placeholders using Drupal's Ajax system.
*/
((Drupal, drupalSettings) => {
/**
* CSS selector for script elements to process on page load.
*
* @type {string}
*/
const replacementsSelector = `script[data-big-pipe-replacement-for-placeholder-with-id]`;
/**
* Ajax object that will process all the BigPipe responses.
*
* Create a Drupal.Ajax object without associating an element, a progress
* indicator or a URL.
*
* @type {Drupal.Ajax}
*/
const ajaxObject = Drupal.ajax({
url: '',
base: false,
element: false,
progress: false,
});
/**
* Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX
* response.
*
* @param {string} content
* The text content of a <script type="application/vnd.drupal-ajax"> DOM
* node.
* @return {Array|boolean}
* The parsed Ajax response containing an array of Ajax commands, or false
* in case the DOM node hasn't fully arrived yet.
*/
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
try {
return JSON.parse(content);
} catch (e) {
return false;
}
}
/**
* Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
*
* These Ajax commands replace placeholders with HTML and load missing CSS/JS.
*
* @param {HTMLScriptElement} replacement
* Script tag created by BigPipe.
*/
function processReplacement(replacement) {
const id = replacement.dataset.bigPipeReplacementForPlaceholderWithId;
// The content is not guaranteed to be complete at this point, but trimming
// it will not make a big change, since json will not be valid if it was
// not fully loaded anyway.
const content = replacement.textContent.trim();
// Ignore any placeholders that are not in the known placeholder list. Used
// to avoid someone trying to XSS the site via the placeholdering mechanism.
if (typeof drupalSettings.bigPipePlaceholderIds[id] === 'undefined') {
return;
}
const response = mapTextContentToAjaxResponse(content);
if (response === false) {
return;
}
// Immediately remove the replacement to prevent it being processed twice.
delete drupalSettings.bigPipePlaceholderIds[id];
// Then, simulate an AJAX response having arrived, and let the Ajax system
// handle it.
ajaxObject.success(response, 'success');
}
/**
* Checks if node is valid big pipe replacement.
*/
function checkMutation(node) {
return Boolean(
node.nodeType === Node.ELEMENT_NODE &&
node.nodeName === 'SCRIPT' &&
node.dataset?.bigPipeReplacementForPlaceholderWithId &&
typeof drupalSettings.bigPipePlaceholderIds[
node.dataset.bigPipeReplacementForPlaceholderWithId
] !== 'undefined',
);
}
/**
* Check that the element is valid to process and process it.
*
* @param {HTMLElement} node
* The node added to the body element.
*/
function checkMutationAndProcess(node) {
if (checkMutation(node)) {
processReplacement(node);
}
// Checks if parent node of target node has not been processed, which can
// occur if the script node was first observed with empty content and then
// the child text node was added in full later.
// @see `@ingroup large_chunk` for more information.
// If an element is added and then immediately (faster than the next
// setImmediate is triggered) removed to a watched element of a
// MutationObserver, the observer will notice and add a mutation for both
// the addedNode and the removedNode - but the referenced element will not
// have a parent node.
else if (node.parentNode !== null && checkMutation(node.parentNode)) {
processReplacement(node.parentNode);
}
}
/**
* Handles the mutation callback.
*
* @param {MutationRecord[]} mutations
* The list of mutations registered by the browser.
*/
function processMutations(mutations) {
mutations.forEach(({ addedNodes, type, target }) => {
addedNodes.forEach(checkMutationAndProcess);
// Checks if parent node of target node has not been processed.
// @see `@ingroup large_chunk` for more information.
if (
type === 'characterData' &&
checkMutation(target.parentNode) &&
drupalSettings.bigPipePlaceholderIds[
target.parentNode.dataset.bigPipeReplacementForPlaceholderWithId
] === true
) {
processReplacement(target.parentNode);
}
});
}
const observer = new MutationObserver(processMutations);
// Attach behaviors early, if possible.
Drupal.attachBehaviors(document);
// If loaded asynchronously there might already be replacement elements
// in the DOM before the mutation observer is started.
document.querySelectorAll(replacementsSelector).forEach(processReplacement);
// Start observing the body element for new children and for new changes in
// Text nodes of elements. We need to track Text nodes because content
// of the node can be too large, browser will receive not fully loaded chunk
// and render it as is. At this moment json inside script will be invalid and
// we need to track new changes to that json (Text node), once it will be
// fully loaded it will be processed.
// @ingroup large_chunk
observer.observe(document.body, {
childList: true,
// Without this options characterData will not be triggered inside child nodes.
subtree: true,
characterData: true,
});
// As soon as the document is loaded, no more replacements will be added.
// Immediately fetch and process all pending mutations and stop the observer.
window.addEventListener('DOMContentLoaded', () => {
const mutations = observer.takeRecords();
observer.disconnect();
if (mutations.length) {
processMutations(mutations);
}
// No more mutations will be processed, remove the leftover Ajax object.
Drupal.ajax.instances[ajaxObject.instanceIndex] = null;
});
})(Drupal, drupalSettings);