diff --git a/.cspell.json b/.cspell.json index b3e10009914b..39e8c795e7af 100644 --- a/.cspell.json +++ b/.cspell.json @@ -9,7 +9,7 @@ "**/**/CHANGELOG.md", "**/**/CONTRIBUTORS.md", "**/**/ROADMAP.md", - "**/*.json" + "**/*.{json,snap}" ], "dictionaries": [ "typescript", diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index c20b6a390254..1a465e160aab 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -55,12 +55,10 @@ "@types/debug": "^4.1.5", "@types/glob": "^7.1.1", "@types/is-glob": "^4.0.1", - "@types/lodash.isplainobject": "^4.0.4", "@types/lodash.unescape": "^4.0.4", "@types/semver": "^6.2.0", "@types/tmp": "^0.1.0", "@typescript-eslint/shared-fixtures": "2.14.0", - "lodash.isplainobject": "4.0.6", "tmp": "^0.1.0", "typescript": "*" }, diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 82755ef99079..fb62c090bb79 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -366,7 +366,7 @@ function parseAndGenerateServices( )!; /** - * Determine whatever or not two-way maps of converted AST nodes should be preserved + * Determine if two-way maps of converted AST nodes should be preserved * during the conversion process */ const shouldPreserveNodeMaps = diff --git a/packages/typescript-estree/tests/ast-alignment/parse.ts b/packages/typescript-estree/tests/ast-alignment/parse.ts index a56efd778f64..42f6e0f68fcb 100644 --- a/packages/typescript-estree/tests/ast-alignment/parse.ts +++ b/packages/typescript-estree/tests/ast-alignment/parse.ts @@ -3,7 +3,6 @@ import { ParserPlugin } from '@babel/parser'; import { codeFrameColumns } from '@babel/code-frame'; import * as parser from '../../src/parser'; -import * as parseUtils from './utils'; function createError( message: string, @@ -92,14 +91,10 @@ export function parse( try { switch (opts.parser) { case '@typescript-eslint/typescript-estree': - result.ast = parseUtils.normalizeNodeTypes( - parseWithTypeScriptESTree(text, opts.jsx), - ); + result.ast = parseWithTypeScriptESTree(text, opts.jsx); break; case '@babel/parser': - result.ast = parseUtils.normalizeNodeTypes( - parseWithBabelParser(text, opts.jsx), - ); + result.ast = parseWithBabelParser(text, opts.jsx); break; default: throw new Error( diff --git a/packages/typescript-estree/tests/ast-alignment/spec.ts b/packages/typescript-estree/tests/ast-alignment/spec.ts index 4c16a4143c8d..9e308b73c643 100644 --- a/packages/typescript-estree/tests/ast-alignment/spec.ts +++ b/packages/typescript-estree/tests/ast-alignment/spec.ts @@ -78,7 +78,7 @@ fixturesToTest.forEach(fixture => { ), ).toEqual( parseUtils.removeLocationDataAndSourceTypeFromProgramNode( - typeScriptESTreeResult.ast, + parseUtils.preprocessTypescriptAST(typeScriptESTreeResult.ast), fixture.ignoreSourceType, ), ); diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 78b7df66fe67..4830a7f1f5a5 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -1,90 +1,25 @@ // babel types are something we don't really care about /* eslint-disable @typescript-eslint/no-explicit-any */ -import { AST_NODE_TYPES } from '../../src/ts-estree'; -import isPlainObject from 'lodash.isplainobject'; - -/** - * By default, pretty-format (within Jest matchers) retains the names/types of nodes from the babylon AST, - * quick and dirty way to avoid that is to JSON.stringify and then JSON.parser the - * ASTs before comparing them with pretty-format - * - * @param {Object} ast raw AST - * @returns {Object} normalized AST - */ -export function normalizeNodeTypes(ast: any): any { - return JSON.parse(JSON.stringify(ast)); -} - -/** - * Removes the given keys from the given AST object recursively - * @param root A JavaScript object to remove keys from - * @param keysToOmit Names and predicate functions use to determine what keys to omit from the final object - * @param nodes advance ast modifications - * @returns {Object} formatted object - */ -export function omitDeep( - root: any, - keysToOmit: { key: string; predicate: Function }[], - nodes: Record void> = {}, -): any { - function shouldOmit(keyName: string, val: any): boolean { - if (keysToOmit?.length) { - return keysToOmit.some( - keyConfig => keyConfig.key === keyName && keyConfig.predicate(val), - ); - } - return false; - } - - function visit(node: any, parent: any): void { - if (!node) { - return; - } - - for (const prop in node) { - if (Object.prototype.hasOwnProperty.call(node, prop)) { - if (shouldOmit(prop, node[prop])) { - delete node[prop]; - continue; - } - - const child = node[prop]; - - if (Array.isArray(child)) { - for (const el of child) { - visit(el, node); - } - } else if (isPlainObject(child)) { - visit(child, node); - } - } - } - - if (typeof node.type === 'string' && node.type in nodes) { - nodes[node.type](node, parent); - } - } - - visit(root, null); - return root; -} +import { AST_NODE_TYPES, TSESTree } from '../../src/ts-estree'; +import { deeplyCopy, omitDeep } from '../../tools/test-utils'; +import * as BabelTypes from '@babel/types'; /** * Common predicates for Babylon AST preprocessing */ const always = (): boolean => true; -const ifNumber = (val: any): boolean => typeof val === 'number'; +const ifNumber = (val: unknown): boolean => typeof val === 'number'; /** * - Babylon wraps the "Program" node in an extra "File" node, normalize this for simplicity for now... * - Remove "start" and "end" values from Babylon nodes to reduce unimportant noise in diffs ("loc" data will still be in * each final AST and compared). * - * @param {Object} ast raw babylon AST - * @returns {Object} processed babylon AST + * @param ast raw babylon AST + * @returns processed babylon AST */ -export function preprocessBabylonAST(ast: any): any { - return omitDeep( +export function preprocessBabylonAST(ast: BabelTypes.File): any { + return omitDeep( ast.program, [ { @@ -130,7 +65,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSCallSignatureDeclaration(node: any) { + TSCallSignatureDeclaration(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -143,7 +78,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSConstructSignatureDeclaration(node: any) { + TSConstructSignatureDeclaration(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -156,7 +91,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSFunctionType(node: any) { + TSFunctionType(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -169,7 +104,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSConstructorType(node: any) { + TSConstructorType(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -182,7 +117,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSMethodSignature(node: any) { + TSMethodSignature(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -215,7 +150,7 @@ export function preprocessBabylonAST(ast: any): any { }; } }, - ClassProperty(node: any) { + ClassProperty(node) { /** * Babel: ClassProperty + abstract: true * ts-estree: TSAbstractClassProperty @@ -233,7 +168,7 @@ export function preprocessBabylonAST(ast: any): any { node.declare = false; } }, - TSExpressionWithTypeArguments(node: any, parent: any) { + TSExpressionWithTypeArguments(node, parent: any) { if (parent.type === 'TSInterfaceDeclaration') { node.type = 'TSInterfaceHeritage'; } else if ( @@ -266,17 +201,17 @@ export function preprocessBabylonAST(ast: any): any { * babel: sets optional property as true/undefined * ts-estree: sets optional property as true/false */ - MemberExpression(node: any) { + MemberExpression(node) { if (!node.optional) { node.optional = false; } }, - CallExpression(node: any) { + CallExpression(node) { if (!node.optional) { node.optional = false; } }, - OptionalCallExpression(node: any) { + OptionalCallExpression(node) { if (!node.optional) { node.optional = false; } @@ -286,7 +221,7 @@ export function preprocessBabylonAST(ast: any): any { * babel: sets asserts property as true/undefined * ts-estree: sets asserts property as true/false */ - TSTypePredicate(node: any) { + TSTypePredicate(node) { if (!node.asserts) { node.asserts = false; } @@ -302,9 +237,9 @@ export function preprocessBabylonAST(ast: any): any { * * See: https://github.com/babel/babel/issues/6681 * - * @param {Object} ast the raw AST with a Program node at its top level - * @param {boolean} ignoreSourceType fix for issues with unambiguous type detection - * @returns {Object} the ast with the location data removed from the Program node + * @param ast the raw AST with a Program node at its top level + * @param ignoreSourceType fix for issues with unambiguous type detection + * @returns the ast with the location data removed from the Program node */ export function removeLocationDataAndSourceTypeFromProgramNode( ast: any, @@ -317,3 +252,12 @@ export function removeLocationDataAndSourceTypeFromProgramNode( } return ast; } + +/** + * Returns a raw copy of the typescript AST + * @param ast the AST object + * @returns copy of the AST object + */ +export function preprocessTypescriptAST(ast: T): T { + return deeplyCopy(ast); +} diff --git a/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap index a73f908a3623..84f3780a21c2 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap @@ -132662,7 +132662,7 @@ Object { "pattern": "foo.", }, "type": "Literal", - "value": Object {}, + "value": /foo\\./, }, "loc": Object { "end": Object { @@ -132859,7 +132859,7 @@ Object { "pattern": "[\\\\u{0000000000000061}-\\\\u{7A}]", }, "type": "Literal", - "value": Object {}, + "value": /\\[\\\\u\\{0000000000000061\\}-\\\\u\\{7A\\}\\]/u, }, "loc": Object { "end": Object { @@ -133253,7 +133253,7 @@ Object { "pattern": "foo", }, "type": "Literal", - "value": Object {}, + "value": /foo/u, }, "loc": Object { "end": Object { @@ -133450,7 +133450,7 @@ Object { "pattern": "foo", }, "type": "Literal", - "value": Object {}, + "value": /foo/y, }, "loc": Object { "end": Object { diff --git a/packages/typescript-estree/tools/test-utils.ts b/packages/typescript-estree/tools/test-utils.ts index b9fcb51cab64..2a9b2f11ddfa 100644 --- a/packages/typescript-estree/tools/test-utils.ts +++ b/packages/typescript-estree/tools/test-utils.ts @@ -1,22 +1,6 @@ import * as parser from '../src/parser'; import { TSESTreeOptions } from '../src/parser-options'; -/** - * Returns a raw copy of the given AST - * @param {Object} ast the AST object - * @returns {Object} copy of the AST object - */ -export function getRaw(ast: parser.TSESTree.Program): parser.TSESTree.Program { - return JSON.parse( - JSON.stringify(ast, (key, value) => { - if ((key === 'start' || key === 'end') && typeof value === 'number') { - return undefined; - } - return value; - }), - ); -} - export function parseCodeAndGenerateServices( code: string, config: TSESTreeOptions, @@ -27,24 +11,24 @@ export function parseCodeAndGenerateServices( /** * Returns a function which can be used as the callback of a Jest test() block, * and which performs an assertion on the snapshot for the given code and config. - * @param {string} code The source code to parse - * @param {TSESTreeOptions} config the parser configuration - * @param {boolean} generateServices Flag determining whether to generate ast maps and program or not - * @returns {jest.ProvidesCallback} callback for Jest it() block + * @param code The source code to parse + * @param config the parser configuration + * @param generateServices Flag determining whether to generate ast maps and program or not + * @returns callback for Jest it() block */ export function createSnapshotTestBlock( code: string, config: TSESTreeOptions, generateServices?: true, -): () => void { +): jest.ProvidesCallback { /** - * @returns {Object} the AST object + * @returns the AST object */ function parse(): parser.TSESTree.Program { const ast = generateServices ? parser.parseAndGenerateServices(code, config).ast : parser.parse(code, config); - return getRaw(ast); + return deeplyCopy(ast); } return (): void => { @@ -84,3 +68,84 @@ export function isJSXFileType(fileType: string): boolean { } return fileType === 'js' || fileType === 'jsx' || fileType === 'tsx'; } + +/** + * Returns a raw copy of the typescript AST + * @param ast the AST object + * @returns copy of the AST object + */ +export function deeplyCopy(ast: T): T { + return omitDeep(ast) as T; +} + +type UnknownObject = Record; + +function isObjectLike(value: unknown | null): value is UnknownObject { + return ( + typeof value === 'object' && !(value instanceof RegExp) && value !== null + ); +} + +/** + * Removes the given keys from the given AST object recursively + * @param root A JavaScript object to remove keys from + * @param keysToOmit Names and predicate functions use to determine what keys to omit from the final object + * @param selectors advance ast modifications + * @returns formatted object + */ +export function omitDeep( + root: T, + keysToOmit: { key: string; predicate: (value: unknown) => boolean }[] = [], + selectors: Record< + string, + (node: UnknownObject, parent: UnknownObject | null) => void + > = {}, +): UnknownObject { + function shouldOmit(keyName: string, val: unknown): boolean { + if (keysToOmit?.length) { + return keysToOmit.some( + keyConfig => keyConfig.key === keyName && keyConfig.predicate(val), + ); + } + return false; + } + + function visit( + oNode: UnknownObject, + parent: UnknownObject | null, + ): UnknownObject { + if (!Array.isArray(oNode) && !isObjectLike(oNode)) { + return oNode; + } + + const node = { ...oNode }; + + for (const prop in node) { + if (Object.prototype.hasOwnProperty.call(node, prop)) { + if (shouldOmit(prop, node[prop]) || typeof node[prop] === 'undefined') { + delete node[prop]; + continue; + } + + const child = node[prop]; + if (Array.isArray(child)) { + const value = []; + for (const el of child) { + value.push(visit(el, node)); + } + node[prop] = value; + } else if (isObjectLike(child)) { + node[prop] = visit(child, node); + } + } + } + + if (typeof node.type === 'string' && node.type in selectors) { + selectors[node.type](node, parent); + } + + return node; + } + + return visit(root as UnknownObject, null); +} diff --git a/yarn.lock b/yarn.lock index 8ce1cf0d491b..56703239ab25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1390,13 +1390,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== -"@types/lodash.isplainobject@^4.0.4": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#757d2dcdecbb32f4452018b285a586776092efd1" - integrity sha512-8G41YFhmOl8Ck6NrwLK5hhnbz6ADfuDJP+zusDnX3PoYhfC60+H/rQE6zmdO4yFzPCPJPY4oGZK2spbXm6gYEA== - dependencies: - "@types/lodash" "*" - "@types/lodash.memoize@^4.1.4": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.6.tgz#3221f981790a415cab1a239f25c17efd8b604c23" @@ -5487,11 +5480,6 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= -lodash.isplainobject@4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - lodash.map@^4.5.1: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy