reactjs - 如何配置 webpack 来转译其他 lerna 包中的文件(从 create-react-app 中弹出)

标签 reactjs webpack ecmascript-6 babeljs create-react-app

我正在尝试使用 create-react-app 包和一个简单的组件库构建一个 lerna 包。我的组件如下:

import React, { Component } from "react";
import PropTypes from "prop-types";

    class Layout extends Component {
        render = () => {
            let style = {
                fontSize: 14,
                fontFamily:
                    "-apple-system, system-ui, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif",
                fontWeight: 400
            };
    
            return <div style={style}>{this.props.children}</div>;
        };
    }
    
    export default Layout;

我原来的create-react-app如下:

index.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './components/app/App/App';

ReactDOM.render(<App />, document.getElementById('root'));

App.js

import React, { Component } from "react";

import Layout from "@project/webux/lib/Layout";

class App extends Component {
    render = () => {
        return (
            <Layout>
                Hello!
            </Layout>
        );
    };
}

export default App;

运行时,出现以下错误:

../webux/lib/Layout/index.js
SyntaxError: /Volumes/workspace/dev/packages/webux/lib/Layout/index.js: Support for the experimental syntax 'classProperties' isn't currently enabled (5:12):

  3 |
  4 | class Layout extends Component {
> 5 |     render = () => {
    |            ^
  6 |         let style = {
  7 |             fontSize: 14,
  8 |             fontFamily:

Add @babel/plugin-proposal-class-properties (https://git.io/vb4SL) to the 'plugins' section of your Babel config to enable transformation.

发生此错误的原因是 create-react-app 不会在其项目之外转译文件。由于我的组件 Layout 驻留在另一个目录中的另一个 lerna 包中,因此它不会被转译。

为了解决这个问题,我弹出了 create-react-app 应用程序,并最终得到以下 webpack 配置文件,其中添加了 ==== INCLUDED=== 一段用于设置 input 目录的代码(我已在项目上方添加了该目录,因为这将指向我的lerna \packages 目录,因此所有包文件都会被处理:

  ...
    resolve: {
      // This allows you to set a fallback for where Webpack should look for modules.
      // We placed these paths second because we want `node_modules` to "win"
      // if there are any conflicts. This matches Node resolution mechanism.
      // https://github.com/facebook/create-react-app/issues/253
      modules: ['node_modules', paths.appNodeModules].concat(
        modules.additionalModulePaths || []
      ),
      // These are the reasonable defaults supported by the Node ecosystem.
      // We also include JSX as a common component filename extension to support
      // some tools, although we do not recommend using it, see:
      // https://github.com/facebook/create-react-app/issues/290
      // `web` extension prefixes have been added for better support
      // for React Native Web.
      extensions: paths.moduleFileExtensions
        .map(ext => `.${ext}`)
        .filter(ext => useTypeScript || !ext.includes('ts')),
      alias: {
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
        'react-native': 'react-native-web',
        // Allows for better profiling with ReactDevTools
        ...(isEnvProductionProfile && {
          'react-dom$': 'react-dom/profiling',
          'scheduler/tracing': 'scheduler/tracing-profiling',
        }),
        ...(modules.webpackAliases || {}),
      },
      plugins: [
        // Adds support for installing with Plug'n'Play, leading to faster installs and adding
        // guards against forgotten dependencies and such.
        PnpWebpackPlugin,
        // Prevents users from importing files from outside of src/ (or node_modules/).
        // This often causes confusion because we only process files within src/ with babel.
        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
        // please link the files into your node_modules/ and let module-resolution kick in.
        // Make sure your source files are compiled, as they will not be processed in any way.
        new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
      ],
    },
    resolveLoader: {
      plugins: [
        // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
        // from the current package.
        PnpWebpackPlugin.moduleLoader(module),
      ],
    },
    module: {
      strictExportPresence: true,
      rules: [
        // Disable require.ensure as it's not a standard language feature.
        { parser: { requireEnsure: false } },

        // First, run the linter.
        // It's important to do this before Babel processes the JS.
        {
          test: /\.(js|mjs|jsx|ts|tsx)$/,
          enforce: 'pre',
          use: [
            {
              options: {
                cache: true,
                formatter: require.resolve('react-dev-utils/eslintFormatter'),
                eslintPath: require.resolve('eslint'),
                resolvePluginsRelativeTo: __dirname,
                
              },
              loader: require.resolve('eslint-loader'),
            },
          ],
          //=================== INCLUDED =====================/
          //
          // Included the lenrna packages directory (up directory) 
          // in order to transpile all files from other packages.
          //
          //===================================================
          include: [path.resolve(__dirname, "../.."), paths.appSrc],
        },
        {
          // "oneOf" will traverse all following loaders until one will
          // match the requirements. When no loader matches it will fall
          // back to the "file" loader at the end of the loader list.
          oneOf: [
            // "url" loader works like "file" loader except that it embeds assets
            // smaller than specified limit in bytes as data URLs to avoid requests.
            // A missing `test` is equivalent to a match.
            {
              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
              loader: require.resolve('url-loader'),
              options: {
                limit: imageInlineSizeLimit,
                name: 'static/media/[name].[hash:8].[ext]',
              },
            },
            // Process application JS with Babel.
            // The preset includes JSX, Flow, TypeScript, and some ESnext features.
            {
              test: /\.(js|mjs|jsx|ts|tsx)$/,
              /// Renato Mendes
              /// This was added to support transpiling of monorepo modules.
              /// See https://github.com/webpack/webpack/issues/6799
              ///
              /// Original:
              /// include: paths.appSrc
              ///
              include: [path.resolve(__dirname, "../.."), path.resolve(paths.lernaRoot + "/packages"), paths.appSrc],
              //              include: paths.appSrc,
              include: [paths.lernaRoot, paths.appSrc],
              loader: require.resolve('babel-loader'),
              options: {
                customize: require.resolve(
                  'babel-preset-react-app/webpack-overrides'
                ),
                
                plugins: [
                  [
                    require.resolve('babel-plugin-named-asset-import'),
                    {
                      loaderMap: {
                        svg: {
                          ReactComponent:
                            '@svgr/webpack?-svgo,+titleProp,+ref![path]',
                        },
                      },
                    },
                  ],
                ],
                // This is a feature of `babel-loader` for webpack (not Babel itself).
                // It enables caching results in ./node_modules/.cache/babel-loader/
                // directory for faster rebuilds.
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                compact: isEnvProduction,
              },
            },
            // Process any JS outside of the app with Babel.
            // Unlike the application JS, we only compile the standard ES features.
            {
              test: /\.(js|mjs)$/,
              exclude: /@babel(?:\/|\\{1,2})runtime/,
              loader: require.resolve('babel-loader'),
              options: {
                babelrc: false,
                configFile: false,
                compact: false,
                presets: [
                  [
                    require.resolve('babel-preset-react-app/dependencies'),
                    { helpers: true },
                  ],
                ],
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                
                // If an error happens in a package, it's possible to be
                // because it was compiled. Thus, we don't want the browser
                // debugger to show the original code. Instead, the code
                // being evaluated would be much more helpful.
                sourceMaps: false,
              },
            },
            // "postcss" loader applies autoprefixer to our CSS.
            // "css" loader resolves paths in CSS and adds assets as dependencies.
            // "style" loader turns CSS into JS modules that inject <style> tags.
            // In production, we use MiniCSSExtractPlugin to extract that CSS
            // to a file, but in development "style" loader enables hot editing
            // of CSS.
            // By default we support CSS Modules with the extension .module.css
            {
              test: cssRegex,
              exclude: cssModuleRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction && shouldUseSourceMap,
              }),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },
            // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
            // using the extension .module.css
            {
              test: cssModuleRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction && shouldUseSourceMap,
                modules: true,
                getLocalIdent: getCSSModuleLocalIdent,
              }),
            },
            // Opt-in support for SASS (using .scss or .sass extensions).
            // By default we support SASS Modules with the
            // extensions .module.scss or .module.sass
            {
              test: sassRegex,
              exclude: sassModuleRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 2,
                  sourceMap: isEnvProduction && shouldUseSourceMap,
                },
                'sass-loader'
              ),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },
            // Adds support for CSS Modules, but using SASS
            // using the extension .module.scss or .module.sass
            {
              test: sassModuleRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 2,
                  sourceMap: isEnvProduction && shouldUseSourceMap,
                  modules: true,
                  getLocalIdent: getCSSModuleLocalIdent,
                },
                'sass-loader'
              ),
            },
            // "file" loader makes sure those assets get served by WebpackDevServer.
            // When you `import` an asset, you get its (virtual) filename.
            // In production, they would get copied to the `build` folder.
            // This loader doesn't use a "test" so it will catch all modules
            // that fall through the other loaders.
            {
              loader: require.resolve('file-loader'),
              // Exclude `js` files to keep "css" loader working as it injects
              // its runtime that would otherwise be processed through "file" loader.
              // Also exclude `html` and `json` extensions so they get processed
              // by webpacks internal loaders.
              exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
              options: {
                name: 'static/media/[name].[hash:8].[ext]',
              },
            },
            // ** STOP ** Are you adding a new loader?
            // Make sure to add the new loader(s) before the "file" loader.
          ],
        },
      ],
    }
    ...

我仍然收到错误,因为我的外部组件没有被转换。

如何使上述 webpack 配置转译驻留在我的 lerna 项目的另一个包中的代码?还缺少其他配置吗?我做错了什么?

最佳答案

坏消息:这是一个常见问题。 Create React App doesn't support monorepos ,截至 3.2.0/2019 年末。如果您想在 lerna 兄弟包之间共享组件,许多人要么避免使用“CRApp”,要么在其组件库包中包含构建脚本并提交和导出预编译的 ES5文件。

好消息:我发现了一个似乎有效的修复,并且不需要弹出 CRA。通过本地构建和测试部署到 github 页面进行了测试。

它使用 craco ,它提供了一个 API,用于编辑 CRA 的 webpack 配置而不弹出。 Craco 有添加 webpack 加载器等的插件;我们需要craco-babel-loader :

npm i --save @craco/craco craco-babel-loader

...然后还有一些进一步的 CRACO 设置步骤,请检查 https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#installation最新的。在撰写本文时,您需要替换 package.json 中的以下 CRA scripts:

  • react-scripts 启动 => craco 启动
  • react-scripts 构建 => craco 构建
  • react-scripts 测试 => craco 测试

然后我们需要在 CRA/craco 包的根目录中创建一个配置文件 craco.config.js,该文件从同级包接收 ES6+ JSX 组件,我们需要列出要发送到 babel 的包名称:

// crago.config.js
// see: https://github.com/sharegate/craco

const path = require('path')
const fs = require('fs')
const cracoBabelLoader = require('craco-babel-loader')

// Handle relative paths to sibling packages
const appDirectory = fs.realpathSync(process.cwd())
const resolvePackage = relativePath => path.resolve(appDirectory, relativePath)

module.exports = {
  plugins: [
    {
      plugin: cracoBabelLoader,
      options: {
        includes: [
          // No "unexpected token" error importing components from these lerna siblings:
          resolvePackage('../some-component-library'),
          resolvePackage('../more-components'),
          resolvePackage('../another-components-package'),
        ],
      },
    },
  ],
}

关于reactjs - 如何配置 webpack 来转译其他 lerna 包中的文件(从 create-react-app 中弹出),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58323916/

相关文章:

javascript - 无法理解这段关于 ES6 解构的片段

javascript - 无法访问 Redis 远程服务器并且无法读取或写入 Redis 数据库

javascript - 想要动态处理与分页相关的页码显示 : ReactJS

javascript - "this.setState is not a function"在嵌套的 addEventListener 函数中使用 setState 时

javascript - React - 如何将输入字段(受控)重置为默认值

reactjs - 将 Flask 服务器添加到 create-react-app 默认应用程序,/dist/bundle.js 上的 404

typescript - `TypeORM` 和 `Electron` 在尝试访问数据库时导致 "This option/function is not supported in the browser environment"错误

css - 悬停时如何更改特定元素?

reactjs - React.js 未捕获错误 : Invariant Violation

vue.js - Vue webpack 将 #/添加到所有 URL