1
+ // compiler-sfc src: https://github.com/vuejs/vue-next/blob/master/packages/compiler-sfc/src/index.ts#L1
2
+ import {
3
+ compileStyleAsync as sfc_compileStyleAsync ,
4
+ compileTemplate as sfc_compileTemplate ,
5
+ parse as sfc_parse ,
6
+ } from '@vue/component-compiler-utils'
1
7
8
+ import * as vueTemplateCompiler from 'vue-template-compiler'
9
+ import { VueTemplateCompiler } from '@vue/component-compiler-utils/dist/types'
10
+ import { TemplateCompileOptions } from '@vue/component-compiler-utils/dist/compileTemplate'
2
11
3
12
4
13
import {
@@ -19,10 +28,6 @@ import { formatError, withCache, hash, renameDynamicImport, parseDeps, interopRe
19
28
20
29
import { Options , LoadModule , ModuleExport , CustomBlockCallback } from './types.ts'
21
30
22
-
23
-
24
-
25
-
26
31
/**
27
32
* the version of the library (process.env.VERSION is set by webpack, at compile-time)
28
33
*/
@@ -46,8 +51,153 @@ export async function createSFCModule(source : string, filename : string, option
46
51
const component = { } ;
47
52
48
53
49
- const { delimiters, moduleCache, compiledCache, addStyle, log, additionalBabelPlugins = [ ] , customBlockHandler } = options ;
54
+ const { compiledCache, addStyle, log, additionalBabelPlugins = [ ] , customBlockHandler } = options ;
55
+
56
+ // vue-loader next: https://github.com/vuejs/vue-loader/blob/next/src/index.ts#L91
57
+ const descriptor = sfc_parse ( {
58
+ source,
59
+ filename,
60
+ needMap : genSourcemap ,
61
+ compiler : vueTemplateCompiler as VueTemplateCompiler }
62
+ ) ;
63
+
64
+ const customBlockCallbacks : CustomBlockCallback [ ] = customBlockHandler !== undefined ? await Promise . all ( descriptor . customBlocks . map ( ( block ) => customBlockHandler ( block , filename , options ) ) ) : [ ] ;
65
+
66
+ const componentHash = hash ( filename , version ) ;
67
+ const scopeId = `data-v-${ componentHash } ` ;
68
+
69
+ // hack: asynchronously preloads the language processor before it is required by the synchronous preprocessCustomRequire() callback, see below
70
+ if ( descriptor . template && descriptor . template . lang )
71
+ await loadModule ( descriptor . template . lang , options ) ;
72
+
73
+
74
+ const hasScoped = descriptor . styles . some ( e => e . scoped ) ;
75
+
76
+ const compileTemplateOptions : TemplateCompileOptions = descriptor . template ? {
77
+ // hack, since sourceMap is not configurable an we want to get rid of source-map dependency. see genSourcemap
78
+ source : descriptor . template . content ,
79
+ filename,
80
+ compiler : vueTemplateCompiler as VueTemplateCompiler ,
81
+ compilerOptions : undefined ,
82
+ preprocessLang : descriptor . template . lang ,
83
+ isProduction : isProd ,
84
+ prettify : false
85
+ } : null ;
86
+
87
+ if ( descriptor . script ) {
88
+
89
+ // eg: https://github.com/vuejs/vue-loader/blob/v15.9.6/lib/index.js
90
+
91
+ const [ depsList , transformedScriptSource ] = await withCache ( compiledCache , [ componentHash , descriptor . script . content ] , async ( { preventCache } ) => {
92
+
93
+ const babelParserPlugins : babel_ParserPlugin [ ] = [ ] ;
94
+
95
+ let ast : t . File
96
+ try {
97
+ ast = babel_parse ( descriptor . script . content , {
98
+ // doc: https://babeljs.io/docs/en/babel-parser#options
99
+ // if: https://github.com/babel/babel/blob/main/packages/babel-parser/typings/babel-parser.d.ts#L24
100
+ plugins : [
101
+ ...babelParserPlugins
102
+ ] ,
103
+ sourceType : 'module' ,
104
+ sourceFilename : filename
105
+ } ) ;
106
+
107
+ } catch ( ex ) {
108
+ log ?.( 'error' , 'SFC script' , formatError ( ex . message , filename , source , ex . loc . line , ex . loc . column + 1 ) ) ;
109
+ throw ex ;
110
+ }
111
+
112
+ renameDynamicImport ( ast ) ;
113
+ const depsList = parseDeps ( ast ) ;
114
+
115
+ // doc: https://babeljs.io/docs/en/babel-core#transformfromastasync
116
+ const transformedScript = await babel_transformFromAstAsync ( ast , descriptor . script . content , {
117
+ sourceMaps : genSourcemap , // https://babeljs.io/docs/en/options#sourcemaps
118
+ plugins : [ // https://babeljs.io/docs/en/options#plugins
119
+ babelPluginTransformModulesCommonjs , // https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs#options
120
+ ...additionalBabelPlugins ,
121
+ ] ,
122
+ babelrc : false ,
123
+ configFile : false ,
124
+ highlightCode : false ,
125
+ } ) ;
126
+
127
+ return [ depsList , transformedScript . code ] ;
128
+ } ) ;
129
+
130
+ await loadDeps ( filename , depsList , options , loadModule ) ;
131
+ Object . assign ( component , interopRequireDefault ( createModule ( filename , transformedScriptSource , options , loadModule ) . exports ) . default ) ;
132
+ }
133
+
134
+
135
+ if ( descriptor . template !== null ) {
136
+ // compiler-sfc src: https://github.com/vuejs/vue-next/blob/15baaf14f025f6b1d46174c9713a2ec517741d0d/packages/compiler-sfc/src/compileTemplate.ts#L39
137
+ // compileTemplate eg: https://github.com/vuejs/vue-loader/blob/next/src/templateLoader.ts#L33
138
+ const [ templateDepsList , templateTransformedSource ] = await withCache ( compiledCache , [ componentHash , descriptor . template . content ] , async ( { preventCache } ) => {
139
+
140
+ const template = sfc_compileTemplate ( compileTemplateOptions ) ;
141
+ // "@vue/component-compiler-utils" does NOT assume any module system, and expose render in global scope.
142
+ template . code += "\nmodule.exports = {render: render, staticRenderFns: staticRenderFns}"
143
+
144
+ if ( template . errors . length ) {
145
+
146
+ preventCache ( ) ;
147
+ for ( const err of template . errors ) {
148
+
149
+ // @ts -ignore (Property 'message' does not exist on type 'string | CompilerError')
150
+ log ?.( 'error' , 'SFC template' , err ) ;
151
+ }
152
+ }
153
+
154
+ for ( const err of template . tips )
155
+ log ?.( 'info' , 'SFC template' , err ) ;
156
+
157
+ return await transformJSCode ( template . code , true , filename , options ) ;
158
+ } ) ;
159
+
160
+ await loadDeps ( filename , templateDepsList , options , loadModule ) ;
161
+ Object . assign ( component , createModule ( filename , templateTransformedSource , options , loadModule ) . exports ) ;
162
+ }
163
+
164
+
165
+ for ( const descStyle of descriptor . styles ) {
166
+
167
+ // hack: asynchronously preloads the language processor before it is required by the synchronous preprocessCustomRequire() callback, see below
168
+ if ( descStyle . lang )
169
+ await loadModule ( descStyle . lang , options ) ;
170
+
171
+ const style = await withCache ( compiledCache , [ componentHash , descStyle . content ] , async ( { preventCache } ) => {
172
+
173
+ // src: https://github.com/vuejs/vue-next/blob/15baaf14f025f6b1d46174c9713a2ec517741d0d/packages/compiler-sfc/src/compileStyle.ts#L70
174
+ const compiledStyle = await sfc_compileStyleAsync ( {
175
+ source : descStyle . content ,
176
+ filename,
177
+ id : scopeId ,
178
+ scoped : descStyle . scoped ,
179
+ trim : false , // Should we enable it, as it requires postcss trimPlugin ?
180
+ preprocessLang : descStyle . lang
181
+ } ) ;
182
+
183
+ if ( compiledStyle . errors . length ) {
184
+
185
+ preventCache ( ) ;
186
+ for ( const err of compiledStyle . errors ) {
187
+
188
+ // @ts -ignore (Property 'line' does not exist on type 'Error' and Property 'column' does not exist on type 'Error')
189
+ log ?.( 'error' , 'SFC style' , formatError ( err . message , filename , source , err . line , err . column ) ) ;
190
+ }
191
+ }
192
+
193
+ return compiledStyle . code ;
194
+ } ) ;
195
+
196
+ addStyle ( style , descStyle . scoped ? scopeId : undefined ) ;
197
+ }
50
198
199
+ if ( customBlockHandler !== undefined )
200
+ await Promise . all ( customBlockCallbacks . map ( cb => cb ?.( component ) ) ) ;
51
201
52
202
return component ;
53
203
}
0 commit comments