diff options
Diffstat (limited to 'node_modules/jest-snapshot/build/InlineSnapshots.js')
-rw-r--r-- | node_modules/jest-snapshot/build/InlineSnapshots.js | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/node_modules/jest-snapshot/build/InlineSnapshots.js b/node_modules/jest-snapshot/build/InlineSnapshots.js new file mode 100644 index 0000000..8e20fe9 --- /dev/null +++ b/node_modules/jest-snapshot/build/InlineSnapshots.js @@ -0,0 +1,503 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.saveInlineSnapshots = saveInlineSnapshots; + +var path = _interopRequireWildcard(require('path')); + +var fs = _interopRequireWildcard(require('graceful-fs')); + +var _semver = _interopRequireDefault(require('semver')); + +var _utils = require('./utils'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : {default: obj}; +} + +function _getRequireWildcardCache(nodeInterop) { + if (typeof WeakMap !== 'function') return null; + var cacheBabelInterop = new WeakMap(); + var cacheNodeInterop = new WeakMap(); + return (_getRequireWildcardCache = function (nodeInterop) { + return nodeInterop ? cacheNodeInterop : cacheBabelInterop; + })(nodeInterop); +} + +function _interopRequireWildcard(obj, nodeInterop) { + if (!nodeInterop && obj && obj.__esModule) { + return obj; + } + if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return {default: obj}; + } + var cache = _getRequireWildcardCache(nodeInterop); + if (cache && cache.has(obj)) { + return cache.get(obj); + } + var newObj = {}; + var hasPropertyDescriptor = + Object.defineProperty && Object.getOwnPropertyDescriptor; + for (var key in obj) { + if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) { + var desc = hasPropertyDescriptor + ? Object.getOwnPropertyDescriptor(obj, key) + : null; + if (desc && (desc.get || desc.set)) { + Object.defineProperty(newObj, key, desc); + } else { + newObj[key] = obj[key]; + } + } + } + newObj.default = obj; + if (cache) { + cache.set(obj, newObj); + } + return newObj; +} + +var global = (function () { + if (typeof globalThis !== 'undefined') { + return globalThis; + } else if (typeof global !== 'undefined') { + return global; + } else if (typeof self !== 'undefined') { + return self; + } else if (typeof window !== 'undefined') { + return window; + } else { + return Function('return this')(); + } +})(); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; + +var global = (function () { + if (typeof globalThis !== 'undefined') { + return globalThis; + } else if (typeof global !== 'undefined') { + return global; + } else if (typeof self !== 'undefined') { + return self; + } else if (typeof window !== 'undefined') { + return window; + } else { + return Function('return this')(); + } +})(); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; + +var global = (function () { + if (typeof globalThis !== 'undefined') { + return globalThis; + } else if (typeof global !== 'undefined') { + return global; + } else if (typeof self !== 'undefined') { + return self; + } else if (typeof window !== 'undefined') { + return window; + } else { + return Function('return this')(); + } +})(); + +var jestWriteFile = + global[Symbol.for('jest-native-write-file')] || fs.writeFileSync; + +var global = (function () { + if (typeof globalThis !== 'undefined') { + return globalThis; + } else if (typeof global !== 'undefined') { + return global; + } else if (typeof self !== 'undefined') { + return self; + } else if (typeof window !== 'undefined') { + return window; + } else { + return Function('return this')(); + } +})(); + +var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; + +var global = (function () { + if (typeof globalThis !== 'undefined') { + return globalThis; + } else if (typeof global !== 'undefined') { + return global; + } else if (typeof self !== 'undefined') { + return self; + } else if (typeof window !== 'undefined') { + return window; + } else { + return Function('return this')(); + } +})(); + +var jestReadFile = + global[Symbol.for('jest-native-read-file')] || fs.readFileSync; + +// prettier-ignore +const babelTraverse = // @ts-expect-error requireOutside Babel transform +require(require.resolve('@babel/traverse', { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for('jest-resolve-outside-vm-option')]: true +})).default; // prettier-ignore + +const generate = require(require.resolve('@babel/generator', { // @ts-expect-error requireOutside Babel transform + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true +})).default; // @ts-expect-error requireOutside Babel transform + +const {file, templateElement, templateLiteral} = require(require.resolve( + '@babel/types', + { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true + } +)); // @ts-expect-error requireOutside Babel transform + +const {parseSync} = require(require.resolve('@babel/core', { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true +})); + +function saveInlineSnapshots(snapshots, prettierPath) { + let prettier = null; + + if (prettierPath) { + try { + // @ts-expect-error requireOutside Babel transform + prettier = require(require.resolve(prettierPath, { + [(global['jest-symbol-do-not-touch'] || global.Symbol).for( + 'jest-resolve-outside-vm-option' + )]: true + })); + } catch { + // Continue even if prettier is not installed. + } + } + + const snapshotsByFile = groupSnapshotsByFile(snapshots); + + for (const sourceFilePath of Object.keys(snapshotsByFile)) { + saveSnapshotsForFile( + snapshotsByFile[sourceFilePath], + sourceFilePath, + prettier && _semver.default.gte(prettier.version, '1.5.0') + ? prettier + : undefined + ); + } +} + +const saveSnapshotsForFile = (snapshots, sourceFilePath, 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 (/\.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 = []; + const ast = parseSync(sourceFile, { + filename: sourceFilePath, + plugins, + presets, + root: path.dirname(sourceFilePath) + }); + + if (!ast) { + throw new Error(`jest-snapshot: Failed to parse ${sourceFilePath}`); + } + + traverseAst(snapshots, ast, snapshotMatcherNames); // substitute in the snapshots in reverse order, so slice calculations aren't thrown off. + + const sourceFileWithSnapshots = snapshots.reduceRight( + (sourceSoFar, nextSnapshot) => { + if ( + !nextSnapshot.node || + typeof nextSnapshot.node.start !== 'number' || + typeof nextSnapshot.node.end !== 'number' + ) { + throw new Error('Jest: no snapshot insert location found'); + } + + return ( + sourceSoFar.slice(0, nextSnapshot.node.start) + + generate(nextSnapshot.node, { + retainLines: true + }).code.trim() + + sourceSoFar.slice(nextSnapshot.node.end) + ); + }, + sourceFile + ); + const newSourceFile = prettier + ? runPrettier( + prettier, + sourceFilePath, + sourceFileWithSnapshots, + snapshotMatcherNames + ) + : sourceFileWithSnapshots; + + if (newSourceFile !== sourceFile) { + jestWriteFile(sourceFilePath, newSourceFile); + } +}; + +const groupSnapshotsBy = createKey => snapshots => + snapshots.reduce((object, inlineSnapshot) => { + const key = createKey(inlineSnapshot); + return {...object, [key]: (object[key] || []).concat(inlineSnapshot)}; + }, {}); + +const groupSnapshotsByFrame = groupSnapshotsBy(({frame: {line, column}}) => + typeof line === 'number' && typeof column === 'number' + ? `${line}:${column - 1}` + : '' +); +const groupSnapshotsByFile = groupSnapshotsBy(({frame: {file}}) => file); + +const indent = (snapshot, numIndents, indentation) => { + const lines = snapshot.split('\n'); // Prevent re-indentation of inline snapshots. + + if ( + lines.length >= 2 && + lines[1].startsWith(indentation.repeat(numIndents + 1)) + ) { + return snapshot; + } + + return lines + .map((line, index) => { + if (index === 0) { + // First line is either a 1-line snapshot or a blank line. + return line; + } else if (index !== lines.length - 1) { + // Do not indent empty lines. + if (line === '') { + return line; + } // Not last line, indent one level deeper than expect call. + + return indentation.repeat(numIndents + 1) + line; + } else { + // The last line should be placed on the same level as the expect call. + return indentation.repeat(numIndents) + line; + } + }) + .join('\n'); +}; + +const resolveAst = fileOrProgram => { + // Flow uses a 'Program' parent node, babel expects a 'File'. + let ast = fileOrProgram; + + if (ast.type !== 'File') { + ast = file(ast, ast.comments, ast.tokens); + delete ast.program.comments; + } + + return ast; +}; + +const traverseAst = (snapshots, fileOrProgram, snapshotMatcherNames) => { + const ast = resolveAst(fileOrProgram); + const groupedSnapshots = groupSnapshotsByFrame(snapshots); + const remainingSnapshots = new Set(snapshots.map(({snapshot}) => snapshot)); + babelTraverse(ast, { + CallExpression({node}) { + const {arguments: args, callee} = node; + + if ( + callee.type !== 'MemberExpression' || + callee.property.type !== 'Identifier' || + callee.property.loc == null + ) { + return; + } + + const {line, column} = callee.property.loc.start; + const snapshotsForFrame = groupedSnapshots[`${line}:${column}`]; + + if (!snapshotsForFrame) { + return; + } + + if (snapshotsForFrame.length > 1) { + throw new Error( + 'Jest: Multiple inline snapshots for the same call are not supported.' + ); + } + + snapshotMatcherNames.push(callee.property.name); + const snapshotIndex = args.findIndex( + ({type}) => type === 'TemplateLiteral' + ); + const values = snapshotsForFrame.map(inlineSnapshot => { + inlineSnapshot.node = node; + const {snapshot} = inlineSnapshot; + remainingSnapshots.delete(snapshot); + return templateLiteral( + [ + templateElement({ + raw: (0, _utils.escapeBacktickString)(snapshot) + }) + ], + [] + ); + }); + const replacementNode = values[0]; + + if (snapshotIndex > -1) { + args[snapshotIndex] = replacementNode; + } else { + args.push(replacementNode); + } + } + }); + + if (remainingSnapshots.size) { + throw new Error("Jest: Couldn't locate all inline snapshots."); + } +}; + +const runPrettier = ( + prettier, + sourceFilePath, + sourceFileWithSnapshots, + snapshotMatcherNames +) => { + // Resolve project configuration. + // For older versions of Prettier, do not load configuration. + const config = prettier.resolveConfig + ? prettier.resolveConfig.sync(sourceFilePath, { + editorconfig: true + }) + : null; // Detect the parser for the test file. + // For older versions of Prettier, fallback to a simple parser detection. + // @ts-expect-error + + const inferredParser = prettier.getFileInfo + ? prettier.getFileInfo.sync(sourceFilePath).inferredParser + : (config && typeof config.parser === 'string' && config.parser) || + simpleDetectParser(sourceFilePath); + + if (!inferredParser) { + throw new Error( + `Could not infer Prettier parser for file ${sourceFilePath}` + ); + } // Snapshots have now been inserted. Run prettier to make sure that the code is + // formatted, except snapshot indentation. Snapshots cannot be formatted until + // after the initial format because we don't know where the call expression + // will be placed (specifically its indentation), so we have to do two + // prettier.format calls back-to-back. + + return prettier.format( + prettier.format(sourceFileWithSnapshots, { + ...config, + filepath: sourceFilePath + }), + { + ...config, + filepath: sourceFilePath, + parser: createFormattingParser(snapshotMatcherNames, inferredParser) + } + ); +}; // This parser formats snapshots to the correct indentation. + +const createFormattingParser = + (snapshotMatcherNames, inferredParser) => (text, parsers, options) => { + // Workaround for https://github.com/prettier/prettier/issues/3150 + options.parser = inferredParser; + const ast = resolveAst(parsers[inferredParser](text, options)); + babelTraverse(ast, { + CallExpression({node: {arguments: args, callee}}) { + var _options$tabWidth, _options$tabWidth2; + + if ( + callee.type !== 'MemberExpression' || + callee.property.type !== 'Identifier' || + !snapshotMatcherNames.includes(callee.property.name) || + !callee.loc || + callee.computed + ) { + return; + } + + let snapshotIndex; + let snapshot; + + for (let i = 0; i < args.length; i++) { + const node = args[i]; + + if (node.type === 'TemplateLiteral') { + snapshotIndex = i; + snapshot = node.quasis[0].value.raw; + } + } + + if (snapshot === undefined || snapshotIndex === undefined) { + return; + } + + const useSpaces = !options.useTabs; + snapshot = indent( + snapshot, + Math.ceil( + useSpaces + ? callee.loc.start.column / + ((_options$tabWidth = options.tabWidth) !== null && + _options$tabWidth !== void 0 + ? _options$tabWidth + : 1) + : callee.loc.start.column / 2 // Each tab is 2 characters. + ), + useSpaces + ? ' '.repeat( + (_options$tabWidth2 = options.tabWidth) !== null && + _options$tabWidth2 !== void 0 + ? _options$tabWidth2 + : 1 + ) + : '\t' + ); + const replacementNode = templateLiteral( + [ + templateElement({ + raw: snapshot + }) + ], + [] + ); + args[snapshotIndex] = replacementNode; + } + }); + return ast; + }; + +const simpleDetectParser = filePath => { + const extname = path.extname(filePath); + + if (/\.tsx?$/.test(extname)) { + return 'typescript'; + } + + return 'babel'; +}; |