Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

note loaders #1776

Merged
merged 17 commits into from
Nov 6, 2024
Merged
36 changes: 30 additions & 6 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {existsSync} from "node:fs";
import {copyFile, readFile, rm, stat, writeFile} from "node:fs/promises";
import {basename, dirname, extname, join} from "node:path/posix";
import type {Config} from "./config.js";
import {CliError} from "./error.js";
import {CliError, enoent} from "./error.js";
import {getClientPath, prepareOutput} from "./files.js";
import {findModule, getModuleHash, readJavaScript} from "./javascript/module.js";
import {transpileModule} from "./javascript/transpile.js";
Expand All @@ -16,6 +16,7 @@ import type {Resolvers} from "./resolvers.js";
import {getModuleResolvers, getResolvers} from "./resolvers.js";
import {resolveStylesheetPath} from "./resolvers.js";
import {bundleStyles, rollupClient} from "./rollup.js";
import type {Params} from "./route.js";
import {searchIndex} from "./search.js";
import {Telemetry} from "./telemetry.js";
import {tree} from "./tree.js";
Expand Down Expand Up @@ -74,6 +75,23 @@ export async function build(
let assetCount = 0;
let pageCount = 0;
const pagePaths = new Set<string>();

const {title} = config;
const buildManifest: BuildManifest = {...(title && {title}), pages: [], modules: [], files: []};
const addToManifest = (
type: string,
file: string,
{title, path, params}: {title?: string | null; path?: string; params?: Params}
) => {
const source = path == null || path === file.slice(1) ? null : join("/", path);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is path == null || path === file.slice(1) testing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path === file.slice(1) is for files that point to themselves (e.g. path=chart.js file=/chart.js)

the path is null for pages that have no source
this is only used in the tests for parseMarkdown… I can find a better way to express this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit better now; I'm still a bit unhappy with the naming (too many things we call "path")

buildManifest[type].push({
path: config.normalizePath(file),
...(title != null && {title}),
...(source && {source}),
...(params && {params})
Fil marked this conversation as resolved.
Show resolved Hide resolved
});
};

for await (const path of config.paths()) {
effects.output.write(`${faint("load")} ${path} `);
const start = performance.now();
Expand All @@ -95,6 +113,7 @@ export async function build(
}
const file = loaders.find(path);
if (file) {
addToManifest("files", path, file);
effects.output.write(`${faint("copy")} ${join(root, path)} ${faint("→")} `);
const sourcePath = join(root, await file.load({useStale: true}, effects));
await effects.copyFile(sourcePath, path);
Expand Down Expand Up @@ -192,7 +211,11 @@ export async function build(
// Copy over referenced files, accumulating hashed aliases.
for (const file of files) {
effects.output.write(`${faint("copy")} ${join(root, file)} ${faint("→")} `);
const sourcePath = join(root, await loaders.loadFile(join("/", file), {useStale: true}, effects));
const path = join("/", file);
const loader = loaders.find(path);
if (!loader) throw enoent(path);
addToManifest("files", path, loader);
const sourcePath = join(root, await loader.load({useStale: true}, effects));
const contents = await readFile(sourcePath);
const hash = createHash("sha256").update(contents).digest("hex").slice(0, 8);
const alias = applyHash(join("/_file", file), hash);
Expand Down Expand Up @@ -255,6 +278,7 @@ export async function build(
if (!module) throw new Error(`import not found: ${path}`);
const sourcePath = join(root, module.path);
const importPath = join("_import", module.path);
addToManifest("modules", path, module);
effects.output.write(`${faint("copy")} ${sourcePath} ${faint("→")} `);
const resolveImport = loaders.getModuleResolver(path);
const input = await readJavaScript(sourcePath);
Expand Down Expand Up @@ -320,15 +344,13 @@ export async function build(
}

// Render pages!
const buildManifest: BuildManifest = {pages: []};
if (config.title) buildManifest.title = config.title;
for (const [path, output] of outputs) {
effects.output.write(`${faint("render")} ${path} ${faint("→")} `);
if (output.type === "page") {
const {page, resolvers} = output;
const html = await renderPage(page, {...config, path, resolvers});
await effects.writeFile(`${path}.html`, html);
buildManifest.pages.push({path: config.normalizePath(path), title: page.title});
addToManifest("pages", path, page);
} else {
const {resolvers} = output;
const source = await renderModule(root, path, resolvers);
Expand Down Expand Up @@ -489,5 +511,7 @@ export class FileBuildEffects implements BuildEffects {

export interface BuildManifest {
title?: string;
pages: {path: string; title: string | null}[];
pages: {path: string; title?: string | null; source?: string; params?: Params}[];
modules: {path: string; source?: string; params?: Params}[];
files: {path: string; source?: string; params?: Params}[];
}
14 changes: 2 additions & 12 deletions src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,15 @@ export class LoaderResolver {
);
}

/**
* Loads the file at the specified path, returning a promise to the path to
* the (possibly generated) file relative to the source root.
*/
async loadFile(path: string, options?: LoadOptions, effects?: LoadEffects): Promise<string> {
const loader = this.find(path);
if (!loader) throw enoent(path);
return await loader.load(options, effects);
}

/**
* Loads the page at the specified path, returning a promise to the parsed
* page object.
*/
async loadPage(path: string, options: LoadOptions & ParseOptions, effects?: LoadEffects): Promise<MarkdownPage> {
const loader = this.findPage(path);
if (!loader) throw enoent(path);
const source = await readFile(join(this.root, await loader.load(options, effects)), "utf8");
return parseMarkdown(source, {params: loader.params, ...options});
const input = await readFile(join(this.root, await loader.load(options, effects)), "utf8");
return parseMarkdown(input, {source: loader.path, params: loader.params, ...options});
Fil marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
5 changes: 4 additions & 1 deletion src/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface MarkdownPage {
data: FrontMatter;
style: string | null;
code: MarkdownCode[];
path?: string;
params?: Params;
}

Expand Down Expand Up @@ -216,6 +217,7 @@ export interface ParseOptions {
head?: Config["head"];
header?: Config["header"];
footer?: Config["footer"];
source?: string;
params?: Params;
}

Expand All @@ -242,7 +244,7 @@ export function createMarkdownIt({
}

export function parseMarkdown(input: string, options: ParseOptions): MarkdownPage {
const {md, path, params} = options;
const {md, source, path, params} = options;
const {content, data} = readFrontMatter(input);
const code: MarkdownCode[] = [];
const context: ParseContext = {code, startLine: 0, currentLine: 0, path, params};
Expand All @@ -258,6 +260,7 @@ export function parseMarkdown(input: string, options: ParseOptions): MarkdownPag
title,
style: getStyle(data, options),
code,
path: source,
params
};
}
Expand Down
5 changes: 4 additions & 1 deletion src/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ export class PreviewServer {
}
throw enoent(path);
} else if (pathname.startsWith("/_file/")) {
send(req, await loaders.loadFile(pathname.slice("/_file".length)), {root}).pipe(res);
const path = pathname.slice("/_file".length);
const loader = loaders.find(path);
if (!loader) throw enoent(path);
send(req, await loader.load(), {root}).pipe(res);
} else {
if ((pathname = normalize(pathname)).startsWith("..")) throw new Error("Invalid path: " + pathname);

Expand Down
12 changes: 7 additions & 5 deletions test/build-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,13 @@ describe("build", () => {
effects.buildManifest!.pages.sort((a, b) => ascending(a.path, b.path));
assert.deepEqual(effects.buildManifest, {
pages: [
{path: "/", title: "Hello, world!"},
{path: "/cities/", title: "Cities"},
{path: "/cities/portland", title: "Portland"},
{path: "/weather", title: "It's going to be !"}
]
{path: "/", title: "Hello, world!", source: "/index.md"},
{path: "/cities/", title: "Cities", source: "/cities/index.md"},
{path: "/cities/portland", title: "Portland", source: "/cities/portland.md"},
{path: "/weather", title: "It's going to be !", source: "/weather.md"}
],
files: [{path: "/weather.txt"}],
modules: []
});

await Promise.all([inputDir, cacheDir, outputDir].map((dir) => rm(dir, {recursive: true}))).catch(() => {});
Expand Down
Loading