-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
index.ts
177 lines (163 loc) · 5.53 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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 function getHeaders() {
console.warn('getHeaders() have been deprecated. Use getHeadings() function instead.');
return getHeadings();
};
export async function Content() {
const { layout, ...content } = frontmatter;
content.file = file;
content.url = url;
content.astro = {};
Object.defineProperty(content.astro, 'headings', {
get() {
throw new Error('The "astro" property is no longer supported! To access "headings" from your layout, try using "Astro.props.headings."')
}
});
Object.defineProperty(content.astro, 'html', {
get() {
throw new Error('The "astro" property is no longer supported! To access "html" from your layout, try using "Astro.props.compiledContent()."')
}
});
Object.defineProperty(content.astro, 'source', {
get() {
throw new Error('The "astro" property is no longer supported! To access "source" from your layout, try using "Astro.props.rawContent()."')
}
});
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',
},
},
};
}
},
};
}