@analogjs/platform
Version:
The fullstack meta-framework for Angular
194 lines • 16.8 kB
JavaScript
import { normalizePath } from 'vite';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { globSync } from 'tinyglobby';
/**
* Content plugin that provides markdown and content file processing for Analog.
*
* IMPORTANT: This plugin uses tinyglobby for file discovery.
* Key pitfall with { dot: true }:
* - Returns relative paths from cwd (e.g., "apps/blog-app/src/content/...")
* - These paths CANNOT be used directly in ES module imports
* - Relative paths without ./ or ../ are treated as package names
* - Must convert to absolute paths for imports to work correctly
*/
export function contentPlugin({ highlighter, markedOptions, shikiOptions, prismOptions, } = {}, options) {
const cache = new Map();
let markedHighlighter;
const workspaceRoot = normalizePath(options?.workspaceRoot ?? process.cwd());
let config;
let root;
if (!highlighter) {
return [
{
name: 'analogjs-external-content',
config() {
return {
build: {
rollupOptions: {
external: ['@analogjs/content'],
},
},
};
},
},
{
name: 'analogjs-exclude-content-import',
transform(code) {
/**
* Remove the package so it doesn't get
* referenced when building for serverless
* functions.
*/
if (code.includes(`import('@analogjs/content')`)) {
return {
code: code.replace(`import('@analogjs/content')`, 'Promise.resolve({})'),
};
}
return;
},
},
];
}
return [
{
name: 'analogjs-content-frontmatter',
async transform(code, id) {
// Transform only the frontmatter into a JSON object for lists
if (!id.includes('analog-content-list=true')) {
return;
}
const cachedContent = cache.get(id);
// There's no reason to run `readFileSync` and frontmatter parsing if the
// `transform` hook is called with the same code. In such cases, we can simply
// return the cached attributes, which is faster than repeatedly reading files
// synchronously during the build process.
if (cachedContent?.code === code) {
return `export default ${cachedContent.attributes}`;
}
const fm = await import('front-matter');
// The `default` property will be available in CommonJS environment, for instance,
// when running unit tests. It's safe to retrieve `default` first, since we still
// fallback to the original implementation.
const frontmatter = fm.default || fm;
const fileContents = readFileSync(id.split('?')[0], 'utf8');
const { attributes } = frontmatter(fileContents);
const content = {
code,
attributes: JSON.stringify(attributes),
};
cache.set(id, content);
return `export default ${content.attributes}`;
},
},
{
name: 'analogjs-content-file',
enforce: 'post',
async load(id) {
if (!id.includes('analog-content-file=true')) {
return;
}
if (!markedHighlighter) {
if (highlighter === 'shiki') {
const { getShikiHighlighter } = await import('./content/shiki/index.js');
markedHighlighter = getShikiHighlighter(shikiOptions);
}
else {
const { getPrismHighlighter } = await import('./content/prism/index.js');
markedHighlighter = getPrismHighlighter();
const langs = [
'bash',
'css',
'javascript',
'json',
'markup',
'typescript',
];
if (Array.isArray(prismOptions?.additionalLangs) &&
prismOptions?.additionalLangs?.length > 0) {
langs.push(...prismOptions.additionalLangs);
}
const loadLanguages = await import('prismjs/components/index.js');
loadLanguages.default(langs);
}
}
const fm = await import('front-matter');
// The `default` property will be available in CommonJS environment, for instance,
// when running unit tests. It's safe to retrieve `default` first, since we still
// fallback to the original implementation.
const frontmatterFn = fm.default || fm;
const fileContents = readFileSync(id.split('?')[0], 'utf8');
const { body, frontmatter } = frontmatterFn(fileContents);
// parse markdown and highlight
const { getMarkedSetup } = await import('./content/marked/index.js');
const markedSetupService = getMarkedSetup({ mangle: true, ...(markedOptions || {}) }, markedHighlighter);
const mdContent = (await markedSetupService
.getMarkedInstance()
.parse(body));
return `export default ${JSON.stringify(`---\n${frontmatter}\n---\n\n${mdContent}`)}`;
},
},
{
name: 'analog-content-glob-routes',
config(_config) {
config = _config;
root = normalizePath(resolve(workspaceRoot, config.root || '.') || '.');
},
transform(code) {
if (code.includes('ANALOG_CONTENT_FILE_LIST')) {
const contentFilesList = globSync([
`${root}/src/content/**/*.md`,
...(options?.additionalContentDirs || [])?.map((glob) => `${workspaceRoot}${glob}/**/*.md`),
], { dot: true });
const eagerImports = [];
contentFilesList.forEach((module, index) => {
// CRITICAL: tinyglobby returns relative paths like "apps/blog-app/src/content/file.md"
// These MUST be converted to absolute paths for ES module imports
// Otherwise Node.js treats "apps" as a package name and throws "Cannot find package 'apps'"
const absolutePath = module.startsWith('/')
? module
: `${workspaceRoot}/${module}`;
eagerImports.push(`import { default as analog_module_${index} } from "${absolutePath}?analog-content-list=true";`);
});
let result = code.replace('let ANALOG_CONTENT_FILE_LIST = {};', `
let ANALOG_CONTENT_FILE_LIST = {${contentFilesList.map((module, index) => `"${module.replace(root, '')}": analog_module_${index}`)}};
`);
if (!code.includes('analog_module_')) {
result = `${eagerImports.join('\n')}\n${result}`;
}
return {
code: result,
map: { mappings: '' },
};
}
return;
},
},
{
name: 'analogjs-invalidate-content-dirs',
configureServer(server) {
function invalidateContent(path) {
if (path.includes(normalizePath(`/content/`))) {
server.moduleGraph.fileToModulesMap.forEach((mods) => {
mods.forEach((mod) => {
if (mod.id?.includes('analogjs') &&
mod.id?.includes('content')) {
server.moduleGraph.invalidateModule(mod);
mod.importers.forEach((imp) => {
server.moduleGraph.invalidateModule(imp);
});
}
});
});
server.ws.send({
type: 'full-reload',
});
}
}
server.watcher.on('add', invalidateContent);
server.watcher.on('unlink', invalidateContent);
},
},
];
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGVudC1wbHVnaW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9wbGF0Zm9ybS9zcmMvbGliL2NvbnRlbnQtcGx1Z2luLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDckMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUN2QyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFvQnRDOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQzNCLEVBQ0UsV0FBVyxFQUNYLGFBQWEsRUFDYixZQUFZLEVBQ1osWUFBWSxNQUNZLEVBQUUsRUFDNUIsT0FBaUI7SUFFakIsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQW1CLENBQUM7SUFFekMsSUFBSSxpQkFBMkMsQ0FBQztJQUNoRCxNQUFNLGFBQWEsR0FBRyxhQUFhLENBQUMsT0FBTyxFQUFFLGFBQWEsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUM3RSxJQUFJLE1BQWtCLENBQUM7SUFDdkIsSUFBSSxJQUFZLENBQUM7SUFFakIsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2pCLE9BQU87WUFDTDtnQkFDRSxJQUFJLEVBQUUsMkJBQTJCO2dCQUNqQyxNQUFNO29CQUNKLE9BQU87d0JBQ0wsS0FBSyxFQUFFOzRCQUNMLGFBQWEsRUFBRTtnQ0FDYixRQUFRLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQzs2QkFDaEM7eUJBQ0Y7cUJBQ0YsQ0FBQztnQkFDSixDQUFDO2FBQ0Y7WUFDRDtnQkFDRSxJQUFJLEVBQUUsaUNBQWlDO2dCQUN2QyxTQUFTLENBQUMsSUFBSTtvQkFDWjs7Ozt1QkFJRztvQkFDSCxJQUFJLElBQUksQ0FBQyxRQUFR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