diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7ed915760..360a89639 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -213,9 +213,10 @@ module.exports = { }, { files: [ - "tests/format/**/jsfmt.spec.js", "tests/config/**/*.js", + "tests/format/**/jsfmt.spec.js", "tests/integration/**/*.js", + "tests/unit/**/*.js", "scripts/release/__tests__/**/*.spec.js", ], env: { diff --git a/jest.config.mjs b/jest.config.mjs index 6ed3b77d1..8769702a0 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -46,7 +46,11 @@ const config = { escapeString: false, printBasicPrototype: false, }, - testRegex: "jsfmt\\.spec\\.js$|__tests__/.*\\.js$", + testMatch: [ + "/tests/format/**/jsfmt.spec.js", + "/tests/integration/__tests__/**/*.js", + "/tests/unit/**/*.js", + ], testPathIgnorePatterns, collectCoverage: ENABLE_CODE_COVERAGE, collectCoverageFrom: ["/src/**/*.js", "/bin/**/*.js"], diff --git a/src/common/ast-path.js b/src/common/ast-path.js index eef2b6402..851542a3b 100644 --- a/src/common/ast-path.js +++ b/src/common/ast-path.js @@ -62,17 +62,21 @@ class AstPath { value = value[name]; stack.push(name, value); } - const result = callback(this); - stack.length = length; - return result; + try { + return callback(this); + } finally { + stack.length = length; + } } callParent(callback, count = 0) { const stackIndex = getNodeStackIndexHelper(this.stack, count + 1); const parentValues = this.stack.splice(stackIndex + 1); - const result = callback(this); - this.stack.push(...parentValues); - return result; + try { + return callback(this); + } finally { + this.stack.push(...parentValues); + } } // Similar to AstPath.prototype.call, except that the value obtained by @@ -89,13 +93,15 @@ class AstPath { stack.push(name, value); } - for (let i = 0; i < value.length; ++i) { - stack.push(i, value[i]); - callback(this, i, value); - stack.length -= 2; + try { + for (let i = 0; i < value.length; ++i) { + stack.push(i, value[i]); + callback(this, i, value); + stack.length -= 2; + } + } finally { + stack.length = length; } - - stack.length = length; } // Similar to AstPath.prototype.each, except that the results of the @@ -109,21 +115,6 @@ class AstPath { return result; } - /** - * @param {() => void} callback - * @internal Unstable API. Don't use in plugins for now. - */ - try(callback) { - const { stack } = this; - const stackBackup = [...stack]; - try { - return callback(); - } finally { - stack.length = 0; - stack.push(...stackBackup); - } - } - /** * @param {...( * | ((node: any, name: string | null, number: number | null) => boolean) diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js index 1b977abbd..09a410c46 100644 --- a/src/language-js/print/call-arguments.js +++ b/src/language-js/print/call-arguments.js @@ -110,26 +110,24 @@ function printCallArguments(path, options, print) { let printedExpanded = []; try { - path.try(() => { - iterateCallArgumentsPath(path, (argPath, i) => { - if (shouldGroupFirst && i === 0) { - printedExpanded = [ - [ - print([], { expandFirstArg: true }), - printedArguments.length > 1 ? "," : "", - hasEmptyLineFollowingFirstArg ? hardline : line, - hasEmptyLineFollowingFirstArg ? hardline : "", - ], - ...printedArguments.slice(1), - ]; - } - if (shouldGroupLast && i === lastArgIndex) { - printedExpanded = [ - ...printedArguments.slice(0, -1), - print([], { expandLastArg: true }), - ]; - } - }); + iterateCallArgumentsPath(path, (argPath, i) => { + if (shouldGroupFirst && i === 0) { + printedExpanded = [ + [ + print([], { expandFirstArg: true }), + printedArguments.length > 1 ? "," : "", + hasEmptyLineFollowingFirstArg ? hardline : line, + hasEmptyLineFollowingFirstArg ? hardline : "", + ], + ...printedArguments.slice(1), + ]; + } + if (shouldGroupLast && i === lastArgIndex) { + printedExpanded = [ + ...printedArguments.slice(0, -1), + print([], { expandLastArg: true }), + ]; + } }); } catch (caught) { if (caught instanceof ArgExpansionBailout) { diff --git a/tests/unit/ast-path.js b/tests/unit/ast-path.js new file mode 100644 index 000000000..a7916b289 --- /dev/null +++ b/tests/unit/ast-path.js @@ -0,0 +1,76 @@ +import AstPath from "../../src/common/ast-path.js"; + +const error = new Error("A dummy error"); +const throwError = () => { + throw error; +}; + +describe("AstPath", () => { + const ast = { + property: { + deep: { name: "deep" }, + }, + children: [{ index: 0 }, { index: 1 }], + }; + + test("AstPath#call()", () => { + const path = new AstPath(ast); + + expect(path.call(() => path.getValue(), "property")).toBe(ast.property); + expect(() => path.call(throwError, "property")).toThrow(error); + expect(path.stack.length).toBe(1); + }); + + test("AstPath#callParent()", () => { + const path = new AstPath(ast); + path.stack.push("property", ast.property, "deep", ast.property.deep); + + expect(path.callParent(() => path.getValue())).toBe(ast.property); + expect(() => path.callParent(throwError)).toThrow(error); + expect(path.stack.length).toBe(5); + }); + + test("AstPath#each()", () => { + const path = new AstPath(ast); + + { + const called = []; + path.each(() => called.push(path.getValue()), "children"); + expect(called).toEqual(ast.children); + } + + { + const called = []; + expect(() => { + path.each((_, index) => { + if (index === 1) { + throwError(); + } + called.push(path.getValue()); + }, "children"); + }).toThrow(error); + expect(called.length).toBe(1); + expect(path.stack.length).toBe(1); + } + }); + + test("AstPath#map()", () => { + const path = new AstPath(ast); + + expect(path.map(() => path.getValue(), "children")).toEqual(ast.children); + + { + let result; + expect(() => { + result = path.map((_, index) => { + if (index === 1) { + throwError(); + } + return path.getValue(); + }, "children"); + }).toThrow(error); + expect(result).toBeUndefined(); + expect(path.stack.length).toBe(1); + } + }); +});