Skip to content

Commit

Permalink
Catch Docusaurus build issues
Browse files Browse the repository at this point in the history
Break the docs build on states that are acceptable for the current
NextJS-based docs engine but cause Docusaurus builds to fail:

- **Unresolved partial parameters:** Edit the `remark-includes` linter
  to ensure that all parameters declared within a partial (using `{{
  param }}` syntax) are either (a) assigned by the user or (b) given a
  default value. Otherwise, when we move to the new docs engine, builds
  will fail on unresolved parameters.

- **Add more config checks:** Throw an exception for duplicate redirects
  and redirects where the `source` points to an existing file. With the
  current logic, this check takes place with every page build. While
  this is not ideal, and leads to noisy error output, we should be
  migrating soon and will not need to deal with this for long.
  • Loading branch information
ptgott committed Oct 10, 2024
1 parent 48e9ee6 commit b25c045
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 2 deletions.
59 changes: 58 additions & 1 deletion server/config-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,41 @@ export const checkURLsForCorrespondingFiles = (
}, []);
};

// checkForRedirectsFromExistingFiles returns an array of redirects in which the
// source corresponds to a file at a path rooted at dirRoot.
export const checkForRedirectsFromExistingFiles = (
dirRoot: string,
redirects: Redirect[]
): Redirect[] => {
let result: Redirect[] = [];

redirects.forEach((r) => {
if (correspondingFileExistsForURL(dirRoot, r.source)) {
result.push(r);
}
});
return result;
};

// checkDuplicateRedirects checks the provided redirects for duplicates and
// returns an array of Redirect objects. Duplicate checks are based on the
// source of each redirect.
export const checkDuplicateRedirects = (redirects: Redirect[]): Redirect[] => {
const result: Redirect[] = [];
const uniques = new Set();
redirects.forEach((r) => {
if (uniques.has(r.source)) {
result.push(r);
return;
}
uniques.add(r.source);
});
return result;
};

// checkURLForCorrespondingFile determines whether a file exists in the content
// directory rooted at dirRoot for the file corresponding to the provided URL path.// If a file does not exist, it returns false.
// directory rooted at dirRoot for the file corresponding to the provided URL path.
// If a file does not exist, it returns false.
const correspondingFileExistsForURL = (
dirRoot: string,
urlpath: string
Expand Down Expand Up @@ -326,6 +359,30 @@ export const loadConfig = (version: string) => {
);
}

const redirsFrom = checkForRedirectsFromExistingFiles(
join("content", version, "docs", "pages"),
config.redirects
);

if (redirsFrom.length > 0) {
throw new Error(
"Error parsing docs config file " +
join("content", version, "docs", "config.json") +
': Each of the following redirects includes a "source" that corresponds to an existing file: ' +
JSON.stringify(redirsFrom, null, 2)
);
}

const duplicateRedirects = checkDuplicateRedirects(config.redirects);
if (duplicateRedirects.length > 0) {
throw new Error(
"Error parsing docs config file " +
join("content", version, "docs", "config.json") +
": Found redirects with duplicate sources: " +
JSON.stringify(duplicateRedirects, null, 2)
);
}

validateConfig<Config>(validator, config);

config.navigation.forEach((item, i) => {
Expand Down
5 changes: 5 additions & 0 deletions server/fixtures/includes-vars-erroneous-include.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Installation

Here is a test for including variables in an MDX file.

(!install-version.mdx version="10" unsupport="9" !)
15 changes: 15 additions & 0 deletions server/remark-includes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,21 @@ const resolveIncludes = ({
content = content.replace(varRegexp, finalVal);
}

// Catch unresolved parameters, which can break docs builds
const paramRE = new RegExp(`{{ ?\\w+ ?}}`, "g");
const unresolvedParams = Array.from(content.matchAll(paramRE));
if (unresolvedParams.length > 0) {
const errs = unresolvedParams
.map((el) => {
return el[0];
})
.join(",");

error =
`${includePath}: the following partial parameters were not assigned and have no default value: ` +
errs;
}

return content;
} else {
error = `Wrong import path ${includePath} in file ${filePath}.`;
Expand Down
93 changes: 92 additions & 1 deletion uvu-tests/config-docs.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Redirect } from "next/dist/lib/load-custom-routes";

Check failure on line 1 in uvu-tests/config-docs.test.ts

View workflow job for this annotation

GitHub Actions / Lint code base

Duplicate identifier 'Redirect'.
import { suite } from "uvu";
import * as assert from "uvu/assert";
import { Config, checkURLsForCorrespondingFiles } from "../server/config-docs";
import {
Config,
checkURLsForCorrespondingFiles,
checkForRedirectsFromExistingFiles,
checkDuplicateRedirects,
} from "../server/config-docs";
import { generateNavPaths } from "../server/pages-helpers";
import { randomUUID } from "crypto";
import { join } from "path";
import { Volume, createFsFromVolume } from "memfs";
import type { Redirect } from "next/dist/lib/load-custom-routes";

Check failure on line 14 in uvu-tests/config-docs.test.ts

View workflow job for this annotation

GitHub Actions / Lint code base

Duplicate identifier 'Redirect'.

const Suite = suite("server/config-docs");

Expand Down Expand Up @@ -459,4 +465,89 @@ title: Deploying the Database Service on Kubernetes
);
});

Suite("Checks for duplicate redirects", () => {
const redirects: Array<Redirect> = [
{
source: "/getting-started/",
destination: "/get-started/",
permanent: true,
},
{
source: "/getting-started/",
destination: "/get-started/",
permanent: true,
},
{
source: "/application-access/",
destination: "/connecting-apps/",
permanent: true,
},
{
source: "/application-access/",
destination: "/connecting-apps/",
permanent: true,
},
{
source: "/database-access/",
destination: "/connecting-databases/",
permanent: true,
},
];

const expected: Array<Redirect> = [
{
source: "/getting-started/",
destination: "/get-started/",
permanent: true,
},
{
source: "/application-access/",
destination: "/connecting-apps/",
permanent: true,
},
];

const actual = checkDuplicateRedirects(redirects);
assert.equal(actual, expected);
});

Suite("Checks for redirects from existing paths", () => {
const redirects: Array<Redirect> = [
{
source: "/contact/offices/",
destination: "/get-in-touch/offices/",
permanent: true,
},
{
source: "/locations/",
destination: "/contact/offices/",
permanent: true,
},
{
source: "/about/projects/project1/",
destination: "/project1/",
permanent: true,
},
];

const expected: Array<Redirect> = [
{
source: "/contact/offices/",
destination: "/get-in-touch/offices/",
permanent: true,
},
{
source: "/about/projects/project1/",
destination: "/project1/",
permanent: true,
},
];

const actual = checkForRedirectsFromExistingFiles(
join("server", "fixtures", "fake-content"),
redirects
);
assert.equal(actual, expected);
});

Suite.run();
21 changes: 21 additions & 0 deletions uvu-tests/remark-includes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,27 @@ Suite("Resolves template variables in includes", () => {
assert.equal(result, expected);
});

Suite("Throws an error if a variable is unresolved and has no default", () => {
const value = readFileSync(
resolve("server/fixtures/includes-vars-erroneous-include.mdx"),
"utf-8"
);

const out = transformer(
{
value,
path: "/content/4.0/docs/pages/filename.mdx",
},
{ lint: true }
);

assert.equal(out.messages.length, 1);
assert.equal(
out.messages[0].reason,
"The following partial parameters were not assigned and have no default value: {{ unsupported }}"
);
});

Suite(
"Resolves relative links in partials based on the path of the partial",
() => {
Expand Down

0 comments on commit b25c045

Please sign in to comment.