@@ -3,6 +3,7 @@ import { Repl, ReplStore } from '@vue/repl'
3
3
import ' @vue/repl/style.css'
4
4
import data from ' ./data.json'
5
5
import { inject , watchEffect , version , Ref } from ' vue'
6
+ import { resolveSFCExample , resolveNoBuildExample } from ' ./utils'
6
7
7
8
const store = new ReplStore ({
8
9
defaultVueRuntimeURL: ` https://unpkg.com/vue@${version }/dist/vue.esm-browser.js `
@@ -28,171 +29,11 @@ function updateExample() {
28
29
}
29
30
store .setFiles (
30
31
preferSFC .value
31
- ? resolveSFCExample (data [hash ])
32
- : resolveNoBuildExample (data [hash ]),
32
+ ? resolveSFCExample (data [hash ], preferComposition . value )
33
+ : resolveNoBuildExample (data [hash ], preferComposition . value ),
33
34
preferSFC .value ? ' App.vue' : ' index.html'
34
35
)
35
36
}
36
-
37
- type ExampleData = {
38
- [key : string ]: Record <string , string >
39
- } & {
40
- ' import-map.json' : string
41
- }
42
-
43
- function resolveSFCExample(raw : ExampleData ) {
44
- const files: Record <string , string > = {}
45
- forEachComponent (
46
- raw ,
47
- files ,
48
- (filename , { template , composition , options , style }) => {
49
- let sfcContent =
50
- filename === ' App' ? ` <!--\n ${raw [' description.txt' ]}\n -->\n\n ` : ` `
51
- if (preferComposition .value ) {
52
- sfcContent += ` <script setup>\n ${toScriptSetup (
53
- composition ,
54
- template
55
- )}<\/ script> `
56
- } else {
57
- sfcContent += ` <script>\n ${options }<\/ script> `
58
- }
59
- sfcContent += ` \n\n <template>\n ${indent (template )}</template>`
60
- if (style ) {
61
- sfcContent += ` \n\n <style>\n ${style }</style>`
62
- }
63
- files [filename + ' .vue' ] = sfcContent
64
- }
65
- )
66
- return files
67
- }
68
-
69
- function resolveNoBuildExample(raw : ExampleData ) {
70
- const files: Record <string , string > = {}
71
-
72
- let html = ` <!--\n ${raw [' description.txt' ]}\n -->\n\n `
73
- let css = ' '
74
-
75
- // set it first for ordering
76
- files [' index.html' ] = html
77
- forEachComponent (
78
- raw ,
79
- files ,
80
- (filename , { template , composition , options , style }) => {
81
- let js = preferComposition .value ? composition : options
82
- // rewrite imports to *.vue
83
- js = js .replace (/ import (. * ) from '(. * )\. vue'/ g , " import $1 from '$2.js'" )
84
-
85
- const _template = indent (toKebabTags (template ).trim ())
86
- if (style ) css += style
87
-
88
- if (filename === ' App' ) {
89
- html += ` <script type="module">\n ${injectCreateApp (js )}<\/ script> `
90
- html += ` \n\n <div id="app">\n ${_template }</div>`
91
- } else {
92
- // html += `\n\n<template id="${filename}">\n${_template}</template>`
93
- js = js .replace (
94
- / export default \{ ([^ ] * )\n \} / ,
95
- ` export default {$1,\n template: \`\n ${_template }\n \`\n } `
96
- )
97
- files [filename + ' .js' ] = js
98
- }
99
- }
100
- )
101
- files [' index.html' ] = html
102
- if (css ) {
103
- files [' style.css' ] = css
104
- }
105
- return files
106
- }
107
-
108
- function forEachComponent(
109
- raw : ExampleData ,
110
- files : Record <string , string >,
111
- cb : (filename : string , file : Record <string , string >) => void
112
- ) {
113
- for (const filename in raw ) {
114
- const content = raw [filename ]
115
- if (filename === ' description.txt' ) {
116
- continue
117
- } else if (typeof content === ' string' ) {
118
- files [filename ] = content
119
- } else {
120
- const {
121
- ' template.html' : template,
122
- ' composition.js' : composition,
123
- ' options.js' : options,
124
- ' style.css' : style
125
- } = content
126
- cb (filename , { template , composition , options , style })
127
- }
128
- }
129
- }
130
-
131
- function toScriptSetup(src : string , template : string ): string {
132
- const exportDefaultIndex = src .indexOf (' export default' )
133
- const lastReturnIndex = src .lastIndexOf (' return {' )
134
-
135
- let setupCode =
136
- lastReturnIndex > - 1
137
- ? deindent (
138
- src
139
- .slice (exportDefaultIndex , lastReturnIndex )
140
- .replace (/ export default[^ ] +? setup\( [^ )] * \) \s * {/ , ' ' )
141
- .trim ()
142
- )
143
- : ' '
144
-
145
- const propsStartIndex = src .indexOf (` \n props:` )
146
- if (propsStartIndex > - 1 ) {
147
- const propsEndIndex = src .indexOf (` \n }` , propsStartIndex ) + 4
148
- const propsVar =
149
- / \b props\b / .test (template ) || / \b props\b / .test (src )
150
- ? ` const props = `
151
- : ` `
152
- const propsDef = deindent (
153
- src
154
- .slice (propsStartIndex , propsEndIndex )
155
- .trim ()
156
- .replace (/ ,$ / , ' ' )
157
- .replace (/ ^ props: / , ` ${propsVar }defineProps( ` ) + ' )' ,
158
- 1
159
- )
160
- setupCode = (propsDef + ' \n\n ' + setupCode ).trim ()
161
- }
162
-
163
- return src .slice (0 , exportDefaultIndex ) + setupCode + ' \n '
164
- }
165
-
166
- function indent(str : string ): string {
167
- return str
168
- .split (' \n ' )
169
- .map ((l ) => (l .trim () ? ` ${l } ` : l ))
170
- .join (' \n ' )
171
- }
172
-
173
- function deindent(str : string , tabsize = 2 ): string {
174
- return str
175
- .split (' \n ' )
176
- .map ((l ) => l .replace (tabsize === 1 ? / ^ \s {2} / : / ^ \s {4} / , ' ' ))
177
- .join (' \n ' )
178
- }
179
-
180
- function injectCreateApp(src : string ): string {
181
- const importVueRE = / import {(. *? )} from 'vue'/
182
- if (importVueRE .test (src )) {
183
- src = src .replace (importVueRE , ` import { createApp,$1} from 'vue' ` )
184
- } else {
185
- const newline = src .startsWith (` import ` ) ? ` \n ` : ` \n\n `
186
- src = ` import { createApp } from 'vue'${newline }${src } `
187
- }
188
- return src .replace (/ export default ({[^ ] * \n })/ , " createApp($1).mount('#app')" )
189
- }
190
-
191
- function toKebabTags(str : string ): string {
192
- return str .replace (/ (<\/ ? )([A-Z ] \w + )(\s | >)/ g , (_ , open , tagName , end ) => {
193
- return open + tagName .replace (/ \B ([A-Z ] )/ g , ' -$1' ).toLowerCase () + end
194
- })
195
- }
196
37
</script >
197
38
198
39
<template >
0 commit comments