-
Notifications
You must be signed in to change notification settings - Fork 399
Expand file tree
/
Copy pathengine_react.js
More file actions
236 lines (203 loc) · 6.35 KB
/
engine_react.js
File metadata and controls
236 lines (203 loc) · 6.35 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
* react pattern engine for patternlab-node - v0.1.0 - 2016
*
* Geoffrey Pursell, Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
*
* Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice.
*
*/
'use strict';
const fs = require('fs');
const path = require('path');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const Babel = require('babel-core');
const Handlebars = require('handlebars');
const beautify = require('js-beautify');
const cheerio = require('cheerio');
const _require = require;
var errorStyling = `
<style>
.plError {
background: linear-gradient(to bottom, #f1f1f1 0%,#ffffff 60%);
color: #444;
padding: 30px;
}
.plError h1 {
font-size: 16pt;
color: #733;
background: #fcfcfc;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
padding: 17px 30px;
margin: -30px -30px 0 -30px;
}
.plError dt { font-weight: bold; }
</style>
`;
// This holds the config from from core. The core has to call
// usePatternLabConfig() at load time for this to be populated.
let patternLabConfig = {};
let enableRuntimeCode = true;
const outputTemplate = Handlebars.compile(
fs.readFileSync(path.join(__dirname, './outputTemplate.hbs'), 'utf8')
);
let registeredComponents = {
byPatternPartial: {},
};
function moduleCodeString(pattern) {
return pattern.template || pattern.extendedTemplate;
}
function babelTransform(pattern) {
let transpiledModule = Babel.transform(moduleCodeString(pattern), {
presets: [require('babel-preset-react')],
plugins: [require('babel-plugin-transform-es2015-modules-commonjs')],
});
// eval() module code in this little scope that injects our
// custom wrap of require();
((require) => {
/* eslint-disable no-eval */
transpiledModule = eval(transpiledModule.code);
})(customRequire);
return transpiledModule;
}
function customRequire(id) {
const registeredPattern = registeredComponents.byPatternPartial[id];
if (registeredPattern) {
return babelTransform(registeredPattern);
} else {
return _require(id);
}
}
var engine_react = {
engine: React,
engineName: 'react',
engineFileExtension: ['.jsx', '.js'],
// hell no
expandPartials: false,
// regexes, stored here so they're only compiled once
findPartialsRE: /import .* from '[^']+'/g,
findPartialsWithStyleModifiersRE: null,
findPartialsWithPatternParametersRE: null,
findListItemsRE: null,
findPartialRE: /from '([^']+)'/,
// render it
renderPattern(pattern, data, partials) {
let renderedHTML = '';
const transpiledModule = babelTransform(pattern);
const staticMarkup = ReactDOMServer.renderToStaticMarkup(
React.createFactory(transpiledModule)(data)
);
renderedHTML = outputTemplate({
htmlOutput: staticMarkup,
});
return Promise.resolve(renderedHTML).catch((e) => {
var errorMessage = `Error rendering React pattern "${
pattern.patternName
}" (${pattern.relPath}): [${e.toString()}]`;
console.log(errorMessage);
renderedHTML = `${errorStyling} <div class="plError">
<h1>Error rendering React pattern "${pattern.patternName}"</h1>
<dl>
<dt>Message</dt><dd>${e.toString()}</dd>
<dt>Partial name</dt><dd>${pattern.patternName}</dd>
<dt>Template path</dt><dd>${pattern.relPath}</dd>
</dl>
</div>
`;
});
},
registerPartial(pattern) {
// add to registry
registeredComponents.byPatternPartial[pattern.patternPartial] = pattern;
},
/**
* Find regex matches within both pattern strings and pattern objects.
*
* @param {string|object} pattern Either a string or a pattern object.
* @param {object} regex A JavaScript RegExp object.
* @returns {array|null} An array if a match is found, null if not.
*/
patternMatcher(pattern, regex) {
var matches;
if (typeof pattern === 'string') {
matches = pattern.match(regex);
} else if (
typeof pattern === 'object' &&
typeof pattern.template === 'string'
) {
matches = pattern.template.match(regex);
}
return matches;
},
// find and return any `import X from 'template-name'` within pattern
findPartials(pattern) {
const self = this;
const matches = pattern.template.match(this.findPartialsRE);
if (!matches) {
return [];
}
// Remove unregistered imports from the matches
matches.map((m) => {
const key = self.findPartial(m);
if (!registeredComponents.byPatternPartial[key]) {
const i = matches.indexOf(m);
if (i > -1) {
matches.splice(i, 1);
}
}
});
return matches;
},
// returns any patterns that match {{> value(foo:'bar') }} or {{>
// value:mod(foo:'bar') }} within the pattern
findPartialsWithPatternParameters(pattern) {
return [];
},
findListItems(pattern) {
return [];
},
// given a pattern, and a partial string, tease out the "pattern key" and
// return it.
findPartial(partialString) {
let partial = partialString.match(this.findPartialRE)[1];
return partial;
},
rawTemplateCodeFormatter(unformattedString) {
return beautify(unformattedString, { e4x: true, indent_size: 2 });
},
renderedCodeFormatter(unformattedString) {
return unformattedString;
},
markupOnlyCodeFormatter(unformattedString, pattern) {
// const $ = cheerio.load(unformattedString);
// return beautify.html($('.reactPatternContainer').html(), {indent_size: 2});
return unformattedString;
},
/**
* Add custom output files to the pattern output
* @param {object} patternlab - the global state object
* @returns {(object|object[])} - an object or array of objects,
* each with two properties: path, and content
*/
addOutputFiles(paths, patternlab) {
return [];
},
/**
* Accept a Pattern Lab config object from the core and put it in
* this module's closure scope so we can configure engine behavior.
*
* @param {object} config - the global config object from core
*/
usePatternLabConfig: function (config) {
patternLabConfig = config;
try {
enableRuntimeCode = patternLabConfig.engines.react.enableRuntimeCode;
} catch (error) {
console.log(
'You’re missing the engines.react.enableRuntimeCode setting in your config file.'
);
}
},
};
module.exports = engine_react;