如何允许 CRA 中的命名空间?

How to allowNamespaces in CRA?

我使用以下方法创建了一个 TypeScript React 应用程序:

yarn create react-app my-app --template typescript

这样,我的项目就用Babel and then bundled by webpack. Now I want to use TypeScript namespaces, which aren't supported in Babel by default, but they can be enabled编译了。我安装了所有必要的软件包:

package.json 中我将 react-scripts start 更改为 react-app-rewired start

最后,我创建了一个 config-overrides.js 文件:

const {
    override,
    addExternalBabelPlugin
  } = require("customize-cra");

module.exports = override(
    addExternalBabelPlugin([
        "@babel/plugin-transform-typescript",
        { allowNamespaces: true }
    ])
);

但是编译还是报语法错误,好像没有启用插件:

SyntaxError: /home/m93a/my-app/script.ts: Namespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel. To enable and review caveats see: https://babeljs.io/docs/en/babel-plugin-transform-typescript

如何正确设置我的项目,以便它甚至编译非声明性名称空间?


EDIT: 我的项目没有运行的原因实际上和我想的大不相同。查看我自己的答案以获取更多详细信息。

使用rescripts覆盖Babel配置 请按照以下步骤操作:

  1. 使用 create-react-app my-app --template=typescript 创建新应用或使用现有应用
  2. 安装 rescriptsnpm i -D @rescripts/cli
  3. package.json 中的 rescripts 替换 react-scripts 就像 rescripts docs
  4. 为 babel 配置安装 rescript 插件 @rescripts/rescript-use-babel-config
  5. 将此 snipper 添加到您的 package.json 文件
"rescripts": [
    [
        "use-babel-config",
            {
                "presets": [
                    "react-app",
                    [
                        "@babel/preset-typescript",
                        {
                            "allowNamespaces": true
                        }
                    ]
                ]
            }
        ]
    ]

Rescripts 将使用插件,这将扩展 babel 配置。我们扩展了默认的 react-app babel 配置,所以我们不会破坏任何东西。然后我们覆盖预设(已经由 react-app 安装),但是我们设置了 allowNamespaces 标志。

你必须使用 addBabelPlugin instead of addExternalBabelPlugin.

tl;博士

如果我们查看文档,我们会看到:

addExternalBabelPlugin(plugin)

create-react-app actually has two rules in its webpack config for babel-loader: one for code in addSrc (src/ by default) and the other for code external to that folder (like node_modules). You can add plugins to the external loader using addExternalBabelPlugin in the same way you'd use addBabelPlugin.

如果我们检查 react-scripts/config/webpack.config.js 内部,我们可以看到这两个 babel-loader 条目:

  1. webpack.config.js#L396 there is the babel-loader for the include: paths.appSrc and for \.(js|mjs|jsx|ts|tsx)$/ which addBabelPlugin 添加插件,注释为:

    // Process application JS with Babel.
    // The preset includes JSX, Flow, TypeScript, and some ESnext features.
    
  2. webpack.config.js#L452 is the babel-loader for /\.(js|mjs)$/ which addExternalBabelPlugin 添加插件,注释为:

    // Process any JS outside of the app with Babel.
    // Unlike the application JS, we only compile the standard ES features.
    

我们希望 @babel/plugin-transform-typescript to apply to the first babel-loader regarding application src folder so we have to use addBabelPlugin 适用于 babel-loader 配置。

最后但同样重要的是,确保您已将 package.json 脚本从 react-app-rewired:

更新为 运行
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"

您可以使用来自此处命名空间的 class 检查一个非常简单的 CRA Typescript 项目:https://github.com/clytras/cra-ts-namespaces

在我的特殊情况下,即使我听从了其他人的建议,我也无法让它工作的原因如下:Babel 中有一个错误阻止它合并 enumnamespace。例如,这段代码是行不通的:

export enum Foo
{
    a, b, c, d
}

export namespace Foo
{
  export function bar()
  {
    return "bar"
  }
}

但是由于一些奇怪的原因,Babel 在遇到这段代码时会抛出一个非常具有误导性的错误:

SyntaxError: script.ts: Namespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel. To enable and review caveats see: https://babeljs.io/docs/en/babel-plugin-transform-typescript

我提交了 an issue,所以希望这个错误能尽快得到修复,或者至少得到一个体面的错误消息。同时,可以使用一个变通方法:

export enum Foo_
{
    a, b, c, d
}

export type Foo = Foo_

export namespace Foo
{
  export const a = Foo_.a
  export const b = Foo_.b
  export const c = Foo_.c
  export const d = Foo_.d
  export function bar()
  {
    return "bar"
  }
}