Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jonathanmza/e0468bc70ceeab693d356595bec347ac to your computer and use it in GitHub Desktop.
Save jonathanmza/e0468bc70ceeab693d356595bec347ac to your computer and use it in GitHub Desktop.
Step by Step creating web components in typescript using rollup

Getting Started

  • Install Dependencies
    npm init
    npm install --save-dev ts-node typescript tslib express @types/express
    

Create your web server

  • Create server.ts in root folder of your app.
    import * as express from 'express';
    import * as http from 'http';
    import * as path from 'path';
    
    import { AddressInfo } from 'net';
    
    const app = express();
    
    const PORT = 3000;
    const HOST_NAME = `localhost`;
    const PUBLIC_FOLDER = 'public';
    
    app.use(`/${PUBLIC_FOLDER}`, express.static(path.resolve(PUBLIC_FOLDER)));
    
    app.all('/*', function(req: express.Request, res: express.Response) {
      res.sendFile('index.html', { root: path.resolve(PUBLIC_FOLDER) })
    })
    
    const server = http.createServer(app);
    server.listen(PORT, HOST_NAME)
      .on('listening', function() {
        const { port, address } = server.address() as AddressInfo;
        console.log(`Express server started on port ${port} at ${address}.`); 
      })
  • Create public folder
  • Create public/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>TypeScript Web Component</title>
    </head>
    <body>
      <h1>Hello World.</h1>
    </body>
    </html>
  • Create scripts in package.json
     "scripts": {
      "serve": "ts-node -O '{ \"module\": \"commonjs\" }' server.ts"
     } 
  • Run your server
    npm run serve
    
  • Browse your application
    http://localhost:3000
    

Create Web Components

  • Create src folder
  • Create src/hello-world.ts
  • "Hello World" Custom Elements
     class HelloWorld extends HTMLElement {
    
      connectedCallback() {
        this.innerHTML = `<h1>Hello World.</h1>`
      }
    
    }
    
    customElements.define('hello-world', HelloWorld);
  • Update public/index.html file
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>TypeScript Web Component</title>
    </head>
    <body>
      <hello-world></hello-world>
      <script src="public/bundle.js"></script>
    </body>
    </html>

Setup Build tools using Rollup

  • Add build dependencies

    npm install --save-dev rollup rollup-plugin-typescript2 rollup-plugin-node-resolve
    
  • Create build.ts

    import * as path from 'path';
    
    import { rollup } from 'rollup';
    const typescript2 = require('rollup-plugin-typescript2');
    const resolve = require('rollup-plugin-node-resolve');
    
    const ENTRY_FILE = `src/hello-world.ts`;
    
    const rollupConfig = {
      inputOptions: {
        treeshake: true,
        input: ENTRY_FILE,
        external: [],
        plugins: [
          typescript2({
            check: false,
            cacheRoot: path.join(path.resolve(), 'node_modules/.tmp/.rts2_cache'), 
            useTsconfigDeclarationDir: true       
          }),
          resolve()
        ],
        onwarn (warning) {
          if (warning.code === 'THIS_IS_UNDEFINED') { return; }
          console.log("Rollup warning: ", warning.message);
        }
      },
      outputOptions: {
        sourcemap: true,
        exports: 'named',
        file: 'public/bundle.js',
        name: 'hello-world', 
        format: 'es'
      }
    }
    
    function rollupBuild({ inputOptions, outputOptions }): Promise<any> {
      return rollup(inputOptions).then(bundle => bundle.write(outputOptions));
    }
    
    rollupBuild(rollupConfig);
  • Add scripts to your package.json

     "scripts": {
      ...
      "build": "ts-node -O '{ \"module\": \"commonjs\" }' build.ts"
     } 
  • Build & Run your application

    npm run build && npm run serve
    
  • Browse your app

    http://localhost:3000
    
  • Expected error when running the application

    • In the browser (chrome) open the DevTools (right click the browser then Inspect)
    • Go to console tab of the browser
    • Check the error (in red color)
      bundle.js:17 Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
      at new HelloWorld (bundle.js:17)
      at bundle.js:24
      
  • Cause of the error.

    This error means, that you need a polyfill for browser that do support (!) Custom Elements. 
    That sounds a bit strange, but Custom Elements are defined for EcmaScript 2015+ 
    (bases upon classes) and for supporting older browser, we normally compile down to EcmaScript 5 today. 
    The output of your javascript is in es5 format. 
    
  • How to resolve or fix?

    • Install custom-elements polyfill.
      npm i @webcomponents/custom-elements
      
    • In your server.ts file add node_modules folder as one of your static folders
       app.use(`/node_modules`, express.static(path.resolve('node_modules')));
    • Add polyfill as script tag in your public/index.html
        <script src="node_modules/@webcomponents/custom-elements/src/native-shim.js"></script>
        <script src="public/bundle.js"></script>
  • Run your application

    npm run serve
    
  • Browse your app

    http://localhost:3000
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment