/
vueTransform.js
121 lines (110 loc) · 3.02 KB
/
vueTransform.js
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
import compiler from 'vue-template-compiler'
import transpile from 'vue-template-es2015-compiler'
import MagicString from 'magic-string'
import { resolve } from 'path'
import { readFileSync } from 'fs'
export default function vueTransform (code, id, scripts) {
const nodes = compiler.parseComponent(code)
const s = new MagicString(code)
let exportOffset = 0
if (nodes.script) {
if (nodes.script.src) {
let script = readSrc(id, nodes.script.src)
exportOffset = indexOfExport(script, 0)
if (exportOffset) {
s.overwrite(0, exportOffset, script.slice(0, exportOffset))
s.overwrite(exportOffset, code.length, script.slice(exportOffset))
}
} else {
s.remove(nodes.script.end, s.original.length)
s.remove(0, nodes.script.start)
exportOffset = indexOfExport(s.toString(), nodes.script.start)
}
}
// The script cannot be valid so let's overwrite it
if (exportOffset < 15) {
exportOffset = 16
s.overwrite(0, 16, 'export default {')
s.overwrite(16, code.length, '\nstub: 1\n}')
}
// Precompile and inject Vue template
if (nodes.template) {
scripts[id] = injectTemplate(s, nodes.template, exportOffset, id)
}
// Import css as a module
// Example: import "./src/App.vue.component.css"
let css
if (nodes.styles) {
nodes.styles.forEach(style => {
if (style.src) {
s.prepend('import ' + JSON.stringify(style.src) + '\n')
} else {
let lang = checkLang(style) || 'css'
s.prepend('import ' + JSON.stringify(id + '.component.' + lang) + '\n')
css = style.map((it) => {
return it.content
}).join('\n')
}
})
}
return {
code: s.toString(),
map: s.generateMap({ hires: true }),
css
}
}
function readSrc (id, src) {
if (src.startsWith('./') || src.startsWith('/') || src.startsWith('../')) {
src = resolve(id, '..', src)
} else {
src = require.resolve(src)
}
return readFileSync(src, 'utf8')
}
function indexOfExport (code, start) {
var match = /export\s+default\s+\{/.exec(code)
if (match && match[0]) {
return match.index + match[0].length + start
}
return 0
}
/**
* Only support for es5 modules
*
* @param script
* @param template
* @returns {string}
*/
function injectTemplate (s, node, offset, id) {
const t = node.src ? readSrc(id, node.src) : node.content
// Compile template
const compiled = compiler.compile(t)
const renderFuncs = '\nrender: ' + toFunction(compiled.render) + ',' +
'\nstaticRenderFns: [' + compiled.staticRenderFns.map(toFunction).join(',') + '],'
s.appendLeft(offset, renderFuncs)
return renderFuncs
}
/**
* Wrap a piece of code in a function
*
* @param {String} code
* @return {String}
*/
function toFunction (code) {
return transpile('(function(){' + code + '})').slice(1, -1)
}
/**
* Check the lang attribute node.
*
* @param {Node} node
* @return {String|undefined}
*/
function checkLang (nodes) {
let lang
nodes.map((it) => {
if (it.lang !== 'css') {
lang = it.lang
}
})
return lang
}