${text}
`;
19 | node.children = undefined;
20 | }
21 | }
22 | });
23 | };
24 | }
25 |
26 | export default function (options) {
27 | // Read the constants file and store it for later
28 | const constantsFile = options?.constantsFile || '';
29 | constantsMap = JSON.parse(fs.readFileSync(constantsFile, 'utf8'));
30 |
31 | return {
32 | name: 'myAddTooltips',
33 | hooks: {
34 | 'astro:config:setup': ({ updateConfig }) => {
35 | updateConfig({
36 | markdown: {
37 | remarkPlugins: [addTooltips],
38 | },
39 | });
40 | },
41 | },
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/integrations/my_autolink_headings.js:
--------------------------------------------------------------------------------
1 | import { CONTINUE, SKIP, visit } from 'unist-util-visit';
2 | import { fromHtmlIsomorphic } from 'hast-util-from-html-isomorphic';
3 | import { toString } from 'hast-util-to-string';
4 | import { isElement } from 'hast-util-is-element';
5 | import { matches } from 'hast-util-select';
6 |
7 | // Add IDs and SVG permalinks to headings
8 | // (rehype-autolink-headings is good, but can't be configured to ignore some headings)
9 |
10 | const anchor = fromHtmlIsomorphic(
11 | '',
12 | { fragment: true },
13 | ).children[0];
14 |
15 | // Should match the method in bin/build/checks/links.pl
16 | function slugIt(heading) {
17 | return toString(heading)
18 | .trim()
19 | .toLowerCase()
20 | .replace(/\s+/g, '-')
21 | .replace(/[^a-z0-9_-]/g, '');
22 | }
23 |
24 | function autoLinkHeadings(options) {
25 | const { headings, exclude } = options;
26 | return function (tree) {
27 | visit(tree, 'element', (node) => {
28 | if (!isElement(node, headings) || (exclude && matches(exclude, node))) {
29 | return CONTINUE;
30 | }
31 | const newAnchor = structuredClone(anchor);
32 | if (node.properties.id) {
33 | const id = node.properties.id;
34 | newAnchor.properties = { ...newAnchor.properties, href: '#' + id };
35 | } else {
36 | const id = slugIt(node);
37 | newAnchor.properties = { ...newAnchor.properties, href: '#' + id };
38 | node.properties.id = id;
39 | }
40 | node.children.unshift(newAnchor);
41 | return SKIP;
42 | });
43 | };
44 | }
45 |
46 | // The headings to process
47 | const defaultHeadings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
48 | // Headings that match this selector are ignored
49 | const defaultExclude = undefined;
50 |
51 | export default function (options) {
52 | const headings =
53 | options?.headings !== undefined ? options.headings : defaultHeadings;
54 | const exclude =
55 | options?.exclude !== undefined ? options.exclude : defaultExclude;
56 | return {
57 | name: 'myAutoLinkHeadings',
58 | hooks: {
59 | 'astro:config:setup': ({ updateConfig, logger }) => {
60 | logger.debug('Headings: ' + headings);
61 | logger.debug('Exclude: ' + exclude);
62 | updateConfig({
63 | markdown: {
64 | rehypePlugins: [
65 | [autoLinkHeadings, { headings: headings, exclude: exclude }],
66 | ],
67 | },
68 | });
69 | },
70 | },
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/integrations/my_build_checks.js:
--------------------------------------------------------------------------------
1 | import runChecks from '../bin/build/prebuild.js';
2 |
3 | function buildChecks(logger) {
4 | logger.info('Running pre-build checks:');
5 | runChecks(logger, false);
6 | }
7 |
8 | export default function () {
9 | let doChecks;
10 | return {
11 | name: 'myBuildChecks',
12 | hooks: {
13 | 'astro:config:setup': ({ command }) => {
14 | doChecks = command === 'build' && process.env.UE_NOCHECK === undefined;
15 | },
16 | 'astro:config:done': ({ logger }) => {
17 | if (doChecks) {
18 | buildChecks(logger);
19 | }
20 | },
21 | },
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/integrations/my_cleanup_html.js:
--------------------------------------------------------------------------------
1 | import { visit, SKIP } from 'unist-util-visit';
2 |
3 | // Clean up any weird HTML artefacts, especially those that fail validation
4 |
5 | function cleanupHtml() {
6 | return function (tree) {
7 | // Remove `is:raw=""` that's on `code` elements, probably from Prism.
8 | visit(tree, 'element', (node) => {
9 | if (node.tagName == 'code') {
10 | delete node.properties['is:raw'];
11 | }
12 | });
13 |
14 | // Remove any comments
15 | visit(tree, 'comment', (node, index, parent) => {
16 | parent.children.splice(index, 1);
17 | return SKIP;
18 | });
19 | };
20 | }
21 |
22 | export default function () {
23 | return {
24 | name: 'myCleanupHtml',
25 | hooks: {
26 | 'astro:config:setup': ({ updateConfig }) => {
27 | updateConfig({
28 | markdown: {
29 | rehypePlugins: [cleanupHtml],
30 | },
31 | });
32 | },
33 | },
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/integrations/my_fixup_links.js:
--------------------------------------------------------------------------------
1 | import { visit } from 'unist-util-visit';
2 |
3 | // Prepend `base` to URLs in the markdown file.
4 | // It seems that [Astro does not do this](https://github.com/withastro/astro/issues/3626)
5 |
6 | function fixupLinks(basePath) {
7 | return function (tree) {
8 | visit(tree, 'element', (node) => {
9 | if (node.tagName == 'a' && node.properties.href) {
10 | // Add basePath prefix to local URLs that lack it
11 | // [Astro does not do this](https://github.com/withastro/astro/issues/3626)
12 | if (
13 | node.properties.href.startsWith('/') &&
14 | !node.properties.href.startsWith(basePath + '/')
15 | ) {
16 | node.properties.href = basePath + node.properties.href;
17 | }
18 |
19 | // Add rel="external noopener" and target="_blank" attributes to off-site links
20 | if (
21 | !node.properties.href.startsWith('/') &&
22 | !node.properties.href.startsWith('#')
23 | ) {
24 | node.properties.rel = ['external', 'noopener'];
25 | node.properties.target = '_blank';
26 | }
27 | }
28 | });
29 | };
30 | }
31 |
32 | export default function () {
33 | return {
34 | name: 'myFixupLinks',
35 | hooks: {
36 | 'astro:config:setup': ({ config, updateConfig }) => {
37 | updateConfig({
38 | markdown: {
39 | rehypePlugins: [[fixupLinks, config.base]],
40 | },
41 | });
42 | },
43 | },
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/integrations/my_htaccess.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 |
3 | // Write a .htaccess file to set the correct 404 page
4 |
5 | function writeHtaccess(base, dir, logger) {
6 | const file = dir.pathname + '.htaccess';
7 | const contents = `ErrorDocument 404 ${base}/404.html\n`;
8 | fs.writeFileSync(file, contents);
9 | logger.info(`Wrote .htaccess file to ${file}`);
10 | }
11 |
12 | export default function (base) {
13 | return {
14 | name: 'myHtaccess',
15 | hooks: {
16 | 'astro:build:done': ({ dir, logger }) => {
17 | writeHtaccess(base, dir, logger);
18 | },
19 | },
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/integrations/my_search_index.js:
--------------------------------------------------------------------------------
1 | import { visit, SKIP, CONTINUE } from 'unist-util-visit';
2 | import { matches } from 'hast-util-select';
3 | import fs from 'fs';
4 |
5 | // File scoped to accumulate the index across calls to mySearchIndex
6 | const searchIndex = [];
7 |
8 | function isExcludedFrontmatter(frontmatter, exclude) {
9 | return exclude.frontmatter.some(
10 | (test) => frontmatter[test.key] === test.value,
11 | );
12 | }
13 |
14 | // Recursively concatenate all text in child nodes while respecting exclusions
15 | function getText(node, exclude) {
16 | if (node.type === 'text') {
17 | // [\u202F\u00A0] is a non-breaking space
18 | return node.value.replace(/[\u202F\u00A0]/, ' ');
19 | }
20 |
21 | if (node.type !== 'element' || matches(exclude.ignore, node)) {
22 | return '';
23 | }
24 |
25 | // Add some minimal formatting for tables
26 | let separator = '';
27 | if (node.type === 'element') {
28 | if (node.tagName === 'tr') {
29 | separator = ' | ';
30 | } else if (node.tagName === 'tbody' || node.tagName === 'table') {
31 | separator = 'No results
22 |