-
Notifications
You must be signed in to change notification settings - Fork 17
/
myst.ts
154 lines (142 loc) · 4.37 KB
/
myst.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
import { mystParse } from 'myst-parser';
import {
mathPlugin,
footnotesPlugin,
keysPlugin,
basicTransformationsPlugin,
enumerateTargetsPlugin,
resolveReferencesPlugin,
WikiTransformer,
GithubTransformer,
DOITransformer,
RRIDTransformer,
linksPlugin,
ReferenceState,
getFrontmatter,
htmlPlugin
} from 'myst-transforms';
import { unified } from 'unified';
import { VFile } from 'vfile';
import { validatePageFrontmatter } from 'myst-frontmatter';
import {
copyNode,
GenericParent as Root,
RoleSpec,
RoleData,
ParseTypesEnum,
GenericNode
} from 'myst-common';
import { cardDirective } from 'myst-ext-card';
import { gridDirective } from 'myst-ext-grid';
import { tabDirectives } from 'myst-ext-tabs';
import { StaticNotebook } from '@jupyterlab/notebook';
import { getCellList } from './utils';
import { imageUrlSourceTransform } from './images';
import { internalLinksPlugin } from './links';
import { addCiteChildrenPlugin } from './citations';
const evalRole: RoleSpec = {
name: 'eval',
body: {
type: ParseTypesEnum.string,
required: true
},
run(data: RoleData): GenericNode[] {
const value = data.body as string;
return [{ type: 'inlineExpression', value }];
}
};
export function markdownParse(text: string): Root {
const mdast = mystParse(text, {
directives: [cardDirective, gridDirective, ...tabDirectives],
roles: [evalRole]
});
// Parsing individually here requires that link and footnote references are contained to the cell
// This is consistent with the current Jupyter markdown renderer
unified()
.use(basicTransformationsPlugin)
.use(htmlPlugin, {
htmlHandlers: {
comment(h: any, node: any) {
const result = h(node, 'comment');
(result as any).value = node.value;
return result;
}
}
})
.runSync(mdast as any);
return mdast as Root;
}
export function parseContent(
notebook: StaticNotebook
): undefined | Promise<void> {
const cells = getCellList(notebook)?.filter(
// In the future, we may want to process the code cells as well, but not now
cell => cell.model.type === 'markdown'
);
if (!cells) return undefined;
const blocks = cells.map(cell => {
const text = cell.model?.value.text ?? '';
if (!cell.myst.pre) {
// This will be cleared when the cell is executed, and parsed again here
cell.myst.pre = markdownParse(text);
}
return { type: 'block', children: copyNode(cell.myst.pre).children };
});
const mdast = { type: 'root', children: blocks };
const linkTransforms = [
new WikiTransformer(),
new GithubTransformer(),
new DOITransformer(),
new RRIDTransformer()
];
const file = new VFile();
const references = {
cite: { order: [], data: {} },
footnotes: {},
article: mdast as any
};
const { frontmatter: frontmatterRaw } = getFrontmatter(
// This is a bit weird, but if there is a YAML block in the first cell, this is where it will be.
mdast.children[0]?.children[0] as any,
{
removeYaml: true,
removeHeading: true
}
);
const frontmatter = validatePageFrontmatter(frontmatterRaw, {
property: 'frontmatter',
messages: {}
});
const state = new ReferenceState({
numbering: frontmatter.numbering,
file
});
unified()
.use(mathPlugin, { macros: frontmatter?.math ?? {} }) // This must happen before enumeration, as it can add labels
.use(enumerateTargetsPlugin, { state })
.use(linksPlugin, { transformers: linkTransforms })
.use(footnotesPlugin, { references })
.use(resolveReferencesPlugin, { state })
.use(internalLinksPlugin, { notebook })
.use(addCiteChildrenPlugin)
.use(keysPlugin)
.runSync(mdast as any, file);
(notebook as any).myst = { references, frontmatter, mdast };
if (file.messages.length > 0) {
// TODO: better error messages in the future
console.log(file.messages);
}
// Render the full result in each cell using React
// Any cell can have side-effects into other cells, so this is necessary
const promises = cells.map(async (cell, index) => {
try {
// Go through all links and replace the source if they are local
await imageUrlSourceTransform(mdast.children[index] as any, { cell });
} catch (error) {
// pass
}
cell.myst.post = mdast.children[index];
cell.mystRender();
});
return Promise.all(promises).then(() => undefined);
}