Skip to content

Commit

Permalink
fix: resolves #14, #15 & #16
Browse files Browse the repository at this point in the history
  • Loading branch information
techfg committed Apr 7, 2024
1 parent 0e6b893 commit 36b48fb
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 65 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"debug": "^4.3.4",
"github-slugger": "^2.0.0",
"gray-matter": "^4.0.3",
"unist-util-visit": "^4.1.2"
"unist-util-visit": "^4.1.2",
"zod": "^3.22.4"
},
"peerDependencies": {
"astro": ">=2.x <5"
Expand Down
67 changes: 28 additions & 39 deletions src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
isValidRelativeLink,
splitPathFromQueryAndFragment,
normaliseAstroOutputPath,
generateSlug,
resolveSlug,
} from "./utils.mjs";

// This package makes a lot of assumptions based on it being used with Astro
Expand Down Expand Up @@ -49,34 +51,22 @@ function astroRehypeRelativeMarkdownLinks(options = {}) {
// read gray matter from relative file
const relativeFileContent = fs.readFileSync(relativeFile);
const { data: frontmatter } = matter(relativeFileContent);
const relativeFileCustomSlug = frontmatter.slug;
const relativeFileHasCustomSlug = Boolean(relativeFileCustomSlug);

let webPathFinal;
const webPath = relativeFile.split(
options.contentPath || defaultContentPath,
)[1];

if (relativeFileHasCustomSlug) {
webPathFinal =
path.posix.sep +
[
webPath.split(path.sep)[1], // this should be the content collection
relativeFileCustomSlug,
].join(path.posix.sep);
} else {
webPathFinal = replaceExt(webPath, "");
}

webPathFinal = webPathFinal.split(path.sep);

// Remove index from the end of the path to satsify instances where we'll be generating
// index.html files for directories
if (webPathFinal[webPathFinal.length - 1] === "index") {
webPathFinal.pop();
}

webPathFinal = webPathFinal.join(path.posix.sep);
const frontmatterSlug = frontmatter.slug;
const contentDir = options.contentPath || defaultContentPath;

const relativeToContentPath = path.relative(contentDir, relativeFile);
const collectionName = path.dirname(relativeToContentPath).split(path.posix.sep)[0];
const relativeToCollectionPath = path.relative(collectionName, relativeToContentPath);
const withoutFileExt = replaceExt(relativeToCollectionPath, "")
const pathSegments = withoutFileExt.split(path.posix.sep);
const generatedSlug = generateSlug(pathSegments);
const resolvedSlug = resolveSlug(generatedSlug, frontmatterSlug);

let webPathFinal = path.posix.sep +
[
collectionName,
resolvedSlug,
].join(path.posix.sep);

if (queryStringAndFragment) {
webPathFinal += queryStringAndFragment;
Expand All @@ -86,17 +76,16 @@ function astroRehypeRelativeMarkdownLinks(options = {}) {

// Debugging
debug("--------------------------------");
debug("md/mdx AST Current File : %s", currentFile);
debug("md/mdx AST Current File Dir : %s", currentFileDirectory);
debug("md/mdx AST href full : %s", nodeHref);
debug("md/mdx AST href path : %s", url);
debug("md/mdx AST href qs and/or hash : %s", queryStringAndFragment);
debug("File relative to current md/mdx: %s", relativeFile);
debug("File relative has custom slug : %s", relativeFileHasCustomSlug);
if (relativeFileHasCustomSlug) {
debug("File relative custom slug : %s", relativeFileCustomSlug);
}
debug("Final URL path : %s", webPathFinal);
debug("md/mdx AST Current File : %s", currentFile);
debug("md/mdx AST Current File Dir : %s", currentFileDirectory);
debug("md/mdx AST href full : %s", nodeHref);
debug("md/mdx AST href path : %s", url);
debug("md/mdx AST href qs and/or hash : %s", queryStringAndFragment);
debug("File relative to current md/mdx : %s", relativeFile);
debug("File relative custom slug : %s", frontmatterSlug);
debug("File relative generated slug : %s", generatedSlug);
debug("File relative resolved slug : %s", resolvedSlug);
debug("Final URL path : %s", webPathFinal);

node.properties.href = webPathFinal;
});
Expand Down
2 changes: 2 additions & 0 deletions src/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export type SplitPathFromQueryAndFragmentFn = (
export type ReplaceExtFn = (path: string, ext: string) => string;
export type IsCurrentDirectoryFn = (path: string) => boolean;
export type IsValidRelativeLinkFn = (link: string) => boolean;
export type GenerateSlug = (pathSegments: string[]) => string;
export type ResolveSlug = (generatedSlug: string, frontmatterSlug?: unknown) => string;
33 changes: 17 additions & 16 deletions src/utils.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from "path";
import { slug as githubSlug } from "github-slugger";
import { z } from 'zod';

const pathSeparator = path.sep;
const validMarkdownExtensions = [".md", ".mdx"];
Expand Down Expand Up @@ -82,26 +83,26 @@ export const normaliseAstroOutputPath = (initialPath, options = {}) => {
return;
}

const [pathWithoutQueryAndFragment, queryStringAndFragment] =
splitPathFromQueryAndFragment(initialPath);

const pathSegments = pathWithoutQueryAndFragment.split(pathSeparator);

let normalisedPath = pathSegments
.map((segment) => githubSlug(segment))
.join("/");

if (queryStringAndFragment) {
normalisedPath += queryStringAndFragment;
}

if (!options.basePath) {
return normalisedPath;
return initialPath;
}

if (options.basePath.startsWith("/")) {
return path.join(options.basePath, normalisedPath);
return path.join(options.basePath, initialPath);
}

return "/" + path.join(options.basePath, normalisedPath);
return "/" + path.join(options.basePath, initialPath);
};

/** @type {import('./utils').GenerateSlug} */
export const generateSlug = (pathSegments) => {
return pathSegments
.map((segment) => githubSlug(segment))
.join("/")
.replace(/\/index$/, '');
}

/** @type {import('./utils').ResolveSlug} */
export const resolveSlug = (generatedSlug, frontmatterSlug) => {
return z.string().default(generatedSlug).parse(frontmatterSlug);
}
102 changes: 93 additions & 9 deletions src/utils.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
normaliseAstroOutputPath,
replaceExt,
splitPathFromQueryAndFragment,
generateSlug,
resolveSlug
} from "./utils.mjs";

describe("replaceExt", () => {
Expand Down Expand Up @@ -104,46 +106,128 @@ describe("splitPathFromQueryAndFragment", () => {
});
});

describe("normaliseAstroOutputPath", () => {
describe("generateSlug", () => {
test("removes uppercase characters", () => {
const actual = normaliseAstroOutputPath("/Foo-TESTING-test");
const actual = generateSlug(["/Foo-TESTING-test"]);

assert.equal(actual, "/foo-testing-test");
assert.equal(actual, "foo-testing-test");
});

test("replaces spaces with dashes", () => {
const actual = normaliseAstroOutputPath("/foo testing test");
const actual = generateSlug(["/foo testing test"]);

assert.equal(actual, "foo-testing-test");
});

test("removes periods", () => {
const actual = generateSlug(["/foo.testing.test"]);

assert.equal(actual, "footestingtest");
});

test("removes uppercase across multiple segments", () => {
const actual = generateSlug(["/FOO", "/foo-TESTING-test"]);

assert.equal(actual, "foo/foo-testing-test");
});

test("removes spaces across multiple segments with no leading slashes", () => {
const actual = generateSlug(["FOO", "foo TESTING test"]);

assert.equal(actual, "/foo-testing-test");
assert.equal(actual, "foo/foo-testing-test");
});

test("should strip index when subdirectory", () => {
const actual = generateSlug(["foo", "index"]);

assert.equal(actual, "foo");
});

test("should strip mixed-case index when subdirectory", () => {
const actual = generateSlug(["foo", "iNdEX"]);

assert.equal(actual, "foo");
});

test("should not strip index root", () => {
const actual = generateSlug(["index"]);

assert.equal(actual, "index");
});

test("should not strip mixed case index root", () => {
const actual = generateSlug(["iNdEX"]);

assert.equal(actual, "index");
});
});

describe("resolveSlug", () => {
test("uses generated slug when frontmatter undefined", () => {
const actual = resolveSlug("foo/bar");

assert.equal(actual, "foo/bar");
});

test("uses frontmatter when frontmatter empty string", () => {
const actual = resolveSlug("foo/bar", "");

assert.equal(actual, "");
});

test("uses frontmatter when frontmatter is index", () => {
const actual = resolveSlug("foo/bar", "index");

assert.equal(actual, "index");
});

test("uses frontmatter when frontmatter has extension", () => {
const actual = resolveSlug("foo/bar", "foo.md");

assert.equal(actual, "foo.md");
});

test("throws exception when no valid slug", () => {
assert.throws(() => resolveSlug());
});

test("throws exception when frontmatter is null", () => {
assert.throws(() => resolveSlug("foo/bar", null ));
});

test("throws exception when frontmatter is number", () => {
assert.throws(() => resolveSlug("foo/bar", 5 ));
});
});

describe("normaliseAstroOutputPath", () => {
describe("prefix base to path", () => {
test("base with no slashes", () => {
const actual = normaliseAstroOutputPath("/foo testing test", {
const actual = normaliseAstroOutputPath("/foo-testing-test", {
basePath: "base",
});

assert.equal(actual, "/base/foo-testing-test");
});

test("base with slash at start", () => {
const actual = normaliseAstroOutputPath("/foo testing test", {
const actual = normaliseAstroOutputPath("/foo-testing-test", {
basePath: "/base",
});

assert.equal(actual, "/base/foo-testing-test");
});

test("base with slash at end", () => {
const actual = normaliseAstroOutputPath("/foo testing test", {
const actual = normaliseAstroOutputPath("/foo-testing-test", {
basePath: "base/",
});

assert.equal(actual, "/base/foo-testing-test");
});

test("base with slash at start and end", () => {
const actual = normaliseAstroOutputPath("/foo testing test", {
const actual = normaliseAstroOutputPath("/foo-testing-test", {
basePath: "/base/",
});

Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ __metadata:
prettier: "npm:^4.0.0-alpha.8"
rehype: "npm:^13.0.1"
unist-util-visit: "npm:^4.1.2"
zod: "npm:^3.22.4"
peerDependencies:
astro: ">=2.x <5"
languageName: unknown
Expand Down Expand Up @@ -1170,6 +1171,13 @@ __metadata:
languageName: node
linkType: hard

"zod@npm:^3.22.4":
version: 3.22.4
resolution: "zod@npm:3.22.4"
checksum: 10c0/7578ab283dac0eee66a0ad0fc4a7f28c43e6745aadb3a529f59a4b851aa10872b3890398b3160f257f4b6817b4ce643debdda4fb21a2c040adda7862cab0a587
languageName: node
linkType: hard

"zwitch@npm:^2.0.0, zwitch@npm:^2.0.4":
version: 2.0.4
resolution: "zwitch@npm:2.0.4"
Expand Down

0 comments on commit 36b48fb

Please sign in to comment.