如何让 IntelliSense 感知 package.json 中的导出?

Red*_*Red 11 intellisense webstorm typescript visual-studio-code

我得到了一个package.json ,我在其中使用导出字段导出不同的脚本。

\n
"exports": {\n  ".": {\n    "default": "./dist/main.es.js",\n    "require": "./dist/main.cjs.js",\n    "types": "./dist/main.d.ts"\n  },\n  "./utils": {\n    "default": "./dist/utils.es.js",\n    "require": "./dist/utils.cjs.js",\n    "types": "./dist/utils.d.ts"\n  },\n  "./segments/*": {\n    "default": "./dist/webvtt/segments/*.es.js",\n    "require": "./dist/webvtt/segments/*.cjs.js",\n    "types": "./dist/webvtt/segments/*.d.js"\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

文件结构如下

\n
dist\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.cjs.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.d.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.es.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 utils.cjs.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 utils.d.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 utils.es.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 vite.svg\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 vtt.cjs.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 vtt.d.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 vtt.es.js\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 webvtt\n  \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 segments\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Comment.cjs.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Comment.d.ts\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Comment.es.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Cue.cjs.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Cue.d.ts\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Cue.es.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Header.cjs.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Header.d.ts\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Header.es.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Segment.cjs.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Segment.d.ts\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Segment.es.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Style.cjs.js\n     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Style.d.ts\n     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Style.es.js\n
Run Code Online (Sandbox Code Playgroud)\n

在 VSCode 中,它现在将utils和显示segments为导出路径

\n

在此输入图像描述

\n

但是,当从中导入脚本时,segments不会显示我可以从该路径导入哪些脚本。

\n

在此输入图像描述

\n

但如果我继续,并从段文件夹中导入任何脚本,它就可以正常工作。

\n

在此输入图像描述

\n

如何让IntelliSense显示可以从路径导入的脚本segments

\n
\n

包含源代码的存储库可以在这里找到

\n

https://github.com/codeit-ninja/js-vtt

\n

JΛY*_*ÐΞV 22

您的项目的配置与其本身不协调

\n

package.json这意味着,您在&文件中设置的某些字段tsconfig.json与同一文件中的其他设置相冲突。最值得注意的是您配置项目来解析模块的方式,我将对此进行解释。

\n

与许多其他包维护者一样,您选择添加对“ECMAScript 模块”(又名 ESM)和“Common-JS 模块”(又名 CJS)的模块化支持。也像许多包维护者一样,您未能配置包以使两个模块和谐共存,更具体地说:您为 TS 配置模块解析的方式(换句话说,字段tsconfig.json"moduleResolution"与 Node 的方式不同.js 当前正在配置为解析package.json配置中的模块。

\n

此外,您还没有明确定义 ESM 和 CJS 的构建;当通过导入使用模块时,或者通过 CJSrequire()方法使用模块时,您没有为模块定义入口点。

\n
注意:如果你不需要修复你的包裹,我不会轰炸你。我花了 2 - 3 个小时写这篇文章,希望能分享我花了几个月的时间才学到的东西。
\n
\n

模块加载器

\n

因此,澄清什么是模块加载器,特别是“模块加载器对于 Node.js 来说”是什么,这一点很重要。模块加载器是开发人员用来指示想要消耗某种资源的语法。Node.js 支持两种不同的加载器,您可能熟悉这两种加载器。

\n
注意:我使用 Node\ 的文件系统fs库,仅用于示例目的。我选择它fs只是因为它对很多人来说都很熟悉。
\n

#1 \xe2\x80\x94 ECMAScript 模块加载器

\n
    \n
  • import * as fs from \'node:fs\';

    \n
  • \n
\n

值得注意的是,这个加载器不仅在语法上不同,而且在机制上也不同,因为它是异步的。

\n
\n

#2 \xe2\x80\x94 和 CommonJS 模块加载器:

\n
    \n
  • const fs = require(\'node:fs\');

    \n您可能能够推断require加载程序是同步的。
  • \n
\n

两个加载器之间的异步和同步差异使得两个模块在大多数情况下不兼容(任何例外都超出了我们所讨论的主题)。

\n
\n

你的package.json文件

\n

现在,如上所述,让我们看看您的 package.json 文件。在package.json文件内部,您已设置"type"字段,如下所示。

\n
    { \n      "type": "module"\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

设置"type"本身"module"并不是一件坏事,但是,设置此字段的方式将影响 Node.js 处理发出的 JavaScript 项目的方式(实际上,JS 文件是实际的机制,对吧? TS 更多或从某种意义上来说,不仅仅是静态类型的蓝图)。从这个角度来看,您可以很容易地看出了解 Node.js 的工作原理非常重要。将 TypeScript 配置为与 Node 协调工作非常重要,否则您将遇到不希望的结果,例如“您的智能感知未按预期工作”

\n
\n
\n

理解这4点很重要

\n
    \n
  1. 设置该"type"字段以"module"使 Node 将.js文件视为 ESM JavaScript
  2. \n
  3. 当设置"type"为“commonjs”或完全省略它时,将配置节点将.js文件视为 CommonJS JavaScript。
  4. \n
  5. 当然,.mjs无论最近文件的“类型”字段的值如何,文件总是作为 ESM 加载package.json
  6. \n
  7. 并且文件总是作为 CommonJS 加载,无论最近文件的“类型”字段.cjs的值如何。package.json
  8. \n
\n
\n

配置 TypeScript 的模块解析

\n

您的配置中存在多个错误,但是,最值得注意且最容易修复的错误是您为“moduleResolution”tsconfig.json字段设置的值。您的“moduleResolution”字段当前配置如下所示。 至,如下所示...tsconfig.json["node"][1]

\n
    "compilerOptions": { \n      "moduleResolution": "node" \n    }\n
Run Code Online (Sandbox Code Playgroud)\n

应该注意的是,设置"moduleResolution"改变了(部分)TypeScript 使用的解析策略。在你的例子中,你将其设置为"node",这告诉 TS 模仿Node\'s CommonJS 模块解析算法`。

\n

这是我们获得好东西的地方

\n

因此,换句话说,如果您将package.json\'s"type"字段设置为“module”,则您会告诉 node.js 将“.js”文件解析为 ECMAScript 模块,如上所述,这是解析模块的异步方式。

\n

如果你"moduleResolution"tsconfig.json文件中设置为"node",你就是在告诉 TypeScript 模仿“commonjs”模块使用的算法,这是一种同步解析策略,遵循与 ESM 完全不同的规范。

\n

您是否发现您的项目未配置为和谐地解析模块?

\n

我不知道关于 VS Code 中的 TypeScript 智能感知如何工作的所有细节,但我确实知道它涉及一个语言服务器,该服务器由 TSC 本身提供支持,并且 TSC 配置为解析模块的方式将具有影响智能感知在导入和导出文件时的行为方式。它不会破坏 IntelliSense,但如果无法在导入语句中解析资源,IntelliSense 将向您显示一个列表,其中不包含您要查找的“无论它是什么”。

\n

另外,我不知道为什么要导出类型,这也会影响事情。您应该通过在 package.json 文件中设置“types”字段来导出类型。

\n

根据 TypeScript 文档:

\n
\n

TypeScript 将使用 package.json 中的一个字段命名types来反映“main”的用途 - 编译器将使用它来查找 \xe2\x80\x9cmain\xe2\x80\x9d 定义文件以供参考。

\n
\n
注意:“"types"有一个别名,即"typings"
\n

你想像这样设置两者:

\n
// "tsconfig.json"\n{\n  "compilerOptions": {\n    "declaration": true,\n    "declarationDir": "./types"\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    // "package.json"\n    {\n      "types": "./types"\n    }\n
Run Code Online (Sandbox Code Playgroud)\n
\n

也许稍微不同地配置您的导出?

\n

我执行以下操作

\n

构建目录

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模块类型文件路径
CJS"builds/CommonJS"
ESM"builds/ECMAScript"
\n

导出的入口点

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模块类型文件路径
CJS"./builds/CommonJS/main.cjs"
ESM"./builds/ECMAScript/main.mjs"
\n

\n

源目录树

\n
    .\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n    \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 sum-lib.ts\n    |\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.cts\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.mts\n
Run Code Online (Sandbox Code Playgroud)\n
\n

发送到以下构建树

\n
    .\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CommonJS\n    \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n    \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.cjs\n    \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 package.json    <--------  Notice the package.json?\n    |\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ECMAScript\n        \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n        \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.mjs\n        \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 package.json    <--------  Notice the package.json?\n
Run Code Online (Sandbox Code Playgroud)\n

CommonJS&目录ECMAScript都包含一个package.json,不包含项目的 base-dir,当包含基础 package.json 文件时,该项目总共有 3 个 package.json 文件你只需要 2 个就可以了,但我喜欢 3 个,它使配置更加健壮,并且当项目文件结构发生更改时更难以破坏。

\n

CommonJS&目录下的 package.json 文件ECMAScript很简单 \xe2\x80\x94非常非常简单\xe2\x80\x94 它们只包含绝对需要的内容。

\n

仅供参考,我向您展示了 package.json 文件,因为看起来您没有在任何地方定义 commonjs 模块,这只能使用文件来完成package.json,并且每个模块类型都需要由其自己的 package.json 文件定义。我看到您设置了一个 CJS 条目,并且有 CJS 文件,但没有指向 CJS 模块的显式配置,这也足以破坏您的智能 (我使用粗体是因为它回答了直接问题)

\n

我向您展示的使用文件树的构建中的额外 package.json 文件如下所示。

\n
    // ECMAScript/package.json\n    {\n      "type": "module"\n    }\n
Run Code Online (Sandbox Code Playgroud)\n
    // CommonJS/package.json\n    {\n      "type": "commonjs"\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

项目基地package.json文件

\n
    /* \n    > "package.json"\n    > I left out dependencies, repo url, and other things, and left the \n    important settings */\n\n    {\n      "name": "rgb-interface",\n      "author": "Andrew Chambers <W3Dojo@Gmail.com>",\n      "version": "0.0.3",\n      "type": "module",\n      "types": "types",\n      "exports": {\n        "import": "./builds/ECMAScript/main.mjs", // ESM Entry\n        "require": "./builds/CommonJS/main.cjs" // CJS Entry\n      },\n    }\n}\n// I didn\'t leave out "main", I just don\'t define it, as exports defines my entry points"\n\n
Run Code Online (Sandbox Code Playgroud)\n

只有一个“.mts”文件和一个“.cts”文件,其余的都是“.ts”。只要我正确定义项目的模块,以便 typescript 和 node.js 使用相同的算法,一切都会和谐地工作。我可以从相同的文件导入到 main.cts 和 main.mts,而无需维护同一文件的单独 cts 和 mts 版本。1 个源,2 个构建。

\n

TypeScript 配置

\n

您必须配置 typescript 来构建 CJS 构建,并构建 ESM 构建。有几种不同的概念都可以实现相同的目标,但它们的外观和工作方式彼此非常不同。我认为最简单的方法是利用新的 TS 3.X 功能,这些功能是为了发出多个构建而引入的,今后将使用命令$ tsc --build+ 标志。

\n

在 TSConfig 中,您只需要reference两个不同的构建。

\n
{\n    "files": [],\n    "references": [\n    { "path": "tsconfig.esm.json" },\n    { "path": "tsconfig.cjs.json" }],\n\n    "compilerOptions": {\n        "incremental": true,\n        "tsBuildInfoFile": ".Cache/tsc.buildinfo.json",\n        // "listEmittedFiles": true,\n        "forceConsistentCasingInFileNames": true,\n        "noEmit": true,\n        "listEmittedFiles": true\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后使用另外两个 tsconfig 文件进行构建。

\n

像这样:

\n
    // "tsconfig.esm.json"\n    {\n      "files": [\n        "src/main.mts",\n        "src/test/esm-color-format.test.mts",\n        "src/lib/ansi-static.ts",\n        "src/lib/color-log.ts",\n        "src/lib/ansi.ts"\n      ],\n\n      "exclude": ["node_modules", "**/*.cts"],\n  \n      "compilerOptions": {\n        // ECMAS Module + ES2021 + Node-16LTS\n        "target": "ES2021",\n        "module": "Node16",\n        "moduleResolution": "Node16",\n        "esModuleInterop": false,\n        // STRUCTURE\n        "outDir": "builds/ECMAScript",\n        "declarationDir": "types",\n        "rootDir": "src",\n        "sourceRoot": "src",\n        "composite": true, // <-- must be on\n        // EMISSIONS\n        "declaration": true,\n        "declarationMap": true,  // maps add extra intellisense features\n        "sourceMap": true, // both "*.d.ts.map" and "*.js.map" files\n        "inlineSources": true,\n        "noEmitOnError": true,\n        "noEmit": false, // Can be used when you want to emit only one build\n    }\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n
\n
    // tsconfig.cjs.json\n    {\n      "files": [\n        "src/main.cts",\n        "src/test/cjs-color-format.test.cts",\n        "src/lib/ansi-static.ts",\n        "src/lib/color-log.ts",\n        "src/lib/ansi.ts"\n      ],\n\n      "exclude": ["node_modules", "**/*.mts"],\n\n      "compilerOptions": {\n        // CommonJS + Node-8 + ES5 (Early Support)\n        "target": "ES5",\n        "module": "CommonJS",\n        "moduleResolution": "node",\n        "esModuleInterop": true,\n        // STRUCTURE\n        "outDir": "builds/CommonJS",\n        "declarationDir": "types",\n        "rootDir": "src",\n        "sourceRoot": "src",\n        "composite": true,\n        // EMISSIONS\n        "declaration": true,\n        "declarationMap": true,\n        "sourceMap": true,\n        "inlineSources": true,\n        "noEmitOnError": true,\n        "noEmit": false,\n      }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

不管你相信与否,我删除了所有类型检查和其他不需要的东西来获得我可以支持的所有智能感知功能,并发出一个双重 esm 和 cjs 项目。

\n

无论如何,希望这有帮助

\n
\n
\n
\n