Webformula core
Welcome Getting started Routing Serve / Build Signals and binding Web component Templates Fetcher Multiple languages github-circle-black-transparent GitHub github-circle-black-transparent Example app

Getting started

Build a basic web application
Install
        
  npm install @webformula/core
        
      
Routing
@Webformula/core uses directory based routing. All routes go in a 'routes' folder.
        
  app/
  └── routes/
      ├── index/
      │   └── index.js     # /
      ├── 404/
      │   └── index.js     # /404 (or any url that is not found)
      ├── one/
      │   └── index.js     # one/
      ├── two[id?]/
      │   └── index.js     # two/:id?
      ├── three/
      │   └── [id]/
      │       └── index.js # three/:id
      └── four/
          └── [...all]/
              └── index.js # four/* (four/a/b/)
        
      
app/routes/index/index.js → /
app/routes/one/index.js → one
app/routes/two[id?]/index.js → two/:id?
app/routes/three/[id]/index.js → three/:id
app/routes/four/[...rest]/index.js → four/*
Directory route details
routes/index/index.js Root page (/)
routes/404/index.js Not found page. Auto redirect on non matching routes
index.js Route component file
[id] Directory that represents a url parameter
[id?] Directory that represents an options url parameter
name[id?] Inline url parameter to avoid sub folder
[...rest] Directory that represents catch-all route
[...rest?] Directory that represents optional catch-all route
index.html
        
  <!doctype html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="Cache-Control" content="no-store" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
      <title></title>
      
      <!-- app.js and app.css will automatically be updated to match bundle outputs -->
      <link href="app.css" rel="stylesheet">
      <script src="app.js" type="module"></script>
    </head>
    
    <body>
      <!-- page template render into this element -->
      <page-content></page-content>

      <!-- Alternative using id attribute -->
      <div id="page-content"></div>
    </body>
  </html>
        
      
Main application file
app.js
Required: Automatically uses app.js as entry file for bundling
        
  /* Main app file
  * you can import any code in here
  */
  
  import someModule from './someModule.js';
  
  // routes are automatically loaded
        
      
Prevent navigation allows you to lock down the app for uses like authentication
        
  import { preventNavigation } from '@webformula/core';
  
  // if not authenticated redirect to login and prevent navigation
  if (!document.cookie.includes('authenticated=true')) {
    if (location.pathname !== '/login') location.href = '/login';
    preventNavigation(true);
  }
        
      
Main application Styles
app.css
Optional: Will bundle and minify into a single file
        
  @import url('./other.css');

  body {
    background-color: white;
  }
        
      
Page
page.js and page.html
        
  import { Component, Signal, html } from '@webformula/core';
  import htmlTemplate from './page.html'; // automatically bundles
  
  export default class extends Component {
    // html page title
    static pageTitle = 'Page';

    /**
     * Pass in HTML string. Use for imported .HTML
     * Supports template literals: <div>${this.var}</div>
     * @type {String}
     */
    static htmlTemplate = htmlTemplate;

    someVar = new Signal('Some var');
    clickIt_bound = this.clickIt.bind(this);
    
    
    constructor() {
      super();
    }
    
    connectedCallback() {
      console.log(this.urlParameters); // { id: 'value' }
      console.log(this.searchParameters); // { id: 'value' }
    }
    
    disconnectedCallback() { }
    
    // not called on initial render
    beforeRender() { }
    
    afterEnder() {
      this.querySelector('#event-listener-button').addEventListener('click', this.clickIt_bound);
    }
    
    clickIt() {
      console.log('clicked it!');
    }
    
    changeValue() {
      this.someVar.value = 'Value updated';
    }
    
    /**
    * Alternative method for html templates, instead of importing html file
    */
    template() {
      return html`
        <div>Page Content</div>
        <div>${this.someVar}</div>

        ${
          // nested html
          this.show ? html`<div>Showing</div>` : ''
        }

        <!--
          You can comment out expressions
          text
        -->
        
        <!-- "page" will reference the current page class -->
        <button onclick="page.clickIt()">Click Method</button>
        <button id="event-listener-button">Event listener</button>
        <button onclick="page.changeValue()">Change value</button>
      `;
    }
  }
        
      
Build app
build.js
No need for webpack or other bundlers. ESbuild is packed in and pre configured.
Build config
        
  import build from '@webformula/core/build';
  
  /**
  * runs dev server by default on port 3000 with livereload
  * basedir defaults to 'app/'
  */
  build({ basedir: 'app/' });
      
    
Run commands
      
  # Development run
  node build.js

  # Development run with watch to enable livereload
  node --watch-path=./app build.js

  # Production run. minifies and gzips
  NODE_ENV=production node build.js
  
        
      
Serve app
server.js
Handle routing and file serving with middleware. GZIP compression is automatically handled.
Serve config
Native server
        
  import { createServer } from 'node:http';
  import { middlewareNode } from '@webformula/core/middleware';
  
  const middleware = middlewareNode({
    basedir: 'docs/',
    outdir: 'dist/',
    copyFiles: [
      { from: 'docs/favicon.ico', to: 'dist/' }
    ]
  });
  
  createServer(async (req, res) => {
    const handled = await middleware(req, res);
    if (handled === true) return;
    
    // Do other stuff
  }).listen(3000);
        
      
Express server
        
  import express from 'express';
  import { middlewareExpress } from '@webformula/core/middleware';
  
  const app = express();
  app.use(middlewareExpress({
    basedir: 'docs/',
    outdir: 'dist/',
    copyFiles: [
      { from: 'docs/favicon.ico', to: 'dist/' }
    ]
  }));
  app.use(express.static('./docs'));
  app.listen(3000);
        
      
Livereload
        
  # Simply use node --watch to enable livereload
  node --watch-path=./src --watch-path=./docs server.js