├── .gitignore ├── README.md ├── docker-compose.yml ├── nginx.conf ├── pure-web-component-chart-app ├── package.json ├── src │ ├── assets │ │ ├── img │ │ │ └── 290408.png │ │ └── js │ │ │ └── com.bundle.js │ ├── components │ │ └── chart-container.js │ ├── index.html │ └── index.js └── webpack.config.js ├── pure-web-component-main-app ├── package.json ├── src │ ├── assets │ │ └── js │ │ │ ├── chart.bundle.js │ │ │ └── menu.bundle.js │ ├── components │ │ └── main-layout.js │ ├── index.html │ └── index.js └── webpack.config.js └── pure-web-component-menu-app ├── package.json ├── src ├── assets │ └── img │ │ └── 290408.png ├── components │ └── left-menu.js ├── index.html └── index.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | pure-web-component-chart-app/node_modules/ 2 | pure-web-component-main-app/node_modules/ 3 | pure-web-component-menu-app/dist/ 4 | pure-web-component-menu-app/node_modules/ 5 | */.idea/* 6 | pure-web-component-chart-app/dist/ 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is the demo project to prove the potential of micro frontend architecture. 2 | 3 | # Build the micro apps 4 | 5 | Go to pure-web-component-menu-app and run 6 | 7 | ```sh 8 | npm install 9 | ``` 10 | After successfully installing dependencies we build the project 11 | ```sh 12 | npm run build 13 | ``` 14 | 15 | Repeat above steps with pure-web-component-chart-app and pure-web-component-main-app 16 | 17 | 18 | 19 | # Run app 20 | 21 | We simulate the way of micro frontends works, so we need Docker to create many nodes of FE. The Docker installed in your pc is required. 22 | 23 | Create a docker network by running the command: 24 | 25 | Mac OS 26 | ```sh 27 | docker network create nginx_network 28 | ``` 29 | Windows 30 | ```sh 31 | docker network create --driver nat nginx_network 32 | ``` 33 | 34 | Start the docker containers by run 35 | 36 | ```sh 37 | docker-compose up 38 | ``` 39 | 40 | Launch the browser navigate to [Localhost](http://localhost:8000/) 41 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx: 4 | image: nginx 5 | volumes: 6 | - ./nginx.conf:/etc/nginx/conf.d/default.conf 7 | - ./pure-web-component-main-app/dist/:/home/main/ 8 | - ./pure-web-component-menu-app/dist/:/home/left-side/ 9 | - ./pure-web-component-chart-app/dist/:/home/right-side/ 10 | ports: 11 | - 8000:8000 12 | - 2000:2000 13 | - 3000:3000 14 | networks: 15 | - nginx_network 16 | networks: 17 | nginx_network: 18 | external: true 19 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | server { 3 | server_name gateway; 4 | listen 8000; 5 | 6 | location /left { 7 | proxy_pass http://localhost:2000; 8 | } 9 | 10 | location /right { 11 | proxy_pass http://localhost:3000; 12 | } 13 | 14 | location / { 15 | root /home/main/; 16 | } 17 | } 18 | 19 | server { 20 | server_name left; 21 | listen 2000; 22 | 23 | location / { 24 | root /home/left-side/; 25 | } 26 | } 27 | server { 28 | server_name right; 29 | listen 3000; 30 | 31 | location / { 32 | root /home/right-side/; 33 | } 34 | } -------------------------------------------------------------------------------- /pure-web-component-chart-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pure-web-component-main-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --mode production", 8 | "watch": "webpack --watch", 9 | "start": "webpack-dev-server --open" 10 | }, 11 | "devDependencies": { 12 | "@webcomponents/webcomponentsjs": "^2.4.4", 13 | "clean-webpack-plugin": "^3.0.0", 14 | "copy-webpack-plugin": "^6.0.3", 15 | "css-loader": "^4.2.2", 16 | "file-loader": "^6.0.0", 17 | "html-webpack-plugin": "^4.3.0", 18 | "sass-loader": "^10.0.1", 19 | "style-loader": "^1.2.1", 20 | "url-loader": "^4.1.0", 21 | "webpack": "^4.44.1", 22 | "webpack-cli": "^3.3.12", 23 | "webpack-dev-server": "^3.11.0" 24 | }, 25 | "author": "", 26 | "license": "ISC", 27 | "dependencies": { 28 | "chart.js": "^2.9.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pure-web-component-chart-app/src/assets/img/290408.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo-shared/microfrontend-pure-webcomponent/8f2ec837ebb03404e203f751c4d5cf29f681afdf/pure-web-component-chart-app/src/assets/img/290408.png -------------------------------------------------------------------------------- /pure-web-component-chart-app/src/assets/js/com.bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);class r extends HTMLElement{connectedCallback(){const e=document.createElement("div");this.attachShadow({mode:"open"}).appendChild(e)}}customElements.define("r-left-menu",r);class o extends HTMLElement{connectedCallback(){this.innerHTML='
'}}customElements.define("r-widget-frame",o);class c extends HTMLElement{connectedCallback(){this.innerHTML='
\n This is chart\n
'}}customElements.define("r-main-layout",c)}]); -------------------------------------------------------------------------------- /pure-web-component-chart-app/src/components/chart-container.js: -------------------------------------------------------------------------------- 1 | import Chart from 'chart.js'; 2 | export class ChartContainer extends HTMLElement { 3 | 4 | get type() { 5 | return this.getAttribute('type'); 6 | } 7 | set type(newVal) { 8 | this.setAttribute('type', newVal); 9 | } 10 | 11 | 12 | constructor() { 13 | super(); 14 | this.innerHTML = this.renderPage(); 15 | this.initChart(); 16 | } 17 | 18 | renderPage() { 19 | return ` 20 |
21 | 22 |
`; 23 | } 24 | initChart(){ 25 | const chartType = this.type; 26 | const ctx = document.getElementById('myChart'); 27 | const myChart = new Chart(ctx, { 28 | type: chartType, 29 | data: { 30 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], 31 | datasets: [{ 32 | label: '# of Votes', 33 | data: [12, 19, 3, 5, 2, 3], 34 | backgroundColor: [ 35 | 'rgba(255, 99, 132, 0.2)', 36 | 'rgba(54, 162, 235, 0.2)', 37 | 'rgba(255, 206, 86, 0.2)', 38 | 'rgba(75, 192, 192, 0.2)', 39 | 'rgba(153, 102, 255, 0.2)', 40 | 'rgba(255, 159, 64, 0.2)' 41 | ], 42 | borderColor: [ 43 | 'rgba(255, 99, 132, 1)', 44 | 'rgba(54, 162, 235, 1)', 45 | 'rgba(255, 206, 86, 1)', 46 | 'rgba(75, 192, 192, 1)', 47 | 'rgba(153, 102, 255, 1)', 48 | 'rgba(255, 159, 64, 1)' 49 | ], 50 | borderWidth: 1 51 | }] 52 | }, 53 | options: { 54 | scales: { 55 | yAxes: [{ 56 | ticks: { 57 | beginAtZero: true 58 | } 59 | }] 60 | } 61 | } 62 | }); 63 | } 64 | 65 | } 66 | customElements.define('chart-container', ChartContainer); 67 | -------------------------------------------------------------------------------- /pure-web-component-chart-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example of Webcomponent 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /pure-web-component-chart-app/src/index.js: -------------------------------------------------------------------------------- 1 | import './components/chart-container'; -------------------------------------------------------------------------------- /pure-web-component-chart-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | 6 | module.exports = (env, argv)=>({ 7 | mode: 'development', 8 | entry: { 9 | app: './src/index.js' 10 | }, 11 | devServer: { 12 | contentBase: path.join(__dirname, 'dist'), 13 | historyApiFallback: true 14 | }, 15 | devtool: argv.mode === 'production'? 'none' : 'inline-source-map', 16 | externals: { 17 | 'react': {commonjs: 'react'}, 18 | 'react-dom': {commonjs: 'react-dom'} 19 | }, 20 | plugins: [ 21 | new CleanWebpackPlugin(), 22 | new HtmlWebpackPlugin({ 23 | template: './src/index.html' 24 | }), 25 | new CopyWebpackPlugin({ 26 | patterns: [ 27 | { 28 | context: 'node_modules/@webcomponents/webcomponentsjs', 29 | from: '**/*.js', 30 | to: 'webcomponents' 31 | }, 32 | { 33 | from: './src/assets/img/*', 34 | to: './', 35 | flatten: true 36 | } 37 | ] 38 | }) 39 | ], 40 | output: { 41 | filename: 'chart.bundle.js', 42 | path: path.resolve(__dirname, 'dist') 43 | }, 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.tsx?$/, 48 | use: 'ts-loader', 49 | exclude: /node_modules/ 50 | }, 51 | { 52 | test: /\.css$/, 53 | include: path.resolve(__dirname, 'src'), 54 | use: [ 55 | 'style-loader', 56 | 'css-loader' 57 | ] 58 | }, 59 | {test: /\.s[a|c]ss$/, use: [{loader: "style-loader"}, {loader: "css-loader"}, {loader: "sass-loader"}]}, 60 | {test: /\.(png|gif|jpg|cur)$/i, loader: 'url-loader', options: {limit: 8192}}, 61 | { 62 | test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/i, 63 | loader: 'url-loader', 64 | options: {limit: 10000, mimetype: 'application/font-woff2'} 65 | }, 66 | { 67 | test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i, 68 | loader: 'url-loader', 69 | options: {limit: 10000, mimetype: 'application/font-woff'} 70 | }, 71 | {test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'file-loader'} 72 | ] 73 | }, 74 | resolve: { 75 | extensions: ['.js', 'jsx'] 76 | } 77 | }); -------------------------------------------------------------------------------- /pure-web-component-main-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pure-web-component-main-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --mode production", 8 | "watch": "webpack --watch", 9 | "start": "webpack-dev-server --open" 10 | }, 11 | "devDependencies": { 12 | "@webcomponents/webcomponentsjs": "^2.4.4", 13 | "clean-webpack-plugin": "^3.0.0", 14 | "copy-webpack-plugin": "^6.0.3", 15 | "css-loader": "^4.2.2", 16 | "file-loader": "^6.0.0", 17 | "html-webpack-plugin": "^4.3.0", 18 | "sass-loader": "^10.0.1", 19 | "style-loader": "^1.2.1", 20 | "url-loader": "^4.1.0", 21 | "webpack": "^4.44.1", 22 | "webpack-cli": "^3.3.12", 23 | "webpack-dev-server": "^3.11.0" 24 | }, 25 | "author": "", 26 | "license": "ISC", 27 | "dependencies": { 28 | "@google-web-components/google-chart": "^4.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pure-web-component-main-app/src/assets/js/menu.bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);class i extends HTMLElement{get items(){return JSON.parse(this.getAttribute("items"))}set items(e){this.setAttribute("items",JSON.stringify(e))}get active(){return this.getAttribute("active")}set active(e){this.setAttribute("active",e)}get template(){const e=document.createElement("template");return e.innerHTML='\n
\n
\n \n
\n
',e}constructor(){super(),this.attachShadow({mode:"open"}),this.items=[{id:"bar",name:"Bar chart"},{id:"line",name:"Line chart"},{id:"radar",name:"Radar chart"},{id:"bubble",name:"Bubble chart"},{id:"pie",name:"Pie chart"}],this.shadowRoot.appendChild(this.template.content.cloneNode(!0)),this.renderChildren()}renderChildren(){const e=this.shadowRoot.querySelector("#menu-list");this.items&&this.items.length>0&&this.items.forEach(t=>{const n=document.createElement("li");n.className="list-group-item"+(this.active===t.id?" active":""),n.innerText=t.name,n.key=t.id,n.addEventListener("click",e=>{this.dispatchEvent(new CustomEvent("my-event",{bubbles:!0,detail:{menuId:n.key}}))}),e.appendChild(n)})}}customElements.define("left-menu",i)}]); -------------------------------------------------------------------------------- /pure-web-component-main-app/src/components/main-layout.js: -------------------------------------------------------------------------------- 1 | 2 | export class MainLayout extends HTMLElement { 3 | 4 | get active() { 5 | return this.getAttribute('active'); 6 | } 7 | set active(newVal) { 8 | this.setAttribute('active', newVal); 9 | } 10 | 11 | static get observedAttributes() { 12 | return ['active']; 13 | } 14 | constructor() { 15 | super(); 16 | this.active = 'bar'; 17 | this.innerHTML = this.renderPage(); 18 | this.addEventListener('my-event', (event)=> { 19 | this.setAttribute('active', event.detail.menuId); 20 | this.active = event.detail.menuId; 21 | }); 22 | } 23 | 24 | renderPage() { 25 | return ` 26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 |
`; 34 | } 35 | 36 | 37 | attributeChangedCallback(name, oldValue, newValue) { 38 | const isNew = oldValue !== newValue; 39 | if (name === 'active' && isNew) { 40 | this.innerHTML = this.renderPage(); 41 | } 42 | } 43 | } 44 | customElements.define('main-layout', MainLayout); 45 | -------------------------------------------------------------------------------- /pure-web-component-main-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example of Webcomponent 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pure-web-component-main-app/src/index.js: -------------------------------------------------------------------------------- 1 | import './components/main-layout'; 2 | -------------------------------------------------------------------------------- /pure-web-component-main-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | 6 | module.exports = (env, argv)=>({ 7 | mode: 'development', 8 | entry: { 9 | app: './src/index.js' 10 | }, 11 | devServer: { 12 | contentBase: path.join(__dirname, 'dist'), 13 | historyApiFallback: true 14 | }, 15 | devtool: argv.mode === 'production'? 'none' : 'inline-source-map', 16 | externals: { 17 | 'react': {commonjs: 'react'}, 18 | 'react-dom': {commonjs: 'react-dom'} 19 | }, 20 | plugins: [ 21 | new CleanWebpackPlugin(), 22 | new HtmlWebpackPlugin({ 23 | template: './src/index.html' 24 | }), 25 | new CopyWebpackPlugin({ 26 | patterns: [ 27 | { 28 | context: 'node_modules/@webcomponents/webcomponentsjs', 29 | from: '**/*.js', 30 | to: 'webcomponents' 31 | }, 32 | { 33 | from: './src/assets/js/*', 34 | to: './', 35 | flatten: true 36 | } 37 | ] 38 | }) 39 | ], 40 | output: { 41 | filename: '[name].bundle.js', 42 | path: path.resolve(__dirname, 'dist') 43 | }, 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.tsx?$/, 48 | use: 'ts-loader', 49 | exclude: /node_modules/ 50 | }, 51 | { 52 | test: /\.css$/, 53 | include: path.resolve(__dirname, 'src'), 54 | use: [ 55 | 'style-loader', 56 | 'css-loader' 57 | ] 58 | }, 59 | {test: /\.s[a|c]ss$/, use: [{loader: "style-loader"}, {loader: "css-loader"}, {loader: "sass-loader"}]}, 60 | {test: /\.(png|gif|jpg|cur)$/i, loader: 'url-loader', options: {limit: 8192}}, 61 | { 62 | test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/i, 63 | loader: 'url-loader', 64 | options: {limit: 10000, mimetype: 'application/font-woff2'} 65 | }, 66 | { 67 | test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i, 68 | loader: 'url-loader', 69 | options: {limit: 10000, mimetype: 'application/font-woff'} 70 | }, 71 | {test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'file-loader'} 72 | ] 73 | }, 74 | resolve: { 75 | extensions: ['.js', 'jsx'] 76 | } 77 | }); -------------------------------------------------------------------------------- /pure-web-component-menu-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pure-web-component-menu-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --mode production", 8 | "watch": "webpack --watch", 9 | "start": "webpack-dev-server --open" 10 | }, 11 | "devDependencies": { 12 | "@webcomponents/webcomponentsjs": "^2.4.4", 13 | "clean-webpack-plugin": "^3.0.0", 14 | "copy-webpack-plugin": "^6.0.3", 15 | "css-loader": "^4.2.2", 16 | "file-loader": "^6.0.0", 17 | "html-webpack-plugin": "^4.3.0", 18 | "sass-loader": "^10.0.1", 19 | "style-loader": "^1.2.1", 20 | "url-loader": "^4.1.0", 21 | "webpack": "^4.44.1", 22 | "webpack-cli": "^3.3.12", 23 | "webpack-dev-server": "^3.11.0" 24 | }, 25 | "author": "", 26 | "license": "ISC", 27 | "dependencies": {} 28 | } 29 | -------------------------------------------------------------------------------- /pure-web-component-menu-app/src/assets/img/290408.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo-shared/microfrontend-pure-webcomponent/8f2ec837ebb03404e203f751c4d5cf29f681afdf/pure-web-component-menu-app/src/assets/img/290408.png -------------------------------------------------------------------------------- /pure-web-component-menu-app/src/components/left-menu.js: -------------------------------------------------------------------------------- 1 | export class LeftMenu extends HTMLElement { 2 | 3 | get items() { 4 | return JSON.parse(this.getAttribute('items')); 5 | } 6 | set items(newVal) { 7 | this.setAttribute('items', JSON.stringify(newVal)); 8 | } 9 | 10 | get active() { 11 | return this.getAttribute('active'); 12 | } 13 | set active(newVal) { 14 | this.setAttribute('active', newVal); 15 | } 16 | 17 | get template() { 18 | const template = document.createElement('template'); 19 | template.innerHTML = ` 20 |
21 |
22 | 24 |
25 |
`; 26 | return template; 27 | } 28 | 29 | 30 | constructor() { 31 | super(); 32 | this.attachShadow({ 33 | mode: 'open' 34 | }); 35 | 36 | this.items = [ 37 | { 38 | id: 'bar', 39 | name: 'Bar chart', 40 | },{ 41 | id: 'line', 42 | name: 'Line chart' 43 | },{ 44 | id: 'radar', 45 | name: 'Radar chart' 46 | },{ 47 | id: 'bubble', 48 | name: 'Bubble chart' 49 | },{ 50 | id: 'pie', 51 | name: 'Pie chart' 52 | } 53 | ]; 54 | this.shadowRoot.appendChild(this.template.content.cloneNode(true)); 55 | this.renderChildren(); 56 | } 57 | 58 | renderChildren(){ 59 | const parent = this.shadowRoot.querySelector('#menu-list'); 60 | if(this.items && this.items .length >0) { 61 | this.items.forEach(item => { 62 | const li = document.createElement('li') 63 | li.className = `list-group-item${this.active === item.id?' active' : ''}`; 64 | li.innerText = item.name; 65 | li.key = item.id; 66 | li.addEventListener('click', (event)=> { 67 | this.dispatchEvent(new CustomEvent('my-event', { 68 | bubbles: true, 69 | detail: { 70 | menuId: li.key 71 | } 72 | })); 73 | }); 74 | parent.appendChild(li); 75 | }); 76 | } 77 | } 78 | 79 | 80 | } 81 | customElements.define('left-menu', LeftMenu); 82 | -------------------------------------------------------------------------------- /pure-web-component-menu-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example of Webcomponent 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pure-web-component-menu-app/src/index.js: -------------------------------------------------------------------------------- 1 | import './components/left-menu'; -------------------------------------------------------------------------------- /pure-web-component-menu-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | 6 | module.exports = (env, argv)=>({ 7 | mode: 'development', 8 | entry: { 9 | app: './src/index.js' 10 | }, 11 | devServer: { 12 | contentBase: path.join(__dirname, 'dist'), 13 | historyApiFallback: true 14 | }, 15 | devtool: argv.mode === 'production'? 'none' : 'inline-source-map', 16 | externals: { 17 | 'react': {commonjs: 'react'}, 18 | 'react-dom': {commonjs: 'react-dom'} 19 | }, 20 | plugins: [ 21 | new CleanWebpackPlugin(), 22 | new HtmlWebpackPlugin({ 23 | template: './src/index.html' 24 | }), 25 | new CopyWebpackPlugin({ 26 | patterns: [ 27 | { 28 | context: 'node_modules/@webcomponents/webcomponentsjs', 29 | from: '**/*.js', 30 | to: 'webcomponents' 31 | }, 32 | { 33 | from: './src/assets/img/*', 34 | to: './', 35 | flatten: true 36 | } 37 | ] 38 | }) 39 | ], 40 | output: { 41 | filename: 'menu.bundle.js', 42 | path: path.resolve(__dirname, 'dist') 43 | }, 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.css$/, 48 | include: path.resolve(__dirname, 'src'), 49 | use: [ 50 | 'style-loader', 51 | 'css-loader' 52 | ] 53 | }, 54 | {test: /\.s[a|c]ss$/, use: [{loader: "style-loader"}, {loader: "css-loader"}, {loader: "sass-loader"}]}, 55 | {test: /\.(png|gif|jpg|cur)$/i, loader: 'url-loader', options: {limit: 8192}}, 56 | { 57 | test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/i, 58 | loader: 'url-loader', 59 | options: {limit: 10000, mimetype: 'application/font-woff2'} 60 | }, 61 | { 62 | test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i, 63 | loader: 'url-loader', 64 | options: {limit: 10000, mimetype: 'application/font-woff'} 65 | }, 66 | {test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'file-loader'} 67 | ] 68 | }, 69 | resolve: { 70 | extensions: ['.js', 'jsx'] 71 | } 72 | }); --------------------------------------------------------------------------------