Skip to content

Commit

Permalink
Merge pull request #2 from bramsmulders/module-loader
Browse files Browse the repository at this point in the history
Module loader
  • Loading branch information
geoffp committed Feb 23, 2018
1 parent 4dbca68 commit 89f5a67
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 100 deletions.
24 changes: 13 additions & 11 deletions packages/patternengine-node-react/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
# The React engine for Pattern Lab / Node
This is the **very preliminary barely worth mentioning** React engine for Patternlab/Node. It's more or less a proof of concept.
This is the **very preliminary** React engine for Patternlab/Node.

## Status
You can author standalone React components that include only the main React module, which I know isn't much yet. We're still working out how React components will resolve and load the modules they depend on, including other patterns. We believe this is tricky, but doable.
You can author standalone React components that include only the main React module, which I know isn't much yet.
We're still working out how React components will resolve and load the modules they depend on. We believe this is tricky, but doable.

## Supported features
Kind of nothing works yet. **Very early in development.**
## Supported Pattern Lab

- [ ] [Includes](http://patternlab.io/docs/pattern-including.html)
- [ ] Lineage
- [ ] [Hidden Patterns](http://patternlab.io/docs/pattern-hiding.html)
- [ ] [Pseudo-Patterns](http://patternlab.io/docs/pattern-pseudo-patterns.html)
- [ ] [Pattern States](http://patternlab.io/docs/pattern-states.html)
- [ ] [Pattern Parameters](http://patternlab.io/docs/pattern-parameters.html)
- [ ] [Style Modifiers](http://patternlab.io/docs/pattern-stylemodifier.html)
- [x] [Includes](http://patternlab.io/docs/pattern-including.html)
- [x] Data inheritance: This can be achieved by combining react `props` & `defaultProps`
- [x] [Hidden Patterns](http://patternlab.io/docs/pattern-hiding.html)
- [x] [Pseudo-Patterns](http://patternlab.io/docs/pattern-pseudo-patterns.html)
- [x] [Pattern States](http://patternlab.io/docs/pattern-states.html#node)
- [x] [Pattern Parameters](http://patternlab.io/docs/pattern-parameters.html): With react props
- [x] [Style Modifiers](http://patternlab.io/docs/pattern-stylemodifier.html): With react props
- [x] Lineage
- [x] Incremental builds

## Usage
* `*.jsx` files are detected as patterns.
Expand Down
155 changes: 88 additions & 67 deletions packages/patternengine-node-react/lib/engine_react.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
* Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice.
*
*/


"use strict";
'use strict';

const fs = require('fs');
const path = require('path');
Expand All @@ -19,13 +17,14 @@ const Babel = require('babel-core');
const Hogan = require('hogan.js');
const beautify = require('js-beautify');
const cheerio = require('cheerio');
const webpack = require('webpack');
const _require = require;

// 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 = Hogan.compile(
fs.readFileSync(
path.join(__dirname, './outputTemplate.mustache'),
Expand All @@ -34,14 +33,39 @@ const outputTemplate = Hogan.compile(
);

let registeredComponents = {
byModuleName: {},
byGroup: {}
byPatternPartial: {}
};

function moduleCodeString(pattern) {
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',
Expand All @@ -51,73 +75,45 @@ var engine_react = {
expandPartials: false,

// regexes, stored here so they're only compiled once
findPartialsRE: null,
findPartialsRE: /import .* from '[^']+'/g,
findPartialsWithStyleModifiersRE: null,
findPartialsWithPatternParametersRE: null,
findListItemsRE: null,
findPartialRE: null,
findPartialRE: /from '([^']+)'/,

// render it
renderPattern(pattern, data, partials) {
renderPattern (pattern, data, partials) {
try {
let runtimeCode = [];

// the all-important Babel transform, runtime version
runtimeCode.push(Babel.transform(moduleCodeString(pattern), {
presets: [ require('babel-preset-react') ],
plugins: [[require('babel-plugin-transform-es2015-modules-umd'), {
globals: {
"react": "React"
}
}]]
}).code);
const transpiledModule = babelTransform(pattern);

const staticMarkup = ReactDOMServer.renderToStaticMarkup(
React.createFactory(transpiledModule)(data)
);

return outputTemplate.render({
patternPartial: pattern.patternPartial,
json: JSON.stringify(data),
htmlOutput: ReactDOMServer.renderToStaticMarkup(
React.createFactory(pattern.module)(data)
),
runtimeCode: runtimeCode.join(';')
htmlOutput: staticMarkup
});
}
catch (e) {
console.log("Error rendering React pattern.", e);
return "";
console.log('Error rendering React pattern.', e);
return '';
}
},

registerPartial(pattern) {
const customRequire = function (id) {
const registeredPattern = registeredComponents.byModuleName[id];
return registeredPattern ? registeredPattern.module : _require(id);
};

// the all-important Babel transform, server-side version
const compiledModule = Babel.transform(moduleCodeString(pattern), {
presets: [ require('babel-preset-react') ],
plugins: [ require('babel-plugin-transform-es2015-modules-commonjs') ]
});

registerPartial (pattern) {
// add to registry
registeredComponents.byModuleName[pattern.patternBaseName] = pattern;

// eval() module code in this little scope that injects our
// custom wrap of require();
((require) => {
/* eslint-disable no-eval */
pattern.module = eval(compiledModule.code);
})(customRequire);
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) {
patternMatcher (pattern, regex) {
var matches;
if (typeof pattern === 'string') {
matches = pattern.match(regex);
Expand All @@ -127,40 +123,60 @@ var engine_react = {
return matches;
},

// find and return any {{> template-name }} within pattern
findPartials(pattern) {
return [];
// 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;
},
findPartialsWithStyleModifiers(pattern) {

findPartialsWithStyleModifiers (pattern) {
return [];
},

// returns any patterns that match {{> value(foo:"bar") }} or {{>
// value:mod(foo:"bar") }} within the pattern
findPartialsWithPatternParameters(pattern) {
// returns any patterns that match {{> value(foo:'bar') }} or {{>
// value:mod(foo:'bar') }} within the pattern
findPartialsWithPatternParameters (pattern) {
return [];
},
findListItems(pattern) {
findListItems (pattern) {
return [];
},

// given a pattern, and a partial string, tease out the "pattern key" and
// return it.
findPartial(partialString) {
return [];
findPartial (partialString) {
let partial = partialString.match(this.findPartialRE)[1];
return partial;
},

rawTemplateCodeFormatter(unformattedString) {
rawTemplateCodeFormatter (unformattedString) {
return beautify(unformattedString, {e4x: true, indent_size: 2});
},

renderedCodeFormatter(unformattedString) {
renderedCodeFormatter (unformattedString) {
return unformattedString;
},

markupOnlyCodeFormatter(unformattedString, pattern) {
const $ = cheerio.load(unformattedString);
return beautify.html($('.reactPatternContainer').html(), {indent_size: 2});
markupOnlyCodeFormatter (unformattedString, pattern) {
// const $ = cheerio.load(unformattedString);
// return beautify.html($('.reactPatternContainer').html(), {indent_size: 2});
return unformattedString;
},

/**
Expand All @@ -169,8 +185,7 @@ var engine_react = {
* @returns {(object|object[])} - an object or array of objects,
* each with two properties: path, and content
*/
addOutputFiles(paths, patternlab) {

addOutputFiles (paths, patternlab) {
return [];
},

Expand All @@ -183,6 +198,12 @@ var engine_react = {
*/
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.');
}
}

};
Expand Down
19 changes: 1 addition & 18 deletions packages/patternengine-node-react/lib/outputTemplate.mustache
Original file line number Diff line number Diff line change
@@ -1,18 +1 @@
<div class="reactPatternContainer" id="reactPatternContainer--{{{patternPartial}}}">
{{{htmlOutput}}}
</div>

<!-- pattern JSON (React props) -->
<script data-type="patternJSON" id="patternJSON--{{{patternPartial}}}" type="application/json">
{{{json}}}
</script>

<!-- runtime React output -->
<script>
{{{runtimeCode}}};
<!-- runtime rendering -->
var component = unknown.default;
var patternJSON = document.getElementById('patternJSON--{{{patternPartial}}}').textContent;
ReactDOM.render(React.createElement(component, JSON.parse(patternJSON)), document.getElementById('reactPatternContainer--{{{patternPartial}}}'));
</script>
{{{htmlOutput}}}
5 changes: 1 addition & 4 deletions packages/patternengine-node-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@
"main": "lib/engine_react.js",
"dependencies": {
"babel-core": "^6.17.0",
"babel-plugin-module-alias": "^1.6.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.16.0",
"babel-plugin-transform-es2015-modules-umd": "^6.12.0",
"babel-preset-react": "^6.16.0",
"cheerio": "^0.22.0",
"hogan": "^1.0.2",
"js-beautify": "^1.6.4",
"react": "^15.3.2",
"react-dom": "^15.3.2",
"webpack": "^1.13.2"
"react-dom": "^15.3.2"
},
"devDependencies": {},
"keywords": [
Expand Down

0 comments on commit 89f5a67

Please sign in to comment.