diff --git a/package.json b/package.json index ed3ab504..2e4eb6f5 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "jquery": "^3.1.1", "jsbn": "^1.1.0", "jsrsasign": "^7.1.0", + "lodash": "^4.17.4", "moment": "^2.17.1", "moment-timezone": "^0.5.11", "sladex-blowfish": "^0.8.1", diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index e0902c6b..142b1aa8 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -272,6 +272,9 @@ const Categories = [ "CSS selector", "Strip HTML tags", "Diff", + "To Snake case", + "To Camel case", + "To Kebab case", ] }, { diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index 453cc040..325dc8ac 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -3250,6 +3250,69 @@ const OperationConfig = { }, ] }, + "To Snake case": { + description: [ + "Converts the input string to snake case.", + "

", + "Snake case is all lower case with underscores as word boundaries.", + "

", + "e.g. this_is_snake_case", + "

", + "'Attempt to be context aware' will make the operation attempt to nicely transform variable and function names.", + ].join("\n"), + run: Code.runToSnakeCase, + inputType: "string", + outputType: "string", + args: [ + { + name: "Attempt to be context aware", + type: "boolean", + value: false, + }, + ] + }, + "To Camel case": { + description: [ + "Converts the input string to camel case.", + "

", + "Camel case is all lower case except letters after word boundaries which are uppercase.", + "

", + "e.g. thisIsCamelCase", + "

", + "'Attempt to be context aware' will make the operation attempt to nicely transform variable and function names.", + ].join("\n"), + run: Code.runToCamelCase, + inputType: "string", + outputType: "string", + args: [ + { + name: "Attempt to be context aware", + type: "boolean", + value: false, + }, + ] + }, + "To Kebab case": { + description: [ + "Converts the input string to kebab case.", + "

", + "Kebab case is all lower case with dashes as word boundaries.", + "

", + "e.g. this-is-kebab-case", + "

", + "'Attempt to be context aware' will make the operation attempt to nicely transform variable and function names.", + ].join("\n"), + run: Code.runToKebabCase, + inputType: "string", + outputType: "string", + args: [ + { + name: "Attempt to be context aware", + type: "boolean", + value: false, + }, + ] + }, "Extract EXIF": { description: [ ].join("\n"), @@ -3257,7 +3320,7 @@ const OperationConfig = { inputType: "byteArray", outputType: "string", args: [ - ] + ], }, }; diff --git a/src/core/operations/Code.js b/src/core/operations/Code.js index 613c6b42..7d59b4b0 100755 --- a/src/core/operations/Code.js +++ b/src/core/operations/Code.js @@ -1,3 +1,5 @@ +import {camelCase, kebabCase, snakeCase} from "lodash"; + import Utils from "../Utils.js"; import vkbeautify from "vkbeautify"; import {DOMParser as dom} from "xmldom"; @@ -415,6 +417,84 @@ const Code = { .join(delimiter); }, + /** + * This tries to rename variable names in a code snippet according to a function. + * + * @param {string} input + * @param {function} replacer - this function will be fed the token which should be renamed. + * @returns {string} + */ + _replaceVariableNames(input, replacer) { + let tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig; + + return input.replace(tokenRegex, (...args) => { + let match = args[0], + quotes = args[1]; + + if (!quotes) { + return match; + } else { + return replacer(match); + } + }); + }, + + + /** + * Converts to snake_case. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + * + */ + runToSnakeCase(input, args) { + let smart = args[0]; + + if (smart) { + return Code._replaceVariableNames(input, snakeCase); + } else { + return snakeCase(input); + } + }, + + + /** + * Converts to camelCase. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + * + */ + runToCamelCase(input, args) { + let smart = args[0]; + + if (smart) { + return Code._replaceVariableNames(input, camelCase); + } else { + return camelCase(input); + } + }, + + + /** + * Converts to kebab-case. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + * + */ + runToKebabCase(input, args) { + let smart = args[0]; + + if (smart) { + return Code._replaceVariableNames(input, kebabCase); + } else { + return kebabCase(input); + } + }, }; export default Code; diff --git a/src/core/operations/StrUtils.js b/src/core/operations/StrUtils.js index 00d2c561..54f82282 100755 --- a/src/core/operations/StrUtils.js +++ b/src/core/operations/StrUtils.js @@ -592,7 +592,6 @@ const StrUtils = { return output; }, - }; export default StrUtils; diff --git a/test/index.js b/test/index.js index 3530b36a..3687d9d0 100644 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ import "babel-polyfill"; import TestRegister from "./TestRegister.js"; import "./tests/operations/Base58.js"; import "./tests/operations/ByteRepr.js"; +import "./tests/operations/Code.js"; import "./tests/operations/Compress.js"; import "./tests/operations/FlowControl.js"; import "./tests/operations/Image.js"; diff --git a/test/tests/operations/Code.js b/test/tests/operations/Code.js new file mode 100644 index 00000000..5f6a4329 --- /dev/null +++ b/test/tests/operations/Code.js @@ -0,0 +1,132 @@ +/** + * Code tests. + * + * @author tlwr [toby@toby.codes] + * + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister.js"; + +TestRegister.addTests([ + { + name: "To Camel case (dumb)", + input: "hello world", + expectedOutput: "helloWorld", + recipeConfig: [ + { + "op": "To Camel case", + "args": [false] + } + ], + }, + { + name: "To Snake case (dumb)", + input: "hello world", + expectedOutput: "hello_world", + recipeConfig: [ + { + "op": "To Snake case", + "args": [false] + } + ], + }, + { + name: "To Kebab case (dumb)", + input: "hello world", + expectedOutput: "hello-world", + recipeConfig: [ + { + "op": "To Kebab case", + "args": [false] + } + ], + }, + { + name: "To Camel case (smart)", + input: [ + "test='hello'", + "echo $test", + "a_camel_case_function", + "$a_camel_case_variable;", + "function function_name() {", + " console.log('things inside quotes do not get broken');", + " console.log(\"things inside quotes do not get broken\");", + "}", + ].join("\n"), + expectedOutput: [ + "test='hello'", + "echo $test", + "aCamelCaseFunction", + "$aCamelCaseVariable;", + "function functionName() {", + " console.log('things inside quotes do not get broken');", + " console.log(\"things inside quotes do not get broken\");", + "}", + ].join("\n"), + recipeConfig: [ + { + "op": "To Camel case", + "args": [true] + } + ], + }, + { + name: "To Snake case (smart)", + input: [ + "test='hello'", + "echo $test", + "aSnakeCaseFunction", + "$aSnakeCaseVariable;", + "function functionName() {", + " console.log('things inside quotes do not get broken');", + " console.log(\"things inside quotes do not get broken\");", + "}", + ].join("\n"), + expectedOutput: [ + "test='hello'", + "echo $test", + "a_snake_case_function", + "$a_snake_case_variable;", + "function function_name() {", + " console.log('things inside quotes do not get broken');", + " console.log(\"things inside quotes do not get broken\");", + "}", + ].join("\n"), + recipeConfig: [ + { + "op": "To Snake case", + "args": [true] + } + ], + }, + { + name: "To Kebab case (smart)", + input: [ + "test='hello'", + "echo $test", + "aKebabCaseFunction", + "$aKebabCaseVariable;", + "function functionName() {", + " console.log('things inside quotes do not get broken');", + " console.log(\"things inside quotes do not get broken\");", + "}", + ].join("\n"), + expectedOutput: [ + "test='hello'", + "echo $test", + "a-kebab-case-function", + "$a-kebab-case-variable;", + "function function-name() {", + " console.log('things inside quotes do not get broken');", + " console.log(\"things inside quotes do not get broken\");", + "}", + ].join("\n"), + recipeConfig: [ + { + "op": "To Kebab case", + "args": [true] + } + ], + }, +]);