From 9a08828e907a05963fc09e9e6f17afc83158dd71 Mon Sep 17 00:00:00 2001 From: Eric Olkowski <70952936+thatblindgeye@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:12:38 -0400 Subject: [PATCH] feat(Modal): promoted Next implementation (#660) * feat(Modal): promoted Next implementation * Created test helpers file --- .../src/rules/helpers/testHelpers.ts | 25 ++++ .../v6/modalDeprecated/modal-deprecated.md | 17 +++ .../modalDeprecated/modal-deprecated.test.ts | 126 ++++++++++++++++++ .../v6/modalDeprecated/modal-deprecated.ts | 29 ++++ .../modalDeprecated/modalDeprecatedInput.tsx | 1 + .../modalDeprecated/modalDeprecatedOutput.tsx | 3 + .../modalNextPromoted/modalNext-promoted.md | 17 +++ .../modalNext-promoted.test.ts | 111 +++++++++++++++ .../modalNextPromoted/modalNext-promoted.ts | 20 +++ .../modalNextPromotedInput.tsx | 1 + .../modalNextPromotedOutput.tsx | 3 + ...toolbar-replaced-spacer-spaceItems.test.ts | 52 ++++---- 12 files changed, 381 insertions(+), 24 deletions(-) create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/helpers/testHelpers.ts create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.md create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.test.ts create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.ts create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedInput.tsx create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedOutput.tsx create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.md create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.test.ts create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.ts create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedInput.tsx create mode 100644 packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedOutput.tsx diff --git a/packages/eslint-plugin-pf-codemods/src/rules/helpers/testHelpers.ts b/packages/eslint-plugin-pf-codemods/src/rules/helpers/testHelpers.ts new file mode 100644 index 000000000..600cde24b --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/helpers/testHelpers.ts @@ -0,0 +1,25 @@ +import { RuleTester } from "eslint"; + +export type ValidTests = Array; +export type InvalidTests = RuleTester.InvalidTestCase[]; +type TestErrors = { + message: string; + type: string; +}[]; + +export function createValidTest(code: string) { + return { + code, + }; +} +export function createInvalidTest( + code: string, + output: string, + errors: TestErrors +) { + return { + code, + output, + errors, + }; +} diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.md new file mode 100644 index 000000000..40ea240b9 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.md @@ -0,0 +1,17 @@ +### modal-deprecated [(#10358)](https://github.com/patternfly/patternfly-react/pull/10358) + +Our previous implementation of Modal has been deprecated. This rule will update import paths to our deprecated directory, but we recommend using our newly promoted implementation. + +#### Examples + +In: + +```jsx +%inputExample% +``` + +Out: + +```jsx +%outputExample% +``` diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.test.ts new file mode 100644 index 000000000..f0cc4dc58 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.test.ts @@ -0,0 +1,126 @@ +const ruleTester = require("../../ruletester"); +import * as rule from "./modal-deprecated"; +import { + ValidTests, + InvalidTests, + createValidTest, + createInvalidTest, +} from "../../helpers/testHelpers"; + +const specifiersToMove = [ + "Modal", + "ModalVariant", + "ModalBox", + "ModalBoxBody", + "ModalBoxCloseButton", + "ModalBoxHeader", + "ModalBoxFooter", + "ModalContent", +]; + +const validTests: ValidTests = []; +const invalidTests: InvalidTests = []; + +specifiersToMove.forEach((specifier) => { + if (!specifier.endsWith("Variant")) { + validTests.push(createValidTest(`<${specifier} />`)); + } + validTests.push( + createValidTest( + `import { ${specifier} /* data-codemods */ } from '@patternfly/react-core';` + ) + ); + validTests.push( + createValidTest( + `import { ${specifier} } from '@patternfly/react-core/deprecated';` + ) + ); + validTests.push( + createValidTest( + `export { ${specifier} /* data-codemods */ } from '@patternfly/react-core';` + ) + ); + validTests.push( + createValidTest( + `export { ${specifier} } from '@patternfly/react-core/deprecated';` + ) + ); + + const errorMessage = `${specifier} has been deprecated. This rule will update import paths to our deprecated directory, but we recommend using our newly promoted Modal implementation.`; + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core';`, + `import {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} as CustomSpecifier } from '@patternfly/react-core';`, + `import {\n\t${specifier} as CustomSpecifier\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/dist/esm/components/Modal/index.js';`, + `import {\n\t${specifier}\n} from '@patternfly/react-core/dist/esm/deprecated/components/Modal/index.js';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/dist/js/components/Modal/index.js';`, + `import {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/dist/dynamic/components/Modal/index.js';`, + `import {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core';`, + `export {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/dist/esm/components/Modal/index.js';`, + `export {\n\t${specifier}\n} from '@patternfly/react-core/dist/esm/deprecated/components/Modal/index.js';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/dist/js/components/Modal/index.js';`, + `export {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/dist/dynamic/components/Modal/index.js';`, + `export {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier}, Button } from '@patternfly/react-core';`, + `import {\n\tButton\n} from '@patternfly/react-core'; +import {\n\t${specifier}\n} from '@patternfly/react-core/deprecated';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); +}); + +ruleTester.run("modal-deprecated", rule, { + valid: validTests, + invalid: invalidTests, +}); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.ts new file mode 100644 index 000000000..de4e3fe98 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modal-deprecated.ts @@ -0,0 +1,29 @@ +import { moveSpecifiers } from "../../helpers"; + +// https://github.com/patternfly/patternfly-react/pull/10358 + +const specifiersToMove = [ + "Modal", + "ModalVariant", + "ModalBox", + "ModalBoxBody", + "ModalBoxCloseButton", + "ModalBoxHeader", + "ModalBoxFooter", + "ModalContent", +]; + +const fromPackage = "@patternfly/react-core"; +const toPackage = "@patternfly/react-core/deprecated"; +const messageAfterImportNameChange = + "been deprecated. This rule will update import paths to our deprecated directory, but we recommend using our newly promoted Modal implementation."; + +module.exports = { + meta: { fixable: "code" }, + create: moveSpecifiers( + specifiersToMove, + fromPackage, + toPackage, + messageAfterImportNameChange + ), +}; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedInput.tsx new file mode 100644 index 000000000..b284e01fb --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedInput.tsx @@ -0,0 +1 @@ +import { Modal } from "@patternfly/react-core"; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedOutput.tsx new file mode 100644 index 000000000..595fb30cb --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalDeprecated/modalDeprecatedOutput.tsx @@ -0,0 +1,3 @@ +import { + Modal +} from '@patternfly/react-core/deprecated'; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.md new file mode 100644 index 000000000..ed0747bd5 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.md @@ -0,0 +1,17 @@ +### modalNext-promoted [(#10358)](https://github.com/patternfly/patternfly-react/pull/10358) + +Our Next implementation of Modal has been promoted as our recommended implementation. This rule will update import paths. + +#### Examples + +In: + +```jsx +%inputExample% +``` + +Out: + +```jsx +%outputExample% +``` diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.test.ts new file mode 100644 index 000000000..1bb7ba9b8 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.test.ts @@ -0,0 +1,111 @@ +const ruleTester = require("../../ruletester"); +import * as rule from "./modalNext-promoted"; +import { + ValidTests, + InvalidTests, + createValidTest, + createInvalidTest, +} from "../../helpers/testHelpers"; + +const specifiersToMove = ["Modal", "ModalBody", "ModalHeader", "ModalFooter"]; +const validTests: ValidTests = []; +const invalidTests: InvalidTests = []; + +specifiersToMove.forEach((specifier) => { + if (!specifier.endsWith("Variant")) { + validTests.push(createValidTest(`<${specifier} />`)); + } + validTests.push( + createValidTest( + `import { ${specifier} /* data-codemods */ } from '@patternfly/react-core';` + ) + ); + validTests.push( + createValidTest(`import { ${specifier} } from '@patternfly/react-core';`) + ); + validTests.push( + createValidTest( + `export { ${specifier} /* data-codemods */ } from '@patternfly/react-core';` + ) + ); + validTests.push( + createValidTest(`export { ${specifier} } from '@patternfly/react-core';`) + ); + + const errorMessage = `${specifier} has been promoted. This rule will update import paths.`; + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/next';`, + `import {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} as CustomSpecifier } from '@patternfly/react-core/next';`, + `import {\n\t${specifier} as CustomSpecifier /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/dist/esm/next/components/Modal/index.js';`, + `import {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core/dist/esm/components/Modal/index.js';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/dist/js/next/components/Modal/index.js';`, + `import {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/dist/dynamic/next/components/Modal/index.js';`, + `import {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/next';`, + `export {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/dist/esm/next/components/Modal/index.js';`, + `export {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core/dist/esm/components/Modal/index.js';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/dist/js/next/components/Modal/index.js';`, + `export {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `export { ${specifier} } from '@patternfly/react-core/dist/dynamic/next/components/Modal/index.js';`, + `export {\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ExportNamedDeclaration" }] + ) + ); + invalidTests.push( + createInvalidTest( + `import { ${specifier} } from '@patternfly/react-core/next';\nimport { Button } from '@patternfly/react-core';`, + `\nimport {\n\tButton,\n\t${specifier} /* data-codemods */\n} from '@patternfly/react-core';`, + [{ message: errorMessage, type: "ImportDeclaration" }] + ) + ); +}); + +ruleTester.run("modalNext-promoted", rule, { + valid: validTests, + invalid: invalidTests, +}); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.ts new file mode 100644 index 000000000..90a9b18a6 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNext-promoted.ts @@ -0,0 +1,20 @@ +import { moveSpecifiers } from "../../helpers"; + +// https://github.com/patternfly/patternfly-react/pull/10358 + +const specifiersToMove = ["Modal", "ModalBody", "ModalHeader", "ModalFooter"]; + +const fromPackage = "@patternfly/react-core/next"; +const toPackage = "@patternfly/react-core"; +const messageAfterImportNameChange = + "been promoted. This rule will update import paths."; + +module.exports = { + meta: { fixable: "code" }, + create: moveSpecifiers( + specifiersToMove, + fromPackage, + toPackage, + messageAfterImportNameChange + ), +}; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedInput.tsx new file mode 100644 index 000000000..91e0bfeb0 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedInput.tsx @@ -0,0 +1 @@ +import { Modal } from "@patternfly/react-core/next"; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedOutput.tsx new file mode 100644 index 000000000..460940a00 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/modalNextPromoted/modalNextPromotedOutput.tsx @@ -0,0 +1,3 @@ +import { + Modal /* data-codemods */ +} from '@patternfly/react-core'; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarReplacedSpacerSpaceItems/toolbar-replaced-spacer-spaceItems.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarReplacedSpacerSpaceItems/toolbar-replaced-spacer-spaceItems.test.ts index f44283327..67dfa8661 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarReplacedSpacerSpaceItems/toolbar-replaced-spacer-spaceItems.test.ts +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarReplacedSpacerSpaceItems/toolbar-replaced-spacer-spaceItems.test.ts @@ -1,22 +1,18 @@ const ruleTester = require("../../ruletester"); import * as rule from "./toolbar-replaced-spacer-spaceItems"; -import { RuleTester } from "eslint"; +import { + ValidTests, + InvalidTests, + createValidTest, + createInvalidTest, +} from "../../helpers/testHelpers"; const toolbarComponents = ["ToolbarGroup", "ToolbarToggleGroup", "ToolbarItem"]; -const validTests: Array = []; -const invalidTests: RuleTester.InvalidTestCase[] = []; -const createValidTest = (code: string) => ({ - code, -}); -const createInvalidTest = (code: string, output: string, message: string) => ({ - code, - output, - errors: [ - { - message, - type: "JSXOpeningElement", - }, - ], +const validTests: ValidTests = []; +const invalidTests: InvalidTests = []; +const createErrorObject = (message: string) => ({ + message, + type: "JSXOpeningElement", }); toolbarComponents.forEach((component) => { @@ -34,59 +30,67 @@ toolbarComponents.forEach((component) => { createInvalidTest( `import { ${component} } from '@patternfly/react-core'; <${component} spacer={{default: "spacerNone"}} />`, `import { ${component} } from '@patternfly/react-core'; <${component} gap={{default: "gapNone"}} />`, - spacerErrorMessage + [createErrorObject(spacerErrorMessage)] ) ); invalidTests.push( createInvalidTest( `import { ${component} } from '@patternfly/react-core'; const NO_SPACER = "spacerNone"; <${component} spacer={{default: NO_SPACER}} />`, `import { ${component} } from '@patternfly/react-core'; const NO_SPACER = "spacerNone"; <${component} gap={{default: NO_SPACER}} />`, - spacerErrorMessage + [createErrorObject(spacerErrorMessage)] ) ); invalidTests.push( createInvalidTest( `import { ${component} as CustomComponent } from '@patternfly/react-core'; `, `import { ${component} as CustomComponent } from '@patternfly/react-core'; `, - `The spacer property has been removed from CustomComponent. We recommend instead using our new gap, columnGap, or rowGap properties.` + [ + createErrorObject( + `The spacer property has been removed from CustomComponent. We recommend instead using our new gap, columnGap, or rowGap properties.` + ), + ] ) ); invalidTests.push( createInvalidTest( `import { ${component} } from '@patternfly/react-core'; <${component} spacer={{default: "spacerNone", md: "spacerLg", lg: "spacerSm"}} />`, `import { ${component} } from '@patternfly/react-core'; <${component} gap={{default: "gapNone", md: "gapLg", lg: "gapSm"}} />`, - spacerErrorMessage + [createErrorObject(spacerErrorMessage)] ) ); createInvalidTest( `import { ${component} } from '@patternfly/react-core'; <${component} spaceItems={{default: "spaceItemsNone"}} />`, `import { ${component} } from '@patternfly/react-core'; <${component} />`, - `The ${spaceItemsErrorMessage}` + [createErrorObject(`The ${spaceItemsErrorMessage}`)] ); createInvalidTest( `import { ${component} } from '@patternfly/react-core'; <${component} spacer={{default: "spacerNone"}} spaceItems={{default: "spaceItemsNone"}} />`, `import { ${component} } from '@patternfly/react-core'; <${component} gap={{default: "gapNone"}} />`, - `${spacerErrorMessage} Additionally, the ${spaceItemsErrorMessage}` + [ + createErrorObject( + `${spacerErrorMessage} Additionally, the ${spaceItemsErrorMessage}` + ), + ] ); invalidTests.push( createInvalidTest( `import { ${component} } from '@patternfly/react-core/dist/esm/components/Toolbar/index.js'; <${component} spacer={{default: "spacerNone"}} />`, `import { ${component} } from '@patternfly/react-core/dist/esm/components/Toolbar/index.js'; <${component} gap={{default: "gapNone"}} />`, - spacerErrorMessage + [createErrorObject(spacerErrorMessage)] ) ); invalidTests.push( createInvalidTest( `import { ${component} } from '@patternfly/react-core/dist/js/components/Toolbar/index.js'; <${component} spacer={{default: "spacerNone"}} />`, `import { ${component} } from '@patternfly/react-core/dist/js/components/Toolbar/index.js'; <${component} gap={{default: "gapNone"}} />`, - spacerErrorMessage + [createErrorObject(spacerErrorMessage)] ) ); invalidTests.push( createInvalidTest( `import { ${component} } from '@patternfly/react-core/dist/dynamic/components/Toolbar/index.js'; <${component} spacer={{default: "spacerNone"}} />`, `import { ${component} } from '@patternfly/react-core/dist/dynamic/components/Toolbar/index.js'; <${component} gap={{default: "gapNone"}} />`, - spacerErrorMessage + [createErrorObject(spacerErrorMessage)] ) ); });