@astrojs/markdoc 0.9.3 → 0.9.5

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.
@@ -12,7 +12,7 @@ type Props = {
12
12
  const { stringifiedAst, config } = Astro.props as Props;
13
13
 
14
14
  const ast = Markdoc.Ast.fromJSON(stringifiedAst);
15
- const content = Markdoc.transform(ast, config);
15
+ const content = await Markdoc.transform(ast, config);
16
16
  ---
17
17
 
18
18
  {
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { fileURLToPath } from "node:url";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import Markdoc from "@markdoc/markdoc";
5
5
  import { emitESMImage } from "astro/assets/utils";
6
6
  import matter from "gray-matter";
@@ -25,9 +25,40 @@ async function getContentEntryType({
25
25
  tokens = htmlTokenTransform(tokenizer, tokens);
26
26
  }
27
27
  const ast = Markdoc.parse(tokens);
28
- const usedTags = getUsedTags(ast);
29
28
  const userMarkdocConfig = markdocConfigResult?.config ?? {};
30
29
  const markdocConfigUrl = markdocConfigResult?.fileUrl;
30
+ const pluginContext = this;
31
+ const markdocConfig = await setupConfig(userMarkdocConfig, options);
32
+ const filePath = fileURLToPath(fileUrl);
33
+ raiseValidationErrors({
34
+ ast,
35
+ /* Raised generics issue with Markdoc core https://github.com/markdoc/markdoc/discussions/400 */
36
+ markdocConfig,
37
+ entry,
38
+ viteId,
39
+ astroConfig,
40
+ filePath
41
+ });
42
+ await resolvePartials({
43
+ ast,
44
+ markdocConfig,
45
+ fileUrl,
46
+ allowHTML: options?.allowHTML,
47
+ tokenizer,
48
+ pluginContext,
49
+ root: astroConfig.root,
50
+ raisePartialValidationErrors: (partialAst, partialPath) => {
51
+ raiseValidationErrors({
52
+ ast: partialAst,
53
+ markdocConfig,
54
+ entry,
55
+ viteId,
56
+ astroConfig,
57
+ filePath: partialPath
58
+ });
59
+ }
60
+ });
61
+ const usedTags = getUsedTags(ast);
31
62
  let componentConfigByTagMap = {};
32
63
  for (const tag of usedTags) {
33
64
  const render = userMarkdocConfig.tags?.[tag]?.render;
@@ -42,37 +73,6 @@ async function getContentEntryType({
42
73
  componentConfigByNodeMap[nodeType] = render;
43
74
  }
44
75
  }
45
- const pluginContext = this;
46
- const markdocConfig = await setupConfig(userMarkdocConfig, options);
47
- const filePath = fileURLToPath(fileUrl);
48
- const validationErrors = Markdoc.validate(
49
- ast,
50
- /* Raised generics issue with Markdoc core https://github.com/markdoc/markdoc/discussions/400 */
51
- markdocConfig
52
- ).filter((e) => {
53
- return (
54
- // Ignore `variable-undefined` errors.
55
- // Variables can be configured at runtime,
56
- // so we cannot validate them at build time.
57
- e.error.id !== "variable-undefined" && (e.error.level === "error" || e.error.level === "critical")
58
- );
59
- });
60
- if (validationErrors.length) {
61
- const frontmatterBlockOffset = entry.rawData.split("\n").length + 2;
62
- const rootRelativePath = path.relative(fileURLToPath(astroConfig.root), filePath);
63
- throw new MarkdocError({
64
- message: [
65
- `**${String(rootRelativePath)}** contains invalid content:`,
66
- ...validationErrors.map((e) => `- ${e.error.message}`)
67
- ].join("\n"),
68
- location: {
69
- // Error overlay does not support multi-line or ranges.
70
- // Just point to the first line.
71
- line: frontmatterBlockOffset + validationErrors[0].lines[0],
72
- file: viteId
73
- }
74
- });
75
- }
76
76
  await emitOptimizedImages(ast.children, {
77
77
  astroConfig,
78
78
  pluginContext,
@@ -115,6 +115,107 @@ export const Content = createContentComponent(
115
115
  )
116
116
  };
117
117
  }
118
+ async function resolvePartials({
119
+ ast,
120
+ fileUrl,
121
+ root,
122
+ tokenizer,
123
+ allowHTML,
124
+ markdocConfig,
125
+ pluginContext,
126
+ raisePartialValidationErrors
127
+ }) {
128
+ const relativePartialPath = path.relative(fileURLToPath(root), fileURLToPath(fileUrl));
129
+ for (const node of ast.walk()) {
130
+ if (node.type === "tag" && node.tag === "partial") {
131
+ const { file } = node.attributes;
132
+ if (!file) {
133
+ throw new MarkdocError({
134
+ // Should be caught by Markdoc validation step.
135
+ message: `(Uncaught error) Partial tag requires a 'file' attribute`
136
+ });
137
+ }
138
+ if (markdocConfig.partials?.[file])
139
+ continue;
140
+ let partialPath;
141
+ let partialContents;
142
+ try {
143
+ const resolved = await pluginContext.resolve(file, fileURLToPath(fileUrl));
144
+ let partialId = resolved?.id;
145
+ if (!partialId) {
146
+ const attemptResolveAsRelative = await pluginContext.resolve(
147
+ "./" + file,
148
+ fileURLToPath(fileUrl)
149
+ );
150
+ if (!attemptResolveAsRelative?.id)
151
+ throw new Error();
152
+ partialId = attemptResolveAsRelative.id;
153
+ }
154
+ partialPath = fileURLToPath(new URL(prependForwardSlash(partialId), "file://"));
155
+ partialContents = await fs.promises.readFile(partialPath, "utf-8");
156
+ } catch {
157
+ throw new MarkdocError({
158
+ message: [
159
+ `**${String(relativePartialPath)}** contains invalid content:`,
160
+ `Could not read partial file \`${file}\`. Does the file exist?`
161
+ ].join("\n")
162
+ });
163
+ }
164
+ if (pluginContext.meta.watchMode)
165
+ pluginContext.addWatchFile(partialPath);
166
+ let partialTokens = tokenizer.tokenize(partialContents);
167
+ if (allowHTML) {
168
+ partialTokens = htmlTokenTransform(tokenizer, partialTokens);
169
+ }
170
+ const partialAst = Markdoc.parse(partialTokens);
171
+ raisePartialValidationErrors(partialAst, partialPath);
172
+ await resolvePartials({
173
+ ast: partialAst,
174
+ root,
175
+ fileUrl: pathToFileURL(partialPath),
176
+ tokenizer,
177
+ allowHTML,
178
+ markdocConfig,
179
+ pluginContext,
180
+ raisePartialValidationErrors
181
+ });
182
+ Object.assign(node, partialAst);
183
+ }
184
+ }
185
+ }
186
+ function raiseValidationErrors({
187
+ ast,
188
+ markdocConfig,
189
+ entry,
190
+ viteId,
191
+ astroConfig,
192
+ filePath
193
+ }) {
194
+ const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => {
195
+ return (e.error.level === "error" || e.error.level === "critical") && // Ignore `variable-undefined` errors.
196
+ // Variables can be configured at runtime,
197
+ // so we cannot validate them at build time.
198
+ e.error.id !== "variable-undefined" && // Ignore missing partial errors.
199
+ // We will resolve these in `resolvePartials`.
200
+ !(e.error.id === "attribute-value-invalid" && e.error.message.match(/^Partial .+ not found/));
201
+ });
202
+ if (validationErrors.length) {
203
+ const frontmatterBlockOffset = entry.rawData.split("\n").length + 2;
204
+ const rootRelativePath = path.relative(fileURLToPath(astroConfig.root), filePath);
205
+ throw new MarkdocError({
206
+ message: [
207
+ `**${String(rootRelativePath)}** contains invalid content:`,
208
+ ...validationErrors.map((e) => `- ${e.error.message}`)
209
+ ].join("\n"),
210
+ location: {
211
+ // Error overlay does not support multi-line or ranges.
212
+ // Just point to the first line.
213
+ line: frontmatterBlockOffset + validationErrors[0].lines[0],
214
+ file: viteId
215
+ }
216
+ });
217
+ }
218
+ }
118
219
  function getUsedTags(markdocAst) {
119
220
  const tags = /* @__PURE__ */ new Set();
120
221
  const validationErrors = Markdoc.validate(markdocAst);
@@ -7,9 +7,9 @@ async function shiki(config) {
7
7
  nodes: {
8
8
  fence: {
9
9
  attributes: Markdoc.nodes.fence.attributes,
10
- transform({ attributes }) {
10
+ async transform({ attributes }) {
11
11
  const lang = typeof attributes.language === "string" ? attributes.language : "plaintext";
12
- const html = highlighter.highlight(attributes.content, lang);
12
+ const html = await highlighter.highlight(attributes.content, lang);
13
13
  return unescapeHTML(html);
14
14
  }
15
15
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astrojs/markdoc",
3
3
  "description": "Add support for Markdoc in your Astro site",
4
- "version": "0.9.3",
4
+ "version": "0.9.5",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -64,6 +64,7 @@
64
64
  "kleur": "^4.1.5",
65
65
  "zod": "^3.22.4",
66
66
  "@astrojs/internal-helpers": "0.4.0",
67
+ "@astrojs/markdown-remark": "5.0.0",
67
68
  "@astrojs/prism": "3.0.0"
68
69
  },
69
70
  "peerDependencies": {
@@ -75,8 +76,7 @@
75
76
  "devalue": "^4.3.2",
76
77
  "linkedom": "^0.16.4",
77
78
  "vite": "^5.1.4",
78
- "@astrojs/markdown-remark": "4.3.2",
79
- "astro": "4.5.12",
79
+ "astro": "4.5.15",
80
80
  "astro-scripts": "0.0.14"
81
81
  },
82
82
  "engines": {
@@ -89,6 +89,6 @@
89
89
  "build": "astro-scripts build \"src/**/*.ts\" && tsc",
90
90
  "build:ci": "astro-scripts build \"src/**/*.ts\"",
91
91
  "dev": "astro-scripts dev \"src/**/*.ts\"",
92
- "test": "astro-scripts test --timeout 40000 \"test/**/*.test.js\""
92
+ "test": "astro-scripts test --timeout 60000 \"test/**/*.test.js\""
93
93
  }
94
94
  }