Webpack namespacing es6 modules - javascript

Due to compatibility issues with typescript, babel, and webpack I have to use the export class Test {} syntax rather than export default class Test {}. It solves all of my issues with typescript but causes webpack to namespace everything on an object instead.
I'm having webpack generate umd and am testing the include via requirejs.
However, rather than passing in the function directly I'm now getting an object with a property instead. This won't fly in my real app.
{
Test: function Test() {}
}
webpack.config.js:
module.exports = {
entry: './test.js',
output: {
filename: 'a.js',
libraryTarget: 'umd'
},
module: {
loaders: [{
test: /\.js$/, loader: 'babel-loader'
}]
}
};
.babelrc:
{
"presets": ["es2015"]
}

I'm not sure if I've understood correctly, but after much experimentation I found the cleanest way to use modules in TypeScript is to simply use ES6 syntax in my source files.
// src/foo/Foo.ts
export class Foo {}
// src/bar/Bar.ts
import {Foo} from '../foo/Foo';
export class Bar extends Foo {}
This way your source files can remain agnostic to your output module format.
For large libraries, it's possible to keep an index.ts at the root of each of your "namespaces", which will afford you greater flexibility when exporting modules:
// src/widgets/FooWidget.ts
export class FooWidget {}
// src/widgets/BarWidget.ts
export class BarWidget {}
// src/widgets/index.ts
export * from './FooWidget';
export * from './BarWidget';
// src/index.ts
import * as widgets from './widgets';
import * as badgers from './badgers';
export {
widgets,
badgers
};

Related

Webpack — When building a code bundle, and subsequently importing it, how do I access named exports?

I’m building a custom version of CKEditor which uses webpack (v4) to bundle the src into a minified bundle. My src includes both default and named exports, along the lines of:
export default class MyEditor extends ClassicEditor {}
export class InteractiveMyEditor extends ClassicEditor {}
export const Context = CKContext
The webpack.config.js file includes the following output section
output: {
library: 'MyEditor',
path: path.resolve( __dirname, 'build' ),
filename: 'ckeditor.js',
libraryTarget: 'umd',
libraryExport: 'default'
},
it all builds fine and I can
import MyEditor from '#project/ckeditor-build-myeditor'
// MyEditor is defined
just fine.
But I can't access the InteractiveMyEditor or the Context objects
import { Context, InteractiveMyEditor } from '#project/ckeditor-build-myeditor'
// both Context and InteractiveMyEditor are undefined
I've been trying to make sense of the Webpack4 Docs but can't see what else to do other than try different values for libraryTarget. I've tried commonjs, and 'module just to see if that helped but alas no.
How do I export the named exports correctly?

Build React components library with Webpack 4

I'm currently building a library of React components and bundling it with Webpack 4.
Everything works just fine from building the library's bundle to publishing it on an npm registry.
But then, I'm not able to import any of its components in an other React application and get this error message at runtime:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
And here is the related code:
A dumb component from my components library:
button/index.js
import React from "react";
const Button = () => <button>Foobar</button>;
export { Button };
The main entry point of my library index.js:
import { Button } from "./src/components/Button";
export { Button };
My Webpack config webpack.config.js:
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: "./index.js",
plugins: [new CleanWebpackPlugin()],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
libraryTarget: "commonjs",
library: ""
}
};
And finally, the import of this component in an other application:
import { Button } from "my-design-system";
I guess I'm missing something in my Webpack config or one of the property may be wrong, but after reading multiple posts and tutorials, I can't figure which one.
You're exporting your library as commonjs and trying to import it via import/export syntax. You should change your output to
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
libraryTarget: "umd",
library: "my-design-system"
}
Found a lot of info here: https://webpack.js.org/guides/author-libraries/
What I would do is to export your components as default and then re-export as named from index.js:
/// Button.js
import React from "react";
const Button = () => <button>Foobar</button>;
export default Button ;
// index.js
export { default as Button } from "./src/components/Button";
Then you can do
import { Button } from "my-design-system";
Also make sure you have main set up, pointing to your index.js, in your design system's package.json
Additionally, if you still want to have named exports in some of your components, you can export everything from that component file:
//index.js
export * from "./src/components/ComponentWithNamedExports";
Either way you will make sure there's always one point of export for all your components.
EDIT: As noted in by Maaz Syed Adeeb, you have wrong libraryTarget in your config. I'd remove both libraryTarget and library from there.

Unable to import webpack-bundled UMD library as an ES6 import

I've authored a javascript library using Webpack. The entrypoint someClass.js looks like this:
import x from './x'
/* several more imports here */
class SomeClass {}
export default SomeClass;
My webpack config that bundles this library is as follows:
module.exports = {
entry: './someClass.js',
output: {
path: __dirname,
filename: 'lib.js',
library: 'lib',
libraryTarget: 'umd',
},
I then import the generated lib.js into a simple index.html that is defined as follows:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="app.js" type="module"></script>
</body>
</html>
In app.js, I simply try to import the file as follows:
import * as lib from './lib.js';
console.log(lib);
// Output: Module {Symbol(Symbol.toStringTag): "Module"} Symbol(Symbol.toStringTag): "Module"
However, this import statement does not seem to work as planned (I expect a module with a default field that is my SomeClass constructor).
The only way I can access my library's default export SomeClass is if I do a global import statement in app.js as follows, which sets lib as an object on window:
import './lib.js';
console.log(window.lib);
// Output: Module {default: ƒ, __esModule: true, Symbol(Symbol.toStringTag): "Module"} default: class SomeClass_SomeClass
I don't want my class to available at the global window, as it makes it tough to modularise my code.
I also want to be able to use this library in a variety of places on the web (react app, standalone html files, etc), and so want to minimize dependencies.
Is there anything I can do to import the module as an es6 import?
The ES6 import works only with the export keyword like:
export { A }
export default B
Nodejs's commonjs2 module will not work on the Web, which looks like:
module.exports = { A, default: B }
Webpack's libraryTarget: "umd" output will not keep the ES6 export keyword either,
for reason that the keyword syntax is not ployfill-able and will break on other environment.
So you may want to release with 2 set of files like other packages do (cjs/ and es/), and use babel or rollup to pack the es/ version and keep the ES6 export keyword.
Also check out: Output an ES module using webpack which explains the Webpack part really well.

How to exclude a module from webpack, and instead import it using es6

I am currently using webpack to bundle up a javascript file with react. This file also has another import statement to another module within my project
import { MyModule} from './MyModuleFile.js'
Is there a way to exclude MyModule.js from the webpack bundle, and instead keep the import as is?
What you're after might be the externals option in your webpack.config.js
module.exports = {
//...
externals: {
'./MyModuleFile': 'MyModule',
}
};
This assumes you will include the script in your HTML manually which will expose the MyModule global
If you'd instead really like to use ES6 import, you could use the same technique because everything you put in there will just be exported as is
module.exports = {
//...
externals: {
'./MyModuleFile': 'import("MyModuleUrl")',
}
};
But make sure you use the async version of import
import('./MyModuleFile').then(({default: MyModule}) => doSomethingWith(MyModule));
Or alternatively use the webpackIgnore comment to keep the import as is without changing the config
import(/* webpackIgnore: true */'MyModuleUrl').then(({default: MyModule}) => doSomethingWith(MyModule));
Just add it to the exclude option in your loader configuration of your webpack.config.js:
rules: [
// rules for modules (configure loaders, parser options, etc.)
{
test: /\.js$/,
exclude: [
/node_modules/,
/MyModuleFile/
],
...
}
]
https://webpack.js.org/configuration/module/#rule-exclude

Is my "import" statement being incorrectly transpiled?

It is my understanding that the following two lines are equivalent:
const Up = require('write-up').default
And...
import Up from 'write-up'
Both examples should make the default export of the write-up module available as Up.
Unfortunately, using Babel and Webpack, that's not the behavior I'm seeing.
The first example works just fine. It produces this line:
var Up = __webpack_require__(5).default;
Up is set to the default export of the write-up module, which is the behavior I expect.
The second example does not work. It produces this:
var _writeUp = __webpack_require__(5);
var _writeUp2 = _interopRequireDefault(_writeUp);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Instead of setting Up to the default export, Up is set to an object containing every single export of the write-up module (which includes the default field).
What am I doing wrong?
Here's the relevant portion from my Webpack config:
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015']
}
}
Babel-compiled import statements have a dual behavior. If the module being imported was compiled from ES6 export statements with Babel, then
import foo from 'foo';
will behave (mostly) like
const foo = require('foo').default;
but if foo was not compiled with Babel, or something that tries to be compatible with Babel, then as far as Babel is concerned, it has no special behavior and is a normal CommonJS module. In that case, which appears to be your case, it will behave like
const foo = require('foo');
Babel does this so that you can import normal CommonJS modules like
import fs from 'fs';
where fs is a standard Node module and has no .default property.

Categories