首页 > 解决方案 > 为 TypeScript、Webpack 和 Jasmine 导入 ES 模块

问题描述

背景

我有一组包,我在我的个人项目中使用类似的结构重用它们。

出于生产目的,它们是用TypeScripttsc编写的,并在它们发布到 npm 之前编译成 JavaScript,这样我就可以将它们安装在我的其他项目中。我还使用Jasmine对它们进行测试。

在开发中,我使用Webpack和它ts-loader来编译 TypeScript,让我可以在浏览器中运行的示例脚本中使用它。我通常在我使用这些包的项目中使用 Webpack,尽管因为它们是在发布之前编译的,所以这些项目只会将它们视为 JavaScript,不需要使用ts-loader.

我在任何地方都使用ES 模块语法,因此我可以在每个上下文中轻松地使用相同的语法,而不必担心在 Node 的 CommonJS 和浏览器的 ES 模块语法之间切换。为了在 Jasmine 中允许这样做,我在我的jasmine.json配置文件中设置了"jsLoader": "import".

我有一个关于如何写我的import陈述的问题。特别是,如何以适用于我开发这些包时出现的每个用例的方式指定文件扩展名。


没有文件扩展名

TypeScript 的 Node 模块解析围绕文件扩展做了一些有用的假设:

TypeScript 将模仿 Node.js 运行时解析策略,以便在编译时定位模块的定义文件。为此,TypeScript 将 TypeScript 源文件扩展名(.ts、.tsx 和 .d.ts)覆盖在 Node 的解析逻辑上。

这意味着如果我使用诸如import { foo } from './foo';TypeScript 之类的代码,它将理解查找名为foo.ts.

类似地,Webpack 的resolve配置对象允许在属性中指定一组文件扩展名,extensions以告诉 Webpack 如何尝试解析没有文件扩展名的导入。因此,通过我的配置,extensions: ['.js', '.ts']它将找到与foo.tsTypeScript 相同的文件。

但是,当我尝试在import没有文件扩展名的情况下在 Jasmine 中运行我的测试时,它无法解决。

C:\Users\mark.hanna\Documents\Code Playground\analyser>npm test

> analyser@0.1.0 test C:\Users\mark.hanna\Documents\Code Playground\analyser
> tsc && jasmine

Error: While loading C:/Users/mark.hanna/Documents/Code Playground/analyser/spec/analyser.spec.js: NodeError: Cannot find module 'C:\Users\mark.hanna\Documents\Code Playground\analyser\dist\file-processing' imported from C:\Users\mark.hanna\Documents\Code Playground\analyser\dist\analyser.js
    at C:\Users\mark.hanna\Documents\Code Playground\analyser\node_modules\jasmine\lib\loader.js:22:30
    at async Jasmine._loadFiles (C:\Users\mark.hanna\Documents\Code Playground\analyser\node_modules\jasmine\lib\jasmine.js:182:5)
    at async Jasmine.loadSpecs (C:\Users\mark.hanna\Documents\Code Playground\analyser\node_modules\jasmine\lib\jasmine.js:173:3)
    at async Jasmine.execute (C:\Users\mark.hanna\Documents\Code Playground\analyser\node_modules\jasmine\lib\jasmine.js:479:3)
npm ERR! Test failed.  See above for more details.

.js 扩展名

我的另一个选择是.js在我的导入中指定文件扩展名,例如import { foo } from './foo.js.

虽然我没有发现它有明确的记录,但 TypeScript 的模块解析也清楚地理解这./foo.js实际上可能意味着./foo.ts. 我发现自 TypeScript 2 以来一直支持对此的引用

这意味着我可以指定.js文件扩展名并仍然使用tsc. 然后我也能够正确运行我的 Jasmine 测试,因为它知道在包含文件扩展名时如何进行模块解析。

但是,Webpack 不明白这./foo.js可能实际上意味着./foo.ts. 因此,使用这种方法,当将代码导入脚本以在浏览器中显示示例时,我无法在包中构建代码。

ERROR in ./src/file-processing.ts 2:0-49
Module not found: Error: Can't resolve './AnalyserRows.js' in 'C:\Users\mark.hanna\Documents\Code Playground\analyser\src'
resolve './AnalyserRows.js' in 'C:\Users\mark.hanna\Documents\Code Playground\analyser\src'
  using description file: C:\Users\mark.hanna\Documents\Code Playground\analyser\package.json (relative path: ./src)
    Field 'browser' doesn't contain a valid alias configuration
    using description file: C:\Users\mark.hanna\Documents\Code Playground\analyser\package.json (relative path: ./src/AnalyserRows.js)
      no extension
        Field 'browser' doesn't contain a valid alias configuration
        C:\Users\mark.hanna\Documents\Code Playground\analyser\src\AnalyserRows.js doesn't exist
      .js
        Field 'browser' doesn't contain a valid alias configuration
        C:\Users\mark.hanna\Documents\Code Playground\analyser\src\AnalyserRows.js.js doesn't exist
      .ts
        Field 'browser' doesn't contain a valid alias configuration
        C:\Users\mark.hanna\Documents\Code Playground\analyser\src\AnalyserRows.js.ts doesn't exist
      as directory
        C:\Users\mark.hanna\Documents\Code Playground\analyser\src\AnalyserRows.js doesn't exist
 @ ./src/analyser.ts 4:0-48 69:0-50
 @ ./docs/assets/js/src/docs-script.ts 2:0-53 7:14-36 34:14-36 43:14-36 48:51-68 49:28-45

.js 扩展名,带有 Webpack 别名

到目前为止,我发现的唯一解决方案是一个我真的不喜欢的解决方案,即使用 Webpack 的resolve.alias配置来明确告诉它,当我说./foo.js我的意思是./foo.ts.

例如,我目前在我的@cipscis/csv项目中使用了这种方法:

    resolve: {
        extensions: ['.js', '.ts'],
        alias: {
            '@cipscis/csv': `${srcPath}/csv.ts`,
            './stringify.js': './stringify.ts',
            './parse.js': './parse.ts',
        },
    },

webpack.config.js

不过这感觉很笨拙,而且我必须为我导入的每个文件指定一个这样的别名,所以它根本感觉不到可扩展性。感觉肯定有一种“正确”的方式来做到这一点?

标签: typescriptwebpackjasminets-loader

解决方案


感谢amosq 对我对相关问题的回答的回复,我现在有了这个问题的答案。

Jasmine 在这里有点红鲱鱼,真正的相关性只有它试图与我的设置相关的 ES 模块分辨率。问题的核心是 Webpack 的模块解析无法重现 TypeScript 的模块解析规则的功能,它将*.js路径视为可能指向*.ts文件。

感谢amosq的评论,我已经能够通过使用Webpack解析器插件来解决这个问题:resolve-typescript-plugin

它的说明仅显示了如何在 CommonJS 语法中使用它,尽管正如我在问题中提到的那样,我在任何地方都可以使用 ES Module 语法。要在我的 Webpack 配置中使用它,我已经这样做了:

import resolveTypeScriptPluginModule from 'resolve-typescript-plugin';
const ResolveTypeScriptPlugin = resolveTypeScriptPluginModule.default;

const config = {
    // ...
    resolve: {
        fullySpecified: true,
        plugins: [new ResolveTypeScriptPlugin()],
        // ...
    },
    // ...
};

此设置允许我使用 Webpackts-loader来编译在其导入语句中使用的 TypeScript 文件*.js,作为我也tsc用于编译这些文件的同一设置的一部分。我不再需要使用我在问题末尾提到的 Webpack 别名。


推荐阅读