@@ -4,83 +4,53 @@ const matter = require('gray-matter');
4
4
const normalizeNewline = require ( 'normalize-newline' ) ;
5
5
6
6
const EXREG = / e x p o r t \s d e f a u l t \s / g;
7
- const MODREG = / ^ ( i m p o r t | e x p o r t ) \s / g;
7
+ /*
8
+ * See this link for an explanation of the regex solution:
9
+ * https://stackoverflow.com/questions/6462578/regex-to-match-all-instances-not-inside-quotes/23667311#23667311
10
+ * Note that the regex isn't concerned about code blocks (```).
11
+ * Tracking pairs of ` should be sufficient to caputre code blocks, too.
12
+ */
13
+ const MODREG = / \\ ` | ` (?: \\ ` | [ ^ ` ] ) * ` | ( ^ (?: i m p o r t | e x p o r t ) .* $ ) / gm;
8
14
const SLIDEREG = / \n - - - \n / ;
9
- const CODEBLOCKREG = / ( ` { 3 } ) (?: (? = ( \\ ? ) ) \2[ \s \S ] ) * ?\1/ g;
10
- const INLINECODEREG = / ( ` { 1 } ) (?: (? = ( \\ ? ) ) \2.) * ?\1/ g;
11
- const SNIPPETREG = / S P E C T A C L E - C O D E - S N I P P E T - \d / g;
12
15
13
16
const nameForSlide = index => `MDXContentWrapper${ index } ` ;
14
- const codeSnippetPlaceholder = index => `SPECTACLE-CODE-SNIPPET-${ index } ` ;
15
17
16
18
module . exports = async function ( src ) {
17
19
const { data, content } = matter ( src ) ;
18
20
19
21
const inlineModules = [ ] ;
20
- const codeSnippets = { } ;
21
- let codeSnippetCounter = 0 ;
22
22
23
23
const callback = this . async ( ) ;
24
24
const options = Object . assign ( { } , getOptions ( this ) , {
25
25
filepath : this . resourcePath
26
26
} ) ;
27
27
28
28
/*
29
- Step 1:
30
- * Replace all code blocks and inline code with a temporary string.
31
- * This prevents the next step from accidentally pulling `import` or `export`
32
- * statements out of the code snippets (which would later break the JSX with
33
- * duplicate import statements). We look for pairs of (```) before pairs of
34
- * (`) so that we don't mismatch code block back ticks.
29
+ Step 1:
30
+ * Set aside all inline JSX import and export statements from the MDX file.
31
+ * When mdx.sync() compiles MDX into JSX, it will stub any component that doesn't
32
+ * have a corresponding import. Therefore, we will re-add all of the imports/exports
33
+ * to each slide before compiling the MDX via mdx.sync().
35
34
*/
36
- const contentWithoutCodeSnippets = normalizeNewline ( content )
37
- . replace ( CODEBLOCKREG , codeBlock => {
38
- const placeholderString = codeSnippetPlaceholder ( codeSnippetCounter ) ;
39
- codeSnippets [ placeholderString ] = codeBlock ;
40
- codeSnippetCounter ++ ;
41
- return placeholderString ;
42
- } )
43
- . replace ( INLINECODEREG , inlineCode => {
44
- const placeholderString = codeSnippetPlaceholder ( codeSnippetCounter ) ;
45
- codeSnippets [ placeholderString ] = inlineCode ;
46
- codeSnippetCounter ++ ;
47
- return placeholderString ;
48
- } ) ;
49
-
50
- const slides = contentWithoutCodeSnippets
51
- . split ( '\n' )
52
- /*
53
- Step 2:
54
- * Set aside all inline JSX import and export statements from the MDX file.
55
- * When mdx.sync() compiles MDX into JSX, it will stub any component that doesn't
56
- * have a corresponding import. Therefore, we will re-add all of the imports/exports
57
- * to each slide before compiling the MDX via mdx.sync().
58
- */
59
- . map ( line => {
60
- if ( MODREG . test ( line ) ) {
61
- inlineModules . push ( line ) ;
35
+ const slides = normalizeNewline ( content )
36
+ . replace ( MODREG , ( value , group1 ) => {
37
+ if ( ! group1 ) {
38
+ // group1 is empty, so this is not the import/export case we're looking for
39
+ return value ;
40
+ } else {
41
+ // found an inline export or import statement
42
+ inlineModules . push ( value ) ;
43
+ return '' ;
62
44
}
63
- return line ;
64
- } )
65
- . filter ( line => ! MODREG . test ( line ) )
66
- . join ( '\n' )
67
- /*
68
- Step 3:
69
- * We can now safely put back the code snippets. This is important to do
70
- * before compiling the MDX.
71
- */
72
- . replace ( SNIPPETREG , placeholderString => {
73
- const codeSnippet = codeSnippets [ placeholderString ] ;
74
- return codeSnippet ;
75
45
} )
76
46
/*
77
- Step 4 :
47
+ Step 2 :
78
48
* Split the MDX file by occurences of `---`. This is a reserved symbol
79
49
* to denote slide boundaries.
80
50
*/
81
51
. split ( SLIDEREG )
82
52
/*
83
- Step 5 :
53
+ Step 3 :
84
54
* As referenced before, we need to add the imports and exports to
85
55
* every slide again. That way mdx.sync can find the component definitions
86
56
* for any custom components used in the MDX file.
@@ -91,34 +61,39 @@ ${inlineModules.join('\n')}\n
91
61
${ slide } `
92
62
)
93
63
/*
94
- Step 6 :
64
+ Step 4 :
95
65
* Use mdx.sync to compile a separate JSX component for each slide
96
66
* written in MDX.
97
67
*/
98
68
. map ( slide => mdx . sync ( slide , options ) )
99
69
/*
100
- Step 7 :
70
+ Step 5 :
101
71
* mdx.sync will attempt to default export the component generated for each
102
72
* slide. However, we have multiple slides and thus multiple generated components.
103
73
* We can't export multiple defaults, so we must remove all existing occurences of
104
74
* `export default`.
105
75
*/
106
76
. map ( slide => slide . replace ( EXREG , '' ) )
107
77
/*
108
- Step 8 :
109
- * Remove the inline exports/imports again. We don't want to duplicate import/export
78
+ Step 6 :
79
+ * Remove the inline exports/imports again. We don't want to duplicate inline import/export
110
80
* statements littered throughout the file output.
111
81
*/
112
82
. map ( slide =>
113
- slide
114
- . split ( '\n' )
115
- . filter ( line => ! MODREG . test ( line ) )
116
- . filter ( Boolean )
117
- . join ( '\n' )
83
+ slide . replace ( MODREG , ( value , group1 ) => {
84
+ if ( ! group1 ) {
85
+ // group1 is empty, so this is not the import/export case we're looking for
86
+ return value ;
87
+ } else {
88
+ // found an inline export or import statement
89
+ inlineModules . push ( value ) ;
90
+ return '' ;
91
+ }
92
+ } )
118
93
)
119
94
. map ( slide => slide . trim ( ) )
120
95
/*
121
- Step 9 :
96
+ Step 7 :
122
97
* The generated component from mdx.sync assumes it's the only component that
123
98
* will inhabit a file. It has const definitions outside of the auto-named MDXContent
124
99
* component. This would be fine if we weren't generating a component for each
@@ -138,9 +113,9 @@ ${wrapperName}.isMDXComponent = true;`;
138
113
const { modules = [ ] } = data ;
139
114
let wrapperNames = [ ] ;
140
115
/*
141
- Step 10 :
116
+ Step 8 :
142
117
* Begin composing the final output. Include React, mdx, modules, and the inline
143
- * export/import statements that we removed in Step 8 .
118
+ * export/import statements that we removed in Step 6 .
144
119
*/
145
120
let allCode = `/* @jsx mdx */
146
121
import React from 'react'
@@ -152,15 +127,15 @@ ${inlineModules
152
127
} )
153
128
. join ( '\n' ) } \n\n`;
154
129
/*
155
- Step 11 :
130
+ Step 9 :
156
131
* Add in the slide component definitions. Keep track of the component names.
157
132
*/
158
133
slides . forEach ( ( s , i ) => {
159
134
allCode += s + '\n\n' ;
160
135
wrapperNames . push ( nameForSlide ( i ) ) ;
161
136
} ) ;
162
137
/*
163
- Step 12 :
138
+ Step 10 :
164
139
* Finally, declare the default export as an array of the slide components.
165
140
* See /examples/mdx/test-mdx.js for how to import and use the generated slide
166
141
* components.
0 commit comments