New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@idio/frontend

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@idio/frontend

The Middleware To Serve Front-End JavaScript With JSX And Hot Reload.

  • 2.2.3
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
66
decreased by-12%
Maintainers
1
Weekly downloads
 
Created
Source

@idio/frontend

npm version Node.js CI

@idio/frontend is The Middleware To Serve Front-End JavaScript. It allows to set-up the modern front-end development environment where node_modules are served alongside compiled JSX code (without using Babel, see @a-la/jsx).

yarn add @idio/frontend
npm i @idio/frontend

Table Of Contents

API

The package is available by importing its default function:

import frontend from '@idio/frontend'

frontEnd(
  config=: FrontEndConfig,
): !_goa.Middleware

Create a middleware to serve Front-End JavaScript, including JSX and node_modules.

The middleware constructor will initialise the middleware function to serve files from the specified directory or directories (frontend by default). The files will be updated on-the-fly to fix their imports to relative paths (e.g., preact will be transformed into /node_modules/preact/dist/preact.mjs). Any CSS styles will also be served using an injector script.

Files served with this middleware, either transpiler or plain JS, will be cached using their mtime. There is no need to compute md5 because this middleware is meant for the development purposes, whereas production code can be built with Depack.

FrontEndConfig: Options for the middleware.

NameTypeDescriptionDefault
directory(string | !Array<string>)The directory or directories from which to serve files.frontend
mountstringThe directory on which to mount. The dirname must be inside the mount. E.g., to serve example/src/index.js from /src/index.js, the mount is example/src and directory is src..
override!Object<string, string>Instead of resolving the package.json path for packages and looking up the module and main fields, paths can be passed manually in the override. E.g., { preact: '/node_modules/preact/src/preact.js' } will serve the source code of Preact instead of the resolved dist version.-
pragmastringThe pragma function to import. This enables to skip writing h at the beginning of each file. JSX will be transpiled to have h pragma, therefore to use React it's possible to do import { createElement: h } from 'react'.import { h } from 'preact'
log(boolean | !Function)Log to console when source files were patched.false
jsxOptions!_alaJsx.ConfigOptions for the transpiler.-
exportClassesbooleanWhen serving CSS, also export class names.true
hotReload!HotReloadEnable hot reload for modules. Requires at least to implement getServer method so that WebSocket listener can be set up on the HTTP server.-

The middleware can be used in any _Koa application, or within the idio web server.

import idio, { render } from '@idio/idio'
import frontend from '@idio/frontend'

/**
 * @param {import('..').FrontEndConfig} options
 */
export default async (options = {}, src = 'example/frontend') => {
  const { url, app, router, server } = await idio({
    // logger: { use: true },
    _frontend: {
      use: true,
      middlewareConstructor() {
        return frontend({
          directory: ['example/frontend', 'example/reload'],
          ...options,
        })
      },
    },
  }, { port: process.env.PORT })
  router.get('/', async (ctx) => {
    ctx.body = render(<html>
      <head><title>Example</title></head>
      <body>
        Hello World
        <div id="app" />
        <script src="/hot-reload.js"/>
        <script type="module" src={src}/>
      </body>
    </html>, { addDoctype: true })
  })
  app.use(router.routes())
  console.error('Started on %s', url)
  return { app, url, server }
}
example/frontend
├── Component.jsx
├── index.jsx
└── style.css

The entry point

import { render } from 'preact'
import Component from './Component'
// linked node_modules are also resolved
import Form, { Input } from '@depack/form'

const component = <Component test="Welcome"/>
const form = (<Form>
  <Input placeholder="hello world"/>
</Form>)

const c = render(component, document.body)
const f = render(form, document.body)

/* IDIO HOT RELOAD */
window['idioAddHotReload'] && window['idioAddHotReload'](() => {
  render(component, document.body, c)
  render(form, document.body, f)
})

The component

import { Component } from 'preact'
import './style.css'

export default class Example extends Component {
  constructor() {
    super()
    this.state = {
      count: 0,
    }
  }
  render({ test }) {
    const { count } = this.state
    return (<div onClick={() => {
      this.setState({ count: count + 1 })
    }}>{test} + updated + {count}</div>)
  }
}

The style

body {
  background: lightcyan;
}

Chrome Example

Hot Reload

This package supports hot reload of modules, mainly for the purpose of developing Preact apps.

HotReload: Options for hot reload (real-time automatic update of code in browser).

NameTypeDescriptionDefault
pathstringThe path from which to serve the operational module that provides admin methods./hot-reload.js
ignoreNodeModulesbooleanWhether to ignore paths from node_modules.true
watchers!Object<string, !fs.FSWatcher>Pass an empty object here so that references to FSWatchers can be saved.-
getServer*() => Node.JS Docs!http.ServerThe function used to get the server to enable web socket connection.-

The middleware will append some code at the end of each original file, and when an update is detected, it will use dynamic import to get references to new methods and classes. When dealing with classes, the prototype of the original class will be changed at run-time. E.g., if a render method is changed, after updating the prototype and rerendering the whole app, a new render method will be used.

For example, there's a simple component:

import { Component } from 'preact'
import { $Example } from './style.css'

export default class Example extends Component {
  constructor() {
    super()
    this.example = this.example.bind(this)
  }
  example() {
    console.log('clicked')
  }
  render({ test }) {
    return (<div id={test} onClick={this.example}
      className={$Example}>
      Hello World
    </div>)
  }
}

export const Example2 = () => {
  return (<span>Open Source!</span>)
}

When hot reload is enabled, it's going to have an additional code at the bottom of the file when served via front-end middleware:

import { h } from '/node_modules/preact/dist/preact.mjs'
import { Component } from '/node_modules/preact/dist/preact.mjs'
import { $Example } from './style.css'

export default class Example extends Component {
  constructor() {
    super()
    this.example = this.example.bind(this)
  }
  example() {
    console.log('clicked')
  }
  render({ test }) {
    return (  h('div',{id:test, onClick:this.example,
      className:$Example},
      `Hello World`
    ))
  }
}

export let Example2 = () => {
  return (h('span',{},`Open Source!`))
}

/* IDIO HOT RELOAD */
if (window.idioHotReload) {
  let i = 0
  idioHotReload('example/reload/Example.jsx', async () => {
    i++
    const module = await import(`./Example?ihr=${i}`)
    if(`${Example2}` != `${module['Example2']}`) {
      console.log('Export %s updated', 'Example2')
      Example2 = module['Example2']
    }
    return {
      module,
      classes: {
        'default': Example,
      },
    }
  })
}

The API provided for the reload is implemented in a JS file served from /hot-reload.js path. It has 2 functions:

  • idioAddHotReload: the function to execute to rerender the app, which needs to be implemented by the developer.
  • idioHotReload: the function to register that a module can be hot-reloaded. Called by auto-generated code from the frontend middleware.

After an update is done, the app needs to be rerendered. This is why we have to update the entry file to our application a little bit:

import { render, Component } from 'preact'
import Example, { Example2 } from './Example'

class App extends Component {
  render() {
    return (<html>
      <body>
        <Example test="example"/>
        <Example2 />
      </body>
    </html>)
  }
}

const app = (<App />)
const a = render(app, window.app)

/* IDIO HOT RELOAD */
window['idioAddHotReload'] && window['idioAddHotReload'](() => {
  render(app, document.body, a)
})

In this case, we created a component and passed it for initial render into a container. The return of this function can then be used inside the idioAddHotReload listener to rerender the component without it loosing its state.

At the moment, the following works:

  • Update classes, exported like export default class and export class.
  • Update all other exports, exported like export const [A] = and export let [B] = . When exporting a const, it will be transpiled into a let, because otherwise it's impossible to update the binding.
  • Update styles dynamically upon changes to the CSS files.

What doesn't work:

  • Defining a class or a function first, and then exporting it like:
    class Example {}
    const a = () => {}
    export default Example
    export { a }
    
  • If you're binding a method in a constructor, it won't be unbound after an update, but it also won't be updated, since we don't have access to instances and only update prototypes.
    class E extends Component {
      constructor() {
        super()
        this.example = this.example.bind(this)
      }
      example() {
        // callback
      }
      render() {
        // example won't be reloaded since it was bound.
        return (<div onClick={this.example}>)
      }
    }
    

GNU Affero General Public License v3.0

Art Deco © Art Deco™ for Idio 2020 Idio

Keywords

FAQs

Package last updated on 02 Mar 2020

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc