Export class as a module in webpack - javascript

I'm using Webpack to import a javascript file that has a single class.
my_class.js
console.log('hello from my_class');
class myClass {
// ...
}
index.js
import './my_class.js';
webpack.config.js
const path = require('path')
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
I'm trying to use this class on a page.
index.html
<script src="./dist/bundle.js"></script>
<script>
const myClass = new myClass();
</script>
I am able to seem my console log ("hello from my_class") but myClass is showing up as undefined.
Uncaught ReferenceError: myClass is not defined
What do I need to do such that myClass is exported and available in the markup?

Firstly you should export the class.
export class myClass {
// ...
}
And for browsers you should use IIFE or UMD format:
output: {
library: 'someLibName',
libraryTarget: 'umd',
filename: 'bundle.js'
}
2021 and webpack thinks federated apps are higher priority than adding ES module support 🤷‍♂️ Use rollup if you don't want to use UMD.
And reference: someLibName.myClass

As you have not exported class from my_class.js file, you will not be able to import it.
console.log('hello from my_class');
class myClass {
// ...
}
export default myClass;
And your import should be like
import myClass from './my_class.js';

Related

Rollup imported css in a webcomponent

I am creating simple webcomponent now I want to import css , I found a method using adpotedstylesheet.
Here is how I import it my webcomponet
import sheet from './styles/swal.css' assert { type: 'css' };
class Demo extends HTMLElement{
constructor() {
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(Demo.content.cloneNode(true));
document.adoptedStyleSheets = [sheet];
this.shadowRoot.adoptedStyleSheets = [sheet];
}
}
window.customElements.define("demo-component", Demo);
and here is my rollup settup for bundling my component
import summary from "rollup-plugin-summary";
import { terser } from "rollup-plugin-terser";
import resolve from "#rollup/plugin-node-resolve";
import replace from "#rollup/plugin-replace";
import postcss from "rollup-plugin-postcss";
import { eslint } from "rollup-plugin-eslint";
import babel from "rollup-plugin-babel";
import { uglify } from "rollup-plugin-uglify";
import commonjs from 'rollup-plugin-commonjs';
export default {
input: "index.js",
output: {
file: "dist/index.js",
format: "esm",
},
onwarn(warning) {
if (warning.code !== "THIS_IS_UNDEFINED") {
console.error(`(!) ${warning.message}`);
}
},
plugins: [
postcss({
plugins: [],
extensions: [".css"],
}),
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
eslint({
exclude: ["src/styles/**"],
}),
babel({
exclude: "node_modules/**",
}),
terser({
ecma: 2017,
module: true,
warnings: true,
mangle: {
properties: {
regex: /^__/,
},
},
}),
summary(),
replace({
"Reflect.decorate": "undefined",
preventAssignment: true,
ENV: JSON.stringify(process.env.NODE_ENV || "development"),
}),
process.env.NODE_ENV === "production" && uglify(),
],
};
Now when i run npm run buil I get the following error.
[!] (plugin commonjs) SyntaxError: Unexpected token (3:38)`
What am I doing wrong here ???
Currenly, Rollup doesn't support import assertions. There is open issue for Rollup to address it. There is an experimental Rollup plugin that supports this but it seems to have some issues.
Another option you can try is to use rollup-string-plugin. You can use it to read CSS file as a string and then build your constructible stylesheets and assign it to adoptedStyleSheets property as explained here for Webpack. Following is one example of doing it..
// Read SCSS file as a raw CSS text
import styleText from './my-component.css';
const sheet = new CSSStyleSheet();
sheet.replaceSync(styleText);
// Custom Web component
class FancyComponent1 extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
// Attaching the style sheet to the Shadow DOM of this component
shadowRoot.adoptedStyleSheets = [sheet];
shadowRoot.innerHTML = `
<div>
<p>Hello World</p>
</div>
`;
}
}
Side note: With Webpack, you can use raw-loader.
Be aware that adoptedStyleSheets is currently not supported by Safari on Mac and iOS. But, Rollup might handle this for you - I don't know.
Another solution is to check out:
https://www.npmjs.com/package/csshtml-module
This CLI tool can be set up to let you automatically compile CSS/HTML to native JS modules.
I created that CLI tool to tackle this issue. It might not be for everyone - but maybe it resonate with you.

How do Vite MPAs using the Vue plugin work?

I have a Vite app created and I need it to have multiple pages. I read the docs and found how to do this (can be found here), however when I run the server I get a blank page.
My vite.config.js file:
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
const { resolve } = require('path')
module.exports = {
build: {
rollupOptions: {
input: {
home: resolve(__dirname, 'src/Home/index.html')
}
}
}
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})
Here's what my file structure looks like:
Edit: I have changed the config to what tony posted below, but it is still showing a blank page.
Edit 2: I found out that you don't need to use the vite.config.js routing, there's an easier way
Create a copy of your main.js, App.vue, and index.html file and rename them to something different. After you rename them change the <script type="module" src="index.js"></script> to your new JS file, and change the .vue file import in your new main.js to your new .vue file. Here's my new structure:
All I did was copy the files and change the names and imports, and it worked!
You have two default exports in vite.config.js, but there should only be one:
module.exports = { 1️⃣
//...
}
export default defineConfig({ 2️⃣
//...
})
The config should be:
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
input: {
home: resolve(__dirname, 'src/Home/index.html')
}
}
}
})
GitHub demo

webpack es6 module not found

I am new to webpack, want to bundle my js classes
I am using es6 classes and linqjs.min, jquery-3.4.1.js and gsap.min
for starter I want only to bundle index.js and my class but I get an error
Module not found: cant resolve (myclass) in src directory
in my index.js for importing class:
import myClass from 'js/myClass'
and for exporting my class I used
export class myClass{ } and
module.export= myClass
my webpack.config.js is:
const HTMLWebPackPlugin = require ('html-webpack-plugin');
module.exports = {
module:{
rules:[
{
test: /\.js$/,
exclude:/node_modules/,
use:{
loader:"babel-loader"
}
},
{
test:/\.html$/,
use:[{
loader:"html-loader",
options:{minimize:true}
}]
}
]
},
plugins:[
new HTMLWebPackPlugin({
template:"./src/index.html",
filename: "./index.html"
})
]}
I don't understand my mistake how can I fix it. Thanks.
You are trying to do a default import, whereas you are doing a named export.
Either go with a default export, adding the default keyword:
export default class MyClass {}
along with
import MyClass from './js/MyClass.js';
Or, go with a named export / import:
export class MyClass {}
with
import { MyClass } from './js/MyClass.js';
Please note that by convention, class names start with a capital letter.
Medium has a good article on the difference: https://medium.com/#etherealm/named-export-vs-default-export-in-es6-affb483a0910

Importing from index files in react

I cannot use index files for imports within my component library, but from the outside.
I'm using the following directory structure:
+ components
| index.js
+ atoms
| index.js
+ label
| label.jsx
| label.css
+ hoc
| index.js
+ withFrame
| withFrame.jsx
| withFrame.css
+ clientSpecificStyle
| index.js
| clientLabel.jsx
The index files are just exporting a default import
// just one sample
export { default as Label } from './label/label;
What I want to do is being able to distinguish between typical (basic) styling of components and client specific styling.
My clientLabel is just a label surrounded with a frame:
import React, {Component} from 'react';
// this doesn't work
import Label from '../atoms/index';
import withFrame from '../hoc/index';
// this works
import Label from '../atoms/label/label';
import withFrame from '../hoc/withFrame/withFrame';
#withFrame
class ClientLabel extends Component {
render() {
return (
<Label {...this.props}>{this.props.children}</Label>
);
}
}
export default ClientLabel;
When used from the "outside" (i. e. a demo page located on same folder hierarchy as components) using imports from the component index file, it works as expected. But I cannot import the HoC and the Label from the index files within the ClientLabel (fails with component/function undefined). It works however, when using the HoC and Label component files directly for import. The topmost index file (for the whole library) looks like this
export * from './atoms/index;
export * from './clientSpecificStyle/index';
//...
As I expect this project to grow into many separate components, it'd be more convenient to use index files for all imports, hence allow me to reorganize code as I see fit and only changing one line of code in the corresponding index file.
Is it possible to get this to work?
My webpack (v. 3.6) config works - apart from this issue - as expected. Just for clarity, here's the dev-config:
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
path.resolve('src', 'demo', 'demo.jsx')
],
output: {
path: path.resolve('dist'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'react-hot-loader/webpack!babel-loader',
exclude: [/node_modules/]
},
{
test: /\.css$/,
loaders: [
'style-loader?sourceMap',
'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
]
}
]
},
devServer: {
contentBase: './dist',
hot: true
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve('src', 'demo', 'index.html'),
filename: 'index.html',
inject: 'body'
}),
new webpack.NamedModulesPlugin()
]
};
So just for completeness: the correct way to import is
import {Label} from '../atoms
as I've done a named export.
All kudos go to #ReiDien

Webpack, bundle loader and common chunk plugin issue

I have a page A.js and a page B.js and have base class base.js for it.
I import base.js for each A and B and use it as extend for class A and B.
My router loads those pages via require('bundle!pages' + url + '.js');
I would like to have base.js as a part of one common.js.
I added common chunk plugin to webpack
new webpack.optimize.CommonsChunkPlugin({
children: true,
name: 'common',
filename: 'common.js',
minChunks: 2,
})
but as an output I still have duplicated base.js in class a.js and b.js.
Is there any issue with bundle loader and common chunk plugin?
a.js
import Base from '../base';
class A extends Base {
constructor() {
super();
}
create() {}
}
export default A;
b.js
import Base from '../base';
class B extends Base {
constructor() {
super();
}
create() {}
}
export default B;
base.js
class Base {
constructor() {}
}
export default Base;
router.js
let loader = require('bundle!pages' + url + '.js');
loader((module) => callback(module.default));
Try this:
in webpack.config.js add to the exporting object this property
externals: { 'base': 'Base' }
and include this in your html header
I think that feature is from webpack 2

Categories

Resources