jest icon indicating copy to clipboard operation
jest copied to clipboard

[Bug]: No way to use inline snapshots without Babel

Open Daniel15 opened this issue 1 year ago • 3 comments

Version

29.6.1

Steps to reproduce

At Meta, we upgraded from Jest v24 to v29, and it broke inline snapshots. All of our JavaScript stuff uses the Hermes parser rather than Babel. as we're using some syntax that Babel does not support.

For parsing JS files to insert inline snapshots, Jest v24 used the parser from the Prettier config, whereas v29 is hard-coded to use Babel.

Is there a way to use a custom parser instead of Babel?

Expected behavior

Inline snapshots should be able to use a non-Babel parser.

Actual behavior

Inline snapshots can only use Babel parser.

Additional context

No response

Environment

N/A

Daniel15 avatar Jul 18 '24 17:07 Daniel15

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Aug 17 '24 18:08 github-actions[bot]

This is the patch I ended up with. Just need to figure out the best API to make it properly pluggable:

diff --git a/node_modules/jest-snapshot/build/InlineSnapshots.js b/node_modules/jest-snapshot/build/InlineSnapshots.js
index 597ec93..ab61909 100644
--- a/node_modules/jest-snapshot/build/InlineSnapshots.js
+++ b/node_modules/jest-snapshot/build/InlineSnapshots.js
@@ -8,6 +8,7 @@ var path = _interopRequireWildcard(require('path'));
 var fs = _interopRequireWildcard(require('graceful-fs'));
 var _semver = _interopRequireDefault(require('semver'));
 var _utils = require('./utils');
+var HermesParser = require('hermes-parser');
 function _interopRequireDefault(obj) {
   return obj && obj.__esModule ? obj : {default: obj};
 }
@@ -115,54 +116,15 @@ function saveInlineSnapshots(snapshots, rootDir, prettierPath) {
 const saveSnapshotsForFile = (snapshots, sourceFilePath, rootDir, prettier) => {
   const sourceFile = jestReadFile(sourceFilePath, 'utf8');
 
-  // TypeScript projects may not have a babel config; make sure they can be parsed anyway.
-  const presets = [require.resolve('babel-preset-current-node-syntax')];
-  const plugins = [];
-  if (/\.([cm]?ts|tsx)$/.test(sourceFilePath)) {
-    plugins.push([
-      require.resolve('@babel/plugin-syntax-typescript'),
-      {
-        isTSX: sourceFilePath.endsWith('x')
-      },
-      // unique name to make sure Babel does not complain about a possible duplicate plugin.
-      'TypeScript syntax plugin added by Jest snapshot'
-    ]);
-  }
-
   // Record the matcher names seen during traversal and pass them down one
   // by one to formatting parser.
   const snapshotMatcherNames = [];
-  let ast = null;
-  try {
-    ast = parseSync(sourceFile, {
-      filename: sourceFilePath,
-      plugins,
-      presets,
-      root: rootDir
-    });
-  } catch (error) {
-    // attempt to recover from missing jsx plugin
-    if (error.message.includes('@babel/plugin-syntax-jsx')) {
-      try {
-        const jsxSyntaxPlugin = [
-          require.resolve('@babel/plugin-syntax-jsx'),
-          {},
-          // unique name to make sure Babel does not complain about a possible duplicate plugin.
-          'JSX syntax plugin added by Jest snapshot'
-        ];
-        ast = parseSync(sourceFile, {
-          filename: sourceFilePath,
-          plugins: [...plugins, jsxSyntaxPlugin],
-          presets,
-          root: rootDir
-        });
-      } catch {
-        throw error;
-      }
-    } else {
-      throw error;
-    }
-  }
+  const ast = HermesParser.parse(sourceFile, {
+    babel: true,
+    enableExperimentalComponentSyntax: true,
+    sourceFilename: sourceFilePath,
+    sourceType: 'unambiguous',
+  });
   if (!ast) {
     throw new Error(`jest-snapshot: Failed to parse ${sourceFilePath}`);
   }
 const groupSnapshotsByFile = groupSnapshotsBy(({frame: {file}}) => file);
@@ -310,20 +272,7 @@ const runPrettier = (
       })
     : null;
 
-  // Prioritize parser found in the project config.
-  // If not found detect the parser for the test file.
-  // For older versions of Prettier, fallback to a simple parser detection.
-  // @ts-expect-error - `inferredParser` is `string`
-  const inferredParser =
-    (config && typeof config.parser === 'string' && config.parser) ||
-    (prettier.getFileInfo
-      ? prettier.getFileInfo.sync(sourceFilePath).inferredParser
-      : simpleDetectParser(sourceFilePath));
-  if (!inferredParser) {
-    throw new Error(
-      `Could not infer Prettier parser for file ${sourceFilePath}`
-    );
-  }
+  const inferredParser = 'hermes';
 
   // Snapshots have now been inserted. Run prettier to make sure that the code is
   // formatted, except snapshot indentation. Snapshots cannot be formatted until
@@ -333,12 +282,14 @@ const runPrettier = (
   return prettier.format(
     prettier.format(sourceFileWithSnapshots, {
       ...config,
-      filepath: sourceFilePath
+      filepath: sourceFilePath,
+      plugins: ['prettier-plugin-hermes-parser'],
     }),
     {
       ...config,
       filepath: sourceFilePath,
-      parser: createFormattingParser(snapshotMatcherNames, inferredParser)
+      parser: createFormattingParser(snapshotMatcherNames, inferredParser),
+      plugins: ['prettier-plugin-hermes-parser'],
     }
   );
 };
@@ -348,7 +299,12 @@ const createFormattingParser =
   (snapshotMatcherNames, inferredParser) => (text, parsers, options) => {
     // Workaround for https://github.com/prettier/prettier/issues/3150
     options.parser = inferredParser;
-    const ast = parsers[inferredParser](text, options);
+    const ast = HermesParser.parse(text, {
+      babel: true,
+      enableExperimentalComponentSyntax: true,
+      sourceType: 'unambiguous',
+    });
+
     traverse(ast, (node, ancestors) => {
       if (node.type !== 'CallExpression') return;
       const {arguments: args, callee} = node;

Daniel15 avatar Sep 13 '24 17:09 Daniel15

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Oct 13 '24 18:10 github-actions[bot]

This issue was closed because it has been stalled for 30 days with no activity. Please open a new issue if the issue is still relevant, linking to this one.

github-actions[bot] avatar Nov 12 '24 18:11 github-actions[bot]

This issue was closed because it has been stalled for 30 days with no activity. Please open a new issue if the issue is still relevant, linking to this one.

github-actions[bot] avatar Nov 12 '24 18:11 github-actions[bot]

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

github-actions[bot] avatar Dec 13 '24 00:12 github-actions[bot]