** Heads up!** Signficant work is happening to update and refactor this package for version 3.0.0 in the jsxish branch.
jsxmin
allows you to write JSX and transpile it to plain, vanilla javascript without React or any other runtime libraries.
JSX provides an intuitive and straightforward syntax that's easy to learn, battle-tested, and capable of scaling to large teams and production implementations. However, there are times when using all of React isn't available on your toolchain or environment, or you may be looking for something that is ultra-portable, or maybe you just want to go back to the old days of simple js templating (🤗).
This project attempts to take JSX syntax and transpile it to plain javascript via string concatenation and function calls.
A basic example:
const Button = (props) => <button class="btn primary">{props.label || props.children}</button>
Will output:
const Button = (props) => '<button class="btn primary">' + (props.label || props.children) + '</button>';
Which can then be called like this:
console.log(Button({
label: 'Hello World'
}));
// '<button class="btn primary">Hello World</button>'
Slightly more advanced example using custom elements:
import { Button } from './ui';
class HighFive extends HTMLElement {
constructor() {
super();
this.innerHTML = <Button>🖐</Button>
this.addEventListener('click', () => {
console.log('High five!!');
});
}
}
customElements.define('high-five', HighFive);
And can be called like this (as a quick example):
<html>
<body>
<high-five></high-five> // High five!!
<script src="..."></script>
</body>
</html>
Slightly more advanced example in the vein of SPAs:
import { Button } from './ui';
const App = ({name}) => <>
<p>Hello{name ? ' ' + name : ''}!</p>
<Button>🖐</Button>
</>
document.body.innerHTML = <App/>
And can be called like this (as a quick example):
<html>
<body>
<script src="..."></script>
</body>
</html>
See tests/test.js for more examples.
.transform(source, opts)
Transforms a string with
jsx
to plain javascript and is the primary function of this library.
source
should be a string.
opts
is an object with properties as defined below.
.execute(source, opts)
Transform and execute a string with
jsx
and get the resulting output.
source
should be a string.
opts
is an object with properties as defined below.
Note: Whatever [valid] JavaScript statement is on the last line is what will be returned. See Dynamic usage example below.
enableOutputSimplification[=false]
Takes a second pass to simplify the transformed source.
This step adds additional parsing and traversing and can add significant overhead. If the transformed source code is being passed to another tool to minify (and/or bundle) then this step is probably superfluous.
useWhitespace[=false]
Adds additional whitespace between tags to improve readability.
allowReferencesAsFunctions[=true]
Checks if a reference (variable) is a function and if so, executes the function and uses its output instead.
allowScopedParameterAccess[=false]
Pass along the
props
(the first parameter) to each subsequent function call.
Note: This is experimental and could have unexpected results. Requires allowReferencesAsFunctions
to be set to true
.
transformEsmAsCjs[=false]
Transforms ECMAScript modules to CommonJS -- only the syntax of import/export statements and import expressions is transformed.
Note: This is experimental and could have unexpected results. Also, this requires the @babel/plugin-transform-modules-commonjs
package to be installed (which is an optional dependency of this package).
To install and use as a module in your Nodejs/Babel toolchain, run:
npm install jsxmin
Or to install the babel plug-in, run:
npm install babel-plugin-jsxmin
And add this to your Babel configuration:
{
...
plugins: ['babel-plugin-jsxmin']
...
}
See babel-plugin-jsxmin/README
for more details.
Dynamic usage:
const Jsxmin = require('jsxmin');
const tmpl = Jsxmin.execute(`
({name}) => <p>Hello {name || 'world'}</p>
`, {
// NOTE: these are the default values and are only being passed here for demonstration purposes.
enableOutputSimplification: false,
useWhitespace: false,
allowReferencesAsFunctions: true,
allowScopedParameterAccess: false,
});
console.log(tmpl({name: 'Github'})) // '<p>Hello Github</p>'
Build-time usage:
const Jsxmin = require('jsxmin');
const Fs = require('fs');
const source = Fs.readFileSync('./ui.jsx', 'utf-8');
const compiled = Jsxmin.transform(source);
Fs.writeFileSync('./ui.js', compiled);
jsxmin
does not currently escape or otherwise sanitize user input and thus could be vulnerable to content injection or XSS attacks (or a myriad of other attack vectors). Please ensure all user generated content has been sanitized before passing to any jsxmin
compiled template or function. This project should probably not be considered production-ready until then.
-
Finalize the main api (transpileFile
vstranspileSource
vsrun
) and add documentation. - Support compiling jsx as ES modules (specifically importing and exporting)
- Support ES modules and additional Babel plugins in Fastify and Express plugins
- Resolve TODO on line 28 of babel-plugin/index.js
- Support async/await in Fastify and Express plugins
- Support spread operator for attributes (e.g.,
<Button {...props}></Button>
) - Use template literals instead of string literals for everything
- Clean up internal directory structure:
- Make releasing and incrementing on individual packages easier
- Resolve relative vs absolute package name references
- Ensure everything is installable and runnable
- Security and XSS sanitization
- Add warnings for unsupported attributes (like
className
,dangerouslySetInnerHTML
,htmlFor
,onClick
and other event listeners)- Most of these
- Add more examples
- See tests/test.js for basic and advanced usecases (like partials, control flows, and plugin options)
- Add more integrations
- Add support for including a shared/common util that could do the following:
- Handle escaping and sanitizing user input
- Add support for control structures and loops, etc
- Reduce various manual checks
- ...?
MIT