configorama 0.6.12 → 0.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -24
- package/cli.js +3 -3
- package/package.json +1 -1
- package/src/index.js +22 -32
- package/src/main.js +690 -778
- package/src/parsers/yaml.js +3 -47
- package/src/resolvers/valueFromCron.js +3 -1
- package/src/resolvers/valueFromEnv.js +1 -0
- package/src/resolvers/valueFromEval.js +1 -0
- package/src/resolvers/valueFromFile.js +394 -0
- package/src/resolvers/valueFromGit.js +3 -2
- package/src/resolvers/valueFromOptions.js +1 -0
- package/src/resolvers/valueFromString.js +2 -1
- package/src/sync.js +12 -5
- package/src/utils/parsing/arrayToJsonPath.test.js +56 -0
- package/src/utils/{enrichMetadata.js → parsing/enrichMetadata.js} +177 -15
- package/src/utils/{parse.js → parsing/parse.js} +13 -13
- package/src/utils/parsing/preProcess.js +165 -0
- package/src/utils/{filePathUtils.js → paths/filePathUtils.js} +3 -2
- package/src/utils/paths/findLineForKey.js +47 -0
- package/src/utils/paths/findLineForKey.test.js +126 -0
- package/src/utils/{getFullFilePath.js → paths/getFullFilePath.js} +22 -26
- package/src/utils/{resolveAlias.js → paths/resolveAlias.js} +1 -1
- package/src/utils/regex/index.js +23 -1
- package/src/utils/resolution/preResolveVariable.js +260 -0
- package/src/utils/resolution/preResolveVariable.test.js +98 -0
- package/src/utils/strings/bracketMatcher.js +86 -0
- package/src/utils/strings/bracketMatcher.test.js +135 -0
- package/src/utils/{formatFunctionArgs.js → strings/formatFunctionArgs.js} +3 -2
- package/src/utils/strings/formatFunctionArgs.test.js +77 -0
- package/src/utils/strings/quoteUtils.js +89 -0
- package/src/utils/strings/quoteUtils.test.js +217 -0
- package/src/utils/strings/replaceAll.test.js +82 -0
- package/src/utils/{splitByComma.js → strings/splitByComma.js} +1 -1
- package/src/utils/strings/splitCsv.js +38 -0
- package/src/utils/strings/splitCsv.test.js +96 -0
- package/src/utils/strings/textUtils.test.js +86 -0
- package/src/utils/{configWizard.js → ui/configWizard.js} +177 -38
- package/src/utils/{createEditorLink.js → ui/createEditorLink.js} +11 -2
- package/src/utils/{logs.js → ui/logs.js} +3 -3
- package/src/utils/validation/isValidValue.test.js +64 -0
- package/src/utils/validation/warnIfNotFound.js +52 -0
- package/src/utils/variables/appendDeepVariable.test.js +41 -0
- package/src/utils/{cleanVariable.js → variables/cleanVariable.js} +5 -26
- package/src/utils/{find-nested-variables.js → variables/findNestedVariables.js} +2 -2
- package/src/utils/{find-nested-variables.test.js → variables/findNestedVariables.test.js} +5 -5
- package/src/utils/variables/getVariableType.test.js +109 -0
- package/src/utils/variables/variableUtils.test.js +117 -0
- package/src/utils/isValidValue.js +0 -8
- package/src/utils/splitCsv.js +0 -29
- package/src/utils/trimSurroundingQuotes.js +0 -5
- /package/src/utils/{arrayToJsonPath.js → parsing/arrayToJsonPath.js} +0 -0
- /package/src/utils/{cloudformationSchema.js → parsing/cloudformationSchema.js} +0 -0
- /package/src/utils/{mergeByKeys.js → parsing/mergeByKeys.js} +0 -0
- /package/src/utils/{filePathUtils.test.js → paths/filePathUtils.test.js} +0 -0
- /package/src/utils/{find-project-root.js → paths/findProjectRoot.js} +0 -0
- /package/src/utils/{resolveAlias.test.js → paths/resolveAlias.test.js} +0 -0
- /package/src/utils/{replaceAll.js → strings/replaceAll.js} +0 -0
- /package/src/utils/{splitByComma.test.js → strings/splitByComma.test.js} +0 -0
- /package/src/utils/{textUtils.js → strings/textUtils.js} +0 -0
- /package/src/utils/{chalk.js → ui/chalk.js} +0 -0
- /package/src/utils/{deep-log.js → ui/deep-log.js} +0 -0
- /package/src/utils/{appendDeepVariable.js → variables/appendDeepVariable.js} +0 -0
- /package/src/utils/{cleanVariable.test.js → variables/cleanVariable.test.js} +0 -0
- /package/src/utils/{getVariableType.js → variables/getVariableType.js} +0 -0
- /package/src/utils/{variableUtils.js → variables/variableUtils.js} +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const {
|
|
4
|
+
findOutermostBraces,
|
|
5
|
+
findOutermostBracesDepthFirst,
|
|
6
|
+
findOutermostVariables
|
|
7
|
+
} = require('./bracketMatcher')
|
|
8
|
+
|
|
9
|
+
// Tests for findOutermostBraces
|
|
10
|
+
test('findOutermostBraces - should find simple braces', () => {
|
|
11
|
+
const result = findOutermostBraces('text {content} more')
|
|
12
|
+
assert.equal(result, ['{content}'])
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('findOutermostBraces - should find multiple brace pairs', () => {
|
|
16
|
+
const result = findOutermostBraces('{first} and {second}')
|
|
17
|
+
assert.equal(result, ['{first}', '{second}'])
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('findOutermostBraces - should handle nested braces', () => {
|
|
21
|
+
const result = findOutermostBraces('{outer {inner} content}')
|
|
22
|
+
assert.equal(result, ['{outer {inner} content}'])
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('findOutermostBraces - should find with prefix', () => {
|
|
26
|
+
const result = findOutermostBraces('text ${variable} more', '{', '}', '$')
|
|
27
|
+
assert.equal(result, ['${variable}'])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('findOutermostBraces - should handle multiple nested levels with prefix', () => {
|
|
31
|
+
const result = findOutermostBraces('${outer ${inner ${deepest}}}', '{', '}', '$')
|
|
32
|
+
assert.equal(result, ['${outer ${inner ${deepest}}}'])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('findOutermostBraces - should return empty array when no matches', () => {
|
|
36
|
+
const result = findOutermostBraces('no braces here')
|
|
37
|
+
assert.equal(result, [])
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('findOutermostBraces - should handle custom delimiters', () => {
|
|
41
|
+
const result = findOutermostBraces('text [content] more', '[', ']')
|
|
42
|
+
assert.equal(result, ['[content]'])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('findOutermostBraces - should not match without prefix when prefix is specified', () => {
|
|
46
|
+
const result = findOutermostBraces('{no prefix} ${with prefix}', '{', '}', '$')
|
|
47
|
+
assert.equal(result, ['${with prefix}'])
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Tests for findOutermostBracesDepthFirst
|
|
51
|
+
test('findOutermostBracesDepthFirst - should find simple braces', () => {
|
|
52
|
+
const result = findOutermostBracesDepthFirst('text {content} more')
|
|
53
|
+
assert.equal(result, ['{content}'])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('findOutermostBracesDepthFirst - should find multiple brace pairs', () => {
|
|
57
|
+
const result = findOutermostBracesDepthFirst('{first} and {second}')
|
|
58
|
+
assert.equal(result, ['{first}', '{second}'])
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('findOutermostBracesDepthFirst - should handle nested braces', () => {
|
|
62
|
+
const result = findOutermostBracesDepthFirst('{outer {inner} content}')
|
|
63
|
+
assert.equal(result, ['{outer {inner} content}'])
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('findOutermostBracesDepthFirst - should return empty array when no matches', () => {
|
|
67
|
+
const result = findOutermostBracesDepthFirst('no braces here')
|
|
68
|
+
assert.equal(result, [])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('findOutermostBracesDepthFirst - should handle custom delimiters', () => {
|
|
72
|
+
const result = findOutermostBracesDepthFirst('text [content] more', '[', ']')
|
|
73
|
+
assert.equal(result, ['[content]'])
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('findOutermostBracesDepthFirst - should handle unmatched braces gracefully', () => {
|
|
77
|
+
const result = findOutermostBracesDepthFirst('{opened but not closed')
|
|
78
|
+
assert.equal(result, [])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('findOutermostBracesDepthFirst - should handle complex nested structures', () => {
|
|
82
|
+
const result = findOutermostBracesDepthFirst('{a {b {c}}} {d {e}}')
|
|
83
|
+
assert.equal(result, ['{a {b {c}}}', '{d {e}}'])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Tests for findOutermostVariables
|
|
87
|
+
test('findOutermostVariables - should find simple variable', () => {
|
|
88
|
+
const result = findOutermostVariables('text ${variable} more')
|
|
89
|
+
assert.equal(result, ['${variable}'])
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('findOutermostVariables - should find multiple variables', () => {
|
|
93
|
+
const result = findOutermostVariables('${first} and ${second}')
|
|
94
|
+
assert.equal(result, ['${first}', '${second}'])
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('findOutermostVariables - should handle nested variables', () => {
|
|
98
|
+
const result = findOutermostVariables('${outer ${inner}}')
|
|
99
|
+
assert.equal(result, ['${outer ${inner}}'])
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('findOutermostVariables - should return empty array when no variables', () => {
|
|
103
|
+
const result = findOutermostVariables('no variables here')
|
|
104
|
+
assert.equal(result, [])
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('findOutermostVariables - should ignore plain braces without dollar sign', () => {
|
|
108
|
+
const result = findOutermostVariables('{not a variable} ${is a variable}')
|
|
109
|
+
assert.equal(result, ['${is a variable}'])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('findOutermostVariables - should handle real-world serverless variables', () => {
|
|
113
|
+
const result = findOutermostVariables('${param:xyz}')
|
|
114
|
+
assert.equal(result, ['${param:xyz}'])
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('findOutermostVariables - should handle deeply nested serverless variables', () => {
|
|
118
|
+
const result = findOutermostVariables('${opt:stage, ${env:foo}}')
|
|
119
|
+
assert.equal(result, ['${opt:stage, ${env:foo}}'])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('findOutermostVariables - should find multiple variables in array context', () => {
|
|
123
|
+
const text = "y: !Not [!Equals [!Join ['', ${param:xyz}]]]"
|
|
124
|
+
const result = findOutermostVariables(text)
|
|
125
|
+
assert.equal(result, ['${param:xyz}'])
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('findOutermostVariables - should handle variables in YAML object context', () => {
|
|
129
|
+
const text = 'key: { value: ${self:config}, other: ${env:var} }'
|
|
130
|
+
const result = findOutermostVariables(text)
|
|
131
|
+
assert.equal(result, ['${self:config}', '${env:var}'])
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Run all tests
|
|
135
|
+
test.run()
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
const { trim } = require('
|
|
1
|
+
const { trim } = require('../lodash')
|
|
2
|
+
const { trimSurroundingQuotes } = require('./quoteUtils')
|
|
2
3
|
|
|
3
4
|
function formatArg(arg) {
|
|
4
|
-
const cleanArg = trim(arg)
|
|
5
|
+
const cleanArg = trimSurroundingQuotes(trim(arg), false)
|
|
5
6
|
if (cleanArg.match(/^{([^}]+)}$/)) {
|
|
6
7
|
return JSON.parse(cleanArg)
|
|
7
8
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const formatFunctionArgs = require('./formatFunctionArgs')
|
|
4
|
+
|
|
5
|
+
test('formatFunctionArgs - should handle single string argument', () => {
|
|
6
|
+
const result = formatFunctionArgs('simpleString')
|
|
7
|
+
assert.equal(result, 'simpleString')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('formatFunctionArgs - should trim and remove quotes from string', () => {
|
|
11
|
+
const result = formatFunctionArgs(' "quoted" ')
|
|
12
|
+
assert.equal(result, 'quoted')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('formatFunctionArgs - should parse JSON object', () => {
|
|
16
|
+
const result = formatFunctionArgs('{"key":"value"}')
|
|
17
|
+
assert.is(typeof result, 'object')
|
|
18
|
+
assert.equal(result.key, 'value')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('formatFunctionArgs - should parse JSON array', () => {
|
|
22
|
+
const result = formatFunctionArgs('[1,2,3]')
|
|
23
|
+
assert.is(Array.isArray(result), true)
|
|
24
|
+
assert.equal(result, [1, 2, 3])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('formatFunctionArgs - should handle array of arguments', () => {
|
|
28
|
+
const result = formatFunctionArgs(['arg1', '"arg2"', ' arg3 '])
|
|
29
|
+
assert.is(Array.isArray(result), true)
|
|
30
|
+
assert.equal(result, ['arg1', 'arg2', 'arg3'])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('formatFunctionArgs - should parse JSON objects in array', () => {
|
|
34
|
+
const result = formatFunctionArgs(['{"x":1}', '{"y":2}'])
|
|
35
|
+
assert.is(Array.isArray(result), true)
|
|
36
|
+
assert.equal(result[0].x, 1)
|
|
37
|
+
assert.equal(result[1].y, 2)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('formatFunctionArgs - should parse JSON arrays in array', () => {
|
|
41
|
+
const result = formatFunctionArgs(['[1,2]', '[3,4]'])
|
|
42
|
+
assert.is(Array.isArray(result), true)
|
|
43
|
+
assert.equal(result[0], [1, 2])
|
|
44
|
+
assert.equal(result[1], [3, 4])
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('formatFunctionArgs - should handle mixed array', () => {
|
|
48
|
+
const result = formatFunctionArgs(['simple', '{"obj":"val"}', '[1,2]'])
|
|
49
|
+
assert.equal(result[0], 'simple')
|
|
50
|
+
assert.equal(result[1].obj, 'val')
|
|
51
|
+
assert.equal(result[2], [1, 2])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('formatFunctionArgs - should handle single quotes', () => {
|
|
55
|
+
const result = formatFunctionArgs("'singleQuoted'")
|
|
56
|
+
assert.equal(result, 'singleQuoted')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('formatFunctionArgs - should handle empty object', () => {
|
|
60
|
+
const result = formatFunctionArgs('{}')
|
|
61
|
+
assert.is(typeof result, 'object')
|
|
62
|
+
assert.equal(Object.keys(result).length, 0)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('formatFunctionArgs - should handle empty array', () => {
|
|
66
|
+
const result = formatFunctionArgs('[]')
|
|
67
|
+
// Empty array regex pattern doesn't match /^\[([^}]+)\]$/ so returns the string as-is
|
|
68
|
+
assert.equal(result, '[]')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('formatFunctionArgs - should preserve unquoted strings', () => {
|
|
72
|
+
const result = formatFunctionArgs('unquoted')
|
|
73
|
+
assert.equal(result, 'unquoted')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Run all tests
|
|
77
|
+
test.run()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quote manipulation utilities for string handling
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Removes surrounding quotes (single, double, or backtick) from a string
|
|
7
|
+
* @param {string} str - The string to trim
|
|
8
|
+
* @param {boolean} includeBackticks - Whether to also trim backticks (default: true)
|
|
9
|
+
* @returns {string} The trimmed string
|
|
10
|
+
*/
|
|
11
|
+
function trimSurroundingQuotes(str = '', includeBackticks = true) {
|
|
12
|
+
let result = str
|
|
13
|
+
.replace(/^(")([^"\n]*?)(\1)$/, "$2")
|
|
14
|
+
.replace(/^(')([^'\n]*?)(\1)$/, "$2")
|
|
15
|
+
|
|
16
|
+
if (includeBackticks) {
|
|
17
|
+
result = result.replace(/^(`)([^`\n]*?)(\1)$/, "$2")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return result
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks if a string starts with the given character
|
|
25
|
+
* @param {string} str - The string to check
|
|
26
|
+
* @param {string} char - The character to check for
|
|
27
|
+
* @returns {string} Empty string if starts with char, otherwise the char
|
|
28
|
+
*/
|
|
29
|
+
function startChar(str, char) {
|
|
30
|
+
return (str[0] === char) ? '' : char
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks if a string ends with the given character
|
|
35
|
+
* @param {string} str - The string to check
|
|
36
|
+
* @param {string} char - The character to check for
|
|
37
|
+
* @returns {string} Empty string if ends with char, otherwise the char
|
|
38
|
+
*/
|
|
39
|
+
function endChar(str, char) {
|
|
40
|
+
return (str[str.length - 1] === char) ? '' : char
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Ensures a value (string or array of strings) has quotes around it
|
|
45
|
+
* @param {string|string[]} value - The value to quote
|
|
46
|
+
* @param {string} open - Opening quote character (default: '"')
|
|
47
|
+
* @param {string} close - Closing quote character (default: same as open)
|
|
48
|
+
* @returns {string|string[]} The quoted value(s)
|
|
49
|
+
*/
|
|
50
|
+
function ensureQuote(value, open = '"', close) {
|
|
51
|
+
let i = -1
|
|
52
|
+
const result = []
|
|
53
|
+
const end = close || open
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
return startChar(value, open) + value + endChar(value, end)
|
|
56
|
+
}
|
|
57
|
+
while (++i < value.length) {
|
|
58
|
+
result[i] = startChar(value[i], open) + value[i] + endChar(value[i], end)
|
|
59
|
+
}
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Checks if a string is surrounded by matching quotes (single or double)
|
|
65
|
+
* @param {string} str - The string to check
|
|
66
|
+
* @returns {boolean} True if surrounded by matching quotes
|
|
67
|
+
*/
|
|
68
|
+
function isSurroundedByQuotes(str) {
|
|
69
|
+
if (!str || str.length < 2) return false
|
|
70
|
+
const firstChar = str[0]
|
|
71
|
+
const lastChar = str[str.length - 1]
|
|
72
|
+
return (firstChar === "'" && lastChar === "'") || (firstChar === '"' && lastChar === '"')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Checks if a string starts with a quoted value followed by a pipe
|
|
77
|
+
* @param {string} str - The string to check
|
|
78
|
+
* @returns {boolean} True if matches pattern like 'xyz' | or "xyz" |
|
|
79
|
+
*/
|
|
80
|
+
function startsWithQuotedPipe(str) {
|
|
81
|
+
return /^(['"])(.*?)\1\s*\|/.test(str)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
trimSurroundingQuotes,
|
|
86
|
+
ensureQuote,
|
|
87
|
+
isSurroundedByQuotes,
|
|
88
|
+
startsWithQuotedPipe
|
|
89
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const { trimSurroundingQuotes: trimQuotes, ensureQuote, isSurroundedByQuotes, startsWithQuotedPipe } = require('./quoteUtils')
|
|
4
|
+
|
|
5
|
+
// Tests for double quotes
|
|
6
|
+
test('trimQuotes - should remove surrounding double quotes', () => {
|
|
7
|
+
const result = trimQuotes('"hello"')
|
|
8
|
+
assert.equal(result, 'hello')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('trimQuotes - should not remove non-surrounding double quotes', () => {
|
|
12
|
+
const result = trimQuotes('say "hello" there')
|
|
13
|
+
assert.equal(result, 'say "hello" there')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('trimQuotes - should preserve internal double quotes', () => {
|
|
17
|
+
const result = trimQuotes('"has "nested" quotes"')
|
|
18
|
+
assert.equal(result, '"has "nested" quotes"')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Tests for single quotes
|
|
22
|
+
test('trimQuotes - should remove surrounding single quotes', () => {
|
|
23
|
+
const result = trimQuotes("'hello'")
|
|
24
|
+
assert.equal(result, 'hello')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('trimQuotes - should not remove non-surrounding single quotes', () => {
|
|
28
|
+
const result = trimQuotes("say 'hello' there")
|
|
29
|
+
assert.equal(result, "say 'hello' there")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('trimQuotes - should preserve internal single quotes', () => {
|
|
33
|
+
const result = trimQuotes("'has 'nested' quotes'")
|
|
34
|
+
assert.equal(result, "'has 'nested' quotes'")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Tests for backticks
|
|
38
|
+
test('trimQuotes - should remove surrounding backticks by default', () => {
|
|
39
|
+
const result = trimQuotes('`hello`')
|
|
40
|
+
assert.equal(result, 'hello')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('trimQuotes - should not remove backticks when includeBackticks is false', () => {
|
|
44
|
+
const result = trimQuotes('`hello`', false)
|
|
45
|
+
assert.equal(result, '`hello`')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('trimQuotes - should preserve internal backticks', () => {
|
|
49
|
+
const result = trimQuotes('`has `nested` ticks`')
|
|
50
|
+
assert.equal(result, '`has `nested` ticks`')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Tests for mixed quotes
|
|
54
|
+
test('trimQuotes - should not remove mismatched quotes', () => {
|
|
55
|
+
const result = trimQuotes('"hello\'')
|
|
56
|
+
assert.equal(result, '"hello\'')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('trimQuotes - should handle empty quotes', () => {
|
|
60
|
+
const result = trimQuotes('""')
|
|
61
|
+
assert.equal(result, '')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('trimQuotes - should handle empty single quotes', () => {
|
|
65
|
+
const result = trimQuotes("''")
|
|
66
|
+
assert.equal(result, '')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('trimQuotes - should handle empty backticks', () => {
|
|
70
|
+
const result = trimQuotes('``')
|
|
71
|
+
assert.equal(result, '')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Tests for edge cases
|
|
75
|
+
test('trimQuotes - should handle empty string', () => {
|
|
76
|
+
const result = trimQuotes('')
|
|
77
|
+
assert.equal(result, '')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('trimQuotes - should handle undefined', () => {
|
|
81
|
+
const result = trimQuotes()
|
|
82
|
+
assert.equal(result, '')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('trimQuotes - should handle strings without quotes', () => {
|
|
86
|
+
const result = trimQuotes('hello world')
|
|
87
|
+
assert.equal(result, 'hello world')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('trimQuotes - should handle strings with only opening quote', () => {
|
|
91
|
+
const result = trimQuotes('"hello')
|
|
92
|
+
assert.equal(result, '"hello')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('trimQuotes - should handle strings with only closing quote', () => {
|
|
96
|
+
const result = trimQuotes('hello"')
|
|
97
|
+
assert.equal(result, 'hello"')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('trimQuotes - should not remove quotes from multi-line strings', () => {
|
|
101
|
+
const result = trimQuotes('"hello\nworld"')
|
|
102
|
+
assert.equal(result, '"hello\nworld"')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('trimQuotes - should handle spaces inside quotes', () => {
|
|
106
|
+
const result = trimQuotes('" hello "')
|
|
107
|
+
assert.equal(result, ' hello ')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('trimQuotes - should handle special characters inside quotes', () => {
|
|
111
|
+
const result = trimQuotes('"hello@#$%world"')
|
|
112
|
+
assert.equal(result, 'hello@#$%world')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('trimQuotes - should handle numbers as strings', () => {
|
|
116
|
+
const result = trimQuotes('"12345"')
|
|
117
|
+
assert.equal(result, '12345')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('trimQuotes - should handle JSON-like strings', () => {
|
|
121
|
+
const result = trimQuotes('"{key: value}"')
|
|
122
|
+
assert.equal(result, '{key: value}')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Tests for backtick parameter
|
|
126
|
+
test('trimQuotes - should process all quote types when includeBackticks is true', () => {
|
|
127
|
+
assert.equal(trimQuotes('"test"', true), 'test')
|
|
128
|
+
assert.equal(trimQuotes("'test'", true), 'test')
|
|
129
|
+
assert.equal(trimQuotes('`test`', true), 'test')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('trimQuotes - should not process backticks when includeBackticks is false', () => {
|
|
133
|
+
assert.equal(trimQuotes('"test"', false), 'test')
|
|
134
|
+
assert.equal(trimQuotes("'test'", false), 'test')
|
|
135
|
+
assert.equal(trimQuotes('`test`', false), '`test`')
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// Tests for ensureQuote
|
|
139
|
+
test('ensureQuote - should add double quotes to unquoted string', () => {
|
|
140
|
+
assert.equal(ensureQuote('hello'), '"hello"')
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('ensureQuote - should not double-quote already quoted string', () => {
|
|
144
|
+
assert.equal(ensureQuote('"hello"'), '"hello"')
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('ensureQuote - should use custom open/close characters', () => {
|
|
148
|
+
assert.equal(ensureQuote('hello', "'"), "'hello'")
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('ensureQuote - should handle different open and close characters', () => {
|
|
152
|
+
assert.equal(ensureQuote('hello', '[', ']'), '[hello]')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('ensureQuote - should handle array of strings', () => {
|
|
156
|
+
const result = ensureQuote(['a', 'b', 'c'])
|
|
157
|
+
assert.equal(result, ['"a"', '"b"', '"c"'])
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('ensureQuote - should not re-quote already quoted items in array', () => {
|
|
161
|
+
const result = ensureQuote(['"a"', 'b'])
|
|
162
|
+
assert.equal(result, ['"a"', '"b"'])
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
// Tests for isSurroundedByQuotes
|
|
166
|
+
test('isSurroundedByQuotes - should return true for double-quoted string', () => {
|
|
167
|
+
assert.equal(isSurroundedByQuotes('"hello"'), true)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('isSurroundedByQuotes - should return true for single-quoted string', () => {
|
|
171
|
+
assert.equal(isSurroundedByQuotes("'hello'"), true)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('isSurroundedByQuotes - should return false for unquoted string', () => {
|
|
175
|
+
assert.equal(isSurroundedByQuotes('hello'), false)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('isSurroundedByQuotes - should return false for mismatched quotes', () => {
|
|
179
|
+
assert.equal(isSurroundedByQuotes('"hello\''), false)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
test('isSurroundedByQuotes - should return false for empty string', () => {
|
|
183
|
+
assert.equal(isSurroundedByQuotes(''), false)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('isSurroundedByQuotes - should return false for null/undefined', () => {
|
|
187
|
+
assert.equal(isSurroundedByQuotes(null), false)
|
|
188
|
+
assert.equal(isSurroundedByQuotes(undefined), false)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('isSurroundedByQuotes - should return false for single character', () => {
|
|
192
|
+
assert.equal(isSurroundedByQuotes('"'), false)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// Tests for startsWithQuotedPipe
|
|
196
|
+
test('startsWithQuotedPipe - should match single-quoted value with pipe', () => {
|
|
197
|
+
assert.equal(startsWithQuotedPipe("'value' | filter"), true)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('startsWithQuotedPipe - should match double-quoted value with pipe', () => {
|
|
201
|
+
assert.equal(startsWithQuotedPipe('"value" | filter'), true)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('startsWithQuotedPipe - should not match unquoted value with pipe', () => {
|
|
205
|
+
assert.equal(startsWithQuotedPipe('value | filter'), false)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('startsWithQuotedPipe - should not match quoted value without pipe', () => {
|
|
209
|
+
assert.equal(startsWithQuotedPipe('"value"'), false)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
test('startsWithQuotedPipe - should handle spaces around pipe', () => {
|
|
213
|
+
assert.equal(startsWithQuotedPipe("'xyz' | something"), true)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// Run all tests
|
|
217
|
+
test.run()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const { replaceAll } = require('./replaceAll')
|
|
4
|
+
|
|
5
|
+
test('replaceAll - should replace simple string', () => {
|
|
6
|
+
const result = replaceAll('foo', 'bar', 'foo is foo')
|
|
7
|
+
assert.equal(result, 'bar is bar')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('replaceAll - should replace all occurrences', () => {
|
|
11
|
+
const result = replaceAll('a', 'x', 'aaa bbb aaa')
|
|
12
|
+
assert.equal(result, 'xxx bbb xxx')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('replaceAll - should handle regex special characters in search string', () => {
|
|
16
|
+
const result = replaceAll('.', 'X', 'a.b.c')
|
|
17
|
+
assert.equal(result, 'aXbXc')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('replaceAll - should handle $ in replacement string', () => {
|
|
21
|
+
const result = replaceAll('foo', '$bar', 'foo is foo')
|
|
22
|
+
assert.equal(result, '$bar is $bar')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('replaceAll - should handle parentheses in search string', () => {
|
|
26
|
+
const result = replaceAll('(test)', 'result', 'this (test) is a (test)')
|
|
27
|
+
assert.equal(result, 'this result is a result')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('replaceAll - should handle square brackets in search string', () => {
|
|
31
|
+
const result = replaceAll('[item]', 'value', 'get [item] and [item]')
|
|
32
|
+
assert.equal(result, 'get value and value')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('replaceAll - should handle forward slashes', () => {
|
|
36
|
+
const result = replaceAll('/', '-', 'path/to/file')
|
|
37
|
+
assert.equal(result, 'path-to-file')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('replaceAll - should handle backslashes', () => {
|
|
41
|
+
const result = replaceAll('\\', '/', 'path\\to\\file')
|
|
42
|
+
assert.equal(result, 'path/to/file')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('replaceAll - should handle curly braces', () => {
|
|
46
|
+
const result = replaceAll('{var}', 'value', 'get {var} and {var}')
|
|
47
|
+
assert.equal(result, 'get value and value')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('replaceAll - should handle asterisks', () => {
|
|
51
|
+
const result = replaceAll('*', 'star', '* is *')
|
|
52
|
+
assert.equal(result, 'star is star')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('replaceAll - should handle plus signs', () => {
|
|
56
|
+
const result = replaceAll('+', 'plus', 'a+b+c')
|
|
57
|
+
console.log('result', result)
|
|
58
|
+
assert.equal(result, 'aplusbplusc')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('replaceAll - should handle question marks', () => {
|
|
62
|
+
const result = replaceAll('?', 'Q', 'what? why?')
|
|
63
|
+
assert.equal(result, 'whatQ whyQ')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('replaceAll - should handle empty replacement', () => {
|
|
67
|
+
const result = replaceAll('foo', '', 'foo bar foo')
|
|
68
|
+
assert.equal(result, ' bar ')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('replaceAll - should handle no matches', () => {
|
|
72
|
+
const result = replaceAll('xyz', 'abc', 'hello world')
|
|
73
|
+
assert.equal(result, 'hello world')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('replaceAll - should handle complex regex special characters', () => {
|
|
77
|
+
const result = replaceAll('${var}', 'value', 'get ${var} and ${var}')
|
|
78
|
+
assert.equal(result, 'get value and value')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Run all tests
|
|
82
|
+
test.run()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const { splitByComma } = require('./splitByComma')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Split a string by comma while preserving quoted content
|
|
5
|
+
* NOTE: This is a simpler version that delegates to splitByComma for consistency.
|
|
6
|
+
* For advanced use cases with bracket depth tracking and regex protection, use splitByComma directly.
|
|
7
|
+
* @param {string} str - String to split
|
|
8
|
+
* @param {string} splitter - Optional custom splitter (defaults to ',')
|
|
9
|
+
* @returns {string[]} Array of split strings
|
|
10
|
+
*/
|
|
11
|
+
function splitCsv(str, splitter) {
|
|
12
|
+
// If custom splitter is provided, fall back to original simple implementation
|
|
13
|
+
if (splitter && splitter !== ',') {
|
|
14
|
+
const splitSyntax = splitter
|
|
15
|
+
return str.split(splitSyntax).reduce(
|
|
16
|
+
(acc, curr) => {
|
|
17
|
+
if (acc.isConcatting) {
|
|
18
|
+
acc.soFar[acc.soFar.length - 1] += splitter + curr
|
|
19
|
+
} else {
|
|
20
|
+
acc.soFar.push(curr)
|
|
21
|
+
}
|
|
22
|
+
if (curr.split('"').length % 2 == 0) {
|
|
23
|
+
acc.isConcatting = !acc.isConcatting
|
|
24
|
+
}
|
|
25
|
+
return acc
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
soFar: [],
|
|
29
|
+
isConcatting: false,
|
|
30
|
+
},
|
|
31
|
+
).soFar
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// For standard comma splitting, use the more robust splitByComma
|
|
35
|
+
return splitByComma(str)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { splitCsv }
|