feat: URL support for markdoc tags
This commit is contained in:
parent
32bde967f4
commit
d87544c241
3 changed files with 99 additions and 39 deletions
|
@ -9,7 +9,7 @@ import _Markdoc from '@markdoc/markdoc';
|
||||||
import type { AstroInstance } from 'astro';
|
import type { AstroInstance } from 'astro';
|
||||||
import { heading } from './heading-ids.js';
|
import { heading } from './heading-ids.js';
|
||||||
|
|
||||||
type Render = AstroInstance['default'] | string;
|
type Render = AstroInstance['default'] | URL | string;
|
||||||
|
|
||||||
export type AstroMarkdocConfig<C extends Record<string, any> = Record<string, any>> = Omit<
|
export type AstroMarkdocConfig<C extends Record<string, any> = Record<string, any>> = Omit<
|
||||||
MarkdocConfig,
|
MarkdocConfig,
|
||||||
|
|
|
@ -61,6 +61,9 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration
|
||||||
}
|
}
|
||||||
const userMarkdocConfig = markdocConfigResult?.config ?? {};
|
const userMarkdocConfig = markdocConfigResult?.config ?? {};
|
||||||
|
|
||||||
|
const markdocConfigId = 'astro:markdoc-config';
|
||||||
|
const resolvedMarkdocConfigId = '\x00' + markdocConfigId;
|
||||||
|
|
||||||
function getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
|
function getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
|
||||||
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
|
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
|
||||||
return {
|
return {
|
||||||
|
@ -130,51 +133,45 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration
|
||||||
} from 'astro/runtime/server/index.js';
|
} from 'astro/runtime/server/index.js';
|
||||||
import { Renderer } from '@astrojs/markdoc/components';
|
import { Renderer } from '@astrojs/markdoc/components';
|
||||||
import { collectHeadings, setupConfig, setupConfigSync, Markdoc } from '@astrojs/markdoc/runtime';
|
import { collectHeadings, setupConfig, setupConfigSync, Markdoc } from '@astrojs/markdoc/runtime';
|
||||||
${
|
import userConfig from ${JSON.stringify(markdocConfigId)};${
|
||||||
markdocConfigResult
|
|
||||||
? `import _userConfig from ${JSON.stringify(
|
|
||||||
markdocConfigResultId
|
|
||||||
)};\nconst userConfig = _userConfig ?? {};`
|
|
||||||
: 'const userConfig = {};'
|
|
||||||
}${
|
|
||||||
astroConfig.experimental.assets
|
astroConfig.experimental.assets
|
||||||
? `\nimport { experimentalAssetsConfig } from '@astrojs/markdoc/experimental-assets-config';\nuserConfig.nodes = { ...experimentalAssetsConfig.nodes, ...userConfig.nodes };`
|
? `\nimport { experimentalAssetsConfig } from '@astrojs/markdoc/experimental-assets-config';\nuserConfig.nodes = { ...experimentalAssetsConfig.nodes, ...userConfig.nodes };`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
const stringifiedAst = ${JSON.stringify(
|
const stringifiedAst = ${JSON.stringify(
|
||||||
/* Double stringify to encode *as* stringified JSON */ JSON.stringify(ast)
|
/* Double stringify to encode *as* stringified JSON */ JSON.stringify(ast)
|
||||||
)};
|
)};
|
||||||
export function getHeadings() {
|
export function getHeadings() {
|
||||||
${
|
${
|
||||||
/* Yes, we are transforming twice (once from `getHeadings()` and again from <Content /> in case of variables).
|
/* Yes, we are transforming twice (once from `getHeadings()` and again from <Content /> in case of variables).
|
||||||
TODO: propose new `render()` API to allow Markdoc variable passing to `render()` itself,
|
TODO: propose new `render()` API to allow Markdoc variable passing to `render()` itself,
|
||||||
instead of the Content component. Would remove double-transform and unlock variable resolution in heading slugs. */
|
instead of the Content component. Would remove double-transform and unlock variable resolution in heading slugs. */
|
||||||
''
|
''
|
||||||
}
|
}
|
||||||
const headingConfig = userConfig.nodes?.heading;
|
const headingConfig = userConfig.nodes?.heading;
|
||||||
const config = setupConfigSync(headingConfig ? { nodes: { heading: headingConfig } } : {});
|
const config = setupConfigSync(headingConfig ? { nodes: { heading: headingConfig } } : {});
|
||||||
const ast = Markdoc.Ast.fromJSON(stringifiedAst);
|
const ast = Markdoc.Ast.fromJSON(stringifiedAst);
|
||||||
const content = Markdoc.transform(ast, config);
|
const content = Markdoc.transform(ast, config);
|
||||||
return collectHeadings(Array.isArray(content) ? content : content.children);
|
return collectHeadings(Array.isArray(content) ? content : content.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Content = createComponent({
|
export const Content = createComponent({
|
||||||
async factory(result, props) {
|
async factory(result, props) {
|
||||||
const config = await setupConfig({
|
const config = await setupConfig({
|
||||||
...userConfig,
|
...userConfig,
|
||||||
variables: { ...userConfig.variables, ...props },
|
variables: { ...userConfig.variables, ...props },
|
||||||
});
|
});
|
||||||
|
|
||||||
return renderComponent(
|
return renderComponent(
|
||||||
result,
|
result,
|
||||||
Renderer.name,
|
Renderer.name,
|
||||||
Renderer,
|
Renderer,
|
||||||
{ stringifiedAst, config },
|
{ stringifiedAst, config },
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
propagation: 'self',
|
propagation: 'self',
|
||||||
});`;
|
});`;
|
||||||
return { code: res };
|
return { code: res };
|
||||||
},
|
},
|
||||||
contentModuleTypes: await fs.promises.readFile(
|
contentModuleTypes: await fs.promises.readFile(
|
||||||
|
@ -227,6 +224,48 @@ export const Content = createComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '@astrojs/markdoc:config',
|
||||||
|
resolveId(this: rollup.PluginContext, id: string) {
|
||||||
|
if (id === markdocConfigId) {
|
||||||
|
return resolvedMarkdocConfigId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
load(id: string) {
|
||||||
|
if (id !== resolvedMarkdocConfigId) return;
|
||||||
|
|
||||||
|
// TODO: migrate config loader, invalidate on change
|
||||||
|
if (!markdocConfigResult) {
|
||||||
|
return `export default {}`;
|
||||||
|
}
|
||||||
|
const { config, fileUrl } = markdocConfigResult;
|
||||||
|
let componentPathnameByTag: Record<string, string> = {};
|
||||||
|
const { tags = {}, nodes = {} /* TODO: nodes */ } = config;
|
||||||
|
for (const [name, value] of Object.entries(tags)) {
|
||||||
|
if (value.render instanceof URL) {
|
||||||
|
componentPathnameByTag[name] = value.render.pathname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let stringifiedComponentImports = '';
|
||||||
|
let stringifiedComponentMap = '{';
|
||||||
|
for (const [tag, componentPathname] of Object.entries(componentPathnameByTag)) {
|
||||||
|
stringifiedComponentImports += `import ${tag} from ${JSON.stringify(
|
||||||
|
componentPathname + '?astroPropagatedAssets'
|
||||||
|
)};\n`;
|
||||||
|
stringifiedComponentMap += `${tag},\n`;
|
||||||
|
}
|
||||||
|
stringifiedComponentMap += '}';
|
||||||
|
const code = `import { resolveComponentImports } from '@astrojs/markdoc/runtime';
|
||||||
|
import markdocConfig from ${JSON.stringify(fileUrl.pathname)};
|
||||||
|
${stringifiedComponentImports};
|
||||||
|
|
||||||
|
const tagComponentMap = ${stringifiedComponentMap};
|
||||||
|
export default resolveComponentImports(markdocConfig, tagComponentMap);`;
|
||||||
|
|
||||||
|
console.log('$$$markdoc-config', code);
|
||||||
|
return code;
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { MarkdownHeading } from '@astrojs/markdown-remark';
|
import type { MarkdownHeading } from '@astrojs/markdown-remark';
|
||||||
|
import type { AstroInstance } from 'astro';
|
||||||
import Markdoc, { type RenderableTreeNode } from '@markdoc/markdoc';
|
import Markdoc, { type RenderableTreeNode } from '@markdoc/markdoc';
|
||||||
import type { AstroMarkdocConfig } from './config.js';
|
import type { AstroMarkdocConfig } from './config.js';
|
||||||
import { setupHeadingConfig } from './heading-ids.js';
|
import { setupHeadingConfig } from './heading-ids.js';
|
||||||
|
@ -66,6 +67,26 @@ function mergeConfig(configA: AstroMarkdocConfig, configB: AstroMarkdocConfig):
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveComponentImports(
|
||||||
|
markdocConfig: AstroMarkdocConfig,
|
||||||
|
tagComponentMap: Record<string, AstroInstance['default']>
|
||||||
|
) {
|
||||||
|
const resolvedTags = { ...markdocConfig.tags };
|
||||||
|
for (const [tagName, tagConfig] of Object.entries(resolvedTags)) {
|
||||||
|
if (tagName in tagComponentMap) {
|
||||||
|
resolvedTags[tagName] = {
|
||||||
|
...tagConfig,
|
||||||
|
render: tagComponentMap[tagName],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('resolvedTags', resolvedTags);
|
||||||
|
return {
|
||||||
|
...markdocConfig,
|
||||||
|
tags: resolvedTags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get text content as a string from a Markdoc transform AST
|
* Get text content as a string from a Markdoc transform AST
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue