Skip to content
28 changes: 18 additions & 10 deletions packages/void/src/pages/scan.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync, readFileSync } from 'node:fs';
import { existsSync } from 'node:fs';
import { join, dirname, basename, extname } from 'pathslash';
import { glob } from 'tinyglobby';
import { parseSync } from 'vite';
Expand All @@ -16,6 +16,7 @@ import {
type AstProgram,
type AstVariableDeclaration,
} from '../shared/ast.ts';
import { deriveFromSource } from '../shared/source-analysis-cache.ts';
import { staticExportInitializers, staticLiteral } from '../shared/static-exports.ts';
import { scriptBlocks } from './island-imports.ts';
import { resolveNamedLayout } from './layout-resolution.ts';
Expand Down Expand Up @@ -222,7 +223,8 @@ export async function scanPages(root: string, options?: PageScanOptions): Promis
const layouts: Array<LayoutDefinition> = [];
const namedLayouts: Array<NamedLayoutDefinition> = [];

for (const file of files) {
// Glob traversal order can vary between identical scans. Keep layout inventories stable too.
for (const file of files.sort()) {
const segments = file.split('/');
// Skip files starting with _ in any segment (except _layouts/)
if (segments.some((s) => s.startsWith('_') && s !== '_layouts')) {
Expand Down Expand Up @@ -298,8 +300,11 @@ export async function scanPages(root: string, options?: PageScanOptions): Promis
const candidate = `${serverBase}.server.${serverExt}`;
if (existsSync(join(pagesDir, candidate))) {
serverPath = candidate;
const content = readFileSync(join(pagesDir, candidate), 'utf-8');
const metadata = detectPageServerMetadata(content, candidate);

const metadata = deriveFromSource(join(pagesDir, candidate), 'page-server', (source) =>
detectPageServerMetadata(source, candidate),
);

methods.push(...metadata.methods);
revalidate = metadata.revalidate;
prerender = metadata.prerender;
Expand Down Expand Up @@ -331,13 +336,16 @@ export async function scanPages(root: string, options?: PageScanOptions): Promis
// Extract frontmatter metadata and layout from .md files, or layout from component files
let metadata: { title?: string; description?: string } | undefined;
let layout: string | false | undefined;

if (ext === '.md') {
const mdContent = readFileSync(join(pagesDir, file), 'utf-8');
metadata = extractFrontmatterMeta(mdContent);
layout = extractFrontmatterLayout(mdContent);
({ metadata, layout } = deriveFromSource(join(pagesDir, file), 'page-markdown', (source) => ({
metadata: extractFrontmatterMeta(source),
layout: extractFrontmatterLayout(source),
})));
} else {
const componentContent = readFileSync(join(pagesDir, file), 'utf-8');
layout = componentLayout(componentContent, file);
layout = deriveFromSource(join(pagesDir, file), 'page-layout', (source) =>
componentLayout(source, file),
);
}

pages.push({
Expand All @@ -354,7 +362,7 @@ export async function scanPages(root: string, options?: PageScanOptions): Promis
...rendering,
hasGetPrerenderPaths,
island,
metadata,
metadata: metadata ? { ...metadata } : undefined,
layout,
});
}
Expand Down
7 changes: 5 additions & 2 deletions packages/void/src/plugin-inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type RouteEnvironment,
} from './router/parse-filename.ts';
import { isWebSocketPatternFile } from './router/websocket.ts';
import { deriveFromSource } from './shared/source-analysis-cache.ts';
import { getSsrServerEntryPath } from './ssr/entry.ts';

const SOURCE_GLOB = '**/*.{ts,tsx,mts,js,jsx,mjs,astro}';
Expand Down Expand Up @@ -168,8 +169,10 @@ export function inferProjectBindings(
return;
}
}
const code = readFileSync(file, 'utf-8');
allBindings.push(inferBindingsFromFile(file, code));

allBindings.push(
deriveFromSource(file, 'bindings', (code) => inferBindingsFromFile(file, code)),
);
};

for (const dir of dirs) {
Expand Down
Loading
Loading