使用 yarn 工作区将 ui 库与 rollup 捆绑在一起时导入未定义

import undefined when bundling ui library with rollup using yarn workspaces

对于一个新项目,我开始使用汇总来捆绑一个 UI 库并在 React 应用程序中使用该库。我还使用 yarn 工作区来管理 UI 库和网络应用程序之间的内部依赖关系。

当我尝试在我的网络应用程序中使用 UI 库时,导入 returns 未定义并抛出 "cannot get from undefined" 错误。

TypeError: Cannot read property 'NavBar' of undefined [0] at App (C:/Users/user/dev/project/packages/project-web/src/pages/App.jsx:9:6)

网络应用程序代码:

import React from 'react';
import {NavBar} from 'project-ui';

const App = () => (
  <div>
    <NavBar/>

    <div>App component!x</div>
  </div>
);

root package.json:

{
  "name": "project",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

UI package.json:

{
  "name": "project-ui",
  "version": "1.0.0",
  "main": "dist/project-ui.cjs.js",
  "jsnext:main": "dist/project-ui.es.js",
  "module": "dist/project-ui.es.js",
  "files": ["dist"],
  "scripts": {
    "build": "rollup -c"
  },
  "peerDependencies": {
    "react": "16.3.2",
    "react-dom": "16.3.2"
  },
  "devDependencies": {
    "babel-core": "6.26.3",
    "babel-plugin-external-helpers": "6.22.0",
    "babel-preset-env": "1.6.1",
    "babel-preset-react": "6.24.1",
    "babel-preset-stage-2": "6.24.1",
    "rollup": "0.60.0",
    "rollup-plugin-babel": "3.0.4",
    "rollup-plugin-commonjs": "9.1.3",
    "rollup-plugin-node-resolve": "3.0.0",
    "rollup-plugin-replace": "2.0.0",
    "rollup-plugin-uglify": "4.0.0"
  }
}

网络应用程序 package.json:

{
  "name": "project-web",
  "version": "1.0.0",
  "scripts": {
    "build": "webpack --colors --display-error-details --config=webpack/webpack.dev.js",
    "dev": "concurrently --kill-others \"npm run dev:start\"",
    "dev:start": "node ./server/index.js"
  },
  "dependencies": {
    "babel-polyfill": "^6.26.0",
    "express": "^4.16.3",
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "project-ui": "1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-eslint": "^8.2.3",
    "babel-loader": "^7.1.4",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-plugin-dynamic-import-node": "^1.2.0",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "concurrently": "^3.5.1",
    "eslint": "^4.19.1",
    "eslint-loader": "^2.0.0",
    "eslint-plugin-react": "^7.7.0",
    "piping": "^1.0.0-rc.4",
    "webpack": "^4.6.0",
    "webpack-cli": "^2.0.15",
    "webpack-dev-middleware": "^3.1.3",
    "webpack-dev-server": "^3.1.3",
    "webpack-hot-middleware": "^2.22.1",
    "webpack-node-externals": "^1.7.2"
  }
}

汇总配置:

import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel';
import replace from 'rollup-plugin-replace';
import { uglify } from 'rollup-plugin-uglify';
import pkg from './package.json'


const FORMATS = {
  UMD: 'umd',
  ES: 'es',
  CJS: 'cjs'
};
const allowedFormats = [FORMATS.UMD, FORMATS.ES, FORMATS.CJS];

const bundle = (fileFormat, {format, minify}) => {
  if (!allowedFormats.includes(format)) {
    throw new Error(`Invalid format given: ${format}`);
  }

  const shouldMinify = minify && format === FORMATS.UMD;
  const externals = format === FORMATS.UMD
    ? Object.keys(pkg.peerDependencies || {})
    : [
      ...Object.keys(pkg.dependencies || {}),
      ...Object.keys(pkg.peerDependencies || {})
    ];

  return {
    input: 'src/index.js',
    output: {
      file: fileFormat.replace('{format}', shouldMinify ? `${format}.min` : format),
      format,
      name: 'project-ui',
      exports: 'named',
      globals: {
        react: 'React',
        'prop-types': 'PropTypes'
      }
    },
    external: externals,
    plugins: [
      resolve({ jsnext: true, main: true }),
      commonjs({ include: 'node_modules/**' }),
      babel({
        exclude: 'node_modules/**',
      }),
      format === FORMATS.UMD
        ?  replace({'process.env.NODE_ENV': JSON.stringify(shouldMinify ? 'production' : 'development')})
        : null,
      shouldMinify ? uglify() : null
    ].filter(Boolean)
  };
};

export default [
  bundle('dist/project-ui.{format}.js', {format: FORMATS.UMD, minify: true}),
  bundle('dist/project-ui.{format}.js', {format: FORMATS.CJS}),
  bundle('dist/project-ui.{format}.js', {format: FORMATS.ES})
];

rollup 实际生成的代码:

import React from 'react';

var NavBar = function NavBar() {
  return React.createElement(
    'header',
    null,
    'nav bar'
  );
};
module.exports = exports['default'];

export { NavBar };

原始导航栏:

import React from 'react';

const NavBar = () => (
  <header>
    nav bar
  </header>
);

export default NavBar;

index.js:

export { default as NavBar} from './NavBar/NavBar';

.babelrc:

{
  "presets": [
    ["env", {
      "loose": true,
      "modules": false,
      "targets": {
        "browsers": ["last 2 versions"]
      }
    }],
    "react",
    "stage-2"
  ],
  "plugins": [
    "transform-runtime",
    "add-module-exports",
    "external-helpers"
  ]
}

生成的汇总代码看起来没问题,所以我认为这是一个 yarn 问题,但我不确定。任何帮助将不胜感激!

问候 康乃尔

问题一定出在您使用 babel/rollup 转译代码的方式上。我有一个活生生的例子,说明你的代码在 online babel:

中应该如何显示

为我生成的代码是:

import React from 'react';

const NavBar = () => React.createElement(
  'header',
  null,
  'nav bar'
);

export default NavBar; //  first define default and then we assign export['default']
module.exports = exports['default'];

请注意,在这段代码中,我们首先将 export default 分配给所需的值,然后我们分配 export['defaults'](当我调试您的示例时,我得到 export['default']undefined,因此您会收到错误 Cannot read property 'NavBar' of undefined [0],因为您将未定义传递给导出。

这是由插件 'add-module-exports' 完成的,如果您确实需要 module.exports,那是必需的(除非存在 NodeJS 或某些 RequireJS,否则不需要) .

为了让它工作,只需从项目中的插件中删除 "add-module-exports"-ui .babelrc.