Skip to content

Commit dfcfa00

Browse files
Merge branch 'develop' into feature/webpack
2 parents d0d7e4f + 747db3c commit dfcfa00

File tree

10 files changed

+12287
-13809
lines changed

10 files changed

+12287
-13809
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,10 @@ build/Release
2727
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
2828
node_modules
2929
\.nyc_output/
30+
.expressvue
31+
*.clinic-doctor
32+
*.clinic-flame
33+
*.clinic-doctor.html
34+
*.clinic-flame.html
3035

3136
.expressvue

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
language: node_js
22
node_js:
3-
- v9
43
- v8
54
- v7
65
- v6

.vscode/launch.json

+10-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
// Hover to view descriptions of existing attributes.
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
6-
"configurations": [{
7-
"type": "node",
8-
"request": "attach",
9-
"name": "Attach",
10-
"restart": true,
11-
"protocol": "inspector"
12-
}]
6+
"configurations": [
7+
{
8+
"type": "node",
9+
"request": "attach",
10+
"name": "Attach",
11+
"restart": true,
12+
"protocol": "inspector",
13+
"port": 9230
14+
}
15+
]
1316
}

lib/renderer/process-style.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// @ts-check
22
const fs = require("fs");
33
const CleanCss = require("clean-css");
4+
const autoprefixer = require("autoprefixer");
45
const cssMinify = new CleanCss();
6+
57
//@ts-ignore
68
const compilers = require("vueify/lib/compilers");
9+
// @ts-ignore
10+
const rewriteStyle = require("vueify/lib/style-rewriter");
711
const path = require("path");
812
//@ts-ignore
913
const chalk = require("chalk");
@@ -21,8 +25,11 @@ function processStyle(part, filePath, id, parts) {
2125
return new Promise((resolve, reject) => {
2226
compileAsPromise("style", style, part.lang, filePath)
2327
.then(compiledStyle => {
24-
const minified = minifyStyles(compiledStyle);
25-
resolve(minified);
28+
// @ts-ignore
29+
return rewriteStyle(id, compiledStyle, part.scoped, { postcss: [autoprefixer()] }).then(function(res) {
30+
const minified = minifyStyles(res);
31+
resolve(minified);
32+
});
2633
})
2734
.catch(reject);
2835
});
@@ -79,6 +86,10 @@ function loadSrc(src, filePath) {
7986
function compileAsPromise(type, source, lang, filePath) {
8087
var compile = compilers[lang];
8188
if (compile) {
89+
compile.options = compile.options || {};
90+
compile.emit = compile.emit || function() {
91+
return true;
92+
};
8293
return new Promise(function(resolve, reject) {
8394
/**
8495
* @param {Object} err

lib/renderer/renderer.js

+78-42
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const requireFromString = require("require-from-string");
66
const vueCompiler = require("vue-template-compiler");
77
// @ts-ignore
88
const vueify = require("vueify");
9+
vueify.compiler.loadConfig();
910
const vueServerRenderer = require("vue-server-renderer");
1011
const Vue = require("vue");
1112
const uglify = require("uglify-js");
@@ -60,10 +61,12 @@ class Renderer {
6061
max: 500,
6162
maxAge: 1000 * 60 * 60,
6263
};
64+
this.renderer = vueServerRenderer.createRenderer();
6365
this.lruCache = LRU(this.cacheOptions);
6466
this.internalCache = new Utils.Cache();
6567
this.head = options.head || {};
6668
this.data = options.data || {};
69+
this.propsData = options.propsData || {};
6770
this.template = options.template || {};
6871
const version = Utils.VueVersion(options.vueVersion);
6972
if (version.enabled) {
@@ -93,15 +96,18 @@ class Renderer {
9396
console.error(`Error Precaching \nfilePath -> ${filePath}\n-------\n${error}`);
9497
});
9598
}
96-
});
99+
});
97100
}
98101

99102
this.nodeModulesPath = Utils.FindNodeModules(this.rootPath);
100103
this.jsToStringOptions = {
101104
functions: [
102105
{
103106
name: "data",
104-
// @ts-ignore
107+
/**
108+
* @param {function} script
109+
* @return {string|void}
110+
*/
105111
toString: function(script) {
106112
const func = `module.exports = function data() { return ${jsToString(script())}; };`;
107113
const required = requireFromString(func);
@@ -123,6 +129,14 @@ class Renderer {
123129
return mergedData;
124130
};
125131
}
132+
/**
133+
* @param {Object} oldPropsData
134+
* @param {Object} newPropsData
135+
* @returns {Function}
136+
*/
137+
FixPropsData(oldPropsData, newPropsData) {
138+
return Object.assign({}, oldPropsData, this.propsData, newPropsData);
139+
}
126140
/**
127141
* @param {string} componentFile
128142
* @param {string} filePath
@@ -144,25 +158,27 @@ class Renderer {
144158
let reg;
145159
const isES6 = compiledObject.compiled.includes("use strict");
146160
if (isES6) {
147-
reg = /(?:"use strict";)(.*)(?:module.exports={|exports.default={)(.*)(?:}}\(\);?)(?:.*)(?:__vue__options__.render=function\(\){)(.*)(?:},?;?__vue__options__.staticRenderFns=\[)(.*)(?:\])/gm;
161+
reg = /(?:"use strict";)(.*)(?:module.exports={|exports.default={)(.*)(?:}}\(\);?)(?:.*)(?:__vue__options__.render=function\(\){)(.*)(?:},?;?__vue__options__.staticRenderFns=\[)(.*)(?:\])((?:,?;?__vue__options__._scopeId=")(.*)(?:"))?/gm;
148162
} else {
149-
reg = /(?!"use strict";)(.*)(?:module.exports={|exports.default={)(.*)(?:}\(?\)?;?)(?:.*)(?:__vue__options__.render=function\(\){)(.*)(?:},?;?__vue__options__.staticRenderFns=\[)(.*)(?:\])/gm;
163+
reg = /(?!"use strict";)(.*)(?:module.exports={|exports.default={)(.*)(?:}\(?\)?;?)(?:.*)(?:__vue__options__.render=function\(\){)(.*)(?:},?;?__vue__options__.staticRenderFns=\[)(.*)(?:\])((?:,?;?__vue__options__._scopeId=")(.*)(?:"))?/gm;
150164
}
151165

152166
let vueComponent = "";
153167
let imports = "";
154168
let moduleExports = "";
155169
let renderFunctionContents = "";
156170
let staticRenderFns = "";
171+
let scopeId = "";
157172

158-
let {code} = uglify.minify(compiledObject.compiled, {mangle: false});
173+
let { code } = uglify.minify(compiledObject.compiled, { mangle: false });
159174

160175
const matches = reg.exec(code);
161176
if (matches && matches.length > 0) {
162177
const importMatch = matches[1];
163178
const exportMatch = matches[2];
164179
const renderMatch = matches[3];
165180
const staticMatch = matches[4];
181+
const scopeMatch = matches[6];
166182

167183
if (importMatch && importMatch !== "") {
168184
imports = importMatch;
@@ -180,12 +196,16 @@ class Renderer {
180196
staticRenderFns = `,staticRenderFns: [${staticMatch}]`;
181197
}
182198

199+
if (scopeMatch && scopeMatch !== "") {
200+
scopeId = `,_scopeId: "${scopeMatch}"`;
201+
}
202+
183203
}
184204

185205
if (imports === "") {
186-
vueComponent = `{${moduleExports}${renderFunctionContents}${staticRenderFns}}`;
206+
vueComponent = `{${moduleExports}${renderFunctionContents}${staticRenderFns}${scopeId}}`;
187207
} else {
188-
vueComponent = `function(){${imports}return {${moduleExports}${renderFunctionContents}${staticRenderFns}}}()`;
208+
vueComponent = `function(){${imports}return {${moduleExports}${renderFunctionContents}${staticRenderFns}${scopeId}}}()`;
189209
}
190210
if (vueComponent.includes("Object.defineProperty(exports,\"__esModule\",{value:!0}),return")) {
191211
vueComponent = vueComponent.replace("Object.defineProperty(exports,\"__esModule\",{value:!0}),return", "Object.defineProperty(exports,\"__esModule\",{value:!0});return");
@@ -325,7 +345,7 @@ class Renderer {
325345
filePath: filePath,
326346
};
327347

328-
const stylesArray = vueCompiler.parseComponent(content, {pad: true}).styles;
348+
const stylesArray = vueCompiler.parseComponent(content, { pad: true }).styles;
329349
// @ts-ignore
330350
const compiler = vueify.compiler;
331351
compiler.compile(content, filePath,
@@ -334,31 +354,42 @@ class Renderer {
334354
* @param {string} stringFile
335355
*/
336356
function(error, stringFile) {
337-
if (error) {
338-
reject(error);
339-
}
340-
stringFile = vm.FindAndReplaceScripts(stringFile, filePath);
341-
if (stylesArray.length > 0) {
342-
processStyle(stylesArray[0], filePath, "", resolvedParts)
343-
.then(processedStyle => {
344-
compiled.compiled = stringFile;
345-
compiled.style += processedStyle;
346-
resolve(compiled);
347-
})
348-
.catch(reject);
349-
} else {
350-
compiled.compiled = stringFile;
351-
resolve(compiled);
352-
}
353-
});
357+
if (error) {
358+
reject(error);
359+
}
360+
stringFile = vm.FindAndReplaceScripts(stringFile, filePath);
361+
let id = "";
362+
stringFile.replace(/__vue__options__\._scopeId = "(.*?)"/gm,
363+
/**
364+
* @param {string} match
365+
* @param {string} p1
366+
* @return {string}
367+
*/
368+
function(match, p1) {
369+
id = p1;
370+
return "";
371+
});
372+
if (stylesArray.length > 0) {
373+
processStyle(stylesArray[0], filePath, id, resolvedParts)
374+
.then(processedStyle => {
375+
compiled.compiled = stringFile;
376+
compiled.style += processedStyle;
377+
resolve(compiled);
378+
})
379+
.catch(reject);
380+
} else {
381+
compiled.compiled = stringFile;
382+
resolve(compiled);
383+
}
384+
});
354385
});
355386
});
356387
}
357388
/**
358389
*
359390
* @param {CompiledObjectType} compiledObject
360391
* @param {string} filePath
361-
* @returns {Promise<{data: object}>}
392+
* @returns {Promise<{data: object, propsData: object, props: object}>}
362393
*/
363394
MakeBundle(compiledObject, filePath) {
364395
return new Promise((resolve, reject) => {
@@ -374,18 +405,27 @@ class Renderer {
374405
*
375406
* @param {string} filePath
376407
* @param {Object} data
408+
* @param {{data: Object, propsData: Object} | Object} vueOptions
377409
* @returns {Promise<{vue: object, css: string, script: string}>}
378410
*/
379-
MakeVueClass(filePath, data) {
411+
MakeVueClass(filePath, data, vueOptions = {}) {
380412
return new Promise((resolve, reject) => {
381413
let cachedBundle = this.internalCache.get(filePath);
382414
if (cachedBundle) {
415+
const cachedData = Object.assign({}, cachedBundle.data());
416+
const newbundle = Object.assign({}, cachedBundle.bundle);
417+
383418
if (cachedBundle.bundle.data && typeof cachedBundle.bundle.data === "function") {
384-
cachedBundle.bundle.data = this.FixData(cachedBundle.bundle.data(), data);
419+
newbundle.data = this.FixData(cachedData, data);
385420
}
421+
if (vueOptions.propsData && (cachedBundle.bundle.propsData || cachedBundle.bundle.props)) {
422+
newbundle.propsData = this.FixPropsData(cachedBundle.bundle.propsData || {}, vueOptions.propsData);
423+
}
424+
386425
// @ts-ignore
387-
const vue = new Vue(cachedBundle.bundle);
388-
const cleanBundle = this._deleteCtor(cachedBundle.bundle);
426+
const vue = new Vue(newbundle);
427+
// vue._data = newbundle.data();
428+
const cleanBundle = this._deleteCtor(newbundle);
389429
const object = {
390430
vue: vue,
391431
css: cachedBundle.style,
@@ -398,11 +438,15 @@ class Renderer {
398438
.then(compiled => {
399439
this.MakeBundle(compiled, filePath)
400440
.then(bundle => {
401-
this.internalCache.set(filePath, {bundle: bundle, style: compiled.style});
441+
this.internalCache.set(filePath, { bundle: bundle, style: compiled.style, data: bundle.data });
402442
//Insert Data
403443
if (bundle.data && typeof bundle.data === "function") {
404444
bundle.data = this.FixData(bundle.data(), data);
405445
}
446+
//Insert propsData
447+
if (vueOptions.propsData && (bundle.propsData || bundle.props)) {
448+
bundle.propsData = this.FixPropsData(bundle.propsData || {}, vueOptions.propsData);
449+
}
406450

407451
//Create Vue Class
408452
// @ts-ignore
@@ -479,12 +523,8 @@ class Renderer {
479523
return new Promise((resolve, reject) => {
480524
this.FindFile(vueFile)
481525
.then(filePath => {
482-
this.MakeVueClass(filePath, data)
526+
this.MakeVueClass(filePath, data, vueOptions)
483527
.then(vueClass => {
484-
const rendererOptions = {
485-
cache: this.lruCache,
486-
};
487-
this.renderer = vueServerRenderer.createRenderer(rendererOptions);
488528
const mergedHeadObject = Utils.MergeHead(vueOptions.head, this.head);
489529
const template = Object.assign({}, this.template, vueOptions.template);
490530
//Init Renderer
@@ -511,19 +551,15 @@ class Renderer {
511551
* renderToStream returns a stream from res.renderVue to the client
512552
* @param {string} vueFile - full path to .vue component
513553
* @param {Object} data - data to be inserted when generating vue class
514-
* @param {VueOptionsType} vueOptions - vue options to be used when generating head
554+
* @param {(VueOptionsType|object)} vueOptions - vue options to be used when generating head
515555
* @return {Promise<NodeJS.ReadableStream>}
516556
*/
517557
RenderToStream(vueFile, data, vueOptions) {
518558
return new Promise((resolve, reject) => {
519559
this.FindFile(vueFile)
520560
.then(filePath => {
521-
this.MakeVueClass(filePath, data)
561+
this.MakeVueClass(filePath, data, vueOptions)
522562
.then(vueClass => {
523-
const rendererOptions = {
524-
cache: this.lruCache,
525-
};
526-
this.renderer = vueServerRenderer.createRenderer(rendererOptions);
527563
const mergedHeadObject = Utils.MergeHead(vueOptions.head, this.head);
528564
const headString = Utils.BuildHead(mergedHeadObject);
529565
const template = Object.assign({}, this.template, vueOptions.template);

0 commit comments

Comments
 (0)