How to compile a js file as library using webpack and babel - javascript

Hi how do I compile a single js class file as library using webpack to generate an output that can be included in script tag
<script src='demo/demo.js'></script>
and also an output that can be import
import demo from 'demo'

If you want to use the demo library in different environments such as AMD, CommonJS, Nodejs. You need to use output.library option with its type set to 'umd'.
Besides, we need to set output.globalObject to option to 'this'
When targeting a library, especially the libraryTarget is 'umd', this option indicates what global object will be used to mount the library. To make UMD build available on both browsers and Node.js, set output.globalObject option to 'this'.
An example:
demo/src/demo.js:
export class Demo {
sayHello() {
console.log("hello!");
}
}
demo/webpack.config.js:
const path = require("path");
module.exports = {
entry: "./src/demo.js",
output: {
path: path.resolve(__dirname, "lib"),
filename: "demo.js",
library: {
name: "demo",
type: "umd",
},
globalObject: "this",
},
mode: "development",
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
],
},
};
demo/package.json:
{
"name": "demo",
"version": "1.0.0",
"main": "lib/demo.js",
"devDependencies": {
"#babel/core": "^7.15.0",
"#babel/preset-env": "^7.15.0",
"babel-loader": "^8.2.2",
"webpack": "^5.51.1",
"webpack-cli": "^4.8.0"
}
}
Consumer side
Use the demo library in the browser by script tag:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src='./demo/lib/demo.js'></script>
<script>
window.onload = () => {
const { Demo } = window.demo;
const demo = new Demo()
demo.sayHello();
}
</script>
</body>
</html>
Use the demo in index.js file by ES6 import syntax.
index.js:
import { Demo } from "./demo/lib/demo";
const d = new Demo();
d.sayHello();
Consumer side webpack.config.js:
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname),
filename: "index-bundled.js",
},
mode: "development",
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
],
},
};
After generating the index-bundled.js file, run node index-bundled.js. Output:
⚡ node index-bundled.js
hello!

Related

How to inject css styles from file to WebComponent

I am wondering is it possible to inject .css styles imported from file to WebComponent (If yes then please tell me how can I achieve this). I used Webpack style-loader to load .css styles inside .js a file but then I do not know how (or is it possible) to inject those styles into WebComponent the template.
I know that I can export styles from .js file declared in template string but it is not a solution which I am looking for.
I created a repository with simple example which you can find here: inject-css-from-file. I also include those files here:
index.html :
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Page Title</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
</head>
<body>
<kk-app></kk-app>
</body>
</html>
index.js :
import style from "./index.css";
const template = `
<style>${style}</style>
<div>
<p>Example component</p>
</div>
`;
export class App extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = template;
}
}
customElements.define('kk-app', App);
index.css :
p {
color: red;
}
webpack.config.js :
const HTMLWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
resolve: {
extensions: ['.js'],
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
},
plugins: [
new HTMLWebpackPlugin({
template: path.resolve(__dirname, 'index.html'),
filename: 'index.html',
}),
]
};
So the solution for that was to remove style-loader from webpack.config.js. Rule for .css file should look like that:
rules: [
{
test: /\.css$/,
use: ['css-loader'],
},
],
I do not know why but style-loader changed this .css styles into an object.
P.S. If you are using MiniCssExtractPlugin.loader it will cause same problem

Phoenix 1.4 server is delivering default HTML instead of requested JS files

I am trying to render Vue.js directly in my Phoenix 1.4 application. But the following error is occurring after installing Vue-router:
Refused to execute script from 'http://localhost:4000/0.app.js'
because its MIME type ('text/html') is not executable, and strict MIME
type checking is enabled.
My Phoenix router.ex file looks as follows:
scope "/", Web do
pipe_through :browser
get "/*path", PageController, :index
end
My webpack.config.js:
const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = (env, options) => ({
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
optimization: {
minimizer: [
new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
new OptimizeCSSAssetsPlugin({})
]
},
entry: {
'./js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
},
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../priv/static/js')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.s(c|a)ss$/,
use: [
'css-loader',
{
loader: 'sass-loader',
// Requires sass-loader#^7.0.0
options: {
implementation: require('sass'),
fiber: require('fibers'),
indentedSyntax: true // optional
}
}
]
},
{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, use: 'url-loader' }
]
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
new VueLoaderPlugin()
]
});
I am currently seeing an issue with (I think) the cache manifest whereby my site delivers HTML and CSS files properly but for JS files, the default HTML template is being delivered - which results in an erro.
I can see in the Chrome Dev Tools Network tab that the response from this request http://localhost:4000/0.app.js is the app.html.eex file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Phoenix Framework</title>
</head>
<body>
<div id="vue-app"></div>
<script type="text/javascript" src="<%= Routes.static_path(#conn, "/js/app.js") %>"></script>
</body>
</html>
Any help greatly appreciated.
If you use a wildcard catch-all route to show your index page, and requests to resources are hitting that route, this means that Plug.Static is not set up correctly in your endpoint.ex.
Ours looks something like this:
plug Plug.Static,
at: "/", from: :my_app_name, gzip: true,
only: ~w(css images js favicon.ico robots.txt fonts)
put that anywhere before you do plug MyApp.Router.
Another reason could be that 0.app.js simply doesn't exist in priv/static/js. Check there to see if it isn't actually called app.js or has a hash appended to its name.

How to use function declared in js file that built by webpack in html?

So, what I'm trying to do is, generate a html file called index.html based on template.html, that have styling based on style.css and a function handleClick that declared in index.js. The code below works for the styling, but the handleClick is not working. Is this possbile?
This is my webpack.config.js
const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
'page': './src/index.js'
},
output: {
path: path.join(__dirname, '/dist'),
filename: "[name].js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
'babel-loader'
]
},
{
test: /\.css$/,
use: [
"style-loader",
"css-loader"
]
}
]
},
plugins: [
new HTMLWebpackPlugin({
filename: 'index.html',
template: './src/template.html'
})
]
}
this is my index.js
require('./style.css');
function handleClick(){
alert("called from index")
}
this is my template.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Page's Title</title>
</head>
<body>
<div>This background should be blue</div>
<button onclick="handleClick()"> click me </button>
</body>
</html>
and this is my style.css
div {
background-color:blue;
}
The better way is to add addEventListener to that element using your js file. You can do it like this:
<button id="buttonWithFunction"> click me </button>
<script>
// pure js
document.getElementById('buttonWithFunction').addEventListener('click', handleClick);
//with jquery loaded
$('#buttonWithFunction').on('click',function() {
handleClick();
})
You may need to wrap the following in a document.onload for that to work.
Suggest to check namespace of index.js - as i expect, webpack will wrap it in a namespace.
Try to define function on a window namespace.
window.handleClick = () => console.log('Clicked');

Index.html template isn't loading favicon for HtmlWebpackPlugin

I'm trying to load a favicon using the index.html that is the template for the HtmlWebpackPlugin but it's not loading.
That is how my Webpack config looks like:
'use strict'
const webpack = require('webpack')
const { join, resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: join(__dirname, 'src', 'index'),
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.s?css$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
resolve: {
extensions: ['.js']
},
devServer: {
contentBase: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: join(__dirname, 'public', 'index.html')
})
]
}
And that is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" href="favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
HTMLWebpackPlugin will not parse the HTML to find your resources. You'll need to include it like this in your template:
index.html
<link rel="shortcut icon" href="${require('./favicon.ico')}">
You'll also need to include file-loader for your .ico file:
webpack.config.js
{
test: /\.ico$/,
loader: 'file-loader'
}
HtmlWebpackPlugin has an option called favicon which lets you inject an icon link into the header in development or production.
new HtmlWebpackPlugin({
title: "Title Here",
template: "./src/index.html",
favicon: "./src/favicon.ico",
inject: "body",
})
You should also have a rule to grab the icon, and also import it in your bundler file.
// # Target: favicon
{
test: /\.ico$/i,
type: "asset/resource",
// Use 'generator' to output unique name (based on webpack pattern e.g. [name], [ext], etc.)
generator: {
filename: "[name][ext][query]"
}
},
NB: I'm writing for Webpack 5
I'm not sure if Webpack 4 has the type: "asset/resource" feature, but I assume you can achieve the same thing with file-loader and its options.
{
test: /\.ico$/i,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]"
}
}
},
*Not guaranteed for Webpack 4.

How do I import a react component through a HTML script tag?

So I'm creating a react component which will be deployed with UMD format and can be consumed from unpkg. It is built using webpack.
Webpack config creating the UMD module with external modules:
module.exports = {
plugins: [
new StyleLintPlugin(),
new CleanWebpackPlugin(['../dist']),
new ExtractTextPlugin("index.css")
],
resolve:{
extensions: ["",".scss", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
module: {
preLoaders: [
{
test: /\.tsx$/,
loader: 'tslint-loader'
}
],
loaders: [
{ test: /\.scss$/, loader: ExtractTextPlugin.extract("css-loader!postcss-loader!sass-loader")},
{ test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader!postcss-loader")},
{ test: /\.tsx?$/, loader: "awesome-typescript-loader",exclude: /node_modules/,query:{declaration:false} },
{
test: /\.(png|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader',
}
]
},
devtool: 'source-map',
output: {
filename: '[name].js',
path: path.resolve(__dirname,"..", 'dist'),
library: 'productupload',
libraryTarget: 'umd',
umdNamedDefine: true,
//publicPath: '/'
}
externals: {
'react': 'commonjs react',
'react-dom':'commonjs react-dom',
'mongodb':'commonjs mongodb',
'express':'commonjs express'
}
};
This then creates the package you can see here
And I try to get the react component with the HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Upload Component</title>
<meta name="description" content="It will upload generic product data to server.">
<meta name="author" content="Alexander Morton">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<script src="https://unpkg.com/#mortonprod/product-upload#1.0.1/dist/inputBuilder.min.js"></script>
</head>
<body>
<div id="root"></div>
<section>
</section>
<footer>
Product Upload component create by Alexander Morton
</footer>
<script>window.__IDS__ = ${ids}</script>
<script src= ${options.unpkgUI}></script>
<script type="text/babel">
ReactDOM.render(
<InputBuilder/>,
document.getElementById('root')
);
</script>
</body>
</html>
This should include react, react-dom and my component and then render it to the screen.
The error I get is:
inputBuilder.min.js:1 Uncaught ReferenceError: require is not defined
at Object.r.extends.Object.setPrototypeOf.__proto (inputBuilder.min.js:1)
at e (inputBuilder.min.js:1)
at Object. (inputBuilder.min.js:1)
at e (inputBuilder.min.js:1)
at i.extends.Object.setPrototypeOf.__proto (inputBuilder.min.js:1)
at inputBuilder.min.js:1
at inputBuilder.min.js:1
Require should not be defined in the browser right? Why does webpack use it then?

Categories