Skip to content
This repository has been archived by the owner on Jun 23, 2023. It is now read-only.

Commit

Permalink
Allow more paths in partial link resolution (gravitational#180)
Browse files Browse the repository at this point in the history
Fixes gravitational#167

`handlePartialLink` currently hardcodes `docs/pages` as the location of
all docs pages, and uses this file path segment to construct the absolute
path of the MDX file that includes a partial.

This change adds the root directory of all partials as a parameter of
`handlePartialLink` and uses that instead. Since this root directory is
configurable outside `remark-includes`, this change lets us add unit
tests for `handlePartialLink`.

This change also handles relative path resolution for image and link
definition URLs. The current version of `handlePartialLink` only handles
links paths. As a result, this renames `handlePartialLink` to
`handleURLPath`.
  • Loading branch information
ptgott authored Nov 28, 2022
1 parent 4913b6f commit 9a2f76f
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 12 deletions.
11 changes: 11 additions & 0 deletions server/fixtures/includes/database-access/attach-iam-policies.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Attach the policy and permission boundary you created earlier to the IAM
identity your Teleport Database Service will be using.

For example, if the Database Service runs as an IAM user, go to the page of the IAM user
in the AWS Management Console, attach the created policy in the "Permissions
policies" section, and set the created boundary policy in the "Permissions
boundary" section.

<Figure>
![IAM user](../../../img/database-access/[email protected])
</Figure>
6 changes: 6 additions & 0 deletions server/fixtures/includes/include-relative-link.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

Check out our [instructions](../installation.mdx).

Here is an image showing a successful installation:

[Successful installation](../installation.png)
3 changes: 3 additions & 0 deletions server/fixtures/includes/includes-relative-link-def.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This partial has a relative link [definition].

[definition]: ../../installation.mdx
45 changes: 33 additions & 12 deletions server/remark-includes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { Parent } from "unist";
import type { Content, Code, Text } from "mdast";
import type { VFile } from "vfile";
import type { Node } from "mdast-util-from-markdown/lib";
import { dirname, join, relative } from "path";
import { basename, dirname, join, relative, resolve } from "path";

import { existsSync, readFileSync } from "fs";
import { visitParents } from "unist-util-visit-parents";
Expand Down Expand Up @@ -273,25 +273,46 @@ const isInclude = (node: Code | Text): node is Code | Text =>
* and without:
* docs/image.jpg
*/
const handlePartialLink = (node: Node, path: string, mdxPath: string) => {
if (node.type === "link") {
const handleURLPath = (
node: Node,
rootDir: string,
path: string,
mdxPath: string
) => {
if (
node.type === "image" ||
node.type === "link" ||
node.type === "definition"
) {
const href = node.url;

// Ignore non-strings, absolute paths, or web URLs
if (typeof href !== "string" || href[0] === "/" || /^http/.test(href)) {
return href;
}
// root where all documentation pages store
const absStart = "docs/pages";
// find an "abs" (starting with root) directory path of the file in which the partial doc was inserted
const absMdxPath = dirname(absStart + mdxPath.split(absStart).pop());
const absTargetPath = join(dirname(path), href);
// make the reference path relative to the place where the partial doc was inserted
node.url = relative(absMdxPath, absTargetPath);

// Find the absolute path of the file that includes the partial
const absMdxPath = resolve(mdxPath);
// Construct an absolute path out of the root directory for all partials,
// the directory containing the partial (within the root directory for all
// partials) and the relative path to the target asset, e.g.,
// "docs/pages/includes", "kubernetes", and
// "../../target.png".
const absTargetPath = resolve(rootDir, dirname(path), href);
// Make the reference path relative to the place where the partial doc was
// inserted.
node.url = relative(
// relative() counts all path segments, even the file itself, when
// comparing path segments between the "from" and "to" paths, so we
// start from the directory containing the file that includes the partial.
dirname(absMdxPath),
absTargetPath
);
}

if ("children" in node) {
node.children?.forEach?.((child) =>
handlePartialLink(child, path, mdxPath)
handleURLPath(child, rootDir, path, mdxPath)
);
}
};
Expand Down Expand Up @@ -361,7 +382,7 @@ export default function remarkIncludes({
],
});

handlePartialLink(tree, path, vfile.path);
handleURLPath(tree, resolvedRootDir, path, vfile.path);

const grandParent = ancestors[ancestors.length - 2] as Parent;
const parentIndex = grandParent.children.indexOf(parent);
Expand Down
107 changes: 107 additions & 0 deletions uvu-tests/remark-includes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,111 @@ Suite("Resolves template variables in includes", () => {
assert.equal(result, expected);
});

Suite(
"Resolves relative links in partials based on the path of the partial",
() => {
const includingRelativeLink = `Here are instructions on installing the software:
(!include-relative-link.mdx!)
`;
interface testCase {
includingPage: string;
description: string;
path: string;
expected: string;
}
const testCases: testCase[] = [
{
includingPage: includingRelativeLink,
description: "including file is on the same dir level as the partial",
path: "server/fixtures/dir/samelevel.mdx",
expected: `Here are instructions on installing the software:
Check out our [instructions](../installation.mdx).
Here is an image showing a successful installation:
[Successful installation](../installation.png)
`,
},
{
includingPage: includingRelativeLink,
description: "including file is below the dir level of the partial",
path: "server/fixtures/dir/dir2/below.mdx",
expected: `Here are instructions on installing the software:
Check out our [instructions](../../installation.mdx).
Here is an image showing a successful installation:
[Successful installation](../../installation.png)
`,
},
{
includingPage: includingRelativeLink,
description: "including file is above the dir level of the partial",
path: "server/fixtures/above.mdx",
expected: `Here are instructions on installing the software:
Check out our [instructions](installation.mdx).
Here is an image showing a successful installation:
[Successful installation](installation.png)
`,
},
{
includingPage: `Here's how to attach an IAM policy for DB Access:
(!database-access/attach-iam-policies.mdx!)
`,
description: "relative image path",
path: "server/fixtures/includes/db-policy.mdx",
expected: `Here's how to attach an IAM policy for DB Access:
Attach the policy and permission boundary you created earlier to the IAM
identity your Teleport Database Service will be using.
For example, if the Database Service runs as an IAM user, go to the page of the IAM user
in the AWS Management Console, attach the created policy in the "Permissions
policies" section, and set the created boundary policy in the "Permissions
boundary" section.
<Figure>
![IAM user](../../img/database-access/[email protected])
</Figure>
`,
},
{
includingPage: "(!includes-relative-link-def.mdx!)",
description: "relative definition path",
path: "server/fixtures/definition.mdx",
expected: `This partial has a relative link [definition].
[definition]: ../installation.mdx
`,
},
];

for (const testCase of testCases) {
const actual = transformer({
value: testCase.includingPage,
path: testCase.path,
}).toString();

assert.equal(
actual,
testCase.expected,
new Error(
`${testCase.description}: expected the output:\n` +
testCase.expected +
"\n\n" +
"but got:\n" +
actual
)
);
}
}
);

Suite.run();

0 comments on commit 9a2f76f

Please sign in to comment.