Skip to content

Commit 4dc2a42

Browse files
author
Sergii.Kliuchnyk
committed
refactoring
1 parent e4bee66 commit 4dc2a42

10 files changed

Lines changed: 220 additions & 76 deletions

File tree

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.idea
12
./bin
23
./data
34
./tests

README.md

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,13 @@ It's a function which takes three arguments:
155155
* `callback` - optional Node style callback which will receive html string as second argument
156156

157157
If you pass to `engine` only path and locals then it will return html.
158+
```js
159+
engine('/path/to/view', {prop: 'value'}, (err, html) => console.log(html));
160+
161+
const html = engine('/path/to/view', {prop: 'value'});
162+
```
158163

159-
Also it has method `setOptions` which can modify [options](#options)
164+
Also, it has method `engine.setOptions(options)` which can modify [options](#options)
160165

161166
### options
162167

@@ -167,44 +172,67 @@ const options = require('express-engine-jsx/options');
167172
Object with optional properties:
168173

169174
* `doctype` - string which will be prepended to output html, default value is `"<!DOCTYPE html>\n"`
170-
* `replace` - function which will take output html (without doctype) and it should return new html
175+
* `replace` - function which will take output html (without doctype), and it should return new html
171176
* `templatePath` - path to wrapper of compiled jsx, default value is `express-engine-jsx/template.jsx`. Undefined variable `BODY` will be replaced with your compiled jsx code.
172177
* `parserOptions` - options for [babel.parser](https://babeljs.io/docs/en/babel-parser#options)
178+
* `templateOptions` - options for [babel.template](https://babeljs.io/docs/en/babel-template#options)
173179

174180
### require
175181

176182
```javascript
177-
const requireJSX = require('express-engine-jsx/require');
183+
const requireJSX = engine.require || require('express-engine-jsx/require');
178184
```
179185

180186
This is a function which you can use as regular `require` but this one can run jsx files. It checks if path is jsx file and if it is then `requireJSX` will [convert](#convert) this file to js code and then run it.
181187

182-
Every compiled jsx file will be cached to `requireJSX.cache` object where key will be path to jsx file and value will be [vm.Script](https://nodejs.org/api/vm.html#vm_class_vm_script).
188+
It also takes optional second parameter - `currentWorkingDir` which should be an absolute path to file directory which calls `require` in case when main path is relative.
189+
190+
Every compiled jsx file will be cached to `requireJSX.cache` object where key will be path to jsx file and value will be value of `module.exports` inside jsx file, usually react component.
183191
You can delete any key in this cache, requireJSX will recompile jsx file on next call.
184192

185193
### convert
186194

187195
```javascript
188-
const convert = require('express-engine-jsx/convert');
196+
const convert = engine.convert || require('express-engine-jsx/convert');
189197
```
190198

191-
It is a function which can convert jsx view files to [vm.Script](https://nodejs.org/api/vm.html#vm_class_vm_script).
192-
```js
193-
const script = convert('/path/to/view.jsx');
194-
195-
const context = {
196-
module: {
197-
exports: {}
198-
},
199-
__dirname: script.dirname,
200-
require: requireJSX,
201-
};
199+
It is a function which can convert jsx template code to js code.
200+
201+
Arguments:
202+
203+
* `code` - string of jsx code
204+
* `options`
205+
* `parserOptions` - options for [babel.parser](https://babeljs.io/docs/en/babel-parser#options)
206+
* `template` - string of jsx code wrapper
207+
* `templatePath` - string, path to jsx code wrapper
208+
* `templateOptions` - options for [babel.template](https://babeljs.io/docs/en/babel-template#options)
209+
210+
It also has `convert.cache` object for compiled templates where keys are `templatePath` and values are functions created by [babel.template](https://babeljs.io/docs/en/babel-template)
202211

203-
script.runInNewContext(context);
212+
## run
204213

205-
const ViewComponent = context.module.exports;
214+
```javascript
215+
const run = engine.run || require('express-engine-jsx/run');
216+
```
217+
218+
Function which can execute js code with independent context and returns result of `module.exports` inside js code.
219+
220+
Arguments:
221+
222+
* `code` - string of js code
223+
* `options`
224+
* `path` - string, path to file, usually path to jsx file
225+
* `context` - object which properties will be global variables inside js code
226+
* `scriptOptions` - object options for [vm.Script](https://nodejs.org/api/vm.html#vm_class_vm_script)
227+
228+
## Context
229+
230+
```javascript
231+
const Context = engine.Context || require('express-engine-jsx/Context');
206232
```
207233

234+
React context which used to bypass locals to components
235+
208236
## attr-map
209237

210238
```javascript
@@ -246,14 +274,14 @@ In javascript you can omit `;` and write like this
246274
"second"
247275
```
248276

249-
It do nothing but it's valid code. In JSX you can't do same thing with elements
277+
It does nothing, but it's valid code. In JSX you can't do same thing with elements
250278

251279
```html
252280
<div>first</div>
253281
<div>second</div>
254282
```
255283

256-
It will throw compilation error. It waiting for `;` after first element. You have three options to solve this problem.
284+
It will throw compilation error. It's waiting for `;` after first element. You have three options to solve this problem.
257285

258286
First - use `;`
259287

convert.js

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
const babel = require('@babel/core');
22
const parser = require('@babel/parser');
33
const traverse = require('@babel/traverse').default;
4-
const template = require('@babel/template').default;
4+
const createTemplate = require('@babel/template').default;
55
const t = require('@babel/types');
66
const fs = require('fs');
7-
const {basename} = require('path');
8-
const vm = require('vm');
97
const attrMap = require('./attr-map');
108
const options = require('./options');
119

12-
var createExportFunction;
10+
module.exports = convert;
1311

14-
module.exports = function convert(jsxPath) {
15-
const code = fs.readFileSync(jsxPath).toString();
12+
convert.cache = {};
1613

17-
let ast = parser.parse(code, options.parserOptions);
14+
function convert(code, params = {}) {
15+
if (code instanceof Buffer) {
16+
code = code.toString();
17+
}
18+
19+
let {parserOptions, template, templatePath, templateOptions} = params;
20+
21+
var ast = parser.parse(code, parserOptions || options.parserOptions);
1822

1923
traverse(ast, {
2024
enter: function prepare(path) {
@@ -55,19 +59,33 @@ module.exports = function convert(jsxPath) {
5559
}
5660
});
5761

58-
if (!createExportFunction) {
59-
createExportFunction = template(
60-
fs.readFileSync(options.templatePath).toString(),
61-
{
62-
parser: {
63-
strictMode: false
64-
},
65-
strictMode: false
66-
}
62+
templatePath = templatePath || options.templatePath;
63+
64+
if (templatePath && !template) {
65+
if (convert.cache[templatePath]) {
66+
template = convert.cache[templatePath];
67+
}
68+
else {
69+
template = fs.readFileSync(templatePath).toString();
70+
}
71+
}
72+
73+
if (template instanceof Buffer) {
74+
template = template.toString();
75+
}
76+
77+
if (typeof template === 'string') {
78+
template = createTemplate(
79+
template,
80+
templateOptions || options.templateOptions
6781
);
6882
}
6983

70-
ast = createExportFunction({
84+
if (typeof template !== 'function') {
85+
throw new Error('Undefined template');
86+
}
87+
88+
ast = template({
7189
BODY: ast.program.body
7290
});
7391

@@ -80,9 +98,5 @@ module.exports = function convert(jsxPath) {
8098
]
8199
});
82100

83-
const script = new vm.Script(res.code, {
84-
filename: basename(jsxPath)
85-
});
86-
87-
return script;
88-
};
101+
return res.code;
102+
}

index.d.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,31 @@ declare namespace engine {
66
doctype?: string;
77
replace?: (html: string) => string;
88
templatePath?: string;
9-
parserOptions?: object;
9+
parserOptions?: import('@babel/parser/typings/babel-parser').ParserOptions;
1010
}
1111

12-
export function setOptions(params: Options);
12+
interface ConvertOptions {
13+
parserOptions?: import('@babel/parser/typings/babel-parser').ParserOptions,
14+
template?: string | Buffer | (({BODY}) => any),
15+
templatePath?: string,
16+
templateOptions?: import('babylon').BabylonOptions,
17+
}
18+
19+
interface RunOptions {
20+
path?: string,
21+
context?: object,
22+
scriptOptions?: import('node:vm').ScriptOptions,
23+
}
24+
25+
export function setOptions(options: Options): void;
26+
27+
export function require(path: string, currentWorkingDir?: string): any;
28+
29+
export function convert(code: string|Buffer, options?: ConvertOptions): any;
30+
31+
export function run(code: string|Buffer, options?: RunOptions): any;
32+
33+
export const Context: import('react').Context<{ locales: object, settings: object }>;
1334
}
1435

1536
export = engine;

index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ const React = require('react');
22
const ReactDOM = require('react-dom/server');
33
const options = require('./options');
44
const requireJSX = require('./require');
5+
const convert = require('./convert');
6+
const run = require('./run');
57
const Context = require('./Context');
68

79
module.exports = engine;
@@ -44,4 +46,9 @@ engine.setOptions = function (params) {
4446
}
4547

4648
return engine;
47-
};
49+
};
50+
51+
engine.require = requireJSX;
52+
engine.convert = convert;
53+
engine.run = run;
54+
engine.Context = Context;

options.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ module.exports = {
88
'jsx'
99
],
1010
},
11+
templateOptions: {
12+
strictMode: false
13+
},
1114
doctype: "<!DOCTYPE html>\n",
1215
replace: null,
1316
templatePath: resolve(__dirname, 'template.jsx'),

package-lock.json

Lines changed: 44 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "express-engine-jsx",
3-
"version": "2.1.0",
3+
"version": "3.0.0",
44
"description": "JSX engine for ExpressJS",
55
"main": "index.js",
66
"types": "index.d.ts",
@@ -32,6 +32,9 @@
3232
"@babel/types": "^7.12.13"
3333
},
3434
"devDependencies": {
35+
"@types/babylon": "^6.16.5",
36+
"@types/node": "^14.14.28",
37+
"@types/react": "^17.0.2",
3538
"chai": "^4.3.0",
3639
"mocha": "^8.3.0"
3740
},

0 commit comments

Comments
 (0)