@astrojs/markdoc 0.9.4 → 0.10.0
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.
- package/dist/content-entry-type.js +134 -33
- package/package.json +6 -6
|
@@ -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);
|
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.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -64,8 +64,8 @@
|
|
|
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.
|
|
68
|
-
"@astrojs/prism": "3.
|
|
67
|
+
"@astrojs/markdown-remark": "5.1.0",
|
|
68
|
+
"@astrojs/prism": "3.1.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"astro": "^3.0.0 || ^4.0.0"
|
|
@@ -76,11 +76,11 @@
|
|
|
76
76
|
"devalue": "^4.3.2",
|
|
77
77
|
"linkedom": "^0.16.4",
|
|
78
78
|
"vite": "^5.1.4",
|
|
79
|
-
"astro": "4.
|
|
79
|
+
"astro": "4.6.0",
|
|
80
80
|
"astro-scripts": "0.0.14"
|
|
81
81
|
},
|
|
82
82
|
"engines": {
|
|
83
|
-
"node": "
|
|
83
|
+
"node": "^18.17.1 || ^20.3.0 || >=21.0.0"
|
|
84
84
|
},
|
|
85
85
|
"publishConfig": {
|
|
86
86
|
"provenance": true
|
|
@@ -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
|
|
92
|
+
"test": "astro-scripts test --timeout 60000 \"test/**/*.test.js\""
|
|
93
93
|
}
|
|
94
94
|
}
|