Change docs to rt-import instead of rt-require

This commit is contained in:
Omer Ganim 2016-05-15 16:06:43 +03:00
parent 3913453642
commit 8103d35585
8 changed files with 195 additions and 141 deletions

117
README.md
View File

@ -35,7 +35,7 @@ https://github.com/wix/react-templates-transform-boilerplate
http://plugins.jetbrains.com/plugin/7648
###### Basic concepts for React templates
## Basic concepts for React templates
* Any valid HTML (including comments) is a template
* {} to identify JS expression
* Built-in directives:
@ -44,13 +44,14 @@ http://plugins.jetbrains.com/plugin/7648
* [rt-scope](#rt-scope)
* [rt-props](#rt-props)
* [rt-class](#rt-class)
* [rt-require](#rt-require-and-using-other-components-in-the-template)
* [rt-import](#using-other-components-in-the-template)
* ~~rt-require~~ (deprecated, use rt-import)
* [rt-template](#rt-template-and-defining-properties-template-functions)
* [rt-include](#rt-include)
* [styles](#styles)
* [event handlers](#event-handlers)
###### Why not use JSX?
## Why not use JSX?
Some love JSX, some don't. We don't. More specifically, it seems to us that JSX is only a good fit for components with very little HTML inside.
And this can be accomplished by creating DOM elements in code. Also, we like to separate code and HTML because it just feels right.
@ -410,13 +411,73 @@ define([
});
```
## rt-require, and using other components in the template
In many cases, you'd like to use either external code or other components within your template. An **rt-require** tag lets you include dependencies: `<rt-require dependency="depVarPath" as="depVarName"/>`.
Once included, **depVarName** will be in scope. You can only use rt-require tags at the beginning of your template. When including React components, they can be referred to by their tag name inside a template.
## rt-import and using other components in the template
In many cases, you'd like to use either external code or other components within your template.
To do so, you can use an `rt-import` tag that lets you include dependencies in a syntax similar to ES6 imports:
```xml
<rt-import name="*" as="depVarName" from="depName"/>
```
Once included, **depVarName** will be in scope.
You can only use rt-import tags at the beginning of your template. When including React components, they can be referred to by their tag name inside a template.
For example, `<MySlider prop1="val1" onMyChange="{this.onSliderMoved}">`. Nesting is also supported: `<MyContainer><div>child</div><div>another</div></MyContainer>`.
Children are accessible from **this.props.children**.
###### Sample:
```xml
<rt-import name="member" from="module-name"/>
<rt-import name="member" as="alias2" from="module-name"/>
<rt-import name="*" as="alias3" from="module-name"/>
<rt-import name="default" as="alias4" from="module-name"/>
<div>
</div>
```
###### Compiled (ES6 flag):
```javascript
import * as React from 'react/addons';
import * as _ from 'lodash';
import { member } from 'module-name';
import { member as alias2 } from 'module-name';
import * as alias3 from 'module-name';
import alias4 from 'module-name';
export default function () {
return React.createElement('div', {});
}
```
###### Compiled (AMD):
```javascript
define('div', [
'react/addons',
'lodash',
'module-name',
'module-name',
'module-name',
'module-name'
], function (React, _, member, alias2, alias3, alias4) {
'use strict';
return function () {
return React.createElement('div', {});
};
});
```
###### Compiled (with CommonJS flag):
```javascript
'use strict';
var React = require('react/addons');
var _ = require('lodash');
var member = require('module-name').member;
var alias2 = require('module-name').member;
var alias3 = require('module-name');
var alias4 = require('module-name').default;
module.exports = function () {
return React.createElement('div', {});
};
```
#### deprecated: rt-require
The tag `rt-require` is deprecated and replaced with `rt-import`.
Its syntax is similar to `rt-import` but does not allow default imports:
```html
<rt-require dependency="comps/myComp" as="MyComp"/>
<rt-require dependency="utils/utils" as="utils"/>
@ -424,50 +485,6 @@ Children are accessible from **this.props.children**.
<div>{utils.toLower(item.name)}</div>
</MyComp>
```
###### Compiled (AMD):
```javascript
define([
'react/addons',
'lodash',
'comps/myComp',
'utils/utils'
], function (React, _, MyComp, utils) {
'use strict';
function repeatItem1(item, itemIndex) {
return React.createElement(MyComp, {}, React.createElement('div', {}, utils.toLower(item.name)));
}
return function () {
return _.map(items, repeatItem1.bind(this));
};
});
```
###### Compiled (with CommonJS flag):
```javascript
var React = require('react/addons');
var _ = require('lodash');
var MyComp = require('comps/myComp');
var utils = require('utils/utils');
'use strict';
function repeatItem1(item, itemIndex) {
return React.createElement(MyComp, {}, React.createElement('div', {}, utils.toLower(item.name)));
}
module.exports = function () {
return _.map(items, repeatItem1.bind(this));
};
```
###### Compiled (with ES6 flag):
```javascript
import React from 'react/addons';
import _ from 'lodash';
import MyComp from 'comps/myComp';
import utils from 'utils/utils';
function repeatItem1(item, itemIndex) {
return React.createElement(MyComp, {}, React.createElement('div', {}, utils.toLower(item.name)));
}
export default function () {
return _.map(items, repeatItem1.bind(this));
};
```
## Inline Templates
Although we recommend separating the templates to a separate `.rt` file, there's an option to use a template inline as the render method (à la JSX).

File diff suppressed because one or more lines are too long

View File

@ -27,34 +27,9 @@ function getLine(html, node) {
return { line: linesUntil.length, col: linesUntil[linesUntil.length - 1].length + 1 };
}
//function getLine(node) {
// if (!node) {
// return 0;
// }
// const line = 0;
// const prev = node.prev;
// while (prev) {
// const nl = prev.data.split('\n').length - 1;
// line += nl;
// prev = prev.prev;
// }
//
// line += getLine(node.parent);
// return line + 1;
//}
//function RTCodeError(message, line) {
// this.name = 'RTCodeError';
// this.message = message || '';
// this.line = line || -1;
//}
//RTCodeError.prototype = Error.prototype;
// Redefine properties on Error to be enumerable
/*eslint no-extend-native:0*/
//Object.defineProperty(Error.prototype, 'message', {configurable: true, enumerable: true});
//Object.defineProperty(Error.prototype, 'stack', {configurable: true, enumerable: true});
//Object.defineProperty(Error.prototype, 'line', { configurable: true, enumerable: true });
function norm(n) {
return n === undefined ? -1 : n;
}
/**
* @param {string} message
@ -83,21 +58,15 @@ var RTCodeError = function (_Error) {
_this.column = norm(column);
return _this;
}
//build buildError
return RTCodeError;
}(Error);
function norm(n) {
return n === undefined ? -1 : n;
}
//const norm = n => n === undefined ? -1 : n;
/**
* @type {buildError}
*/
RTCodeError.build = buildError;
RTCodeError.norm = norm;
@ -348,12 +317,45 @@ var templates = {
jsrt: templateJSRTTemplate
};
var isImportAsterisk = _.matches({ member: '*' });
var defaultCase = _.constant(true);
var buildImportTypeScript = _.cond([[isImportAsterisk, function (d) {
return 'import ' + d.alias + ' = require(\'' + d.moduleName + '\');';
}], [defaultCase, function (d) {
return 'import ' + d.alias + ' = require(\'' + d.moduleName + '\').' + d.member + ';';
}]]);
var buildImportES6 = _.cond([[isImportAsterisk, function (d) {
return 'import * as ' + d.alias + ' from \'' + d.moduleName + '\';';
}], [_.matches({ member: 'default' }), function (d) {
return 'import ' + d.alias + ' from \'' + d.moduleName + '\';';
}], [defaultCase, function (d) {
return 'import { ' + d.member + ' as ' + d.alias + ' } from \'' + d.moduleName + '\';';
}]]);
var buildImportCommonJS = _.cond([[isImportAsterisk, function (d) {
return 'var ' + d.alias + ' = require(\'' + d.moduleName + '\');';
}], [defaultCase, function (d) {
return 'var ' + d.alias + ' = require(\'' + d.moduleName + '\').' + d.member + ';';
}]]);
var buildImport = {
typescript: buildImportTypeScript,
es6: buildImportES6,
commonjs: buildImportCommonJS,
amd: buildImportCommonJS,
none: buildImportCommonJS,
jsrt: buildImportCommonJS
};
module.exports = {
htmlSelfClosingTags: htmlSelfClosingTags,
attributesMapping: attributesMapping,
classNameProp: classNameProp,
shouldUseCreateElement: shouldUseCreateElement,
templates: templates
templates: templates,
buildImport: buildImport
};
},{"lodash":101}],7:[function(require,module,exports){
'use strict';
@ -403,6 +405,8 @@ var templateNode = 'rt-template';
var virtualNode = 'rt-virtual';
var includeNode = 'rt-include';
var includeSrcAttr = 'src';
var requireAttr = 'rt-require';
var importAttr = 'rt-import';
var reactTemplatesSelfClosingTags = [includeNode];
@ -564,7 +568,6 @@ function genBind(func, args) {
}
function handleStyleProp(val, node, context) {
/*eslint lodash/prefer-lodash-chain:0*/
var styleStr = _(val).split(';').map(_.trim).filter(function (i) {
return _.includes(i, ':');
}).map(function (i) {
@ -600,9 +603,7 @@ function convertTagNameToConstructor(tagName, context) {
* @return {Context}
*/
function defaultContext(html, options, reportContext) {
var defaultDefines = {};
defaultDefines[options.reactImportPath] = 'React';
defaultDefines[options.lodashImportPath] = '_';
var defaultDefines = [{ moduleName: options.reactImportPath, alias: 'React', member: '*' }, { moduleName: options.lodashImportPath, alias: '_', member: '*' }];
return {
boundParams: [],
injectedFunctions: [],
@ -678,6 +679,9 @@ function convertHtmlToReact(node, context) {
if (node.attribs[ifAttr]) {
validateIfAttribute(node, context, data);
data.condition = node.attribs[ifAttr].trim();
if (!node.attribs.key) {
_.set(node, ['attribs', 'key'], '' + node.startIndex);
}
}
data.props = generateProps(node, context);
@ -817,6 +821,48 @@ function handleSelfClosingHtmlTags(nodes) {
});
}
function handleRequire(tag, context) {
var moduleName = void 0;
var alias = void 0;
var member = void 0;
if (tag.children.length) {
throw RTCodeError.build(context, tag, '\'' + requireAttr + '\' may have no children');
} else if (tag.attribs.dependency && tag.attribs.as) {
moduleName = tag.attribs.dependency;
member = '*';
alias = tag.attribs.as;
}
if (!moduleName) {
throw RTCodeError.build(context, tag, '\'' + requireAttr + '\' needs \'dependency\' and \'as\' attributes');
}
context.defines.push({ moduleName: moduleName, member: member, alias: alias });
}
function handleImport(tag, context) {
var moduleName = void 0;
var alias = void 0;
var member = void 0;
if (tag.children.length) {
throw RTCodeError.build(context, tag, '\'' + importAttr + '\' may have no children');
} else if (tag.attribs.name && tag.attribs.from) {
moduleName = tag.attribs.from;
member = tag.attribs.name;
alias = tag.attribs.as;
if (!alias) {
if (member === '*') {
throw RTCodeError.build(context, tag, "'*' imports must have an 'as' attribute");
} else if (member === 'default') {
throw RTCodeError.build(context, tag, "default imports must have an 'as' attribute");
}
alias = member;
}
}
if (!moduleName) {
throw RTCodeError.build(context, tag, '\'' + importAttr + '\' needs \'name\' and \'from\' attributes');
}
context.defines.push({ moduleName: moduleName, member: member, alias: alias });
}
function convertTemplateToReact(html, options) {
var context = require('./context');
return convertRT(html, context, options);
@ -837,13 +883,10 @@ function parseAndConvertHtmlToReact(html, context) {
}
var firstTag = null;
_.forEach(rootTags, function (tag) {
if (tag.name === 'rt-require') {
if (!tag.attribs.dependency || !tag.attribs.as) {
throw RTCodeError.build(context, tag, "rt-require needs 'dependency' and 'as' attributes");
} else if (tag.children.length) {
throw RTCodeError.build(context, tag, 'rt-require may have no children');
}
context.defines[tag.attribs.dependency] = tag.attribs.as;
if (tag.name === requireAttr) {
handleRequire(tag, context);
} else if (tag.name === importAttr) {
handleImport(tag, context);
} else if (firstTag === null) {
firstTag = tag;
} else {
@ -870,30 +913,20 @@ function convertRT(html, reportContext, options) {
var context = defaultContext(html, options, reportContext);
var body = parseAndConvertHtmlToReact(html, context);
var requirePaths = _(context.defines).keys().map(function (def) {
return '"' + def + '"';
var requirePaths = _.map(context.defines, function (d) {
return '"' + d.moduleName + '"';
}).join(',');
var buildImport = void 0;
if (options.modules === 'typescript') {
buildImport = function buildImport(v, p) {
return 'import ' + v + ' = require(\'' + p + '\');';
};
} else if (options.modules === 'es6') {
// eslint-disable-line
buildImport = function buildImport(v, p) {
return 'import ' + v + ' from \'' + p + '\';';
};
} else {
buildImport = function buildImport(v, p) {
return 'var ' + v + ' = require(\'' + p + '\');';
};
}
var requireNames = _.map(context.defines, function (d) {
return '' + d.alias;
}).join(',');
var buildImport = reactSupport.buildImport[options.modules] || reactSupport.buildImport.commonjs;
var requires = _.map(context.defines, buildImport).join('\n');
var header = options.flow ? '/* @flow */\n' : '';
var vars = header + _(context.defines).map(buildImport).join('\n');
var vars = header + requires;
var data = {
body: body,
injectedFunctions: context.injectedFunctions.join('\n'),
requireNames: _.values(context.defines).join(','),
requireNames: requireNames,
requirePaths: requirePaths,
vars: vars,
name: options.name
@ -1039,6 +1072,10 @@ function validate(options, context, reportContext, node) {
var loc = rtError.getNodeLoc(context, node);
reportContext.warn('rt-if without a key', options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end);
}
if (node.type === 'tag' && node.attribs['rt-require'] && (node.attribs.dependency || node.attribs.as)) {
var _loc = rtError.getNodeLoc(context, node);
reportContext.warn("'rt-require' is obsolete, use 'rt-import' instead", options.fileName, _loc.pos.line, _loc.pos.col, _loc.start, _loc.end);
}
if (node.children) {
node.children.forEach(validate.bind(this, options, context, reportContext));
}

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,8 @@ define(['lodash', 'react', './examples.rt',
'text!./samples/rt-props.code', 'text!./samples/rt-props.rt',
'text!./samples/rt-repeat.code', 'text!./samples/rt-repeat.rt',
'text!./samples/weather.code', 'text!./samples/weather.rt',
'text!./samples/rt-require.rt'
], function (_, React, examplesTemplate, helloCode, helloRT, todoCode, todoRT, rtIfCode, rtIfRT, rtPropsCode, rtPropsRT, rtRepeatCode, rtRepeatRT, weatherCode, weatherRT, rtRequireRT) {
'text!./samples/rt-import.rt'
], function (_, React, examplesTemplate, helloCode, helloRT, todoCode, todoRT, rtIfCode, rtIfRT, rtPropsCode, rtPropsRT, rtRepeatCode, rtRepeatRT, weatherCode, weatherRT, rtImportRT) {
'use strict';
var samples = {
hello: [helloCode, helloRT],
@ -16,22 +16,17 @@ define(['lodash', 'react', './examples.rt',
repeat: [rtRepeatCode, rtRepeatRT],
weather: [weatherCode, weatherRT]
};
//samples = _.map(samples, function (v, k) {
// return {name: k, templateProps: _.template(v[0])({name: k}), templateHTML: v[1]};
//});
_.forEach(samples, function (v, k) {
samples[k] = {name: k, templateProps: _.template(v[0])({name: k}), templateHTML: v[1]};
});
samples = _.mapValues(samples, function (v, k) { return {name: k, templateProps: _.template(v[0])({name: k}), templateHTML: v[1]}; });
return React.createClass({
displayName: 'Examples',
mixins: [React.addons.LinkedStateMixin],
getInitialState: function () {
var codeAmd = window.reactTemplates.convertTemplateToReact(rtRequireRT, {modules: 'amd', name: 'template'});
var codeCJS = window.reactTemplates.convertTemplateToReact(rtRequireRT, {modules: 'commonjs', name: 'template'});
var codeES6 = window.reactTemplates.convertTemplateToReact(rtRequireRT, {modules: 'es6', name: 'template'});
var codeAmd = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'amd', name: 'template'});
var codeCJS = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'commonjs', name: 'template'});
var codeES6 = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'es6', name: 'template'});
return {
rtRequire: {value: rtRequireRT},
rtImport: {value: rtImportRT},
amd: {value: codeAmd},
cjs: {value: codeCJS},
es6: {value: codeES6},

View File

@ -44,12 +44,12 @@
</p>
<playground id="weatherExample" rt-props="this.state.samples.weather" direction="horizontal"></playground>
</div>
<div id="rt-require" class="example">
<h3>rt-require</h3>
<div id="rt-import" class="example">
<h3>rt-import</h3>
<p>
This example shows how to load other React components and libraries into a React template and then use them within the template.
</p>
<viewer rt-props="this.state.rtRequire" mode="javascript" />
<viewer rt-props="this.state.rtImport" mode="javascript" />
</div>
<div id="amd" class="example">
<h3>AMD</h3>

View File

@ -25,9 +25,9 @@ define([
'id': 'weatherExample',
'direction': 'horizontal'
}, this.state.samples.weather))), React.createElement('div', {
'id': 'rt-require',
'id': 'rt-import',
'className': 'example'
}, React.createElement('h3', {}, 'rt-require'), React.createElement('p', {}, '\n This example shows how to load other React components and libraries into a React template and then use them within the template.\n '), React.createElement(viewer, _.assign({}, { 'mode': 'javascript' }, this.state.rtRequire))), React.createElement('div', {
}, React.createElement('h3', {}, 'rt-import'), React.createElement('p', {}, '\n This example shows how to load other React components and libraries into a React template and then use them within the template.\n '), React.createElement(viewer, _.assign({}, { 'mode': 'javascript' }, this.state.rtImport))), React.createElement('div', {
'id': 'amd',
'className': 'example'
}, React.createElement('h3', {}, 'AMD'), React.createElement('p', {}, '\n This example shows the rt-require sample output with AMD support.\n '), React.createElement(viewer, _.assign({}, { 'mode': 'javascript' }, this.state.amd))), React.createElement('div', {

View File

@ -0,0 +1,5 @@
<rt-import name="myComp" from="comps" />
<rt-import name="*" as="utils" from="utils/utils" />
<div>
<myComp customProp="{utils.doSomething()}">myComp is just a regular tag</myComp>
</div>