Code Monkey home page Code Monkey logo

preact-island-plugins's Introduction

preact-island-plugins

Preact server rendered, partial hydration and islands for everyone!

Tip

If you need a more flexible and functional API for building Preact islands, check out Preland

TOC

Highlights

  • Tiny
  • Tree Shakeable
  • Flexibile / Not dependent on folder structure
    • Use either .island.js file extensions for initialize islands
    • or //@island top level file comments
    • Automatic detection of an island (since v0.1.2)
  • Lazy Hydration Modifiers - //@island lazy

Installation

The installation differs based on which plugin you wish to use.

esbuild

npm i esbuild @barelyhuman/preact-island-plugins preact

rollup

npm i rollup preact @barelyhuman/preact-island-plugins @rollup/plugin-babel @rollup/plugin-node-resolve

if using typescript, you should also add that when using rollup

npm i @rollup/plugin-typescript

Usage

// Single Import
const preactIslands = require('@barelyhuman/preact-island-plugins')

preactIslands.rollup(options)
// or
preactIslands.esbuild(options)

// Tree Shakeable Import

// For rollup
const preactIslands = require('@barelyhuman/preact-island-plugins/rollup')

// for esbuild
const preactIslands = require('@barelyhuman/preact-island-plugins/esbuild')

Both bundlers use the same Options type, please read through the API options below to configure the behaviour of the island generation

export interface Options {
  // The working directory of the project, Defaults to '.'
  rootDir: string
  // If using `atomic` components, use the baseURL to specific the path where the JS Assets will be available
  baseURL: string
  // when true, each island has it's own script for lazy loading JS and Interactivity
  atomic?: boolean
  // If working with bundlers where hashing isn't available, you can set the `hash` to true to get browsers
  //  to load the correct JS after loads
  hash?: boolean
  // The plugins use your bundler (rollup, esbuild, etc) to also bundle the client asset with it's own imports
  // so the `client` options define the behavior for that
  client: {
    // path of where to output the bundled components
    output: string
  }
}

Concepts

The overall idea is to be able to define islands without thinking about it.

The plugins provide 2 ways to do this.

File name extensiom

You name the file .island.js or .island.tsx and this will generate the island files for you according to your build configs. Make sure you go through the playground to better understand this.

Top Level comments

The other options is to prefix the code with //@island and this is to be done where you start the file without knowing if it's going to be an islad or not.

This might look, something like this

//@island

export default function Counter() {
  const [count, setCount] = useState(0)
  return <>{count}</>
}
Lazy Hydration

The islands generated by this plugin is already lazy loaded, so you don't have to ever set it up and the browser will take care of handling the cache of the file. Though, we do provide with lazy hydration which can help with performance where the JS is downloaded earlier but is not applied to the DOM element unless it's in view.

It would look something like this

//@island lazy

export default function Counter() {
  const [count, setCount] = useState(0)
  return <>{count}</>
}

You can also define the threshold of visibility by adding a number from 0 to 1 to the lazy modifier.

  • 0 - Hydrate as soon as the element is in view.
  • 0.5(default) - Hydrate after at least 50% of the element is in view
  • 1 - Hydrate after the whole element is in view
//@island lazy:0.2
//              ^ hydrate Counter after 20% of the element is in the viewport

export default function Counter() {
  const [count, setCount] = useState(0)
  return <>{count}</>
}

Limitations

  • Only allows Single default export to be an island right now
  • Bug with Text based containers #4

Example Configurations

// build.js
// for esbuild
const esbuild = require('esbuild')
const preactIslands = require('@barelyhuman/preact-island-plugins/esbuild')

esbuild
  .build({
    entryPoints: ['./server.js'],
    format: 'cjs',
    target: 'node16',
    platform: 'node',
    bundle: true,
    jsx: 'automatic',
    jsxImportSource: 'preact',
    loader: {
      '.js': 'jsx',
    },
    outdir: 'dist',
    plugins: [preactIslands()],
  })
  .then(_ => process.exit(0))
  .catch(_ => process.exit(1))

For rollup, it might look something like this

// rollup.config.js
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const { babel } = require('@rollup/plugin-babel')
const preactIslands = require('@barelyhuman/preact-island-plugins/rollup')
const { DEFAULT_EXTENSIONS } = require('@babel/core')
const typescript = require('@rollup/plugin-typescript').default

/**
 * @type {import("rollup").RollupOptions}
 */
module.exports = {
  input: 'server.js',
  output: {
    dir: 'dist',
    format: 'cjs',
  },
  plugins: [
    // helper plugins to handle typescript for the remaining of the server
    typescript({
      compilerOptions: {
        jsx: 'react-jsx',
        jsxImportSource: 'preact',
      },
    }),
    preactPlugin(),
    // subset handlers for the remaining of the server to handle jsx
    babel({
      plugins: [
        [
          '@babel/plugin-transform-react-jsx',
          { runtime: 'automatic', importSource: 'preact' },
        ],
      ],
      babelHelpers: 'bundled',
      extensions: [...DEFAULT_EXTENSIONS, '.ts', '.tsx'],
    }),
  ],
}

How ?

The source code is pretty small but if it's just the concept behind that you wish to understand, then please keep reading this.

Islands are normally interactive elements or tiny apps that are mounted on parts of a static html. This is done to minimize the amount of JS sent to the client by your app. A lot of frameworks already handle this for you, a few examples are:

There's tiny differences in the implementations that each of us use but the overall concept remains same. The only reason to choose this plugin would be that you don't have to migrate your whole app to the framework just to enjoy islands or get rid of let's say something like old JQuery dependencies. I like JQuery but it'll probably be easier to use something better at handling state today.

This can also be used by someone who doesn't like frameworks and would prefer working with their own set of choices / decisions in their tech stack.

Overall, it's tiny enough to build your own framework on top off and also shove it down the structure you already have.

FAQS

What on earth in islands?

Who's this library/plugins for?

  • Anyone who wishes to setup partial hydration on an existing server codebase.
  • People building meta frameworks for preact

Examples, please?

  • Sure, you can go through the playground folder to see how to use it with esbuild and rollup with an express server. If you have any problems setting it up still, feel free to raise an issue.

Contributing

Contributions are welcome! Here's how you can get involved:

  1. Fork the project repository.
  2. Create a new branch for your feature or bug fix.
  3. Make your changes and commit them, following the project's code style guidelines.
  4. Push your changes to your forked repository.
  5. Submit a pull request with a description of your changes.

License

This project is licensed under the MIT License.

preact-island-plugins's People

Contributors

barelyhuman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

kylesmith0905

preact-island-plugins's Issues

Typescript support

Add in better typescript support without loosing the acorn-js speed.

The fix is simple, add a transformation step for the ts modules to js before moving forward, since the remaining process is based on static analysis of the code.

Experiment: No Build server side solution

Since most of the work is actually done using ASTs, a node loader or register should be able to generate the required files without the need of any build tools.

This would prove helpful for cases where people who aren't using build tools can just add island generation at request and could also help in creating tiny meta frameworks without much effort.

Feat: Automatic Mode

Auto detect islands in a file

The ability to be able to create islands without having to think about it.

There's a few possible solutions

  • Look for use hook imports or signal imports
  • Check if event listeners are being bound on any of the element at the AST level, just the existence of the attribute should be enough to mark the component as an island

The following are things that need to be done before implementing automatic islands

  • Multiple island exports

When using automatic, users will not really think about making default exports which is currently a limitation but easier to reason about since you declare the file as island using .island.js extension or //@island top level comment specifier.

Named default export declaration handling

Add in handling for cases where the default exports are done in this manner

export {
Component as default 
}

These are exports that bundlers and transformers generate so any plugin or preset that does this will be discarded as an island right now.

The solution while simple does require a few changes to the plugin.js code and the ast.js code.

Text Content being duplicated

Any island tree being constructed inside a textContent based DOM Node get's replicated.

Example:

const HelloIsland = ()=>{
 return <h1>Hello</h1>
}


export const get = (ctx)=>{
  return (
    <>
      <p>
        <HelloIsland />
      </p>
    </>
  );
}

Would end up creating multiple <h1> and p tags

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.