I have a file we'll call file1.ts:
export { default as function1 } from 'function1.ts';
export { default as function2 } from 'function2.ts';
I compile this using Webpack and Babel:
webpack.config.js
const path = require("path");
module.exports = {
target: "web",
mode: "production",
entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, "./lib/cjs"),
filename: "index.js",
libraryTarget: "commonjs2",
},
resolve: {
extensions: [".ts", ".tsx", ".js"],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /(node_modules)/,
loader: "babel-loader",
options: {
presets: [
"#babel/preset-react",
"#babel/preset-typescript",
[
"#babel/preset-env",
{
targets: ["last 2 versions"],
},
],
],
plugins: ["babel-plugin-styled-components"],
},
},
],
},
};
I publish this to npm. Now I want to import it into what we'll call file2.ts:
import { function1 } from 'package';
However, function1 does not exist because if I do, for example import a from 'package';, a is undefined.
To resolve this, I decided to create another file, we'll call file0.js to do the following:
module.exports = require('./file1.js');
if I console log the require, it will be a module object with function1 and function2 as i'd expect however, module.exports = require('./file1.js'); is undefined... so I tried the following which works:
var test = require('./file1.js');
module.exports = { ...test };
I don't understand why that works but module.exports = require('./file1.js'); doesn't.
I don't know what the correct way I should be doing this (export an es5 module / file so I can import it in es6)
webpack is not designed to support emitting ES modules. Its ES module support is for apps that use ES modules internally but emit to a different format. I'd recommend using Rollup instead, which has full native ES module support, but can also support CJS with the same config if you still need it.
I think the problem that you're seeing is because your module has no default export.
export { default as function1 } from 'function1.ts';
export { default as function2 } from 'function2.ts';
This code exposes the default export of function1.ts as function1 and the default export of function2.ts as function2, but it does not provide a default export of its own.
You could try the following instead
import { default as function1 } from "function1.ts";
import { default as function2 } from "function2.ts";
export default { function1, function2 };
Heres a CodeSandbox showing this in action. (My example mixes ts and js, but it shouldn't make a difference for what you're doing here)
Hope that helps! 👍
Related
I'm using Rollup to build a UMD version of my module.
This rollup.config.js successfully builds my module, without including #tensorflow/tfjs:
import path from 'path';
import commonjs from '#rollup/plugin-commonjs';
import { nodeResolve } from '#rollup/plugin-node-resolve';
export default {
input: "dist/tmp/index.js",
output: {
file: "dist/umd/index.js",
format: 'umd',
name: 'Foo',
globals: {
'#tensorflow/tfjs': 'tf',
}
},
context: 'window',
external: ['#tensorflow/tfjs'],
}
However, I rely on a second module (tensor-as-base64) that I do want to include in the bundle. I cannot figure out how to include that specific module.
From a lot of googling it seems like I need to use #rollup/plugin-commonjs and #rollup/plugin-node-resolve, but I can't find any examples for how to scope the includes to a specific folder under node_modules. I've tried something like this:
import commonjs from '#rollup/plugin-commonjs';
import { nodeResolve } from '#rollup/plugin-node-resolve';
export default {
input: "dist/tmp/index.js",
output: {
file: "dist/umd/index.js",
format: 'umd',
name: 'Foo',
globals: {
'#tensorflow/tfjs': 'tf',
}
},
context: 'window',
external: ['#tensorflow/tfjs'],
plugins: [
nodeResolve({
}),
commonjs({
include: [/tensor-as-base64/],
namedExports: {
'tensor-as-base64': ['tensorAsBase64'],
},
}),
]
};
This seems to just hang with no output.
Any tips on how to include a single specific module from the node_modules folder (and ignore everything else in that folder)?
Update 1
I tried this config:
export default {
input: "dist/tmp/index.js",
output: {
file: "dist/umd/index.js",
format: 'umd',
name: 'Foo',
globals: {
'#tensorflow/tfjs': 'tf',
}
},
context: 'window',
external: ['#tensorflow/tfjs'],
plugins: [
nodeResolve({
resolveOnly: [
/^(?!.*(#tensorflow\/tfjs))/,
],
}),
],
})
This produces the following output:
dist/tmp/index.js → dist/umd/index.js...
[!] Error: 'default' is not exported by ../../node_modules/tensor-as-base64/dist/index.js, imported by dist/tmp/upscale.js
Which is accurate in that tensor-as-base64 does not export default.
After including the commonjs plugin, it gets into an infinite loop. I think that's where I'm missing some bit of configuration.
I should add that this is a monorepo, so maybe there's an issue with node_modules being at the root of the folder?
EDIT: I gave up with my own Webpack setup and just used react-scripts and now it compiles just fine. So must have been my Webpack/Babel setup, though still can't find out what
I'm trying to play around with the module effector, which has a React binding effector-react. I copy pasted a basic (working) code sandbox example, but I'm getting a webpack error which makes me think my build configuration isn't right.
My simple component is as follows:
import React from "react";
import { useStore } from "effector-react";
import { s_counter } from "stores/counter";
function Count () {
const counter = useStore(s_counter);
return ( <div>{counter}</div> );
};
export default Count;
and my effector counter store is as follows:
import { createStore, createEvent } from "effector";
// Store
const s_counter = createStore(0);
export { s_counter };
I think the issue is with my Webpack/Babel config, which are as follows:
webpack.config.js
const path = require("path");
module.exports = {
entry: './src/index.js',
output: {
path: __dirname + '/src/dist',
publicPath: '/',
filename: 'bundle.js'
},
resolve: {
alias: {
components: path.resolve(__dirname, 'src/components/'),
stores: path.resolve(__dirname, 'src/stores/'),
},
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
I'm using webpack 4, and when I build by just running webpack my app doesn't load and I get the console error:
effector.es.js:35 Uncaught SyntaxError: Invalid or unexpected token
at Object../node_modules/effector/effector.es.js (bundle.js:109)
at __webpack_require__ (bundle.js:20)
at eval (effector-react.es.js:13)
at Module../node_modules/effector-react/effector-react.es.js (bundle.js:97)
at __webpack_require__ (bundle.js:20)
at eval (Count.jsx:4)
at Module../src/components/Count.jsx (bundle.js:311)
at __webpack_require__ (bundle.js:20)
at eval (App.jsx:4)
at Module../src/components/App.jsx (bundle.js:299)
The actual error is caused by importing things from effector-react, specifically this line:
import { useStore } from "effector-react";
doesn't matter what module I import from effector-react, I get that console error. Any ideas for what's going wrong are welcome.
EDIT: same kind of problem if importing from effector itself too
Tested on npm#6.4.1, webpack4.30.0 & node#10.13.0
Try this,
Did you download effector separately as a npm dependency.
I believe you are not able to resolve this module specifically.
I tried using the same modules using.
npm install effector
npm install effector-react
Adding the above two dependencies. It works fine.
Adding a sample of my module property from webpack
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
}]
}
Hope this helps.
I have a file where I'm exporting multiple consts with arrays of children called icons.js.
In another react file, lets call it CloseButton.js. I'm only importing
import { cross } from './icons.js';
and when I run webpack with production mode enabled, all the other icons appear to be imported as well (the icons.js const exports amount to close to 100kB or so, but a single line shouldnt be larger than 1kB) to the transpiled CloseButton.js.
I am using webpack 4.30.0 with #babel/preset-env and #babel/preset-react.
webpack.config.js
const config = {
entry: './CloseButton.js',
output: {
filename: 'CloseButton.js',
},
plugins: [],
module: {
rules: [
{
test: /\.js/,
use: {
loader: 'babel-loader',
options: {
presets: [['#babel/preset-env', {
modules: false
}], '#babel/preset-react']
}
}
}
]
},
mode: 'production'
};
module.exports = config;
I tested to run the same setup but only exports strings from icons.js, and then the code was properly excluding dead code.
Does anybody know if there's a way to only export "cross" from the icons.js file without creating a separate file for each react component defined in icons.js?
I've tested removing all references of consts being exported as React components from icons.js and that works, but that doesn't let me export the icons.
I figured out the issue, basically I was setting the react components directly on the export const myicon = [<path></path>, <path />]; Instead it needs to of course be wrapped with a function call.
such as: ```export const myicon = ()=> {
return ([, ])
}```
I'm trying to get just the basics down, transpiling a jsx file to js. However, my transpiled code needs to be called by non-transpiled code. output.library is supposed to help with that.
In the resulting bundle I see a definition for var react. But just after stepping through the entire bundle, it's clear react still isn't getting set.
my webpack.config.js
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: "./public/js/ui/react/dialog.jsx",
output: {
path: path.resolve(__dirname, "public/js/ui/react/"),
filename: "bundle.js",
libraryTarget: "var",
library: "react"
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx$/,
loader: 'babel-loader',
exclude: [
path.resolve(__dirname, "node_modules/")
],
query: {
presets: ['es2015', "react"]
}
}
]
},
node: {
fs: "empty"
}
}
and the jsx I am trying to transpile:
'use strict';
react.Dialog = class extends React.Component {
render() {
return (
<div class="bubble-speech">Hello World</div>
)
}
}
elsewhere in my code, AND BEFORE THE BUNDLE, I have this, so that the react.Dialog assignment is not a null reference error:
var react = {};
If I take that one line away, the bundle.js will throw an error trying to assign react.Dialog. But if I leave it in, var react remains set to the empty object. That seems like a contradiction! What am I missing here?
I think react should be set as an externally defined global var, like this:
{
output: {
// export itself to a global var
libraryTarget: "var",
// name of the global var: "Foo"
library: "Foo"
},
externals: {
// require("react") is external and available
// on the global var React
"react": "React"
}
}
Babel's 6th version changes the functioning of export default and in particular its relation with commonjs require.
To summarise, while until babel5, require('module') where giving the default export of the module, it now always returns the module object containing all of the exports of the module.
If one only wants the default, he/she must use require('module').default.
As explained here, there is very good reasons behind this and the aim of this question is not to break or hack this behaviour.
However, if one is building a library, he/she usually does not want to distribute a module but the export value of his library (e.g. a function, whatever module system is used internally).
This is well dealt with by webpack and the output.library configuration when using commonjs or AMD. Because prior babel's versions allowed the default export to be required with commonjs, babel was also compatible with this mechanism. However it is not the case anymore: the library now always provides an es6 module object.
Here is an example.
src/main.js
export default "my lib content";
webpack.config.js
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.join(__dirname, "dist"),
filename: "mylib-build.js",
library: 'myLib'
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.join(__dirname, "src"),
query: { presets: ['es2015'] }
}
]
}
};
test.html
<html>
<head></head>
<body>
<script src="dist/mylib-build.js"></script>
<!-- `myLib` will be attached to `window` -->
<script>
console.log(JSON.stringify(myLib)); // { default: "my lib content" }
</script>
</body>
</html>
This is a very simple example but I obviously want the export of mylib to be the string "my lib content" instead of { default: "my lib content" }.
One solution could be to create an export source file in commonjs to perform the transformation:
module.exports = require('./main').default;
However I find this solution quite poor. One should be able to solve it at the compilation level, without changing the source code.
Any idea?
Was just going at this my self. Whether one like to call it a workaround or solution, there seem to be a Babel plugin that "solve it".
Using the plugin babel-plugin-add-module-exports as referenced in https://stackoverflow.com/a/34778391/1592572
Example config
var webpackOptions = {
entry: {
Lib1: './src/Lib1.js',
Lib2: './src/Lib2.js'
},
output: {
filename: "Master.[name].js",
library: ["Master","[name]"],
libraryTarget: "var"
},
module: {
loaders: [
{
loader: 'babel',
query: {
presets: ['es2015'],
plugins: ["add-module-exports"]
}
}
]
}
};
This yields Master.Lib1 to be lib1 instead of Master.Lib1.default.
Webpack 2 now supports es6 modules which partially solves this issue. Migrating from webpack 1 to webpack 2 is relatively painless. One just needs to remember to disable babel's es6 module to commonjs conversion to make this work:
.babelrc
{
"presets": [
["es2015", {"modules": false}]
]
}
However, unfortunately, it does not work properly with export default (but an issue is opened, hopefully a solution will be released eventually).
EDIT
Good news! Webpack 3 supports the output.libraryExport option that can be used to directly expose the default export:
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "mylib-build.js",
library: "myLib",
// Expose the default export.
libraryExport: "default"
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.resolve(__dirname, "src")
}
]
}
};
You can use this solution (this is more like workaround, but it allow you to keep your sources from change):
There is a loader called callback-loader. It allow you to change your sources in a build time by calling a callback and put a result instead of it. In other words you can turn all you require('module') into a require('module').default automatically in a build time.
Here is your config for it:
var webpackConfig = {
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'callback' },
...
]
},
...
callbackLoader: {
require: function() {
return 'require("' + Array.prototype.join.call(arguments, ',') + '").default';
}
}
};