Skip to content

Commit 93be11a

Browse files
author
Sergii.Kliuchnyk
committed
support import ... from, update deps
1 parent d9eaf3f commit 93be11a

7 files changed

Lines changed: 588 additions & 244 deletions

File tree

.travis.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
language: node_js
22
node_js:
3-
- "10"
4-
before_script:
5-
- npm run peer
3+
- "10"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ express-engine-jsx
22
------------------
33
Full featured template engine for express
44

5-
[![Build Status](https://travis-ci.org/redexp/express-engine-jsx.svg?branch=master)](https://travis-ci.org/redexp/express-engine-jsx)
5+
[![Build Status](https://travis-ci.com/redexp/express-engine-jsx.svg?branch=master)](https://travis-ci.com/redexp/express-engine-jsx)
66

77
Example of `users.jsx` template file
88
```jsx harmony

convert.js

Lines changed: 132 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
const babel = require('@babel/core');
2-
const parser = require('@babel/parser');
3-
const traverse = require('@babel/traverse').default;
42
const createTemplate = require('@babel/template').default;
53
const t = require('@babel/types');
64
const fs = require('fs');
@@ -19,121 +17,160 @@ function convert(code, params = {}) {
1917
let {
2018
path,
2119
sourceMap = options.sourceMap,
20+
} = params;
21+
22+
var result = babel.transformSync(code, {
23+
filename: path,
24+
sourceMap,
25+
plugins: [
26+
babel.createConfigItem([transformToComponent, params]),
27+
['@babel/plugin-transform-modules-commonjs', {strictMode: false}],
28+
babel.createConfigItem(transformRequire),
29+
'@babel/plugin-transform-react-jsx',
30+
]
31+
});
32+
33+
return sourceMap ? result : result.code;
34+
}
35+
36+
function transformToComponent(api, params) {
37+
let {
2238
addOnChange = options.addOnChange,
23-
parserOptions = options.parserOptions,
2439
template,
2540
templatePath = options.templatePath,
2641
templateOptions = options.templateOptions
2742
} = params;
2843

29-
var ast = parser.parse(code, parserOptions);
30-
31-
traverse(ast, {
32-
enter: function prepare(path) {
33-
path.get('body').forEach(function (item) {
34-
if (
35-
item.isExpressionStatement() &&
36-
(
37-
item.node.expression.type === 'JSXElement' ||
38-
item.node.expression.type === 'JSXFragment'
39-
)
40-
) {
41-
item.replaceWith(
42-
t.callExpression(
43-
t.memberExpression(t.identifier('__components'), t.identifier('push')),
44-
[item.node.expression]
45-
)
46-
);
47-
}
48-
});
49-
50-
path.traverse({
51-
JSXAttribute: function (attr) {
52-
var name = attr.node.name.name;
53-
var parent = attr.parent;
54-
55-
if (addOnChange && (name === 'value' || name === 'checked') && parent.name && parent.name.name === 'input') {
56-
attr.parent.attributes.push(
57-
t.jsxAttribute(
58-
t.jsxIdentifier('onChange'),
59-
t.jsxExpressionContainer(t.arrowFunctionExpression([], t.booleanLiteral(false)))
44+
return {
45+
visitor: {
46+
Program: {
47+
enter: function (path) {
48+
path.get('body').forEach(function (item) {
49+
if (
50+
item.isExpressionStatement() &&
51+
(
52+
item.get('expression').isJSXElement() ||
53+
item.get('expression').isJSXFragment()
6054
)
61-
);
62-
}
55+
) {
56+
item.replaceWith(
57+
t.callExpression(
58+
t.memberExpression(t.identifier('__components'), t.identifier('push')),
59+
[item.node.expression]
60+
)
61+
);
62+
}
63+
});
64+
},
65+
exit: function (path) {
66+
if (template === false) return;
67+
68+
const {cache} = convert;
6369

64-
if (attrMap.hasOwnProperty(name)) {
65-
attr.node.name.name = attrMap[name];
70+
if (templatePath && !template) {
71+
template = cache[templatePath] || fs.readFileSync(templatePath);
6672
}
67-
},
68-
CallExpression: function (func) {
69-
if (func.node.callee.type === 'Identifier' && func.node.callee.name === 'require') {
70-
func.node.callee.name = 'requireJSX';
71-
func.node.arguments.push(t.identifier('__dirname'));
73+
74+
if (template instanceof Buffer) {
75+
template = template.toString();
7276
}
73-
}
74-
});
7577

76-
path.stop();
77-
}
78-
});
78+
if (typeof template === 'string') {
79+
template = createTemplate(
80+
template,
81+
templateOptions
82+
);
7983

80-
if (template === false) {
81-
return toCode(ast, code, {
82-
filename: path,
83-
sourceMap,
84-
});
85-
}
84+
if (templatePath) {
85+
cache[templatePath] = template;
86+
}
87+
}
8688

87-
let {cache} = convert;
89+
if (typeof template !== 'function') {
90+
throw new Error('Undefined template');
91+
}
8892

89-
if (templatePath && !template) {
90-
if (convert.cache[templatePath]) {
91-
template = convert.cache[templatePath];
92-
}
93-
else {
94-
template = fs.readFileSync(templatePath);
95-
}
96-
}
93+
var IMPORTS = [];
94+
var BODY = [];
9795

98-
if (template instanceof Buffer) {
99-
template = template.toString();
100-
}
96+
path.get('body').forEach(function (item) {
97+
var {node} = item;
10198

102-
if (typeof template === 'string') {
103-
template = createTemplate(
104-
template,
105-
templateOptions
106-
);
99+
if (isExport(node)) {
100+
throw item.buildCodeFrameError('export is not allowed in jsx template');
101+
}
107102

108-
if (templatePath) {
109-
cache[templatePath] = template;
110-
}
111-
}
103+
if (t.isImportDeclaration(node)) {
104+
IMPORTS.push(node);
105+
}
106+
else {
107+
BODY.push(node);
108+
}
109+
});
112110

113-
if (typeof template !== 'function') {
114-
throw new Error('Undefined template');
115-
}
111+
path.node.body = template({
112+
IMPORTS,
113+
BODY,
114+
});
115+
},
116+
},
117+
JSXAttribute: function (path) {
118+
var name = path.node.name.name;
119+
var parent = path.parent;
116120

117-
ast = template({
118-
BODY: ast.program.body
119-
});
121+
if (
122+
addOnChange &&
123+
(name === 'value' || name === 'checked') &&
124+
parent.name &&
125+
parent.name.name === 'input' &&
126+
parent.attributes.every(attr => attr.name.name !== 'onChange')
127+
) {
128+
parent.attributes.push(
129+
t.jsxAttribute(
130+
t.jsxIdentifier('onChange'),
131+
t.jsxExpressionContainer(t.arrowFunctionExpression([], t.booleanLiteral(false)))
132+
)
133+
);
134+
}
120135

121-
return toCode(t.program(ast), code, {
122-
filename: path,
123-
sourceMap,
124-
});
136+
if (attrMap.hasOwnProperty(name)) {
137+
path.node.name.name = attrMap[name];
138+
}
139+
},
140+
}
141+
};
125142
}
126143

127-
function toCode(ast, code = '', params = {}) {
128-
const {filename, sourceMap} = params;
144+
function transformRequire() {
145+
const rule = /^(react|express-engine-jsx\/.+)$/
129146

130-
var result = babel.transformFromAst(ast, code, {
131-
filename,
132-
sourceMap,
133-
plugins: [
134-
'@babel/plugin-transform-react-jsx'
135-
]
136-
});
147+
return {
148+
visitor: {
149+
CallExpression: function ({node}) {
150+
var {callee, arguments: args} = node;
151+
var first = args[0];
137152

138-
return sourceMap ? result : result.code;
153+
if (
154+
t.isIdentifier(callee) &&
155+
callee.name === 'require' &&
156+
args.length === 1 &&
157+
t.isStringLiteral(first) &&
158+
!rule.test(first.value)
159+
) {
160+
callee.name = 'requireJSX';
161+
args.push(t.identifier('__dirname'));
162+
}
163+
}
164+
}
165+
};
166+
}
167+
168+
function isExport(node) {
169+
return (
170+
t.isExportAllDeclaration(node) ||
171+
t.isExportDeclaration(node) ||
172+
t.isExportDefaultDeclaration(node) ||
173+
t.isExportNamedDeclaration(node) ||
174+
t.isExportNamespaceSpecifier(node)
175+
);
139176
}

0 commit comments

Comments
 (0)