Skip to content

Commit 07b9452

Browse files
committed
refactor: extract common generated runtime code into a single module
1 parent 4b7406a commit 07b9452

File tree

2 files changed

+133
-82
lines changed

2 files changed

+133
-82
lines changed

lib/component-normalizer.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
module.exports = function normalizeComponent (
2+
name,
3+
scriptExports,
4+
compiledTemplate,
5+
scopeId,
6+
cssModules
7+
) {
8+
scriptExports = scriptExports || {}
9+
10+
// ES6 modules interop
11+
var type = typeof scriptExports.default
12+
if (type === 'object' || type === 'function') {
13+
// check named exports
14+
if (process.env.NODE_ENV !== 'production') {
15+
if (Object.keys(scriptExports).some(function (key) {
16+
return key !== 'default' && key !== '__esModule'
17+
})) {
18+
console.error('named exports are not supported in *.vue files.')
19+
}
20+
}
21+
scriptExports = scriptExports.default
22+
}
23+
24+
// Vue.extend constructor export interop
25+
var options = typeof scriptExports === 'function'
26+
? scriptExports.options
27+
: scriptExports
28+
29+
// default name option based on filename
30+
if (options.name == null) {
31+
options.name = name
32+
}
33+
34+
// render functions
35+
if (compiledTemplate) {
36+
options.render = compiledTemplate.render
37+
options.staticRenderFns = compiledTemplate.staticRenderFns
38+
}
39+
40+
// scopedId
41+
if (scopeId) {
42+
options._scopeId = scopeId
43+
}
44+
45+
// inject cssModules
46+
if (cssModules) {
47+
var computed = options.computed || (options.computed = {})
48+
Object.keys(cssModules).forEach(function (key) {
49+
var module = cssModules[key]
50+
computed[key] = function () { return module }
51+
})
52+
}
53+
54+
return {
55+
exports: scriptExports,
56+
options: options
57+
}
58+
}

lib/loader.js

Lines changed: 75 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var selectorPath = normalize.lib('selector')
1010
var templateLoaderPath = normalize.lib('template-loader')
1111
var templateCompilerPath = normalize.lib('template-compiler')
1212
var styleRewriterPath = normalize.lib('style-rewriter')
13+
var componentNormalizerPath = normalize.lib('component-normalizer')
1314

1415
// dep loaders
1516
var styleLoaderPath = normalize.dep('vue-style-loader')
@@ -33,10 +34,6 @@ var defaultLang = {
3334
script: 'js'
3435
}
3536

36-
var checkNamedExports =
37-
'if (Object.keys(__vue_exports__).some(function (key) { return key !== "default" && key !== "__esModule" })) {' +
38-
'console.error("named exports are not supported in *.vue files.")}\n'
39-
4037
module.exports = function (content) {
4138
this.cacheable()
4239
var isServer = this.options.target === 'node'
@@ -69,7 +66,7 @@ module.exports = function (content) {
6966
function getRequire (type, part, index, scoped) {
7067
return 'require(' +
7168
getRequireString(type, part, index, scoped) +
72-
')\n'
69+
')'
7370
}
7471

7572
function getRequireString (type, part, index, scoped) {
@@ -88,7 +85,7 @@ module.exports = function (content) {
8885
function getRequireForImport (type, impt, scoped) {
8986
return 'require(' +
9087
getRequireForImportString(type, impt, scoped) +
91-
')\n'
88+
')'
9289
}
9390

9491
function getRequireForImportString (type, impt, scoped) {
@@ -196,9 +193,9 @@ module.exports = function (content) {
196193
}
197194
}
198195

196+
var output = ''
199197
var parts = parse(content, fileName, this.sourceMap)
200198
var hasScoped = parts.styles.some(function (s) { return s.scoped })
201-
var output = 'var __vue_exports__, __vue_options__\n'
202199

203200
// css modules
204201
var cssModules = {}
@@ -220,7 +217,7 @@ module.exports = function (content) {
220217
if (moduleName) {
221218
if (!hasModules) {
222219
hasModules = true
223-
output += 'var __vue_styles__ = {}\n'
220+
output += 'var cssModules = {}\n'
224221
}
225222
if (moduleName in cssModules) {
226223
loaderContext.emitError('CSS module name "' + moduleName + '" is not unique!')
@@ -235,81 +232,89 @@ module.exports = function (content) {
235232
requireString += '.locals'
236233
}
237234

238-
output += '__vue_styles__["' + moduleName + '"] = ' + requireString + '\n'
235+
output += 'cssModules["' + moduleName + '"] = ' + requireString + '\n'
239236
}
240237
} else {
241-
output += requireString
238+
output += requireString + '\n'
242239
}
243240
})
241+
output += '\n'
244242
}
245243

246-
// add require for script
244+
// we require the component normalizer function, and call it like so:
245+
// normalizeComponent(
246+
// name,
247+
// scriptExports,
248+
// compiledTemplate,
249+
// scopeId,
250+
// cssModules
251+
// )
252+
output += 'var Component = require("' + componentNormalizerPath + '")(\n'
253+
254+
// name
255+
output += ' /* name */\n ' + JSON.stringify(path.parse(filePath).name) + ',\n'
256+
257+
// <script>
258+
output += ' /* script */\n '
247259
var script = parts.script
248260
if (script) {
249-
output += '\n/* script */\n'
250-
output +=
251-
'__vue_exports__ = ' + (
252-
script.src
253-
? getRequireForImport('script', script)
254-
: getRequire('script', script)
255-
)
261+
output += script.src
262+
? getRequireForImport('script', script)
263+
: getRequire('script', script)
264+
// inject loader interop
265+
if (query.inject) {
266+
output += '(injections)'
267+
}
268+
} else {
269+
output += 'null'
256270
}
271+
output += ',\n'
257272

258-
var exports =
259-
'__vue_options__ = __vue_exports__ = __vue_exports__ || {}\n' +
260-
// ES6 modules interop
261-
'if (\n' +
262-
' typeof __vue_exports__.default === "object" ||\n' +
263-
' typeof __vue_exports__.default === "function"\n' +
264-
') {\n' +
265-
(isProduction ? '' : checkNamedExports) +
266-
'__vue_options__ = __vue_exports__ = __vue_exports__.default\n' +
267-
'}\n' +
268-
// constructor export interop
269-
'if (typeof __vue_options__ === "function") {\n' +
270-
' __vue_options__ = __vue_options__.options\n' +
271-
'}\n' +
272-
// add filename in dev
273-
(isProduction ? '' : ('__vue_options__.__file = ' + JSON.stringify(filePath))) + '\n'
274-
275-
// add component name based on the filename
276-
exports +=
277-
'if(typeof __vue_options__.name === "undefined") {\n' +
278-
' __vue_options__.name = ' + JSON.stringify(path.parse(filePath).name) + '\n' +
279-
'}'
280-
281-
// add require for template
273+
// <template>
274+
output += ' /* template */\n '
282275
var template = parts.template
283276
if (template) {
284-
output += '\n/* template */\n'
285-
output += 'var __vue_template__ = ' + (
286-
template.src
287-
? getRequireForImport('template', template)
288-
: getRequire('template', template)
289-
)
290-
// attach render functions to exported options
291-
exports +=
292-
'__vue_options__.render = __vue_template__.render\n' +
293-
'__vue_options__.staticRenderFns = __vue_template__.staticRenderFns\n'
277+
output += template.src
278+
? getRequireForImport('template', template)
279+
: getRequire('template', template)
280+
} else {
281+
output += 'null'
294282
}
283+
output += ',\n'
295284

296-
// attach scoped id
297-
if (hasScoped) {
298-
exports += '__vue_options__._scopeId = "' + moduleId + '"\n'
299-
}
285+
// scopeId
286+
output += ' /* scopeId */\n '
287+
output += (hasScoped ? JSON.stringify(moduleId) : 'null') + ',\n'
300288

289+
// cssModules
290+
output += ' /* cssModules */\n '
301291
if (Object.keys(cssModules).length) {
302292
// inject style modules as computed properties
303-
exports +=
304-
'if (!__vue_options__.computed) __vue_options__.computed = {}\n' +
305-
'Object.keys(__vue_styles__).forEach(function (key) {\n' +
306-
'var module = __vue_styles__[key]\n' +
307-
'__vue_options__.computed[key] = function () { return module }\n' +
308-
'})\n'
293+
output += 'cssModules'
294+
} else {
295+
output += 'null'
296+
}
297+
output += '\n'
298+
299+
// close normalizeComponent call
300+
output += ')\n'
301+
302+
// development-only code
303+
if (!isProduction) {
304+
// add filename in dev
305+
output += 'Component.options.__file = ' + JSON.stringify(filePath) + '\n'
306+
// check functional components used with templates
307+
if (template) {
308+
output +=
309+
'if (Component.options.functional) {' +
310+
'console.error("' +
311+
'[vue-loader] ' + fileName + ': functional components are not ' +
312+
'supported with templates, they should use render functions.' +
313+
'")}\n'
314+
}
309315
}
310316

311317
if (!query.inject) {
312-
output += exports
313318
// hot reload
314319
if (
315320
!isServer &&
@@ -325,37 +330,25 @@ module.exports = function (content) {
325330
' module.hot.accept()\n' +
326331
' if (!module.hot.data) {\n' +
327332
// initial insert
328-
' hotAPI.createRecord("' + moduleId + '", __vue_options__)\n' +
333+
' hotAPI.createRecord("' + moduleId + '", Component.options)\n' +
329334
' } else {\n' +
330335
// update
331-
' hotAPI.reload("' + moduleId + '", __vue_options__)\n' +
336+
' hotAPI.reload("' + moduleId + '", Component.options)\n' +
332337
' }\n' +
333338
'})()}\n'
334339
}
335-
// check functional components used with templates
336-
if (!isProduction) {
337-
output +=
338-
'if (__vue_options__.functional && typeof __vue_template__ !== "undefined") {' +
339-
'console.error("' +
340-
`[vue-loader] ${fileName}: functional components are not ` +
341-
'supported with templates, they should use render functions.' +
342-
'")}\n'
343-
}
344340
// final export
345341
if (options.esModule) {
346-
output += '\nexports.__esModule = true;\nexports["default"] = __vue_exports__\n'
342+
output += '\nexports.__esModule = true;\nexports["default"] = Component.exports\n'
347343
} else {
348-
output += '\nmodule.exports = __vue_exports__\n'
344+
output += '\nmodule.exports = Component.exports\n'
349345
}
350346
} else {
351347
// inject-loader support
352-
output +=
348+
output =
353349
'\n/* dependency injection */\n' +
354-
'module.exports = function (injections) {\n' +
355-
' __vue_exports__ = __vue_exports__(injections)\n' +
356-
exports +
357-
' return __vue_exports__\n' +
358-
'}'
350+
'module.exports = function (injections) {\n' + output + '\n' +
351+
'\nreturn Component.exports\n}'
359352
}
360353

361354
// done

0 commit comments

Comments
 (0)