Skip to content

Commit c18ca40

Browse files
authored
A file is local if it exists in the root folder or a subfolder (#72)
* A file is local if it exists in the root folder or a subfolder. Work with normalized paths. closes #71 * apply suggestions from review * don't path-test refs that start with https://
1 parent 7a22ae4 commit c18ca40

File tree

3 files changed

+20
-9
lines changed

3 files changed

+20
-9
lines changed

src/files.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,21 @@ import {readdir, stat} from "node:fs/promises";
33
import {extname, join, normalize, relative} from "node:path";
44
import {isNodeError} from "./error.js";
55

6-
export function canReadSync(path: string): boolean {
6+
// A file is local if it exists in the root folder or a subfolder.
7+
export function isLocalFile(ref: string | null, root: string): boolean {
8+
return (
9+
typeof ref === "string" &&
10+
!/^(\w+:)\/\//.test(ref) &&
11+
!normalize(ref).startsWith("../") &&
12+
canReadSync(join(root, ref))
13+
);
14+
}
15+
16+
export function pathFromRoot(ref: string | null, root: string): string | null {
17+
return isLocalFile(ref, root) ? join(root, ref!) : null;
18+
}
19+
20+
function canReadSync(path: string): boolean {
721
try {
822
accessSync(path, constants.R_OK);
923
return statSync(path).isFile();

src/javascript.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {Parser, tokTypes, type Options} from "acorn";
22
import mime from "mime";
3-
import {join} from "node:path";
4-
import {canReadSync} from "./files.js";
3+
import {isLocalFile} from "./files.js";
54
import {findAwaits} from "./javascript/awaits.js";
65
import {findDeclarations} from "./javascript/declarations.js";
76
import {findFeatures} from "./javascript/features.js";
@@ -50,7 +49,7 @@ export function transpileJavaScript(input: string, options: ParseOptions): Trans
5049
const databases = node.features.filter((f) => f.type === "DatabaseClient").map((f) => ({name: f.name}));
5150
const files = node.features
5251
.filter((f) => f.type === "FileAttachment")
53-
.filter((f) => canReadSync(join(root, f.name)))
52+
.filter((f) => isLocalFile(f.name, root))
5453
.map((f) => ({name: f.name, mimeType: mime.getType(f.name)}));
5554
const inputs = Array.from(new Set<string>(node.references.map((r) => r.name)));
5655
const output = new Sourcemap(input);

src/markdown.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {type RuleInline} from "markdown-it/lib/parser_inline.js";
1010
import {type default as Renderer, type RenderRule} from "markdown-it/lib/renderer.js";
1111
import mime from "mime";
1212
import {readFile} from "node:fs/promises";
13-
import {join} from "node:path";
14-
import {canReadSync} from "./files.js";
13+
import {pathFromRoot} from "./files.js";
1514
import {computeHash} from "./hash.js";
1615
import {transpileJavaScript, type FileReference, type ImportReference, type Transpile} from "./javascript.js";
1716

@@ -301,9 +300,8 @@ function renderIntoPieces(renderer: Renderer, root: string): Renderer["render"]
301300
function normalizePieceHtml(html: string, root: string, context: ParseContext): string {
302301
const {document} = parseHTML(html);
303302
for (const element of document.querySelectorAll("link[href]") as any as Iterable<Element>) {
304-
const href = element.getAttribute("href")!;
305-
if (/^(\w+:)\/\//.test(href)) continue; // absolute url
306-
if (canReadSync(join(root, href))) {
303+
const href = pathFromRoot(element.getAttribute("href"), root);
304+
if (href) {
307305
context.files.push({name: href, mimeType: mime.getType(href)});
308306
element.setAttribute("href", `/_file/${href}`);
309307
}

0 commit comments

Comments
 (0)