@astrojs/markdoc 0.0.0-10745-20240410180016

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +59 -0
  2. package/README.md +38 -0
  3. package/components/Renderer.astro +24 -0
  4. package/components/TreeNode.ts +160 -0
  5. package/components/index.ts +2 -0
  6. package/dist/config.d.ts +57 -0
  7. package/dist/config.js +26 -0
  8. package/dist/content-entry-type.d.ts +8 -0
  9. package/dist/content-entry-type.js +317 -0
  10. package/dist/extensions/prism.d.ts +2 -0
  11. package/dist/extensions/prism.js +21 -0
  12. package/dist/extensions/shiki.d.ts +3 -0
  13. package/dist/extensions/shiki.js +21 -0
  14. package/dist/heading-ids.d.ts +10 -0
  15. package/dist/heading-ids.js +55 -0
  16. package/dist/html/css/parse-inline-css-to-react.d.ts +1 -0
  17. package/dist/html/css/parse-inline-css-to-react.js +21 -0
  18. package/dist/html/css/parse-inline-styles.d.ts +8 -0
  19. package/dist/html/css/parse-inline-styles.js +153 -0
  20. package/dist/html/css/style-to-object.d.ts +12 -0
  21. package/dist/html/css/style-to-object.js +53 -0
  22. package/dist/html/index.d.ts +2 -0
  23. package/dist/html/index.js +6 -0
  24. package/dist/html/tagdefs/html.tag.d.ts +2 -0
  25. package/dist/html/tagdefs/html.tag.js +21 -0
  26. package/dist/html/transform/html-token-transform.d.ts +3 -0
  27. package/dist/html/transform/html-token-transform.js +154 -0
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.js +39 -0
  30. package/dist/load-config.d.ts +8 -0
  31. package/dist/load-config.js +91 -0
  32. package/dist/options.d.ts +4 -0
  33. package/dist/options.js +0 -0
  34. package/dist/runtime-assets-config.d.ts +2 -0
  35. package/dist/runtime-assets-config.js +25 -0
  36. package/dist/runtime.d.ts +29 -0
  37. package/dist/runtime.js +152 -0
  38. package/dist/tokenizer.d.ts +3 -0
  39. package/dist/tokenizer.js +27 -0
  40. package/dist/utils.d.ts +46 -0
  41. package/dist/utils.js +47 -0
  42. package/package.json +94 -0
  43. package/template/content-module-types.d.ts +8 -0
@@ -0,0 +1,91 @@
1
+ import * as fs from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { build as esbuild } from "esbuild";
4
+ import { MarkdocError } from "./utils.js";
5
+ const SUPPORTED_MARKDOC_CONFIG_FILES = [
6
+ "markdoc.config.js",
7
+ "markdoc.config.mjs",
8
+ "markdoc.config.mts",
9
+ "markdoc.config.ts"
10
+ ];
11
+ async function loadMarkdocConfig(astroConfig) {
12
+ let markdocConfigUrl;
13
+ for (const filename of SUPPORTED_MARKDOC_CONFIG_FILES) {
14
+ const filePath = new URL(filename, astroConfig.root);
15
+ if (!fs.existsSync(filePath))
16
+ continue;
17
+ markdocConfigUrl = filePath;
18
+ break;
19
+ }
20
+ if (!markdocConfigUrl)
21
+ return;
22
+ const { code } = await bundleConfigFile({
23
+ markdocConfigUrl,
24
+ astroConfig
25
+ });
26
+ const config = await loadConfigFromBundledFile(astroConfig.root, code);
27
+ return {
28
+ config,
29
+ fileUrl: markdocConfigUrl
30
+ };
31
+ }
32
+ async function bundleConfigFile({
33
+ markdocConfigUrl,
34
+ astroConfig
35
+ }) {
36
+ let markdocError;
37
+ const result = await esbuild({
38
+ absWorkingDir: fileURLToPath(astroConfig.root),
39
+ entryPoints: [fileURLToPath(markdocConfigUrl)],
40
+ outfile: "out.js",
41
+ write: false,
42
+ target: ["node16"],
43
+ platform: "node",
44
+ packages: "external",
45
+ bundle: true,
46
+ format: "esm",
47
+ sourcemap: "inline",
48
+ metafile: true,
49
+ plugins: [
50
+ {
51
+ name: "stub-astro-imports",
52
+ setup(build) {
53
+ build.onResolve({ filter: /.*\.astro$/ }, () => {
54
+ markdocError = new MarkdocError({
55
+ message: "`.astro` files are no longer supported in the Markdoc config.",
56
+ hint: "Use the `component()` utility to specify a component path instead. See https://docs.astro.build/en/guides/integrations-guide/markdoc/"
57
+ });
58
+ return {
59
+ // Stub with an unused default export.
60
+ path: "data:text/javascript,export default true",
61
+ external: true
62
+ };
63
+ });
64
+ }
65
+ }
66
+ ]
67
+ });
68
+ if (markdocError)
69
+ throw markdocError;
70
+ const { text } = result.outputFiles[0];
71
+ return {
72
+ code: text,
73
+ dependencies: result.metafile ? Object.keys(result.metafile.inputs) : []
74
+ };
75
+ }
76
+ async function loadConfigFromBundledFile(root, code) {
77
+ const tmpFileUrl = new URL(`markdoc.config.timestamp-${Date.now()}.mjs`, root);
78
+ fs.writeFileSync(tmpFileUrl, code);
79
+ try {
80
+ return (await import(tmpFileUrl.pathname)).default;
81
+ } finally {
82
+ try {
83
+ fs.unlinkSync(tmpFileUrl);
84
+ } catch {
85
+ }
86
+ }
87
+ }
88
+ export {
89
+ SUPPORTED_MARKDOC_CONFIG_FILES,
90
+ loadMarkdocConfig
91
+ };
@@ -0,0 +1,4 @@
1
+ export interface MarkdocIntegrationOptions {
2
+ allowHTML?: boolean;
3
+ ignoreIndentation?: boolean;
4
+ }
File without changes
@@ -0,0 +1,2 @@
1
+ import type { Config as MarkdocConfig } from '@markdoc/markdoc';
2
+ export declare const assetsConfig: MarkdocConfig;
@@ -0,0 +1,25 @@
1
+ import Markdoc from "@markdoc/markdoc";
2
+ import { Image } from "astro:assets";
3
+ const assetsConfig = {
4
+ nodes: {
5
+ image: {
6
+ attributes: {
7
+ ...Markdoc.nodes.image.attributes,
8
+ __optimizedSrc: { type: "Object" }
9
+ },
10
+ transform(node, config) {
11
+ const attributes = node.transformAttributes(config);
12
+ const children = node.transformChildren(config);
13
+ if (node.type === "image" && "__optimizedSrc" in node.attributes) {
14
+ const { __optimizedSrc, ...rest } = node.attributes;
15
+ return new Markdoc.Tag(Image, { ...rest, src: __optimizedSrc }, children);
16
+ } else {
17
+ return new Markdoc.Tag("img", attributes, children);
18
+ }
19
+ }
20
+ }
21
+ }
22
+ };
23
+ export {
24
+ assetsConfig
25
+ };
@@ -0,0 +1,29 @@
1
+ import type { MarkdownHeading } from '@astrojs/markdown-remark';
2
+ import { type NodeType, type RenderableTreeNode } from '@markdoc/markdoc';
3
+ import type { AstroInstance } from 'astro';
4
+ import { type AstroMarkdocConfig } from './config.js';
5
+ import type { MarkdocIntegrationOptions } from './options.js';
6
+ /**
7
+ * Merge user config with default config and set up context (ex. heading ID slugger)
8
+ * Called on each file's individual transform.
9
+ * TODO: virtual module to merge configs per-build instead of per-file?
10
+ */
11
+ export declare function setupConfig(userConfig: AstroMarkdocConfig | undefined, options: MarkdocIntegrationOptions | undefined): Promise<MergedConfig>;
12
+ /** Used for synchronous `getHeadings()` function */
13
+ export declare function setupConfigSync(userConfig: AstroMarkdocConfig | undefined, options: MarkdocIntegrationOptions | undefined): MergedConfig;
14
+ type MergedConfig = Required<Omit<AstroMarkdocConfig, 'extends'>>;
15
+ /** Merge function from `@markdoc/markdoc` internals */
16
+ export declare function mergeConfig(configA: AstroMarkdocConfig, configB: AstroMarkdocConfig): MergedConfig;
17
+ export declare function resolveComponentImports(markdocConfig: Required<Pick<AstroMarkdocConfig, 'tags' | 'nodes'>>, tagComponentMap: Record<string, AstroInstance['default']>, nodeComponentMap: Record<NodeType, AstroInstance['default']>): Required<Pick<AstroMarkdocConfig, "nodes" | "tags">>;
18
+ /**
19
+ * Get text content as a string from a Markdoc transform AST
20
+ */
21
+ export declare function getTextContent(childNodes: RenderableTreeNode[]): string;
22
+ /**
23
+ * Collect headings from Markdoc transform AST
24
+ * for `headings` result on `render()` return value
25
+ */
26
+ export declare function collectHeadings(children: RenderableTreeNode[], collectedHeadings: MarkdownHeading[]): void;
27
+ export declare function createGetHeadings(stringifiedAst: string, userConfig: AstroMarkdocConfig, options: MarkdocIntegrationOptions | undefined): () => MarkdownHeading[];
28
+ export declare function createContentComponent(Renderer: AstroInstance['default'], stringifiedAst: string, userConfig: AstroMarkdocConfig, options: MarkdocIntegrationOptions | undefined, tagComponentMap: Record<string, AstroInstance['default']>, nodeComponentMap: Record<NodeType, AstroInstance['default']>): import("astro/runtime/server/index.js").AstroComponentFactory;
29
+ export {};
@@ -0,0 +1,152 @@
1
+ import Markdoc, {
2
+ } from "@markdoc/markdoc";
3
+ import { createComponent, renderComponent } from "astro/runtime/server/index.js";
4
+ import {} from "./config.js";
5
+ import { setupHeadingConfig } from "./heading-ids.js";
6
+ import { htmlTag } from "./html/tagdefs/html.tag.js";
7
+ async function setupConfig(userConfig = {}, options) {
8
+ let defaultConfig = setupHeadingConfig();
9
+ if (userConfig.extends) {
10
+ for (let extension of userConfig.extends) {
11
+ if (extension instanceof Promise) {
12
+ extension = await extension;
13
+ }
14
+ defaultConfig = mergeConfig(defaultConfig, extension);
15
+ }
16
+ }
17
+ let merged = mergeConfig(defaultConfig, userConfig);
18
+ if (options?.allowHTML) {
19
+ merged = mergeConfig(merged, HTML_CONFIG);
20
+ }
21
+ return merged;
22
+ }
23
+ function setupConfigSync(userConfig = {}, options) {
24
+ const defaultConfig = setupHeadingConfig();
25
+ let merged = mergeConfig(defaultConfig, userConfig);
26
+ if (options?.allowHTML) {
27
+ merged = mergeConfig(merged, HTML_CONFIG);
28
+ }
29
+ return merged;
30
+ }
31
+ function mergeConfig(configA, configB) {
32
+ return {
33
+ ...configA,
34
+ ...configB,
35
+ ctx: {
36
+ ...configA.ctx,
37
+ ...configB.ctx
38
+ },
39
+ tags: {
40
+ ...configA.tags,
41
+ ...configB.tags
42
+ },
43
+ nodes: {
44
+ ...configA.nodes,
45
+ ...configB.nodes
46
+ },
47
+ functions: {
48
+ ...configA.functions,
49
+ ...configB.functions
50
+ },
51
+ variables: {
52
+ ...configA.variables,
53
+ ...configB.variables
54
+ },
55
+ partials: {
56
+ ...configA.partials,
57
+ ...configB.partials
58
+ },
59
+ validation: {
60
+ ...configA.validation,
61
+ ...configB.validation
62
+ }
63
+ };
64
+ }
65
+ function resolveComponentImports(markdocConfig, tagComponentMap, nodeComponentMap) {
66
+ for (const [tag, render] of Object.entries(tagComponentMap)) {
67
+ const config = markdocConfig.tags[tag];
68
+ if (config)
69
+ config.render = render;
70
+ }
71
+ for (const [node, render] of Object.entries(nodeComponentMap)) {
72
+ const config = markdocConfig.nodes[node];
73
+ if (config)
74
+ config.render = render;
75
+ }
76
+ return markdocConfig;
77
+ }
78
+ function getTextContent(childNodes) {
79
+ let text = "";
80
+ for (const node of childNodes) {
81
+ if (typeof node === "string" || typeof node === "number") {
82
+ text += node;
83
+ } else if (typeof node === "object" && Markdoc.Tag.isTag(node)) {
84
+ text += getTextContent(node.children);
85
+ }
86
+ }
87
+ return text;
88
+ }
89
+ const headingLevels = [1, 2, 3, 4, 5, 6];
90
+ function collectHeadings(children, collectedHeadings) {
91
+ for (const node of children) {
92
+ if (typeof node !== "object" || !Markdoc.Tag.isTag(node))
93
+ continue;
94
+ if (node.attributes.__collectHeading === true && typeof node.attributes.level === "number") {
95
+ collectedHeadings.push({
96
+ slug: node.attributes.id,
97
+ depth: node.attributes.level,
98
+ text: getTextContent(node.children)
99
+ });
100
+ continue;
101
+ }
102
+ for (const level of headingLevels) {
103
+ if (node.name === "h" + level) {
104
+ collectedHeadings.push({
105
+ slug: node.attributes.id,
106
+ depth: level,
107
+ text: getTextContent(node.children)
108
+ });
109
+ }
110
+ }
111
+ collectHeadings(node.children, collectedHeadings);
112
+ }
113
+ }
114
+ function createGetHeadings(stringifiedAst, userConfig, options) {
115
+ return function getHeadings() {
116
+ const config = setupConfigSync(userConfig, options);
117
+ const ast = Markdoc.Ast.fromJSON(stringifiedAst);
118
+ const content = Markdoc.transform(ast, config);
119
+ let collectedHeadings = [];
120
+ collectHeadings(Array.isArray(content) ? content : [content], collectedHeadings);
121
+ return collectedHeadings;
122
+ };
123
+ }
124
+ function createContentComponent(Renderer, stringifiedAst, userConfig, options, tagComponentMap, nodeComponentMap) {
125
+ return createComponent({
126
+ async factory(result, props) {
127
+ const withVariables = mergeConfig(userConfig, { variables: props });
128
+ const config = resolveComponentImports(
129
+ await setupConfig(withVariables, options),
130
+ tagComponentMap,
131
+ nodeComponentMap
132
+ );
133
+ return renderComponent(result, Renderer.name, Renderer, { stringifiedAst, config }, {});
134
+ },
135
+ propagation: "self"
136
+ });
137
+ }
138
+ const HTML_CONFIG = {
139
+ tags: {
140
+ "html-tag": htmlTag
141
+ }
142
+ };
143
+ export {
144
+ collectHeadings,
145
+ createContentComponent,
146
+ createGetHeadings,
147
+ getTextContent,
148
+ mergeConfig,
149
+ resolveComponentImports,
150
+ setupConfig,
151
+ setupConfigSync
152
+ };
@@ -0,0 +1,3 @@
1
+ import type { Tokenizer } from '@markdoc/markdoc';
2
+ import type { MarkdocIntegrationOptions } from './options.js';
3
+ export declare function getMarkdocTokenizer(options: MarkdocIntegrationOptions | undefined): Tokenizer;
@@ -0,0 +1,27 @@
1
+ import Markdoc from "@markdoc/markdoc";
2
+ function getMarkdocTokenizer(options) {
3
+ const key = cacheKey(options);
4
+ if (!_cachedMarkdocTokenizers[key]) {
5
+ const tokenizerOptions = {
6
+ // Strip <!-- comments --> from rendered output
7
+ // Without this, they're rendered as strings!
8
+ allowComments: true
9
+ };
10
+ if (options?.allowHTML) {
11
+ tokenizerOptions.allowIndentation = true;
12
+ tokenizerOptions.html = true;
13
+ }
14
+ if (options?.ignoreIndentation) {
15
+ tokenizerOptions.allowIndentation = true;
16
+ }
17
+ _cachedMarkdocTokenizers[key] = new Markdoc.Tokenizer(tokenizerOptions);
18
+ }
19
+ return _cachedMarkdocTokenizers[key];
20
+ }
21
+ let _cachedMarkdocTokenizers = {};
22
+ function cacheKey(options) {
23
+ return JSON.stringify(options);
24
+ }
25
+ export {
26
+ getMarkdocTokenizer
27
+ };
@@ -0,0 +1,46 @@
1
+ import type { ComponentConfig } from './config.js';
2
+ /**
3
+ * Matches AstroError object with types like error codes stubbed out
4
+ * @see 'astro/src/core/errors/errors.ts'
5
+ */
6
+ export declare class MarkdocError extends Error {
7
+ loc: ErrorLocation | undefined;
8
+ title: string | undefined;
9
+ hint: string | undefined;
10
+ frame: string | undefined;
11
+ type: string;
12
+ constructor(props: ErrorProperties, ...params: any);
13
+ }
14
+ interface ErrorLocation {
15
+ file?: string;
16
+ line?: number;
17
+ column?: number;
18
+ }
19
+ interface ErrorProperties {
20
+ code?: number;
21
+ title?: string;
22
+ name?: string;
23
+ message?: string;
24
+ location?: ErrorLocation;
25
+ hint?: string;
26
+ stack?: string;
27
+ frame?: string;
28
+ }
29
+ /**
30
+ * @see 'astro/src/core/path.ts'
31
+ */
32
+ export declare function prependForwardSlash(str: string): string;
33
+ export declare function isValidUrl(str: string): boolean;
34
+ /**
35
+ * Identifies Astro components with propagated assets
36
+ * @see 'packages/astro/src/content/consts.ts'
37
+ */
38
+ export declare const PROPAGATED_ASSET_FLAG = "astroPropagatedAssets";
39
+ /**
40
+ * @see 'packages/astro/src/content/utils.ts'
41
+ */
42
+ export declare function hasContentFlag(viteId: string, flag: string): boolean;
43
+ /** Identifier for components imports passed as `tags` or `nodes` configuration. */
44
+ export declare const componentConfigSymbol: unique symbol;
45
+ export declare function isComponentConfig(value: unknown): value is ComponentConfig;
46
+ export {};
package/dist/utils.js ADDED
@@ -0,0 +1,47 @@
1
+ class MarkdocError extends Error {
2
+ loc;
3
+ title;
4
+ hint;
5
+ frame;
6
+ type = "MarkdocError";
7
+ constructor(props, ...params) {
8
+ super(...params);
9
+ const { title = "MarkdocError", message, stack, location, hint, frame } = props;
10
+ this.title = title;
11
+ if (message)
12
+ this.message = message;
13
+ this.stack = stack ? stack : this.stack;
14
+ this.loc = location;
15
+ this.hint = hint;
16
+ this.frame = frame;
17
+ }
18
+ }
19
+ function prependForwardSlash(str) {
20
+ return str[0] === "/" ? str : "/" + str;
21
+ }
22
+ function isValidUrl(str) {
23
+ try {
24
+ new URL(str);
25
+ return true;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+ const PROPAGATED_ASSET_FLAG = "astroPropagatedAssets";
31
+ function hasContentFlag(viteId, flag) {
32
+ const flags = new URLSearchParams(viteId.split("?")[1] ?? "");
33
+ return flags.has(flag);
34
+ }
35
+ const componentConfigSymbol = Symbol.for("@astrojs/markdoc/component-config");
36
+ function isComponentConfig(value) {
37
+ return typeof value === "object" && value !== null && componentConfigSymbol in value;
38
+ }
39
+ export {
40
+ MarkdocError,
41
+ PROPAGATED_ASSET_FLAG,
42
+ componentConfigSymbol,
43
+ hasContentFlag,
44
+ isComponentConfig,
45
+ isValidUrl,
46
+ prependForwardSlash
47
+ };
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@astrojs/markdoc",
3
+ "description": "Add support for Markdoc in your Astro site",
4
+ "version": "0.0.0-10745-20240410180016",
5
+ "type": "module",
6
+ "types": "./dist/index.d.ts",
7
+ "author": "withastro",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/withastro/astro.git",
12
+ "directory": "packages/integrations/markdoc"
13
+ },
14
+ "keywords": [
15
+ "astro-integration",
16
+ "astro-component",
17
+ "markdoc"
18
+ ],
19
+ "bugs": "https://github.com/withastro/astro/issues",
20
+ "homepage": "https://docs.astro.build/en/guides/integrations-guide/markdoc/",
21
+ "exports": {
22
+ "./prism": {
23
+ "types": "./dist/extensions/prism.d.ts",
24
+ "default": "./dist/extensions/prism.js"
25
+ },
26
+ "./shiki": {
27
+ "types": "./dist/extensions/shiki.d.ts",
28
+ "default": "./dist/extensions/shiki.js"
29
+ },
30
+ "./config": {
31
+ "types": "./dist/config.d.ts",
32
+ "default": "./dist/config.js"
33
+ },
34
+ ".": "./dist/index.js",
35
+ "./components": "./components/index.ts",
36
+ "./runtime": "./dist/runtime.js",
37
+ "./runtime-assets-config": "./dist/runtime-assets-config.js",
38
+ "./package.json": "./package.json"
39
+ },
40
+ "typesVersions": {
41
+ "*": {
42
+ "config": [
43
+ "./dist/config.d.ts"
44
+ ],
45
+ "prism": [
46
+ "./dist/extensions/prism.d.ts"
47
+ ],
48
+ "shiki": [
49
+ "./dist/extensions/shiki.d.ts"
50
+ ]
51
+ }
52
+ },
53
+ "files": [
54
+ "components",
55
+ "dist",
56
+ "template"
57
+ ],
58
+ "dependencies": {
59
+ "@markdoc/markdoc": "^0.3.5",
60
+ "esbuild": "^0.19.6",
61
+ "github-slugger": "^2.0.0",
62
+ "gray-matter": "^4.0.3",
63
+ "htmlparser2": "^9.0.0",
64
+ "kleur": "^4.1.5",
65
+ "zod": "^3.22.4",
66
+ "@astrojs/internal-helpers": "0.4.0",
67
+ "@astrojs/markdown-remark": "0.0.0-10745-20240410180016",
68
+ "@astrojs/prism": "0.0.0-10745-20240410180016"
69
+ },
70
+ "peerDependencies": {
71
+ "astro": "0.0.0-10745-20240410180016"
72
+ },
73
+ "devDependencies": {
74
+ "@types/html-escaper": "^3.0.2",
75
+ "@types/markdown-it": "^13.0.6",
76
+ "devalue": "^4.3.2",
77
+ "linkedom": "^0.16.4",
78
+ "vite": "^5.1.4",
79
+ "astro": "0.0.0-10745-20240410180016",
80
+ "astro-scripts": "0.0.14"
81
+ },
82
+ "engines": {
83
+ "node": "^18.17.1 || ^20.3.0 || >=21.0.0"
84
+ },
85
+ "publishConfig": {
86
+ "provenance": true
87
+ },
88
+ "scripts": {
89
+ "build": "astro-scripts build \"src/**/*.ts\" && tsc",
90
+ "build:ci": "astro-scripts build \"src/**/*.ts\"",
91
+ "dev": "astro-scripts dev \"src/**/*.ts\"",
92
+ "test": "astro-scripts test --timeout 60000 \"test/**/*.test.js\""
93
+ }
94
+ }
@@ -0,0 +1,8 @@
1
+ declare module 'astro:content' {
2
+ interface Render {
3
+ '.mdoc': Promise<{
4
+ Content(props: Record<string, any>): import('astro').MarkdownInstance<{}>['Content'];
5
+ headings: import('astro').MarkdownHeading[];
6
+ }>;
7
+ }
8
+ }