reactjs 如何解决“SyntaxError:Cannot use import statement outside a module”while running jest test case

6tqwzwtp  于 6个月前  发布在  React
关注(0)|答案(1)|浏览(56)

我是一个初学者reactjs开发者,在我的react typescript中使用monaco-editor库构建代码编辑器。我已经在我的项目中集成了monaco编辑器和web worker。我已经使用CRAwebpack 5创建了项目。
我试图使用jest库编写一个测试用例,但我遇到了以下错误:
"SyntaxError: Cannot use import statement outside a module"
我试着在jest.js.js和babel.js.js中添加一些配置。
jest.config.js

module.exports = {
  transform: {
    "^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
    "node_modules/monaco-editor/esm/vs/language/css/css.worker": "babel-jest",
  },
  globals: {
    "ts-jest": {
      tsconfig: "./tsconfig.json",
      useESM: true 
    },
  },
  moduleNameMapper: {
    // Use the mock for Worker creation
    // 'worker-loader!.*': 'src/__mocks__/workerMock.js',
    "monaco-editor/esm/vs/language/(.*)/(.*).worker":
      `<rootDir>/src/__mocks__/moanco-editor-mock.ts`,
  },
  // setupFiles: [`${__dirname}/jest.setup.js`],
  testEnvironment: "node",  
  modulePaths: ['<rootDir>/node_modules'],
  moduleDirectories: ['node_modules'],
};

字符串
babel.config.js

module.exports = {
  presets: ["@babel/preset-env", "@babel/preset-typescript"],
  plugins: ["transform-import-meta"],
};


该错误与monaco编辑器导入有关,该导入发生在我的MonacoEditor.tsx组件中。该组件在useEffect中创建编辑器并调用MonacoEnvironment.tsx
可能有一些错误,在我的实施,我将感谢任何解决方案,我的实施。
我的MonacoEditor.tsx

import React, { useEffect, useRef } from "react";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
// import * as ts from "monaco-editor/esm/vs/language/typescript/ts.worker.js";
import "../../../styles/MonacoEditor.css";
import MonacoEnvironment from "./editor.worker";

const MonacoEditor: React.FC = () => {
  const editorContainer = useRef<HTMLDivElement | null>(null);
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
  useEffect(() => {
    const createEditor = async () => {
      if (editorContainer.current) {
        const editor = monaco.editor.create(editorContainer.current, {
          value: "",
          language: "typescript",
          theme: "vs-dark",
          minimap: { enabled: true },
          automaticLayout: true,
        });

        editorRef.current = editor;
        MonacoEnvironment(); 

        window.addEventListener("resize", () => {
          // Trigger Monaco Editor's automatic layout adjustment on window resize
          editor.layout();
        });
        return () =>
          window.removeEventListener("resize", () => editor.layout());
      }
    };
    createEditor();
    // Cleanup function
    return () => {
      if (editorRef.current) {
        editorRef.current.dispose();
      }
    };
  }, []);

  return (
    <div className="Monaco_Editor_Outer_Wrapper">
      <div ref={editorContainer} className="Monaco_Editor_Main_Container1" />
    </div>
  );
};
export default MonacoEditor;


这是我的MonacoEnviroment.tsx,它在useEffect中被调用,并创建了一个网页工作程序来冻结UI。
MonacoEnviroment.tsx

import React from "react";

const MonacoEnvironment = () => {
  console.log("MonacoEnvironment");
  window.MonacoEnvironment = {
    getWorker(moduleId: string, label: string) {
      switch (label) {
        case "css":
        case "less":
        case "scss":
          return new Worker(
            new URL(
              "monaco-editor/esm/vs/language/css/css.worker",
              import.meta.url
            )
          );
        case "editorWorkerService":
          return new Worker(
            new URL(
              "monaco-editor/esm/vs/editor/editor.worker",
              import.meta.url
            )
          );
        case "handlebars":
        case "html":
        // return new Worker(new URL("monaco-editor/esm/vs/language/html/html.worker" ,  import.meta.url) )
        case "razor":
          return new Worker(
            new URL(
              "monaco-editor/esm/vs/language/html/html.worker",
              import.meta.url
            )
          );
        case "json":
          return new Worker(
            new URL(
              "monaco-editor/esm/vs/language/json/json.worker",
              import.meta.url
            )
          );
        case "javascript":
        case "typescript":
          return new Worker(
            new URL(
              "monaco-editor/esm/vs/language/typescript/ts.worker",
              import.meta.url
            )
          );
        default:
          throw new Error(`Unknown label ${label}`);
      }
    },
  };
};

export default MonacoEnvironment;


我为MonacoEditor.tsx编写的测试用例用于测试组件。
MonacoEditor.test.tsx

// MonacoEditor.test.tsx
import React from 'react';
import { render } from '@testing-library/react';
import MonacoEditor from '../components/middleComponent/monacoEditor/MonacoEditor';

// Mock MonacoEnvironment module

jest.mock('../components/middleComponent/monacoEditor/editor.worker', () => ({
  __esModule: true,
  default: jest.fn(),
}));

describe('MonacoEditor', () => {
  it('renders successfully', async () => {
    // Render the MonacoEditor component
    render(<MonacoEditor />);

    // Your test code goes here

    // For example, you can assert that MonacoEnvironment was called
    expect(require('../components/middleComponent/monacoEditor/editor.worker').default).toHaveBeenCalled();
  });
});


我在npm run test运行此测试用例时遇到的错误

src/tests/MonacoEditor.test.tsx
  ● Test suite failed to run
                                                                                                                                                                    
    Jest encountered an unexpected token                                                                                                                            
                                                                                                                                                                    
    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.                                                                                                                                                        
                                                                                                                                                                    
    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.                                 
                                                                                                                                                                    
    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    C:\Users\HP\OneDrive\Desktop\prometheus_UI\prometheus_dev_codesimple-frontend\node_modules\monaco-editor\esm\vs\editor\editor.api.js:5
    import { EditorOptions } from './common/config/editorOptions.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      1 | import React, { useEffect, useRef } from "react";
    > 2 | import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
        | ^
      3 | // import * as ts from "monaco-editor/esm/vs/language/typescript/ts.worker.js";
      4 | import "../../../styles/MonacoEditor.css";
      5 | import MonacoEnvironment from "./editor.worker";

      at Runtime.createScriptFromCode (node_modules/react-scripts/node_modules/jest-runtime/build/index.js:1728:14)
      at Object.<anonymous> (src/components/middleComponent/monacoEditor/MonacoEditor.tsx:2:1)
      at Object.<anonymous> (src/tests/MonacoEditor.test.tsx:4:1)


我试图更改webpack和jest的配置以支持错误,但无法解决它。
我的webpack.config.js

const path = require("path");
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");

module.exports = {
  mode: process.env.NODE_ENV,
  entry: {
    app: "./src/index.tsx",
    "editor.worker": "monaco-editor/esm/vs/editor/editor.worker.js",
    "json.worker": "monaco-editor/esm/vs/language/json/json.worker",
    "css.worker": "monaco-editor/esm/vs/language/css/css.worker",
    "html.worker": "monaco-editor/esm/vs/language/html/html.worker",
    "ts.worker": "monaco-editor/esm/vs/language/typescript/ts.worker",
  },
  // output: {
  //   path: path.resolve(__dirname, "dist"),
  //   filename: "bundle.js",
  // },
  output: {
    globalObject: "self",
    filename: (chunkData) => {
      switch (chunkData.chunk.name) {
        case "editor.worker":
          return "editor.worker.js";
        default:
          return "bundle.[hash].js";
      }
    },
    path: path.resolve(__dirname, "dist"),
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.ttf$/,
        use: ["file-loader"],
      },
      {
        test: /\.tsx$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.worker\.tsx$/,
        use: { loader: "worker-loader" },
      },
    ],
  },
  devServer: {
    hot: true,
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new MonacoWebpackPlugin({
      languages: ["javascript", "typescript", "css"],
    }),
    "babel-plugin-transform-import-meta",
    { module: "ES6" },
  ],
};


还安装了一些依赖项,如"babel-plugin-named-exports-order": "^0.0.2", "babel-plugin-transform-import-meta": "^2.2.1",
有关更多信息,请查看我的package.json

{
    "name": "codesimple-frontend",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
        "@emotion/react": "^11.11.1",
        "@emotion/styled": "^11.11.0",
        "@mui/icons-material": "^5.14.18",
        "@mui/material": "^5.14.18",
        "@react-spring/web": "^9.7.3",
        "@testing-library/jest-dom": "^5.17.0",
        "@testing-library/react": "^13.4.0",
        "@testing-library/user-event": "^13.5.0",
        "@types/node": "^16.18.61",
        "@types/react": "^18.2.37",
        "@types/react-dom": "^18.2.15",
        "monaco-editor-webpack-plugin": "^7.1.0",
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "react-icons": "^4.12.0",
        "react-router-dom": "^6.20.1",
        "react-scripts": "5.0.1",
        "typescript": "^4.9.5",
        "web-vitals": "^2.1.4"
    },
    "author": "Ian McFarland",
    "email": "[email protected]",
    "repository": "[email protected]:logomancy/prometheus_dev_codesimple-frontend.git",
    "bugs": {
        "url": "[email protected]:logomancy/prometheus_dev_codesimple-frontend.git/issues",
        "email": "[email protected]"
    },
    "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject",
        "storybook": "storybook dev -p 6006",
        "build-storybook": "storybook build"
    },
    "eslintConfig": {
        "extends": [
            "react-app",
            "react-app/jest",
            "plugin:storybook/recommended"
        ]
    },
    "browserslist": {
        "production": [
            ">0.2%",
            "not dead",
            "not op_mini all"
        ],
        "development": [
            "last 1 chrome version",
            "last 1 firefox version",
            "last 1 safari version"
        ]
    },
    "devDependencies": {
        "@babel/core": "^7.23.7",
        "@babel/preset-env": "^7.23.7",
        "@babel/preset-typescript": "^7.23.3",
        "@jest/globals": "^29.7.0",
        "@storybook/addon-essentials": "^7.5.3",
        "@storybook/addon-interactions": "^7.5.3",
        "@storybook/addon-links": "^7.5.3",
        "@storybook/addon-onboarding": "^1.0.8",
        "@storybook/blocks": "^7.5.3",
        "@storybook/preset-create-react-app": "^7.5.3",
        "@storybook/react": "^7.5.3",
        "@storybook/react-webpack5": "^7.5.3",
        "@storybook/testing-library": "^0.2.2",
        "@types/jest": "^29.5.10",
        "@types/react-icons": "^3.0.0",
        "@types/react-router-dom": "^5.3.3",
        "@types/react-test-renderer": "^18.0.7",
        "babel-jest": "^29.7.0",
        "babel-plugin-named-exports-order": "^0.0.2",
        "babel-plugin-transform-import-meta": "^2.2.1",
        "eslint-plugin-storybook": "^0.6.15",
        "prop-types": "^15.8.1",
        "react-test-renderer": "^18.2.0",
        "storybook": "^7.5.3",
        "ts-jest": "^29.1.1",
        "ts-loader": "^9.5.1",
        "webpack": "^5.89.0",
        "worker-loader": "^3.0.8"
    }
}

t8e9dugd

t8e9dugd1#

AFAIK Jest仍然使用CommonJS模块化系统(const X = require('X'))。
您的错误消息看起来像是您提供了EcmaScript模块(import X from 'X'
当你使用ts-jest来测试你的TypeScript测试时,也发布你的tsconfig.json会很有帮助,这样我就可以进一步分析它。
从其余的,我可以得到的信息是:

C:\Users\HP\OneDrive\Desktop\prometheus_UI\prometheus_dev_codesimple-frontend\node_modules\monaco-editor\esm\vs\editor\editor.api.js:5
    import { EditorOptions } from './common/config/editorOptions.js';

字符串
告诉我们,该文件没有正确转换为CommonJS作为By default "node_modules" folder is ignored by transformers.
Jest已经告诉我们如果我们想做什么:• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
我现在没有一个例子,但我总是这样做:/(?!monaco-editor)。基本上只是告诉Jest忽略除了monaco-editor之外的所有node_modules的转换。适用于基本的Regex和negative-lookaheads
如果错误消息再次出现,使用不同的模块,只需将新模块添加到正则表达式中,如/(?!(monaco-editor|<new-module>))
也许正则表达式的语法有点不同。
希望这对你有帮助,Mika

相关问题