【问题标题】:How to import shared typescript code using create-react-app (no eject)?如何使用 create-react-app(无弹出)导入共享打字稿代码?
【发布时间】:2020-07-16 23:37:48
【问题描述】:

我正在尝试以非弹出模式在 create-react-app 中实现 TypeScript 代码共享,但我遇到了一个臭名昭著的限制,即不允许在 src 之外导入:

You attempted to import ../../common/src/lib.ts which falls outside of the project src/ directory. [...]

对于非 TypeScript 案例,这是 asked & answered here,但我无法获得任何与 TypeScript 一起使用的解决方案。具体而言,建议的解决方案存在以下问题:

  • ts-config.json 中设置baseDir:这里create-react-app 抱怨:你项目的baseUrl 只能设置为srcnode_modules。 Create React App 目前不支持其他值。

  • 基于 react-app-rewired 的方法:更有希望。禁用 ModuleScopePlugin 让我摆脱了“尝试在 src 外部导入”错误,但现在的问题是打字稿文件的加载器无法播放:

    我已经验证 .ts 本身没问题:复制到 ./src 并从那里导入它工作正常。

    我还在ts-config.jsonincludes 列表中添加了../../common/src 文件夹。

    我的猜测是 webpack 加载器配置有一条规则阻止 TypeScript 加载器转译与其预期路径模式不匹配的文件。如何使用 react-app-rewired 解决此问题?

  • 符号链接源也不起作用——同样的问题。可能是因为 webpack 在内部解析了符号链接,并且在正常加载器规则不适用的路径中看到了文件。

  • 基于弹出的解决方案:我暂时不想弹出,但我试过了,但基本上又遇到了同样的问题。

我还发现了其他听起来相关但没有回答问题的问题:


在 mono repo 中重用 TypeScript 类型似乎是一种相当合理的模式。我是否遗漏了什么,或者为什么 create-react-app 让这变得如此困难?


重现:我的代码基本上 100% 是你得到的

npx create-react-app my-app --template typescript

添加了一个到外部的导入。

【问题讨论】:

  • 我认为 monorepo 解决方案最有意义,因为它在引擎盖下进行了符号链接,并且对于未弹出的应用程序非常适用。 classic.yarnpkg.com/en/docs/workspaces 你只需要确保在导入类型之前运行 tsc
  • @azium 嗯,我尝试了各种符号链接模式,但由于 webpack 在内部解析了这些符号链接,我总是以上述错误结束。
  • 而不是导入../something,而是将模块导入你的node_modules文件夹import { YourType } from 'your-local-module'
  • 这个 example 使用带有 typescript 的 create-reat-app 4.0 在 src 之外导入文件 NearSrc.tsx 演示了有效的解决方案 (answer )

标签: javascript reactjs typescript webpack create-react-app


【解决方案1】:

您可以使用 craco(create-react-app 配置覆盖)覆盖 webpack 配置(作为 CRA 的一部分抽象)而不弹出。
此外,您可以使用 ts-loader 直接在外部项目中引用未转译的 ts 代码(例如,如果您想引用/使用共享代码/lib 作为 mono-repo 的一部分)。

假设您的 CRA 应用位于 client 目录中,您的项目结构如下:

client/
|--src/
|--package.json
shared/
|--package.json
|--(ts files)
package.json

cd client
yarn add -D @craco/craco ts-loader
  1. client/ (CRA) 目录中创建一个craco.config.js 文件
  2. 确保craco.config.js 具有以下内容
const path = require("path");

module.exports = {
  webpack: {
    configure: webpackConfig => {

      // ts-loader is required to reference external typescript projects/files (non-transpiled)
      webpackConfig.module.rules.push({
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          transpileOnly: true,
          configFile: 'tsconfig.json',
        },
      })
      
      return webpackConfig;
    }
  }
};
  1. client/package.json 中的react-scripts 命令替换为craco
/* client/package.json */

"scripts": {
-   "start": "react-scripts start",
+   "start": "craco start",
-   "build": "react-scripts build",
+   "build": "craco build"
-   "test": "react-scripts test",
+   "test": "craco test"
}

【讨论】:

  • 请注意,CRACO 不正式支持 create-react-app 4.0 和 its maintainer doesn't seem to be in the mood to maintain it long term。令人讨厌的是,这些针对 CRA 限制默认值的变通办法随着每个主要版本的发布而中断。
  • CRACO 现在支持 CRA 4.*,但我与 ts-loader 有冲突,后者将 webpack 作为依赖项。我没有添加规则并安装 ts-loader,而是修改了 CRA 自己的规则,使其没有 include 字段,该字段将其限制为 src 目录。如果索引发生变化,您可能需要一些额外的检查,但本质上是这样做而不是推送新规则:delete webpackConfig.module.rules[1].oneOf[2].include;
【解决方案2】:

更新:由于 react-app-rewired 只是被动维护,不支持 CRA 版本 2+(在撰写本文时我们是三个主要版本),我不会再推荐这种方法。


经过更多小时的试验和阅读 GitHub 问题后,我终于有了一个可行的解决方案。非常感谢制作this very helpful post on GitHub 的 BirukDmitry。分步指南:

  1. 安装 react-app-rewired 和 customize-cra

     npm i react-app-rewired customize-cra --save-dev
    
  2. 使用最小的config-overrides.js 配置 react-app-rewird,如下所示:

    const { removeModuleScopePlugin, override, babelInclude } = require("customize-cra");
    const path = require("path");
    
    module.exports = override(
      removeModuleScopePlugin(),        // (1)
      babelInclude([
        path.resolve("src"),
        path.resolve("../common/src"),  // (2)
      ])
    );
    

    设置 (1) 类似于绕过一般的 import-outside-src 限制所需的设置(请参阅linked question)。

    设置 (2) 对于其他路径也启用 babel-transpilation(因此,包括我认为的 TS 类型剥离)至关重要。这是您必须添加要从中导入的路径的地方。

  3. tsconfig.json 无需修改。

  4. 使用相对路径导入,例如import * as mymodule from '../../common/src/mymodule'

【讨论】:

  • babelInclude 配置是我解决上述错误所缺少的。感谢分享!
【解决方案3】:

解决方案是react-app-rewire-alias(对于react-app-rewiredcustomize-cracraco):

根据提到的文档替换react-scripts in package.json 并配置下一个:

  • 重新接线的 cra 版本:
// config-overrides.js
const {alias, configPaths} = require('react-app-rewire-alias')
const aliasMap = configPaths('./tsconfig.paths.json') // or jsconfig.paths.json

module.exports = alias(aliasMap)
module.exports.jest = aliasJest(aliasMap)
  • craco 版本:
// craco.config.js
const {CracoAliasPlugin, configPaths} = require('react-app-rewire-alias')

module.exports = {plugins: [{
  plugin: CracoAliasPlugin,
  options: {alias: configPaths('./tsconfig.paths.json')}
}]}

像这样在 json 中配置别名:

// tsconfig.paths.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "common/*": ["../../common/src/*"],
      "@library/*": ["../../library/src/*"]
    }
  }
}

并将此文件添加到主要打字稿配置文件的extends 部分:

// tsconfig.json
{
  "extends": "./tsconfig.paths.json",
  // ...
}

【讨论】:

  • 奇怪,react-app-rewired 和 customize-cra 并没有正式支持 CRA 4,对吧?它只是偶然起作用,还是仅在有限的功能集下起作用?
  • 此解决方案不能解决问题。问题是我们想要在根目录之外导入共享代码。不在src附近。如果您有多个项目并希望在这些项目之间共享组件,但又不想将组件发布到 npm
  • @oklas v1.1.0 of cra 或 ts 还是什么?
  • @user158443,从版本 react-app-rewire-alias 中使用 typescript 从 CRA 根文件夹外部导入(使用 craco 或 rewired cra) v1.1.0
  • @bluenote10,这是 CRA 确切版本 v4.0 中的一些错误,但它也适用于 v4.0 并带有一些附加操作 (more details here),它已在 CRA v4.1 中修复并且因为它起作用了。
【解决方案4】:

问题../../common/src/mymodule 住在哪里? 是另一个项目吗? 如果是另一个项目,为什么不链接他们

  • 在公共代码项目内运行npm link

  • 项目内部将使用通用代码:npm link common-project

如果没有两个不同的项目。为什么它必须在 src 之外?

您发布的链接The create-react-app imports restriction outside of src directory 而你的案例/问题是两个完全不同的东西,而且它们非常困难。让我告诉你这个问题和你的问题。

我们已经知道 CRA 创建了一个 SPA 单页应用程序。这意味着只有一个 html 文件。一切都发生在位于<project>/public/index.htmlindex.html。如果我们编译代码,我们会注意到最终包可能看起来像这样

build
--static
----js
----css
----media
--index.html
--...more hashed crap...
public
src

默认情况下process.env.PUBLIC_URL 设置为“/” 什么?? ? 是的,让我告诉你。有一句老话说一张图解释1000多字。

如果我们查看根据其路径位于./src/styles/imageSlack.jpg 或其他位置的图像。 那么如果我们 console.log 它呢。

哇!他们在哪里做的?你可以做的一件事来测试我的理论是如果你console.log(process.env.PUBLIC_URL)在你的代码中的任何地方。现在这是这个和那个之间的最大区别。

浏览器本身不知道 Typescript。

所以要么你在src 中设置你的代码,我们就快乐地结束,要么 遵循关注点分离原则,我们使用共享代码创建一个 npm 包,并作为任何其他模块导入。

【讨论】:

    猜你喜欢
    • 2019-11-11
    • 1970-01-01
    • 2018-01-14
    • 2017-11-20
    • 2020-10-27
    • 2017-08-22
    • 2019-08-04
    • 2018-04-03
    • 2018-07-27
    相关资源
    最近更新 更多