-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
index.ts
157 lines (143 loc) · 4.67 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { renderMarkdown } from '@astrojs/markdown-remark';
import {
InvalidAstroDataError,
safelyGetAstroData,
} from '@astrojs/markdown-remark/dist/internal.js';
import fs from 'fs';
import matter from 'gray-matter';
import { fileURLToPath } from 'node:url';
import type { Plugin } from 'vite';
import { normalizePath } from 'vite';
import type { AstroSettings } from '../@types/astro';
import { getContentPaths } from '../content/index.js';
import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js';
import type { LogOptions } from '../core/logger/core.js';
import { warn } from '../core/logger/core.js';
import { isMarkdownFile } from '../core/util.js';
import type { PluginMetadata } from '../vite-plugin-astro/types.js';
import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js';
interface AstroPluginOptions {
settings: AstroSettings;
logging: LogOptions;
}
function safeMatter(source: string, id: string) {
try {
return matter(source);
} catch (err: any) {
const markdownError = new MarkdownError({
code: AstroErrorData.UnknownMarkdownError.code,
message: err.message,
stack: err.stack,
location: {
file: id,
},
});
if (err.name === 'YAMLException') {
markdownError.setErrorCode(AstroErrorData.MarkdownFrontmatterParseError.code);
markdownError.setLocation({
file: id,
line: err.mark.line,
column: err.mark.column,
});
markdownError.setMessage(err.reason);
}
throw markdownError;
}
}
// absolute path of "astro/jsx-runtime"
const astroJsxRuntimeModulePath = normalizePath(
fileURLToPath(new URL('../jsx-runtime/index.js', import.meta.url))
);
export default function markdown({ settings, logging }: AstroPluginOptions): Plugin {
return {
enforce: 'pre',
name: 'astro:markdown',
// Why not the "transform" hook instead of "load" + readFile?
// A: Vite transforms all "import.meta.env" references to their values before
// passing to the transform hook. This lets us get the truly raw value
// to escape "import.meta.env" ourselves.
async load(id) {
if (isMarkdownFile(id)) {
const { fileId, fileUrl } = getFileInfo(id, settings.config);
const rawFile = await fs.promises.readFile(fileId, 'utf-8');
const raw = safeMatter(rawFile, id);
const renderResult = await renderMarkdown(raw.content, {
...settings.config.markdown,
fileURL: new URL(`file://${fileId}`),
contentDir: getContentPaths(settings.config).contentDir,
frontmatter: raw.data,
});
const html = renderResult.code;
const { headings } = renderResult.metadata;
const astroData = safelyGetAstroData(renderResult.vfile.data);
if (astroData instanceof InvalidAstroDataError) {
throw new AstroError(AstroErrorData.InvalidFrontmatterInjectionError);
}
const { frontmatter } = astroData;
const { layout } = frontmatter;
if (frontmatter.setup) {
warn(
logging,
'markdown',
`[${id}] Astro now supports MDX! Support for components in ".md" (or alternative extensions like ".markdown") files using the "setup" frontmatter is no longer enabled by default. Migrate this file to MDX.`
);
}
const code = escapeViteEnvReferences(`
import { Fragment, jsx as h } from ${JSON.stringify(astroJsxRuntimeModulePath)};
${layout ? `import Layout from ${JSON.stringify(layout)};` : ''}
const html = ${JSON.stringify(html)};
export const frontmatter = ${JSON.stringify(frontmatter)};
export const file = ${JSON.stringify(fileId)};
export const url = ${JSON.stringify(fileUrl)};
export function rawContent() {
return ${JSON.stringify(raw.content)};
}
export function compiledContent() {
return html;
}
export function getHeadings() {
return ${JSON.stringify(headings)};
}
export async function Content() {
const { layout, ...content } = frontmatter;
content.file = file;
content.url = url;
const contentFragment = h(Fragment, { 'set:html': html });
return ${
layout
? `h(Layout, {
file,
url,
content,
frontmatter: content,
headings: getHeadings(),
rawContent,
compiledContent,
'server:root': true,
children: contentFragment
})`
: `contentFragment`
};
}
Content[Symbol.for('astro.needsHeadRendering')] = ${layout ? 'false' : 'true'};
export default Content;
`);
return {
code,
meta: {
astro: {
hydratedComponents: [],
clientOnlyComponents: [],
scripts: [],
propagation: 'none',
pageOptions: {},
} as PluginMetadata['astro'],
vite: {
lang: 'ts',
},
},
};
}
},
};
}