I am trying to make a library/package from my component.
Tech-stack is: React, Typescript... and a bunch of other dependencies.
I am using Rollup and when I try to build the package I get the following error:
[!] Error: 'DisplayHint' is not exported by
../node_modules/#bestowinc/enroll-sdk-core/build/lib/question-common.js,
imported by ../src/utils/answerTypeConversions.ts
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
Rollup:
import babel from 'rollup-plugin-babel';
import resolve from '#rollup/plugin-node-resolve';
import external from 'rollup-plugin-peer-deps-external';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import reactSvg from 'rollup-plugin-react-svg';
import typescript from 'rollup-plugin-typescript2';
import svg from 'rollup-plugin-svg';
import commonjs from '#rollup/plugin-commonjs';
import json from '#rollup/plugin-json';
import replace from '#rollup/plugin-replace';
import urlResolve from 'rollup-plugin-url-resolve';
export default [
{
input: './src/index.ts',
output: [
{
file: './dist/index.js',
format: 'cjs',
sourcemap: true,
},
{
file: './dist/index.es.ts',
format: 'es',
exports: 'named',
sourcemap: true,
},
],
plugins: [
typescript({
tsconfig: './tsconfig.build.json',
verbosity: 3,
clean: true,
check: true,
}),
babel({
exclude: ['/node_modules'],
presets: [
'#babel/preset-react',
'#babel/preset-flow',
'#babel/preset-env',
],
extensions: ['.ts', '.tsx'],
}),
commonjs({
include: './node_modules',
dynamicRequireTargets: [
// include using a glob pattern (either a string or an array of strings)
'/node_modules/#bestowinc/enroll-sdk-core/build/lib/question-common.js',
'./node_modules/#bestowinc/enroll-sdk-core/build/lib/question-common.js',
],
}),
resolve({
browser: true,
preferBuiltins: true,
mainFields: ['browser'],
}),
urlResolve(),
postcss({
plugins: [],
minimize: true,
}),
external(),
terser(),
reactSvg({
jsx: false,
include: ['custom.d.ts'],
exclude: null,
}),
svg(),
replace({
include: ['../src/icons/**'],
preventAssignment: true,
// Replace ReactComponent to allow resolution of SVG files under Rollup
ReactComponent: 'default',
}),
json(),
],
},
];
DisplayHint:
/**
* An indication of how clients should display a question.
*
* This is inferred from the answer_format and answer_display api properties.
* Those properties should not be used directly, and clients should rely instead
* solely on DisplayHint.
*/
export declare const DisplayHint: {
readonly ButtonGroup: "DisplayHint::ButtonGroup";
readonly Checkbox: "DisplayHint::Checkbox";
};
export declare type DisplayHint = typeof DisplayHint[keyof typeof DisplayHint];
Looks like DisplayHint is a TS type/interface, not an exported JS value.
Rollup per se is a bundler not a TS language analyzer. It cannot tell if a named export is a concrete JS value or merely a non-existing TS type, thus the reported error.
Rollup plugin order matters. To resolve this specific problem, just lift the typescript plugin up in order, at least before babel. TS plugin, if put to work first, will correctly erase TS type from JS code output.
Update
I know another trick, but it’s a workaround-ish one. The trick is to use import type syntax to explicitly mark DisplayHint as a TS type.
import type { DisplayHint } from "#bestowinc/enroll-sdk-core"
import { otherNonTypeStuff } from "#bestowinc/enroll-sdk-core"
This trick is not satisfying because it requires you to explicitly mark everywhere you need a TS type, and you have to separate concrete JS exports from TS type ones, like shown above. This is fine if it’s a one-off thing but if you have a large codebase it’s very tedious.
I have to add, it’s unexpected to me that original solution doesn’t work. My guess is that this module #bestowinc/enroll-sdk-core is somehow quirky. (Why you include them as dynamic import target in the config anyway?)
It’s a JS module with .d.ts annotation, that I can tell, but looks like it’s a private module, I cannot see the code thus cannot dive deeper. My guess is that .d.ts declaration is broken, it doesn’t match up to the real export of JS code.
Anyway, I hope this trick resolve your problem. If you need me to look closer, I’ll have to ask you to setup a minimal reproducible example. With current info there’s not much I can do.
I've had similar issue just happening with linked library (with typescript plugin in place), in my case solution was to add the linked library to config 'external' property. But node_modules should be treated as external anyway so not sure if this will apply
Related
I found a plugin rollup-plugin-multi-input which fixes the problem of not being able to specifiy a glob to the test rollup config. For the unt tests, the entry point is not a single entity from which an import graph can be derived. It is just a collection of source files containing tests, which doesnt fit input requirement of rollup.
However, my attempt at trying to use it was fruitless:
rollup-config.tests.mjs:
import multi from 'rollup-plugin-multi-input';
const testConfig = {
input: ['test/**/*.spec.ts'],
external: ["chai", "mocha", "dirty-chai"],
output: {
format: "es",
file: `dist/${name}-bundle.test.js`,
plugins: [],
sourcemap: true
},
plugins: [
multi(),
resolve(),
commonjs(),
typescript({
tsconfig: "./tsconfig.test.json"
})
],
}
just resulted in this error:
[!] TypeError: multi is not a function
TypeError: multi is not a function
Looking at the exported code from the plugin, I can see that the default export is a function:
var _default = function(param) {
}
exports.default = _default;
So I don't know why this doesnt work.
I since discovered that there is another plugin that does a similar thing: #rollup/plugin-multi-entry:
import entry from "rollup-plugin-multi-entry";
plugins: [
entry(),
resolve(),
commonjs(),
typescript({
tsconfig: "./tsconfig.test.json"
})
],
so configured and invoked in exactly the same way, but now it works in the way that I wanted it to; the test bundle is created and mocha indeed sees all the tests and executes them successfully.
So let's take a look at that export and see if there is a difference in what is exported:
Well the first thing to notice is that its dist folder contains a .mjs file and a .js file. Since we're importing from an ESM package ("type": "module" in package.json), I guess we're using the default export from the .mjs file:
function multiEntry() {
...
}
export default multiEntry;
With rollup-plugin-multi-input, I even tried using the createRequire from "module":
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const multi = require('rollup-plugin-multi-input');
but that failed for the same reason.
So whats the problem here? Why does default import from rollup-plugin-multi-input not work?
When I use only const Example1 = require('./example1.js) statement then code inside example1.js file is getting included in the bundle. And if I use only import Example2 from './example2.js' then also code inside example2.js is getting included in the bundle. But if I use both the statements only import is working and require is not working.
I am using rollup for bundling.
My rollup configuration looks like this
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import postcss from 'rollup-plugin-postcss'
import resolve from 'rollup-plugin-node-resolve'
import url from 'rollup-plugin-url'
import svg from 'rollup-plugin-svg'
import json from 'rollup-plugin-json';
import { terser } from 'rollup-plugin-terser'
export default {
input: 'src/sdk/test.js',
output: [
{
file: "src/sdk/sdk.js",
format: 'cjs'
},
{
file: "src/sdk/sdk.es.js",
format: 'es'
},
{
file: "src/sdk/sdk.iife.js",
format: 'iife'
}
],
plugins: [
resolve({
browser: true,
}),
commonjs(),
external(),
postcss({
modules: true
}),
url({
limit: 100 * 1024,
emitFiles: false
}),
svg(),
babel({
exclude: 'node_modules/**',
"plugins": ["#babel/plugin-proposal-class-properties"]
}),
terser(),
json()
]
}
By default, rollup supports 'esm' modules as entry. If you like to include commonjs files in the project, you can include '#rollup/plugin-commonjs' into plugins field, it usually works.
Maybe the following config will help, I tried with very simple example:
commonjs({transformMixedEsModules:true})
transformMixedEsModules
Instructs the plugin whether or not to enable mixed module transformations. This is useful in scenarios with mixed ES and CommonJS modules. Set to true if it's known that require calls should be transformed, or false if the code contains env detection and the require should survive a transformation.
https://github.com/rollup/plugins/tree/master/packages/commonjs
I have two we components lets call then comp1 and comp2 and my project structure looks like this...
web-components
packages
comp1
package.json
comp2
package.json
lerna.json
rollup.config.json
package.json
Both of these import the Polymer material lib #material/mwc-icon-button. The problem arises when I build the projects. The material code is then injected into both outputs. I want to avoid this but I can't think of how to do it. I thought about using global variables but then the implementer has to know to install the material dependency and properly configure the global but this seems pretty onerous.
So the question is what is the proper way to do this without repeating code in the output?
Here is my full rollup config...
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import pug from 'rollup-plugin-pug';
import postcss from 'rollup-plugin-postcss';
const globals = {}
// const external = Object.keys(pkg.dependencies);
const plugins = [
resolve({
module: true,
main: true,
jsnext:"main",
browser: true
}),
postcss({
plugins: []
}),
commonjs(),
pug()
];
module.exports = {
plugins,
input: 'src/jrg-dropdown.mjs',
output: {
file: 'dist/index.mjs',
format: 'esm',
globals
}
};
I'm working on an Open Source D3/React component library and I'm trying to bundle the library using Rollup.js to offer code splitting, three shaking, etc.
The library is already published in GitHub and NPM and you can check a codesandbox using the library for a reference in case you want to try it.
Next I'm gonna try to highlight the different issues I'm experiencing with this bundle.
The library has already been tested using the code directly in a project and it works perfectly, so the problem is with the bundle and I assume that I'm doing something wrong with the Rollup.js configuration file in my project.
import { readdirSync } from 'fs';
import path from 'path';
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import replace from 'rollup-plugin-replace';
import resolve from 'rollup-plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
const CODES = [
'THIS_IS_UNDEFINED',
'MISSING_GLOBAL_NAME',
'CIRCULAR_DEPENDENCY',
];
const getChunks = URI =>
readdirSync(path.resolve(URI))
.filter(x => x.includes('.js'))
.reduce((a, c) => ({ ...a, [c.replace('.js', '')]: `src/${c}` }), {});
const discardWarning = warning => {
if (CODES.includes(warning.code)) {
return;
}
console.error(warning);
};
const env = process.env.NODE_ENV;
const plugins = [
external(),
babel({
exclude: 'node_modules/**',
}),
resolve(),
replace({ 'process.env.NODE_ENV': JSON.stringify(env) }),
commonjs(),
env === 'production' && terser(),
];
export default [
{
onwarn: discardWarning,
input: 'src/index.js',
output: {
esModule: false,
file: 'umd/silky-charts.js',
format: 'umd',
name: 'silkyCharts',
},
plugins,
},
{
onwarn: discardWarning,
input: getChunks('src'),
output: [
{ dir: 'esm', format: 'esm', sourcemap: true },
{ dir: 'cjs', format: 'cjs', sourcemap: true },
],
plugins,
},
];
The Errors
When I try to use the library using the production bundle (which is the default) directly from the NPM package I got the following error coming from one of the chunks node_modules/silky-charts/esm/chunk-501b9e58.js:5833
TypeError: react__WEBPACK_IMPORTED_MODULE_0___default(...) is not a function
If I stead use the development bundle I get a different error:
Failed to compile
../silky-charts/esm/index.js
Module not found: Can't resolve 'react' in '/Users/davidg/Development/personal/silky-charts/esm'
This error force me to install React, ReactDOM, and styled-components as devDependencies in the project for the library have access to these projects code.
After installing the devDependencies the error I get is the next one:
Hooks can only be called inside the body of a function component.
I already filled an issue in the React project page and according to them this is not a React issue but maybe a Webpack's since is usual to find this error when you have to install React in both the project and the library and Webpack finds there is two instances of React, and I kind of agree since the error varies depending on bundle type or the way the importer project is run as you can see in the codesandbox.
I hope you can help me to spot the error in the Rollup configuration file and if you feel like doing a PR in the project event better 😀.
I followed the steps on the website 'https://angular.io/guide/aot-compiler' to adapt an Angular 2 app to use AoT compilation and everything seemed to work as expected until the rollup step, which in theory cleans up the code of unused components.
Leaving the 'rollup-config.js' file the same as in the instructions, when I run 'node_modules/.bin/rollup -c rollup-config.js' I get the following error:
[!] Error: 'ToastModule' is not exported by node_modules/ng2-toastr/ng2-toastr.js
I've googled it, and I came across a solution that pointed to add a 'namedExports' field to the 'commonjs' parameter. The resulting 'rollup-config.js' is:
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'src/main.js',
dest: 'src/build.js',
sourceMap: false,
format: 'iife',
onwarn: function(warning) {
if(warning.code === 'THIS_IS_UNDEFINED') { return; }
console.warn(warning.message);
},
plugins: [
nodeResolve({jsnext: true, module: true}),
commonjs({
include: 'node_modules/**',
namedExports: {
'node_modules/ng2-toastr/ng2-toastr': [ 'ToastModule' ]
}
}),
uglify()
]
}
Now, the script generates the 'build.js' file and I can load it through the index.html file, but the component 'ng2-toastr' is not working (the component shows a 'toast' like message, appearing on one side of the screen and disappearing after a few seconds).
Did I miss something? Thanks in advance,