How to load web components in webpack project?

Viewed 5024

I have started experimenting with webpack and webcomponents. So far I managed only to run this basic web component example without webpack. If I simply load the web component in the index.html using a <script> tag than I get it's content rendered. However, when I try to run the same example using a basic webpack setup I don't see the rendered template.

The server is launched successfully and I can see the console init message. What is the missing step? I would expect to be able to concatenate them this way.

I have the following files:

index.html

<!DOCTYPE html>
<html lang="en">

    <head>
        <base href="/">
        <meta charset="UTF-8">
        <title>Web Components App</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>

    <body>
        <vs-app></vs-app>
    </body>

</html>

main.ts

module.hot && module.hot.accept();

// Components
import { App } from './app';

// Styles
require('./shared/scss/main.scss');

console.log('Initialise Main');

app.ts

/**
 * App
 */
export class App extends HTMLElement {

    constructor() {
        super();
    }

    connectedCallback() {
        this.innerHTML = this.template;
    }

    get template() {
        return `
        <div>This is a div</div>
        `;
    }
}
window.customElements.define('vs-app', App);

webpack-common.js

const webpack = require("webpack"),
    path = require("path"),
    CopyWebpackPlugin = require('copy-webpack-plugin'),
    HtmlWebpackPlugin = require('html-webpack-plugin'),
    ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');

// Constants
const BUILD_DIR = path.join(__dirname, "../../build"),
    PUBLIC_DIR = path.join(__dirname, "../../public"),
    NODE_MODULES_DIR = path.join(__dirname, "../../node_modules");

// Shared between prod and dev
module.exports = {

    entry: "./public/main.ts",

    output: {
        // Since webpack-dev-middleware handles the files in memory, path property is used only in production.
        path: BUILD_DIR,
        publicPath: "/",
        filename: "bundle.js"
    },

    resolve: {
        extensions: ["*", ".ts", ".js"]
    },

    module: {

        loaders: [{
                test: /\.ts?$/,
                loader: "awesome-typescript-loader",
                include: PUBLIC_DIR,
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                exclude: /node_modules/,
                loader: "style-loader!css-loader!autoprefixer-loader"
            },
            {
                test: /\.scss$/,
                loader: 'style-loader!css-loader!sass-loader'
            },
        ]

    },

    // Base plugins
    plugins: [

        new CopyWebpackPlugin([
            {
                from: `public/shared/images`,
                to: 'images'
            },
        ]),

        new HtmlWebpackPlugin({
            template: 'public/index.html'
        }),

        // Load bundle.js after libs are loaded
        new ScriptExtHtmlWebpackPlugin({
            defaultAttribute: 'defer'
        })
    ],

    stats: {
        colors: true,
        modules: true,
        reasons: true,
        errorDetails: true
    }
};

Edit

Eventually I got the component rendered moving the following line window.customElements.define('vs-app', App); from app.ts to main.ts. In the mean time I discovered that even that is not necessary.

// This is enough to trigger rendering
import { App } from './app';
App;

However, I still have an issue. Because of webpack hot reload, the component ends up registered twice, giving an error. Still looking for a fix.

Edit 2

After some research online I managed to find the problem: I did not have the inject: false property. This triggered webpack to load twice the same stuff. Looks like I can finally move forward with developing the app. However I would love to find out alternative ways to this setup, just to get confirmation that I'm on the right road.

new HtmlWebpackPlugin({
    template: 'public/index.html',
    inject: false 
}),
0 Answers
Related