Visitar URL original
GitHub - infernojs/swc-plugin-inferno · GitHub
Skip to content

About

No description, website, or topics provided.

Resources

Stars

9 stars

Watchers

3 watching

Forks

Repository files navigation

Inferno

Downloads Version License

InfernoJS SWC Plugin

Plugin for SWC to enable JSX/TSX for Inferno

This plugin transforms JSX and TSX code in your projects to Inferno compatible virtual DOM. It is recommended to use this plugin for compiling JSX for inferno. It is different to other JSX plugins, because it outputs highly optimized inferno specific newVNode calls. This plugin also checks children shape during compilation stage to reduce overhead from runtime application.

The plugin generates the same code as babel-plugin-inferno, and its test suite is ported to this repository (tests/babel_plugin_inferno).

Inferno versions

Version 10 of the plugin compiles JSX for Inferno 10. It calls newVNode, newComponentVNode, newFragment and newTextVNode, and writes the flags of each vNode, including the shape of its children, as one number. For example <div>Hello</div> becomes newVNode(3, "div", null, "Hello"): HtmlElement (1) and HasTextChildren (2).

For Inferno 9 and older, use version 3.x of the plugin. Inferno 10 numbers its vNode flags differently, so code compiled by version 3.x has to be compiled again for Inferno 10.

From version 10 on, the major version of the plugin matches the major version of Inferno: use plugin 10.x with Inferno 10.x, plugin 11.x with Inferno 11.x, and so on. The plugin does not declare inferno as a peer dependency, so the package manager does not check this for you. See the Inferno 10 migration guide.

The flags the plugin writes are the VNodeFlags of inferno-vnode-flags 10:

Flag Value
ComponentUnknown 0
HtmlElement 1
HasTextChildren 2
HasNonKeyedChildren 4
HasVNodeChildren 8
HasInvalidChildren 16
HasKeyedChildren 32
SvgElement 64
Fragment 256
InputElement 512
TextareaElement 2048
SelectElement 4096
ContentEditable 131072

Children that are only known at runtime, such as {expression}, get no child bit and are normalized by Inferno.

How to install

npm i --save-dev swc-plugin-inferno

Install the major version that matches your inferno version, see Inferno versions.

How to use

Add swc-plugin-inferno to .swcrc configuration

Enable jsc.parser.jsx and set swc-plugin-inferno into jsc.experimental.plugins For rest of the settings see: https://swc.rs/docs/configuration/compilation

{
  "jsc": {
    "experimental": {
         "plugins": [
            ["swc-plugin-inferno", {
              "pure": true // Enable or disable /*#__PURE__*/ annotations (default: true)
            }]
        ]
    }
  }
}

Unknown plugin options are rejected with an error. The options are:

Option Default Description
pure true Add /*#__PURE__*/ annotations, see below.
importSource "inferno" The module the helpers are imported from, see Options.
development false Enables fast refresh together with refresh. On its own it changes nothing.
refresh off Fast refresh: true, or { "refreshReg": "$RefreshReg$", "refreshSig": "$RefreshSig$", "emitFullSignatures": false }. Only applies when development is true.
uselessFlags "warn" What to do about useless flags: "warn", "error" or "off".

With "pure": true the plugin adds /*#__PURE__*/ to the calls it generates from JSX, and also to hand-written calls of Inferno factories such as forwardRef, createRef, createPortal, newVNode and the deprecated createVNode imported from inferno or from importSource. Minifiers remove these calls when their result is unused, so don't call them only for their side effects. normalizeProps is annotated only when its argument is a freshly created vNode, because it mutates the vNode passed to it.

Fast refresh

With "development": true and "refresh": true the plugin registers components for hot reloading like react-refresh/babel: components are passed to $RefreshReg$, and the hooks of a component to a signature created by $RefreshSig$. The functions are renamed with refreshReg and refreshSig. Your hot reloading runtime has to define them. Any call of a function named use followed by a capital letter counts as a hook, for example useLoaderData of inferno-router. A // @refresh reset comment in a file remounts its components on every edit.

To use SWC with Webpack install swc-loader and add it to the Webpack configuration

{
  mode: 'development',
  entry: './src/index.js',
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          // `.swcrc` can be used to configure swc
          loader: 'swc-loader',
        },
      }
    ]
  }
}

Examples

// Render a simple div
Inferno.render(<div></div>, container);

// Render a div with text
Inferno.render(<div>Hello world</div>, container);

// Render a div with a boolean attribute
Inferno.render(<div autoFocus='true' />, container);

Fragments

All the following syntaxes are reserved for Inferno's newFragment call

<>
    <div>Foo</div>
    <div>Bar</div>
</>


<Fragment>
    <div>Foo</div>
    <div>Bar</div>
</Fragment>

Special flags

This plugin provides few special compile time flags that can be used to optimize an inferno application.

// ChildFlags:
<div $HasTextChildren /> - Children is rendered as pure text
<div $HasVNodeChildren /> - Children is another vNode (Element or Component)
<div $HasNonKeyedChildren /> - Children is always array without keys
<div $HasKeyedChildren /> - Children is array of vNodes having unique keys
<div $ChildFlag={expression} /> - This attribute is used for defining children shape runtime. See inferno-vnode-flags (ChildFlags) for possible values

// Functional flags
<div $Flags={expression} /> - Replaces the vNode flags the plugin computes. See inferno-vnode-flags (VNodeFlags) for possible values

$ReCreate has been removed in Inferno 10, and the plugin reports an error for it. To re-create an element, change its key instead, for example key={version}: Inferno unmounts the old element and mounts a new one when the key changes.

A numeric $ChildFlag is written into the flags like the other child flags. Any other $ChildFlag expression is only known at runtime, so that element is compiled to the deprecated createVNode (or createFragment), which converts the value.

$Flags={expression} replaces the flags of the element; the plugin still adds the child bit of the children, and HasInvalidChildren (16) for a component.

Invalid flags

When the JSX shows that the children cannot have the shape a child flag declares, the plugin reports an error that points at the flag, and the build fails. Inferno would otherwise render the children wrong or throw in development. For example:

// An array is not a single vNode
<div $HasVNodeChildren><a/><b/></div>
<div $HasVNodeChildren>{[a, b]}</div>

// Text is not a vNode, and an element is not text
<div $HasVNodeChildren>text</div>
<div $HasTextChildren><a/></div>

// One element is not an array, and keyed children need keys
<ul $HasNonKeyedChildren><li/></ul>
<ul $HasKeyedChildren><li key="1"/><li/></ul>

// Not a ChildFlags value
<div $ChildFlag={3}>{a}</div>
  x $HasVNodeChildren needs one element or component child, but there are 2 children.
   ,-[/project/src/App.jsx:1:1]
 1 | <div $HasVNodeChildren><a/><b/></div>
   :      ^^^^^^^^^^^^^^^^^
   `----

Dynamic children such as {expression} are not checked, and neither are the children of components. The uselessFlags option does not change this check.

Useless flags

Child flags are only needed for children whose shape the plugin cannot see, such as {expression} children or a children={expression} prop. When the children are written as JSX, the plugin sets the child flags itself. It warns about flags that cannot improve the output:

// The children are known at compile time: the plugin already compiles them with HasVNodeChildren
<div $HasVNodeChildren>
  <h1>Hi</h1>
</div>

// Components get their children in props.children, so child flags do nothing
<Foo $HasKeyedChildren>{items}</Foo>

// Only one child flag applies. The order is $ChildFlag, $HasKeyedChildren, $HasNonKeyedChildren,
// $HasTextChildren, $HasVNodeChildren
<div $HasKeyedChildren $HasNonKeyedChildren>{items}</div>

// Fragments have no vNode flags
<Fragment $Flags={1}>{items}</Fragment>

The uselessFlags option sets what the plugin does about them:

  • "warn" (default): print a warning.
  • "error": fail the build with an error for each useless flag. For example, CI can use it to keep useless flags out.
  • "off": do nothing.
["swc-plugin-inferno", { "uselessFlags": "error" }]

swc does not show the warnings of plugins, so the plugin prints them to stderr itself, in the same format as babel-plugin-inferno. A warning shows the file, line and column, the reason and the code around the flag:

swc-plugin-inferno: /project/src/App.jsx:3:10: $HasVNodeChildren is not needed: the children are known at compile time, so the plugin sets their child flags. Child flags only help with dynamic children such as {expression}.
  1 | function App() {
  2 |   return (
> 3 |     <div $HasVNodeChildren>
    |          ^^^^^^^^^^^^^^^^^
  4 |       <h1>Hi</h1>
  5 |     </div>
  6 |   );

With "error" swc reports the same messages as errors, with its own code frames. Any value other than "warn", "error" or "off" fails with an error, so a typo does not turn the check off silently.

To use a different level in CI, set the plugin options in a JavaScript config, such as the swc-loader options in webpack.config.js. A folder can get its own level from a separate webpack rule.

{
  loader: 'swc-loader',
  options: {
    jsc: {
      parser: { syntax: 'ecmascript', jsx: true },
      experimental: {
        plugins: [['swc-plugin-inferno', {
          // Most CI services set CI=true
          uselessFlags: process.env.CI ? 'error' : 'warn'
        }]]
      }
    }
  }
}

Options

swc-plugin-inferno will automatically import the required methods from inferno library. There is no need to import inferno in every single JSX file. Only import the inferno specific code required by the application.

example:

import {render} from 'inferno'; // only import 'render'

// The plugin will automatically import 'newVNode'
render(<div>1</div>, document.getElementById('root'));

A helper that the file already declares at the top level, for example with import { newVNode } from 'inferno', is used instead of importing it again. In scripts (files without import or export) the helpers are read from require('inferno').

Set importSource to import the helpers from another module, like the imports option of babel-plugin-inferno:

["swc-plugin-inferno", { "importSource": "inferno-compat" }]

Troubleshoot

You can verify swc-plugin-inferno is used by looking at the compiled output. This plugin does not generate calls to createElement or h, but instead it uses low level InfernoJS API newVNode, newComponentVNode, newFragment etc. If you see your JSX being transpiled into createElement calls it is a good indication that your build configuration is not correct.

About

No description, website, or topics provided.

Resources

Stars

9 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages