esbuild-jest icon indicating copy to clipboard operation
esbuild-jest copied to clipboard

Is breakpoints are not working or I'm the only one?

Open RIP21 opened this issue 3 years ago • 23 comments

Hey, I try to set a breakpoint in the test but it never stops.

I run in-band etc. I'm using WebStorm functionality for that. Is it working with VSCode maybe? Anyone can confirm? What command are you using to achieve that?

I'm running the latest esbuild and esbuild-jest

RIP21 avatar Mar 08 '21 23:03 RIP21

@RIP21 it works on vscode i havent tried in webstorm, heres a repository https://github.com/aelbore/vs-code-jest-debug Please check launch.json for some comments

aelbore avatar Mar 09 '21 00:03 aelbore

I can't get it to work on IntelliJ (which is the same thing as webstorm). I suspect sourcemaps since the tests run just viel. I thought it used to work.

jschaf avatar Mar 13 '21 10:03 jschaf

what version you are using? if you are not using jest.mock just use [email protected]

i only tried vscode for break point and it works fine for me

vscode break point here https://github.com/aelbore/vs-code-jest-debug

aelbore avatar Mar 13 '21 10:03 aelbore

After some experimenting, removing this line seems to partially fix debugging in IntelliJ (and should for Webstorm). Some of the mappings are wrong but it at least works inside test files.

    map.sourcesContent = null;

I'm using a modified version of this package. Here's the full code I have working:

const esbuild = require('esbuild');
const path = require('path');

/**
 * Jest ESBuild is a jest transformer that uses esbuild to compile .ts and .tsx
 * into Node.js compatible JavaScript.
 *
 * Originally copied from the MIT licensed
 * https://github.com/aelbore/esbuild-jest/
 * @param {string} sourceText
 * @param {string} sourcePath
 * @param {import('@jest/types').Config} _config
 * @param {import('@jest/transform/build/types').TransformOptions} _options
 * @return {import('@jest/transform/build/types').TransformedSource}
 */
const process = (sourceText, sourcePath, _config, _options) => {
  const loader = path.extname(sourcePath).slice(1);
  if (loader !== 'ts' && loader !== 'tsx' && loader !== 'js' && loader !== 'jsx') {
    throw new Error(`Unsupported extension type '${loader}' for esbuild Jest transformer on file: ${sourcePath}`);
  }
  const esbuildOpts = {
    format: 'cjs',
    target: 'esnext',
    loader: loader,
    sourcemap: true,
    sourcesContent: false,
    sourcefile: sourcePath
  };
  const result = esbuild.transformSync(sourceText, esbuildOpts);
  if (result.map) {
    // Add inline source maps so debugging works.
    result.code += '\n//# sourceMappingURL=data:application/json;base64,';
    result.code += Buffer.from(result.map).toString('base64');
  }
  return result
};

module.exports = {process};

jschaf avatar Mar 13 '21 10:03 jschaf

whats your jest config? did you set the sourcemap to true? what esbuild you are using? maybe theres change on esbuild. it use to work before will check also thanks 👍

setting the sourcesContent: null before works fine but let me verify that one

aelbore avatar Mar 13 '21 10:03 aelbore

Breakpoints work and mappings seem to have fixed themselves. That might have been changing sourcemap: true to sourcemap: 'both'.

whats your jest config? did you set the sourcemap to true?

Good thought, included below. I'm using a modified version of this package so the sourcemap config option is included in my jest-transform-js.js modifications as sourcemap:'both'.

Esbuild version: "esbuild": "^0.9.0",

jest-transform.js

const esbuild = require('esbuild');
const path = require('path');

/**
 * Jest ESBuild is a jest transformer that uses esbuild to compile .ts and .tsx
 * into Node.js compatible JavaScript.
 *
 * This file is JavaScript using commonjs modules (require instead of import)
 * because it's the compiler for TypeScript files when running with jest. We
 * want this file to run directly under Node to avoid bootstrapping.
 *
 * Originally copied from the MIT licensed
 * https://github.com/aelbore/esbuild-jest/
 * @param {string} sourceText
 * @param {string} sourcePath
 * @param {import('@jest/types').Config} _config
 * @param {import('@jest/transform/build/types').TransformOptions} _options
 * @return {import('@jest/transform/build/types').TransformedSource}
 */
const process = (sourceText, sourcePath, _config, _options) => {
  const loader = path.extname(sourcePath).slice(1);
  if (loader !== 'ts' && loader !== 'tsx' && loader !== 'js' && loader !== 'jsx') {
    throw new Error(`Unsupported extension type '${loader}' for esbuild Jest transformer on file: ${sourcePath}`);
  }
  const esbuildOpts = {
    format: 'cjs',
    target: 'esnext',
    loader: loader,
    sourcemap: 'both',
    sourcesContent: true,
    sourcefile: sourcePath
  };
  return esbuild.transformSync(sourceText, esbuildOpts)
};

module.exports = {process};

jest.config.js

/** @type import('@jest/types/build/Config').InitialOptions */
module.exports = {
  displayName: 'myapp',
  errorOnDeprecated: true,
  transform: {
    '[.][jt]sx?$': "./testing/jest-transform-js.js",
    '[.]css$': "./testing/jest-transform-css.js",
    '[.]svg$': "./testing/jest-transform-svg.js",
  },
  setupFiles: ["./testing/jest-setup-polyfills.js"],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
  rootDir: 'src',
  testRegex: '_test[.]tsx?$',
  // Place snapshots next to test files, like app_test.tsx.snap.
  snapshotResolver: './testing/jest-snapshot-resolver.js',
};

jschaf avatar Mar 14 '21 17:03 jschaf

@aelbore passing "both" to sourcemap doesn't work if passed through the jest config as it will be overridden to become sourcemap: true no matter the value you pass. It will be passed correctly to babel transform for this ock( hack.

RIP21 avatar Mar 29 '21 15:03 RIP21

Created a PR (from the Github UI don't blame me :D) https://github.com/aelbore/esbuild-jest/pull/28 Check it please as I'm not sure if sourcemap is passed properly.

RIP21 avatar Mar 29 '21 15:03 RIP21

I manually changed to both and it doesn't help with WebStorm unfortunately. Although esbuild-register works fine with IntelliJ/WebStorm. Check this code. https://github.com/egoist/esbuild-register/blob/master/src/node.ts Unsure if it's possible but maybe it will give some idea.

RIP21 avatar Mar 29 '21 15:03 RIP21

I can confirm that with the setup above of @jschaf it all works until there is a jest.mock of course. So it needs some fixes so it starts to work with this babel workaround for jest.mocks

RIP21 avatar Mar 29 '21 18:03 RIP21

Sorry for the spam... but here is the workaround for fellow WebStorm/IntelliJ users here. esbuild-jest.js

module.exports = require('esbuild-jest')

index.js (you may name it jest.config.js) but in my case it's a module that I'm sharing through the repo.

module.exports = {
  roots: ['<rootDir>/src'],
  transform: {
    '^.+\\.tsx?$': require.resolve('./esbuild-jest.js'),
  },
  modulePaths: ['src'],
  reporters: [require.resolve('jest-standard-reporter')],
  coverageReporters: ['text-summary'],
  collectCoverageFrom: ['src/**/*.{js,jsx,mjs,ts,tsx}'],
}

And breakpoints are working... Note that I'm using Rush.js and PNPM as a package manager, so it may be somehow related to that due to hard links and all that "magic" that PNPM does. And above is a module that I'm sharing.

I still think there are some bugs of some sort in the code due to this options and opts thingy that I'm unsure how they should work together. But other than that, this workaround works.

RIP21 avatar Mar 29 '21 18:03 RIP21

It stopped work again tho... So here is my lifehack to you folks. If you want to speedup Jest with esbuild, just don't, too many hacks to make mocks working :) Just use this https://github.com/alangpierce/sucrase/tree/main/integrations/jest-plugin instead.

Surcrase is mega quick and is similar to Babel and works no problem with Jest via this plugin. It will require you to hoist jest.mock calls yourself which is a bit annoying. But there is a PR opened that should fix that. https://github.com/alangpierce/sucrase/pull/540

RIP21 avatar Mar 29 '21 19:03 RIP21

Hey @RIP21 any chance to share your config for Sucrase? I was trying to configure it with no luck 😞

PatrykMilewski avatar Apr 11 '21 09:04 PatrykMilewski

@PatrykMilewski sure mate! :)

module.exports = {
  roots: ['<rootDir>/src'],
  transform: {
    '^.+\\.tsx?$': require.resolve('@sucrase/jest-plugin'),
  },
  modulePaths: ['src'],
  reporters: [require.resolve('jest-standard-reporter')],
  coverageReporters: ['text-summary'],
  collectCoverageFrom: ['src/**/*.{js,jsx,mjs,ts,tsx}'],
}

It has jest-standard-reporter to work properly with Rush.js so you can ignore this part :) But the rest should just work. You can try latest version, the only thing that may be annoying tho is that you'll have to hoist up manually all jest.mock calls above the imports which is annoying :) Although it should change since this will be published https://github.com/alangpierce/sucrase/pull/540

{
  "name": "@liveflow-io/config-jest",
  "version": "0.0.1",
  "description": "Jest config base",
  "license": "MIT",
  "main": "./index.js",
  "dependencies": {
    "@sucrase/jest-plugin": "~2.0.0",
    "jest-standard-reporter": "~2.0.0"
  },
  "scripts": {
    "build": ""
  }
}

RIP21 avatar Apr 11 '21 16:04 RIP21

@RIP21 Thanks! It works great! Not sure what was wrong with my config lol

PatrykMilewski avatar Apr 11 '21 18:04 PatrykMilewski

@PatrykMilewski proszę bardzo :P Miłego wieczoru :)

RIP21 avatar Apr 11 '21 18:04 RIP21

The issue seems to be that the altered map contains sourcesContent: null. When it is deleted (or set to undefined), then breakpoints started working.

https://github.com/aelbore/esbuild-jest/blob/daa5847b3b382d9ddf6cc26e60ad949d202c4461/src/index.ts#L53-L57

Should be:

 if (enableSourcemaps) { 
   map = { 
     ...JSON.parse(result.map), 
     sourcesContent: undefined,
   } 

Also using sourcemap: 'both', sourcesContent: false seems to not include sourcesContent so I'm not sure why the manual manipulation.

            const sourcemaps = enableSourcemaps ? {
                sourcemap: 'both',
                sourcesContent: false,
                sourcefile: filename
            } : {
            };

Also, when changing any of this, the Jest cache in your temp directory needs deleted between runs.

ngbrown avatar Jul 03 '21 23:07 ngbrown

@aelbore is there any chance to fix that? I would like to eventually use the same transpiler for both normal code and tests, as it's more consistent and safer. Right now I'm using Sucrase Jest plugin, as it's the only thing that I managed to configure to work with breakpoints.

PatrykMilewski avatar Oct 16 '21 10:10 PatrykMilewski

@ngbrown, unfortunately, these changes don't make breakpoints work for me (Webstorm). Are there any other changes needed?

dan-lee avatar Nov 21 '21 14:11 dan-lee

@ngbrown If you don't need decorators support, use @sucrase/jest-plugin jest plugin instead. It works great with Webstorm.

PatrykMilewski avatar Nov 21 '21 17:11 PatrykMilewski

@PatrykMilewski I am working with ESM only currently and with @sucrase/jest-plugin I get the following error, unfortunately:

Test suite failed to run

ReferenceError: require is not defined

dan-lee avatar Nov 21 '21 19:11 dan-lee

@dan-lee I'm also using ESM, check out my minimal example here: https://github.com/PatrykMilewski/sucrase-test just bump the version of Sucrase plugin to the latest in package.json and it should work fine.

PatrykMilewski avatar Nov 21 '21 19:11 PatrykMilewski

Thanks all for the suggestions to use @sucrase/jest-plugin instead, that fixed webstorm breakpoints in my tests!

mattmbrightside avatar Sep 30 '22 20:09 mattmbrightside