Skip to content

Commit a4f1dcb

Browse files
Merge pull request #92 from express-vue/feature/source-string
source string contents and updates to prop contents
2 parents 02dfadb + 1735e64 commit a4f1dcb

File tree

9 files changed

+9833
-9149
lines changed

9 files changed

+9833
-9149
lines changed

package-lock.json

Lines changed: 9717 additions & 9088 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,41 +66,44 @@
6666
"repository": "express-vue/express-vue-renderer",
6767
"license": "Apache-2.0",
6868
"dependencies": {
69-
"babel-eslint": "^8.0.0",
69+
"babel-core": "^6.26.0",
7070
"babel-preset-es2015": "^6.24.1",
7171
"butternut": "^0.4.6",
7272
"camel-case": "^3.0.0",
73-
"clean-css": "^4.1.7",
73+
"clean-css": "^4.1.9",
74+
"debug": "^2.6.9",
7475
"dedupe": "^2.1.0",
75-
"deepmerge": "^1.5.1",
76-
"html-minifier": "^3.5.3",
76+
"deepmerge": "^1.5.2",
77+
"html-minifier": "^3.5.5",
7778
"lru-cache": "^4.1.1",
78-
"pug": "^2.0.0-rc.3",
79-
"require-from-string": "^1.2.1",
79+
"no-case": "^2.3.2",
80+
"pug": "^2.0.0-rc.4",
81+
"require-from-string": "^2.0.1",
8082
"string-hash": "^1.1.3",
81-
"vue": "^2.4.2",
82-
"vue-server-renderer": "^2.4.2",
83-
"vue-template-compiler": "^2.4.2",
83+
"vue": "^2.4.4",
84+
"vue-server-renderer": "^2.4.4",
85+
"vue-template-compiler": "^2.4.4",
8486
"xss": "^0.3.4"
8587
},
8688
"devDependencies": {
8789
"ava": "^0.22.0",
8890
"babel-cli": "^6.26.0",
91+
"babel-eslint": "^8.0.0",
8992
"babel-plugin-transform-flow-strip-types": "^6.22.0",
9093
"babel-preset-env": "^1.6.0",
9194
"babel-preset-flow": "^6.23.0",
9295
"codecov": "^2.3.0",
9396
"coveralls": "^3.0.0",
94-
"eslint": "^4.5.0",
97+
"eslint": "^4.7.2",
9598
"eslint-config-xo-space": "^0.16.0",
96-
"eslint-plugin-flowtype": "^2.35.0",
97-
"express": "^4.15.4",
99+
"eslint-plugin-flowtype": "^2.36.0",
100+
"express": "^4.16.0",
98101
"flow-bin": "^0.56.0",
99102
"flow-remove-types": "^1.2.1",
100103
"generate-release": "^0.14.0",
101-
"nodemon": "^1.11.0",
102-
"nsp": "^2.7.0",
103-
"nyc": "^11.1.0",
104+
"nodemon": "^1.12.1",
105+
"nsp": "^2.8.1",
106+
"nyc": "^11.2.1",
104107
"uuid": "^3.1.0"
105108
}
106109
}

src/utils/head.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class HeadUtil {
5252
const href = metaItem.href ? `href="${metaItem.href}" ` : '';
5353
const sizes = metaItem.sizes ? `sizes="${metaItem.sizes}" ` : '';
5454
this.metaTags += `<link ${rel}${type}${href}${sizes}>\n`;
55+
} else if (metaItem.srcContents) {
56+
this.metaTags += `${metaItem.srcContents}\n`;
5557
}
5658
}
5759
}

src/utils/string.js

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,104 @@
11
// @flow
22
const xss = require('xss');
33

4+
class PropClass {
5+
type: any;
6+
required: boolean;
7+
default: any;
8+
constructor(prop: any) {
9+
prop.required ? this.required = prop.required : null;
10+
prop.default ? this.default = prop.default : null;
11+
}
12+
}
413

514
function routesToString(routes: Object[]): string {
6-
let string = '';
7-
routes.forEach(script => string += scriptToString(script) + ',');
8-
return `[${string}]`;
15+
let routeString = '';
16+
routes.forEach(script => routeString += scriptToString(script) + ',');
17+
return `[${routeString}]`.replace(new RegExp(/,}/, 'g'), '}').replace(new RegExp(/,]/, 'g'), ']');
918
}
1019

1120
function routeComponentsToString(script: Object): string {
12-
let string = '';
21+
let componentString = '';
1322
for (let member in script) {
14-
string += member + ': __' + script[member] + ',';
23+
componentString += member + ': __' + script[member] + ',';
1524
}
16-
return `{${string}}`;
25+
return `{${componentString}}`;
1726
}
1827

1928
function mixinsToString(mixins: Array < Object > ): string {
20-
var string = '';
29+
var mixinString = '';
2130
for (var mixin of mixins) {
22-
string += `${scriptToString(mixin)},`;
31+
mixinString += `${scriptToString(mixin)},`;
2332
}
24-
return string;
33+
return mixinString;
2534
}
2635

27-
function scriptToString(script: Object): string {
28-
let string = '';
36+
function propsToString(props: Object): string {
37+
let propString = '';
38+
if (props[Object.keys(props)[0]].type === null) {
39+
var propsArray = Object.keys(props);
40+
propString = xss(JSON.stringify(propsArray));
41+
} else {
42+
let tempProp = {};
43+
for (var prop in props) {
44+
if (props.hasOwnProperty(prop)) {
45+
var element = new PropClass(props[prop]);
46+
tempProp[prop] = element;
47+
}
48+
}
49+
propString = scriptToString(tempProp);
50+
}
51+
return propString;
52+
}
53+
54+
function scriptToString(script: Object | any): string {
55+
let scriptString = '';
2956
for (let member in script) {
3057
switch (typeof script[member]) {
3158
case 'function':
3259
if (member === 'data') {
3360
const dataObj = xss(JSON.stringify(script[member]()));
34-
string += `${member}: function(){return ${dataObj}},`;
61+
scriptString += `${member}: function(){return ${dataObj}},`;
3562
} else {
36-
string += member + ': ' + String(script[member]) + ',';
63+
scriptString += member + ': ' + String(script[member]) + ',';
3764
}
3865
break;
3966
case 'object':
4067
if (member === 'data') {
41-
string += member + ': ' + xss(JSON.stringify(script[member])) + ',';
68+
scriptString += member + ': ' + xss(JSON.stringify(script[member])) + ',';
4269
} else if (member === 'routes' || member === 'children') {
43-
string += member + ': ' + routesToString(script[member]) + ',';
70+
scriptString += member + ': ' + routesToString(script[member]) + ',';
4471
} else if (member === 'components' && script['path'] !== undefined) { // Checks if 'components' is in a route object
45-
string += member + ': ' + routeComponentsToString(script[member]) + ',';
72+
scriptString += member + ': ' + routeComponentsToString(script[member]) + ',';
4673
} else if (member === 'mixins') {
47-
string += member + ': [' + mixinsToString(script[member]) + '],';
74+
scriptString += member + ': [' + mixinsToString(script[member]) + '],';
4875
} else if (script[member].constructor === Array) {
49-
string += member + ': ' + xss(JSON.stringify(script[member])) + ',';
76+
scriptString += member + ': ' + xss(JSON.stringify(script[member])) + ',';
5077
} else if (member === 'props') {
51-
const propsArray = Object.keys(script[member]);
52-
string += member + ': ' + xss(JSON.stringify(propsArray)) + ',';
78+
if (script[member][Object.keys(script[member])[0]].type === null) {
79+
var propsArray = Object.keys(script[member]);
80+
scriptString += member + ': ' + xss(JSON.stringify(propsArray)) + ',';
81+
} else {
82+
// scriptString += member + ': ' + scriptToString(script[member]) + ',';
83+
const propsString = propsToString(script[member]);
84+
scriptString += `${member}: ${propsString},`;
85+
}
86+
5387
} else {
54-
string += member + ': ' + scriptToString(script[member]) + ',';
88+
scriptString += member + ': ' + scriptToString(script[member]) + ',';
5589
}
5690
break;
5791
default:
5892
if (member === 'component' && script['path'] !== undefined) { // Checks if 'component' is in a route object
59-
string += member + ': __' + script[member] + ',';
93+
scriptString += member + ': __' + script[member] + ',';
6094
} else {
61-
string += member + ': ' + JSON.stringify(script[member]) + ',';
95+
scriptString += member + ': ' + JSON.stringify(script[member]) + ',';
6296
}
6397
break;
6498
}
6599
}
66-
return `{${string}}`;
100+
let finalScriptString = `{${scriptString}}`.replace(new RegExp(/,}/, 'g'), '}').replace(new RegExp(/,]/, 'g'), ']');
101+
return finalScriptString;
67102
}
68103

69104
module.exports.scriptToString = scriptToString;

tests/example/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const options = {
2727
content: 'Page Title'
2828
},
2929
{
30-
script: 'https://unpkg.com/vue@2.4.2/dist/vue.js'
30+
script: 'https://unpkg.com/vue@2.4.4/dist/vue.js'
3131
}, {
3232
name: 'viewport',
3333
content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'

tests/example/components/uuid.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
<script>
99
import inner from '../components/inner.vue';
1010
export default {
11-
props: ['uuid'],
11+
props: {
12+
uuid: {
13+
type: String,
14+
default: 'missing',
15+
required: true
16+
}
17+
},
1218
data: function () {
1319
return {}
1420
},

tests/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const vueOptions = {
2121
}
2222

2323
const exampleHead = `<head>\n<title>Page Title</title>\n<style>.red{color:#9acd32}.pink{color:pink;text-decoration:underline}.test{color:#00f}</style></head>`;
24-
const exampleScript = `<script>(function(){"use strict";var t=function(){return new Vue({mixins:[{methods:{hello:function(){console.log('Hello')}}}],data:function(){return{"title":"Express Vue","message":"Hello world","uuid":"farts"}},methods:{test:function(){console.error('test')}},components:{uuid:{props:["uuid"],data:function(){return{}},components:{inner:{data:function(){return{}},template:"<div><p class=\\"pink\\">Inner Text</p></div>"}},styles:".pink{color:pink;text-decoration:underline}",template:"<div><inner></inner><h2 class=\\"test\\">Uuid: {{uuid ? uuid : 'no uuid'}}</h2></div>"},uuid2:{props:["uuid2"],data:function(){return{}},template:"<div><h3 class=\\"red\\">Uuid2: {{uuid2 ? uuid2 : 'no uuid'}}</h3></div>"}},styles:".pink{color:pink;text-decoration:underline}.test{color:#00f}.red{color:#9acd32}",template:"<div><h1>{{title}}</h1><p>Welcome to the {{title}} demo. Click a link:</p><input v-model=\\"message\\" placeholder=\\"edit me\\"><p>{{message}}</p><uuid :uuid=\\"uuid\\"></uuid><uuid2 :uuid2=\\"uuid2\\"></uuid2><button type=\\"button\\" name=\\"button\\" v-on:click=\\"this.hello\\">Test mixin</button> <button type=\\"button\\" name=\\"button\\" v-on:click=\\"this.test\\">Test method</button></div>"})};typeof module!=='undefined'&&module.exports?(module.exports=t):(this.app=t())}).call(this),app.$mount('#app')</script>`;
24+
const exampleScript = `<script>(function(){"use strict";var t=function(){return new Vue({mixins:[{methods:{hello:function(){console.log('Hello')}}}],data:function(){return{"title":"Express Vue","message":"Hello world","uuid":"farts"}},methods:{test:function(){console.error('test')}},components:{uuid:{props:{uuid:{required:!0,default:"missing"}},data:function(){return{}},components:{inner:{data:function(){return{}},template:"<div><p class=\\"pink\\">Inner Text</p></div>"}},styles:".pink{color:pink;text-decoration:underline}",template:"<div><inner></inner><h2 class=\\"test\\">Uuid: {{uuid ? uuid : 'no uuid'}}</h2></div>"},uuid2:{props:["uuid2"],data:function(){return{}},template:"<div><h3 class=\\"red\\">Uuid2: {{uuid2 ? uuid2 : 'no uuid'}}</h3></div>"}},styles:".pink{color:pink;text-decoration:underline}.test{color:#00f}.red{color:#9acd32}",template:"<div><h1>{{title}}</h1><p>Welcome to the {{title}} demo. Click a link:</p><input v-model=\\"message\\" placeholder=\\"edit me\\"><p>{{message}}</p><uuid :uuid=\\"uuid\\"></uuid><uuid2 :uuid2=\\"uuid2\\"></uuid2><button type=\\"button\\" name=\\"button\\" v-on:click=\\"this.hello\\">Test mixin</button> <button type=\\"button\\" name=\\"button\\" v-on:click=\\"this.test\\">Test method</button></div>"})};typeof module!=='undefined'&&module.exports?(module.exports=t):(this.app=t())}).call(this),app.$mount('#app')</script>`;
2525

2626
test('renders App object', t => {
2727
const renderer = new ExpressVueRenderer(options);

tests/utils/head.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ const newHead = {
4646
type: 'image/png',
4747
href: '/assets/favicons/favicon-32x32.png',
4848
sizes: '32x32'
49+
},
50+
{
51+
srcContents: '<script>var foo = false;</script>'
4952
}
5053
],
5154
structuredData: {
@@ -57,13 +60,14 @@ const newHead = {
5760
const newMetaString = new HeadUtils(newHead).toString();
5861

5962
//New Tests
60-
const newStringIsCorrect = '<head>\n<title>It was a Pleasure</title>\n<meta name="application-name" content="Name of my application"/>\n<meta name="description" content="A description of the page"/>\n<meta name="twitter:title" content="Content Title"/>\n<meta property="fb:app_id" content="123456789"/>\n<meta property="og:title" content="Content Title"/>\n<script src="/assets/scripts/hammer.min.js" charset="utf-8"></script>\n<script src="/assets/scripts/vue-touch.min.js" charset="utf-8"></script>\n<link rel="stylesheet" type="text/css" href="/assets/rendered/style.css">\n<link rel="stylesheet" type="text/css" href="/assets/rendered/style.css">\n<link rel="icon" type="image/png" href="/assets/favicons/favicon-32x32.png" sizes="32x32" >\n<script type="application/ld+json">\n{"foo":true}\n</script>\n</head>'
63+
const newStringIsCorrect = '<head>\n<title>It was a Pleasure</title>\n<meta name="application-name" content="Name of my application"/>\n<meta name="description" content="A description of the page"/>\n<meta name="twitter:title" content="Content Title"/>\n<meta property="fb:app_id" content="123456789"/>\n<meta property="og:title" content="Content Title"/>\n<script src="/assets/scripts/hammer.min.js" charset="utf-8"></script>\n<script src="/assets/scripts/vue-touch.min.js" charset="utf-8"></script>\n<link rel="stylesheet" type="text/css" href="/assets/rendered/style.css">\n<link rel="stylesheet" type="text/css" href="/assets/rendered/style.css">\n<link rel="icon" type="image/png" href="/assets/favicons/favicon-32x32.png" sizes="32x32" >\n<script>var foo = false;</script>\n<script type="application/ld+json">\n{"foo":true}\n</script>\n</head>'
6164
const newHasTitle = newMetaString.includes('<title>It was a Pleasure</title>');
6265
const newHasMetaName = newMetaString.includes(`<meta name="application-name" content="Name of my application"/>`);
6366
const newHasMetaProperty = newMetaString.includes(`<meta property="og:title" content="Content Title"/>`);
6467
const newHasScript = newMetaString.includes(`<script src="/assets/scripts/hammer.min.js" charset="utf-8">`)
6568
const newHasStyle = newMetaString.includes(`<link rel="stylesheet" type="text/css" href="/assets/rendered/style.css">`)
6669
const newHasStructured = newMetaString.includes(`<script type="application/ld+json">\n{"foo":true}\n</script>`);
70+
const newHasSrcContents = newMetaString.includes(`<script>var foo = false;</script>`);
6771

6872
test('Head is correct', t => {
6973
t.is(newMetaString, newStringIsCorrect);
@@ -92,3 +96,7 @@ test('Head has style 🎷', t => {
9296
test('Head has Structured Data', t => {
9397
t.is(newHasStructured, true);
9498
});
99+
100+
test('Head has srcContents', t => {
101+
t.is(newHasSrcContents, true);
102+
});

tests/utils/string.js

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,24 @@ const exampleMixin = {
3939
}
4040
};
4141

42-
const string = scriptToString(object);
42+
const testString = scriptToString(object);
43+
const testString2 = scriptToString(object2);
4344
const mixins = mixinsToString([exampleMixin]);
4445

4546
//Tests
46-
const hasString = string.includes(`string: "foo"`);
47-
const hasNumber = string.includes(`number: 42`);
48-
const hasArray = string.includes(`array: ["one","two","three"]`);
49-
const hasObject = string.includes(`object: {dog: true,cat: false,}`);
50-
const hasBoolean = string.includes(`boolean: true`);
51-
const hasFunction = string.includes(`function: function _function() {
47+
const hasString = testString.includes(`string: "foo"`);
48+
const hasNumber = testString.includes(`number: 42`);
49+
const hasArray = testString.includes(`array: ["one","two","three"]`);
50+
const hasObject = testString.includes(`object: {dog: true,cat: false}`);
51+
const hasBoolean = testString.includes(`boolean: true`);
52+
const hasFunction = testString.includes(`function: function _function() {
5253
return 'baz';
5354
}`);
54-
const hasDataFunc = string.includes(`data: function(){return {"foo":"bar"}}`);
55-
const hasDataObj = scriptToString(object2).includes(`data: {"foo":"bar"}`);
56-
const hasFinal = string === `{string: "foo",function: function _function() {
55+
const hasDataFunc = testString.includes(`data: function(){return {"foo":"bar"}}`);
56+
const hasDataObj = testString2.includes(`data: {"foo":"bar"}`);
57+
const hasFinal = testString === `{string: "foo",function: function _function() {
5758
return 'baz';
58-
},data: function(){return {"foo":"bar"}},array: ["one","two","three"],object: {dog: true,cat: false,},number: 42,boolean: true,}`;
59+
},data: function(){return {"foo":"bar"}},array: ["one","two","three"],object: {dog: true,cat: false},number: 42,boolean: true}`;
5960

6061
test('Has a String', t => {
6162
t.is(hasString, true);
@@ -96,7 +97,7 @@ test('Has a Fully formed String', t => {
9697
test('Mixins', t => {
9798
t.is(mixins, `{methods: {hello: function hello() {
9899
console.log('Hello');
99-
},},},`);
100+
}}},`);
100101
});
101102

102103
// Test routesToString()
@@ -110,7 +111,7 @@ const routes = [
110111
{ name: 'child2', path: 'child2', components: { childview: 'mychildcomponent2', }, },
111112
],
112113
},
113-
{
114+
{
114115
path: '/route3', component: 'mycomponent3',
115116
beforeEnter: function (to, from, next) {
116117
console.log(to, from);
@@ -122,12 +123,12 @@ const routes = [
122123
const routesString = routesToString(routes);
123124
const hasComponent = routesString.includes(`component: __mycomponent3`);
124125
const hasChildComponent = routesString.includes(`component: __mychildcomponent1`);
125-
const hasNamedViewsComponents = routesString.includes(`components: {mainview: __mycomponent1,}`);
126-
const hasNamedViewsChildComponents = routesString.includes(`components: {childview: __mychildcomponent2,}`);
127-
const expectedRoutesString = `[{name: "route1",path: "/route1",alias: ["asd","bsd","csd"],meta: {something: "another",},components: {mainview: __mycomponent1,},},{path: "/route2",components: {mainview: __mycomponent2,},children: [{name: "child1",path: "child1",component: __mychildcomponent1,alias: ["xsd","ysd","zsd"],},{name: "child2",path: "child2",components: {childview: __mychildcomponent2,},},],},{path: "/route3",component: __mycomponent3,beforeEnter: function beforeEnter(to, from, next) {
126+
const hasNamedViewsComponents = routesString.includes(`components: {mainview: __mycomponent1}`);
127+
const hasNamedViewsChildComponents = routesString.includes(`components: {childview: __mychildcomponent2}`);
128+
const expectedRoutesString = `[{name: "route1",path: "/route1",alias: ["asd","bsd","csd"],meta: {something: "another"},components: {mainview: __mycomponent1}},{path: "/route2",components: {mainview: __mycomponent2},children: [{name: "child1",path: "child1",component: __mychildcomponent1,alias: ["xsd","ysd","zsd"]},{name: "child2",path: "child2",components: {childview: __mychildcomponent2}}]},{path: "/route3",component: __mycomponent3,beforeEnter: function beforeEnter(to, from, next) {
128129
console.log(to, from);
129130
next();
130-
},},]`;
131+
}}]`;
131132

132133
test('Has component', t => t.is(hasComponent, true));
133134
test('Has child component', t => t.is(hasChildComponent, true));
@@ -159,5 +160,5 @@ const regularScript = {
159160

160161
const regularScriptString = scriptToString(regularScript);
161162
test('Regular script components', (t) => {
162-
t.is(regularScriptString.includes(`{component: "component",components: {prop0: "prop0",prop1: "prop1",prop2: "prop2",},}`), true);
163+
t.is(regularScriptString.includes(`{component: "component",components: {prop0: "prop0",prop1: "prop1",prop2: "prop2"}}`), true);
163164
});

0 commit comments

Comments
 (0)