- Install Dependencies
npm init npm install --save-dev ts-node typescript tslib express @types/express
- 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
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>
-
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 addnode_modules
folder as one of your static foldersapp.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>
- Install custom-elements polyfill.
-
Run your application
npm run serve
-
Browse your app
http://localhost:3000