1 | import {
|
2 | VueTemplateCompiler,
|
3 | VueTemplateCompilerOptions,
|
4 | ErrorWithRange
|
5 | } from './types'
|
6 |
|
7 | import assetUrlsModule, {
|
8 | AssetURLOptions,
|
9 | TransformAssetUrlsOptions
|
10 | } from './templateCompilerModules/assetUrl'
|
11 | import srcsetModule from './templateCompilerModules/srcset'
|
12 |
|
13 | const consolidate = require('consolidate')
|
14 | const transpile = require('vue-template-es2015-compiler')
|
15 |
|
16 | export interface TemplateCompileOptions {
|
17 | source: string
|
18 | filename: string
|
19 | compiler: VueTemplateCompiler
|
20 | compilerOptions?: VueTemplateCompilerOptions
|
21 | transformAssetUrls?: AssetURLOptions | boolean
|
22 | transformAssetUrlsOptions?: TransformAssetUrlsOptions
|
23 | preprocessLang?: string
|
24 | preprocessOptions?: any
|
25 | transpileOptions?: any
|
26 | isProduction?: boolean
|
27 | isFunctional?: boolean
|
28 | optimizeSSR?: boolean
|
29 | prettify?: boolean
|
30 | }
|
31 |
|
32 | export interface TemplateCompileResult {
|
33 | ast: Object | undefined
|
34 | code: string
|
35 | source: string
|
36 | tips: (string | ErrorWithRange)[]
|
37 | errors: (string | ErrorWithRange)[]
|
38 | }
|
39 |
|
40 | export function compileTemplate(
|
41 | options: TemplateCompileOptions
|
42 | ): TemplateCompileResult {
|
43 | const { preprocessLang } = options
|
44 | const preprocessor = preprocessLang && consolidate[preprocessLang]
|
45 | if (preprocessor) {
|
46 | return actuallyCompile(
|
47 | Object.assign({}, options, {
|
48 | source: preprocess(options, preprocessor)
|
49 | })
|
50 | )
|
51 | } else if (preprocessLang) {
|
52 | return {
|
53 | ast: {},
|
54 | code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
|
55 | source: options.source,
|
56 | tips: [
|
57 | `Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`
|
58 | ],
|
59 | errors: [
|
60 | `Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`
|
61 | ]
|
62 | }
|
63 | } else {
|
64 | return actuallyCompile(options)
|
65 | }
|
66 | }
|
67 |
|
68 | function preprocess(
|
69 | options: TemplateCompileOptions,
|
70 | preprocessor: any
|
71 | ): string {
|
72 | const { source, filename, preprocessOptions } = options
|
73 |
|
74 | const finalPreprocessOptions = Object.assign(
|
75 | {
|
76 | filename
|
77 | },
|
78 | preprocessOptions
|
79 | )
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | let res: any, err
|
86 | preprocessor.render(
|
87 | source,
|
88 | finalPreprocessOptions,
|
89 | (_err: Error | null, _res: string) => {
|
90 | if (_err) err = _err
|
91 | res = _res
|
92 | }
|
93 | )
|
94 |
|
95 | if (err) throw err
|
96 | return res
|
97 | }
|
98 |
|
99 | function actuallyCompile(
|
100 | options: TemplateCompileOptions
|
101 | ): TemplateCompileResult {
|
102 | const {
|
103 | source,
|
104 | compiler,
|
105 | compilerOptions = {},
|
106 | transpileOptions = {},
|
107 | transformAssetUrls,
|
108 | transformAssetUrlsOptions,
|
109 | isProduction = process.env.NODE_ENV === 'production',
|
110 | isFunctional = false,
|
111 | optimizeSSR = false,
|
112 | prettify = true
|
113 | } = options
|
114 |
|
115 | const compile =
|
116 | optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile
|
117 |
|
118 | let finalCompilerOptions = compilerOptions
|
119 | if (transformAssetUrls) {
|
120 | const builtInModules = [
|
121 | transformAssetUrls === true
|
122 | ? assetUrlsModule(undefined, transformAssetUrlsOptions)
|
123 | : assetUrlsModule(transformAssetUrls, transformAssetUrlsOptions),
|
124 | srcsetModule(transformAssetUrlsOptions)
|
125 | ]
|
126 | finalCompilerOptions = Object.assign({}, compilerOptions, {
|
127 | modules: [...builtInModules, ...(compilerOptions.modules || [])],
|
128 | filename: options.filename
|
129 | })
|
130 | }
|
131 |
|
132 | const { ast, render, staticRenderFns, tips, errors } = compile(
|
133 | source,
|
134 | finalCompilerOptions
|
135 | )
|
136 |
|
137 | if (errors && errors.length) {
|
138 | return {
|
139 | ast,
|
140 | code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
|
141 | source,
|
142 | tips,
|
143 | errors
|
144 | }
|
145 | } else {
|
146 | const finalTranspileOptions = Object.assign({}, transpileOptions, {
|
147 | transforms: Object.assign({}, transpileOptions.transforms, {
|
148 | stripWithFunctional: isFunctional
|
149 | })
|
150 | })
|
151 |
|
152 | const toFunction = (code: string): string => {
|
153 | return `function (${isFunctional ? `_h,_vm` : ``}) {${code}}`
|
154 | }
|
155 |
|
156 |
|
157 |
|
158 | let code =
|
159 | transpile(
|
160 | `var __render__ = ${toFunction(render)}\n` +
|
161 | `var __staticRenderFns__ = [${staticRenderFns.map(toFunction)}]`,
|
162 | finalTranspileOptions
|
163 | ) + `\n`
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | code = code.replace(/\s__(render|staticRenderFns)__\s/g, ' $1 ')
|
169 |
|
170 | if (!isProduction) {
|
171 |
|
172 |
|
173 | code += `render._withStripped = true`
|
174 |
|
175 | if (prettify) {
|
176 | try {
|
177 | code = require('prettier').format(code, {
|
178 | semi: false,
|
179 | parser: 'babel'
|
180 | })
|
181 | } catch (e) {
|
182 | if (e.code === 'MODULE_NOT_FOUND') {
|
183 | tips.push(
|
184 | 'The `prettify` option is on, but the dependency `prettier` is not found.\n' +
|
185 | 'Please either turn off `prettify` or manually install `prettier`.'
|
186 | )
|
187 | }
|
188 | tips.push(
|
189 | `Failed to prettify component ${options.filename} template source after compilation.`
|
190 | )
|
191 | }
|
192 | }
|
193 | }
|
194 |
|
195 | return {
|
196 | ast,
|
197 | code,
|
198 | source,
|
199 | tips,
|
200 | errors
|
201 | }
|
202 | }
|
203 | }
|