@astrojs/markdoc 0.2.1 → 0.2.2

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.
@@ -1,4 +1,5 @@
1
1
  ---
2
+ //! astro-head-inject
2
3
  import type { Config } from '@markdoc/markdoc';
3
4
  import Markdoc from '@markdoc/markdoc';
4
5
  import { ComponentNode, createTreeNode } from './TreeNode.js';
@@ -14,4 +15,4 @@ const ast = Markdoc.Ast.fromJSON(stringifiedAst);
14
15
  const content = Markdoc.transform(ast, config);
15
16
  ---
16
17
 
17
- <ComponentNode treeNode={createTreeNode(content)} />
18
+ <ComponentNode treeNode={await createTreeNode(content)} />
@@ -2,7 +2,16 @@ import type { AstroInstance } from 'astro';
2
2
  import { Fragment } from 'astro/jsx-runtime';
3
3
  import type { RenderableTreeNode } from '@markdoc/markdoc';
4
4
  import Markdoc from '@markdoc/markdoc';
5
- import { createComponent, renderComponent, render } from 'astro/runtime/server/index.js';
5
+ import {
6
+ createComponent,
7
+ renderComponent,
8
+ render,
9
+ renderScriptElement,
10
+ renderUniqueStylesheet,
11
+ createHeadAndContent,
12
+ unescapeHTML,
13
+ renderTemplate,
14
+ } from 'astro/runtime/server/index.js';
6
15
 
7
16
  export type TreeNode =
8
17
  | {
@@ -12,6 +21,9 @@ export type TreeNode =
12
21
  | {
13
22
  type: 'component';
14
23
  component: AstroInstance['default'];
24
+ collectedLinks?: string[];
25
+ collectedStyles?: string[];
26
+ collectedScripts?: string[];
15
27
  props: Record<string, any>;
16
28
  children: TreeNode[];
17
29
  }
@@ -32,20 +44,67 @@ export const ComponentNode = createComponent({
32
44
  )}`,
33
45
  };
34
46
  if (treeNode.type === 'component') {
35
- return renderComponent(
36
- result,
37
- treeNode.component.name,
38
- treeNode.component,
39
- treeNode.props,
40
- slots
47
+ let styles = '',
48
+ links = '',
49
+ scripts = '';
50
+ if (Array.isArray(treeNode.collectedStyles)) {
51
+ styles = treeNode.collectedStyles
52
+ .map((style: any) =>
53
+ renderUniqueStylesheet({
54
+ type: 'inline',
55
+ content: style,
56
+ })
57
+ )
58
+ .join('');
59
+ }
60
+ if (Array.isArray(treeNode.collectedLinks)) {
61
+ links = treeNode.collectedLinks
62
+ .map((link: any) => {
63
+ return renderUniqueStylesheet(result, {
64
+ href: link[0] === '/' ? link : '/' + link,
65
+ });
66
+ })
67
+ .join('');
68
+ }
69
+ if (Array.isArray(treeNode.collectedScripts)) {
70
+ scripts = treeNode.collectedScripts
71
+ .map((script: any) => renderScriptElement(script))
72
+ .join('');
73
+ }
74
+
75
+ const head = unescapeHTML(styles + links + scripts);
76
+
77
+ let headAndContent = createHeadAndContent(
78
+ head,
79
+ renderTemplate`${renderComponent(
80
+ result,
81
+ treeNode.component.name,
82
+ treeNode.component,
83
+ treeNode.props,
84
+ slots
85
+ )}`
41
86
  );
87
+
88
+ // Let the runtime know that this component is being used.
89
+ result.propagators.set(
90
+ {},
91
+ {
92
+ init() {
93
+ return headAndContent;
94
+ },
95
+ }
96
+ );
97
+
98
+ return headAndContent;
42
99
  }
43
100
  return renderComponent(result, treeNode.tag, treeNode.tag, treeNode.attributes, slots);
44
101
  },
45
- propagation: 'none',
102
+ propagation: 'self',
46
103
  });
47
104
 
48
- export function createTreeNode(node: RenderableTreeNode | RenderableTreeNode[]): TreeNode {
105
+ export async function createTreeNode(
106
+ node: RenderableTreeNode | RenderableTreeNode[]
107
+ ): Promise<TreeNode> {
49
108
  if (typeof node === 'string' || typeof node === 'number') {
50
109
  return { type: 'text', content: String(node) };
51
110
  } else if (Array.isArray(node)) {
@@ -53,16 +112,17 @@ export function createTreeNode(node: RenderableTreeNode | RenderableTreeNode[]):
53
112
  type: 'component',
54
113
  component: Fragment,
55
114
  props: {},
56
- children: node.map((child) => createTreeNode(child)),
115
+ children: await Promise.all(node.map((child) => createTreeNode(child))),
57
116
  };
58
117
  } else if (node === null || typeof node !== 'object' || !Markdoc.Tag.isTag(node)) {
59
118
  return { type: 'text', content: '' };
60
119
  }
61
120
 
121
+ const children = await Promise.all(node.children.map((child) => createTreeNode(child)));
122
+
62
123
  if (typeof node.name === 'function') {
63
124
  const component = node.name;
64
125
  const props = node.attributes;
65
- const children = node.children.map((child) => createTreeNode(child));
66
126
 
67
127
  return {
68
128
  type: 'component',
@@ -70,12 +130,38 @@ export function createTreeNode(node: RenderableTreeNode | RenderableTreeNode[]):
70
130
  props,
71
131
  children,
72
132
  };
133
+ } else if (isPropagatedAssetsModule(node.name)) {
134
+ const { collectedStyles, collectedLinks, collectedScripts } = node.name;
135
+ const component = (await node.name.getMod())?.default ?? Fragment;
136
+ const props = node.attributes;
137
+
138
+ return {
139
+ type: 'component',
140
+ component,
141
+ collectedStyles,
142
+ collectedLinks,
143
+ collectedScripts,
144
+ props,
145
+ children,
146
+ };
73
147
  } else {
74
148
  return {
75
149
  type: 'element',
76
150
  tag: node.name,
77
151
  attributes: node.attributes,
78
- children: node.children.map((child) => createTreeNode(child)),
152
+ children,
79
153
  };
80
154
  }
81
155
  }
156
+
157
+ type PropagatedAssetsModule = {
158
+ __astroPropagation: true;
159
+ getMod: () => Promise<AstroInstance['default']>;
160
+ collectedStyles: string[];
161
+ collectedLinks: string[];
162
+ collectedScripts: string[];
163
+ };
164
+
165
+ function isPropagatedAssetsModule(module: any): module is PropagatedAssetsModule {
166
+ return typeof module === 'object' && module != null && '__astroPropagation' in module;
167
+ }
package/dist/index.js CHANGED
@@ -20,7 +20,11 @@ function markdocIntegration(legacyConfig) {
20
20
  name: "@astrojs/markdoc",
21
21
  hooks: {
22
22
  "astro:config:setup": async (params) => {
23
- const { config: astroConfig, addContentEntryType } = params;
23
+ const {
24
+ config: astroConfig,
25
+ updateConfig,
26
+ addContentEntryType
27
+ } = params;
24
28
  markdocConfigResult = await loadMarkdocConfig(astroConfig);
25
29
  const userMarkdocConfig = (markdocConfigResult == null ? void 0 : markdocConfigResult.config) ?? {};
26
30
  function getEntryInfo({ fileUrl, contents }) {
@@ -35,6 +39,9 @@ function markdocIntegration(legacyConfig) {
35
39
  addContentEntryType({
36
40
  extensions: [".mdoc"],
37
41
  getEntryInfo,
42
+ // Markdoc handles script / style propagation
43
+ // for Astro components internally
44
+ handlePropagation: false,
38
45
  async getRenderModule({ entry, viteId }) {
39
46
  const ast = Markdoc.parse(entry.body);
40
47
  const pluginContext = this;
@@ -69,7 +76,10 @@ function markdocIntegration(legacyConfig) {
69
76
  filePath: entry._internal.filePath
70
77
  });
71
78
  }
72
- const res = `import { jsx as h } from 'astro/jsx-runtime';
79
+ const res = `import {
80
+ createComponent,
81
+ renderComponent,
82
+ } from 'astro/runtime/server/index.js';
73
83
  import { Renderer } from '@astrojs/markdoc/components';
74
84
  import { collectHeadings, setupConfig, Markdoc } from '@astrojs/markdoc/runtime';
75
85
  import * as entry from ${JSON.stringify(viteId + "?astroContentCollectionEntry")};
@@ -94,14 +104,24 @@ export function getHeadings() {
94
104
  const content = Markdoc.transform(ast, config);
95
105
  return collectHeadings(Array.isArray(content) ? content : content.children);
96
106
  }
97
- export async function Content (props) {
98
- const config = setupConfig({
99
- ...userConfig,
100
- variables: { ...userConfig.variables, ...props },
101
- }, entry);
102
107
 
103
- return h(Renderer, { config, stringifiedAst });
104
- }`;
108
+ export const Content = createComponent({
109
+ factory(result, props) {
110
+ const config = setupConfig({
111
+ ...userConfig,
112
+ variables: { ...userConfig.variables, ...props },
113
+ }, entry);
114
+
115
+ return renderComponent(
116
+ result,
117
+ Renderer.name,
118
+ Renderer,
119
+ { stringifiedAst, config },
120
+ {}
121
+ );
122
+ },
123
+ propagation: 'self',
124
+ });`;
105
125
  return { code: res };
106
126
  },
107
127
  contentModuleTypes: await fs.promises.readFile(
@@ -109,6 +129,26 @@ export async function Content (props) {
109
129
  "utf-8"
110
130
  )
111
131
  });
132
+ updateConfig({
133
+ vite: {
134
+ plugins: [
135
+ {
136
+ name: "@astrojs/markdoc:astro-propagated-assets",
137
+ enforce: "pre",
138
+ // Astro component styles and scripts should only be injected
139
+ // When a given Markdoc file actually uses that component.
140
+ // Add the `astroPropagatedAssets` flag to inject only when rendered.
141
+ resolveId(id, importer) {
142
+ if (importer === (markdocConfigResult == null ? void 0 : markdocConfigResult.fileUrl.pathname) && id.endsWith(".astro")) {
143
+ return this.resolve(id + "?astroPropagatedAssets", importer, {
144
+ skipSelf: true
145
+ });
146
+ }
147
+ }
148
+ }
149
+ ]
150
+ }
151
+ });
112
152
  },
113
153
  "astro:server:setup": async ({ server }) => {
114
154
  server.watcher.on("all", (event, entry) => {
@@ -27,7 +27,7 @@ const heading = {
27
27
  // For components, pass down `level` as a prop,
28
28
  // alongside `__collectHeading` for our `headings` collector.
29
29
  // Avoid accidentally rendering `level` as an HTML attribute otherwise!
30
- typeof render === "function" ? { ...attributes, id: slug, __collectHeading: true, level } : { ...attributes, id: slug }
30
+ typeof render === "string" ? { ...attributes, id: slug } : { ...attributes, id: slug, __collectHeading: true, level }
31
31
  );
32
32
  return new Markdoc.Tag(render, tagProps, children);
33
33
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astrojs/markdoc",
3
3
  "description": "Add support for Markdoc pages in your Astro site",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -40,7 +40,7 @@
40
40
  "zod": "^3.17.3"
41
41
  },
42
42
  "peerDependencies": {
43
- "astro": "^2.5.1"
43
+ "astro": "^2.5.3"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@astrojs/markdown-remark": "^2.2.1",
@@ -53,7 +53,7 @@
53
53
  "mocha": "^9.2.2",
54
54
  "rollup": "^3.20.1",
55
55
  "vite": "^4.3.1",
56
- "astro": "2.5.1",
56
+ "astro": "2.5.3",
57
57
  "astro-scripts": "0.0.14"
58
58
  },
59
59
  "engines": {