Research
Security News
Kill Switch Hidden in npm Packages Typosquatting Chalk and Chokidar
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
tsx-browser-compiler
Advanced tools
Transform a set of `.tsx` file into React element, extremely useful for the "live edit" feature in component library documents.
Transform a set of .tsx
(as well as other extensions) files into React elements, extremely useful for the "live edit" feature in component library documents. Try it out in the playground!
You can install tsx-browser-compiler
via various package managers.
# using npm
npm i tsx-browser-compiler --save
# using yarn
yarn add tsx-browser-compiler
# using pnpm
pnpm add tsx-browser-compiler
To transform the TSX codes to React elements, just provide them to asyncTsxToElement
and render the component
field (or errors
field if you'd like to display errors) in the returned value.
CSS is also supported and will be auto-injected into the document head. You may use the cleanup
field to remove them (e.g. use a useEffect
with a dependency component
).
// Transforming can be slow, so it's recommended to debounce this call.
// e.g. use `useDebouncedEffect` written in `playground/hooks/use-debounced-effect.ts`.
const { component, errors, cleanup } = await asyncTsxToElement({
sources: {
'index.tsx': `
import React from 'react';
import './style.css';
export default function MyComponent() {
return <div className="my-component">Hello, world!</div>;
}
`,
'style.css': `
.my-component {
color: red;
}
`,
},
});
sucrase
, a small and fast Babel alternative, to transform the code. If you only have one .tsx
file and don't need to emit type errors, this is a better choice.There are more options you can provide to asyncTsxToElement
.
entryFile
Fieldstring
sources
object.resolve
FieldPartial<ResolveConfig>
extensions
, externals
, cdnPrefix
.The resolve.extensions
field is an array of strings that will be appended to the generated file path when resolving modules. By default, it's ['.js']
.
For example, assume we have require('./example')
, and the resolve.extensions
is set to ['.mjs', '.cjs', '.js']
, the resolver will try to load the following files:
./example.mjs
./example.cjs
./example.js
./example/index.mjs
./example/index.cjs
./example/index.js
Note that .tsx
, .ts
and .json
files will all be converted to .js
files, please see the code in playground.
The resolve.externals
field is an object that maps module names to global variables. There are several types of externals:
window[value]
exists, it will be treated as the module.https://
or http://
, it will be treated as a UMD file URL. The resolver will load the file and get its exports.cdnPrefix
field (we use UNPKG by default).Note that if you specify the cdnPrefix
field, your CDN must support returning the correct UMD file like UNPKG, e.g. ${cdnPrefix}/axios
should be redirected to ${cdnPrefix}/axios@[a.b.c]/dist/axios.min.js
or return the file directly without redirection.
Here are some examples.
const { component, cleanup } = await asyncTsxToElement({
resolve: {
externals: {
// assume `window.React` exists
'react': 'React',
// load `axios` UMD file from URL
'axios': 'https://unpkg.com/axios@1.6.8/dist/axios.min.js',
// load `antd` from CDN with the version `5.16.2`
'antd': '5.16.2',
},
// jsDelivr is a CDN service that returns the correct UMD file directly
cdnPrefix: 'https://cdn.jsdelivr.net/npm/',
},
});
requireFn
Field(absolutePath: string) => any
require
function to fully control the module resolution. For example, if you already have the exports of module /example1.js
and /example2.js
, you can provide a custom require
function to return them.import React from 'react';
import { example1, example2 } from './mocks';
const { component, cleanup } = await asyncTsxToElement({
requireFn: absolutePath => {
if (absolutePath === 'react') {
return React;
}
if (absolutePath === '/example1.js') {
return example1;
}
if (absolutePath === '/example2.js') {
return example2;
}
},
});
Notice that for most scenarios, you don't need to provide a custom require
function, the resolve.externals
field is enough.
rules
FieldModuleRule[]
Sometimes you may want to transform the source code before it's executed, or you have to use the non-js files. The rules
field provides a way to do this.
import less from 'less';
const { component, cleanup } = await asyncTsxToElement({
rules: [
{
test: /\.less$/,
use: [
/**
* @param content The content of the file
* @param meta Meta information, including `filename`, `options` and other custom fields
* @param callback The callback function to return the transformed content
*/
(content, meta, callback) => {
less.render(
content,
{ filename: meta.filename },
(err: Error, result: { css: string }) => {
if (err) {
callback(err, '', meta);
} else {
callback(null, result.css, meta);
}
},
);
},
],
},
],
});
Here is an example of passing options
to the rule.
import less from 'less';
const { component, cleanup } = await asyncTsxToElement({
rules: [
{
test: /\.less$/,
use: [
{
loader: (content, meta, callback) => { ... },
// will be passwd to the `meta` parameter
options: { ... },
},
],
},
],
});
By default, the rules are applied in the order they are provided. If you want to adjust the order of the rules, you can use the rules[].enforce
field. The value can be 'pre'
or 'post'
.
const { component, cleanup } = await asyncTsxToElement({
rules: [
{ test: /\.less$/, use: [Loader1], enforce: 'post' },
{ test: /\.less$/, use: [Loader2], enforce: 'pre' },
{ test: /\.less$/, use: [Loader3] },
{ test: /\.less$/, use: [Loader4] },
],
});
All loaders with enforce: 'pre'
will be executed before the normal loaders, and all loaders with enforce: 'post'
will be executed after the normal loaders, which means the execution order is Loader2
-> Loader3
-> Loader4
-> Loader1
.
Loaders are much like pure functions that receive the content of the file and return the transformed content. The rules[].use
field is like compose
in functional programming - the first loader is the last one to be executed, and the last loader, on the contrary, is the first one to be executed.
However, sometimes you may want to gather information before the loaders are executed, or to skip the rest of the loaders and return the content directly. You can use the pitch
field in the loader.
const metaKey = Symbol('buildTimeMeasurementLoaderMetaKey');
interface ExtendedLoaderMeta extends LoaderMeta {
[metaKey]: {
startTime: number;
contentLength: number;
};
}
const BuildTimeMeasurementLoader = (content: string, meta: ExtendedLoaderMeta) => {
const endTime = Date.now();
const { startTime, contentLength } = meta[metaKey];
console.log(`LessLoader: transform ${meta.filename} (${contentLength} chars) cost ${endTime - startTime}ms`);
};
BuildTimeMeasurementLoader.pitch = (content: string, meta: ExtendedLoaderMeta) => {
if (!meta[metaKey]) {
meta[metaKey] = {
startTime: Date.now(),
contentLength: content.length,
};
}
};
const { component, cleanup } = await asyncTsxToElement({
rules: [
{ test: /\.less$/, use: [BuildTimeMeasurementLoader, LessLoader] },
],
});
In this example, BuildTimeMeasurementLoader
will measure the time cost of the LessLoader
and log it to the console. The execution order is:
BuildTimeMeasurementLoader.pitch
, which will store the start time and content length in the meta
object.LessLoader.pitch
(if exists).LessLoader
, which should transform the Less content to CSS.BuildTimeMeasurementLoader
, which will log the time cost based on the previously stored start time.An example message in console might be:
LessLoader: transform /style.less (995 chars) cost 4ms
displayName
Fieldstring
TsxToElement
.window
, URL and CDN.enforce
and pitch
fields work more like Webpack.MIT
FAQs
Transform a set of `.tsx` file into React element, extremely useful for the "live edit" feature in component library documents.
We found that tsx-browser-compiler demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.