@invisibleloop/pulse 0.2.2 → 0.2.3
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/package.json +1 -1
- package/public/.pulse-ui-version +1 -1
- package/scripts/build.js +37 -0
- package/scripts/strip-server.test.js +40 -0
package/package.json
CHANGED
package/public/.pulse-ui-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.3
|
package/scripts/build.js
CHANGED
|
@@ -51,11 +51,48 @@ function stripServerOnlyKeys(source) {
|
|
|
51
51
|
* from a JS source string. Uses a character-level scanner to correctly handle
|
|
52
52
|
* nested structures, string literals, template literals, and function expressions.
|
|
53
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Returns true if `pos` in `source` falls inside a string or template literal.
|
|
56
|
+
* Used to avoid stripping keys that appear inside code-example strings in docs pages.
|
|
57
|
+
*/
|
|
58
|
+
function isInsideString(source, pos) {
|
|
59
|
+
let i = 0
|
|
60
|
+
const stack = [] // stack of open delimiters: '"' | "'" | '`' | '{' ('{' = template expression)
|
|
61
|
+
while (i < pos) {
|
|
62
|
+
const c = source[i]
|
|
63
|
+
const top = stack[stack.length - 1]
|
|
64
|
+
if (top === '"' || top === "'") {
|
|
65
|
+
if (c === '\\') { i += 2; continue }
|
|
66
|
+
if (c === top) { stack.pop() }
|
|
67
|
+
i++; continue
|
|
68
|
+
}
|
|
69
|
+
if (top === '`') {
|
|
70
|
+
if (c === '\\') { i += 2; continue }
|
|
71
|
+
if (c === '`') { stack.pop(); i++; continue }
|
|
72
|
+
if (c === '$' && source[i + 1] === '{') { stack.push('{'); i += 2; continue }
|
|
73
|
+
i++; continue
|
|
74
|
+
}
|
|
75
|
+
if (top === '{') {
|
|
76
|
+
// inside a template expression — track nested braces
|
|
77
|
+
if (c === '{') { stack.push('{'); i++; continue }
|
|
78
|
+
if (c === '}') { stack.pop(); i++; continue }
|
|
79
|
+
if (c === '"' || c === "'" || c === '`') { stack.push(c); i++; continue }
|
|
80
|
+
i++; continue
|
|
81
|
+
}
|
|
82
|
+
// top-level
|
|
83
|
+
if (c === '"' || c === "'" || c === '`') { stack.push(c) }
|
|
84
|
+
i++
|
|
85
|
+
}
|
|
86
|
+
return stack.length > 0
|
|
87
|
+
}
|
|
88
|
+
|
|
54
89
|
function removeObjectKey(source, key) {
|
|
55
90
|
const keyRe = new RegExp(`^([ \\t]*)(${key})([ \\t]*:)`, 'gm')
|
|
56
91
|
let match
|
|
57
92
|
|
|
58
93
|
while ((match = keyRe.exec(source)) !== null) {
|
|
94
|
+
if (isInsideString(source, match.index)) { continue }
|
|
95
|
+
|
|
59
96
|
const removeStart = match.index // start of indentation
|
|
60
97
|
const afterColon = match.index + match[0].length // character after ':'
|
|
61
98
|
|
|
@@ -14,10 +14,40 @@ function stripServerOnlyKeys(source) {
|
|
|
14
14
|
return source
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function isInsideString(source, pos) {
|
|
18
|
+
let i = 0
|
|
19
|
+
const stack = []
|
|
20
|
+
while (i < pos) {
|
|
21
|
+
const c = source[i]
|
|
22
|
+
const top = stack[stack.length - 1]
|
|
23
|
+
if (top === '"' || top === "'") {
|
|
24
|
+
if (c === '\\') { i += 2; continue }
|
|
25
|
+
if (c === top) { stack.pop() }
|
|
26
|
+
i++; continue
|
|
27
|
+
}
|
|
28
|
+
if (top === '`') {
|
|
29
|
+
if (c === '\\') { i += 2; continue }
|
|
30
|
+
if (c === '`') { stack.pop(); i++; continue }
|
|
31
|
+
if (c === '$' && source[i + 1] === '{') { stack.push('{'); i += 2; continue }
|
|
32
|
+
i++; continue
|
|
33
|
+
}
|
|
34
|
+
if (top === '{') {
|
|
35
|
+
if (c === '{') { stack.push('{'); i++; continue }
|
|
36
|
+
if (c === '}') { stack.pop(); i++; continue }
|
|
37
|
+
if (c === '"' || c === "'" || c === '`') { stack.push(c); i++; continue }
|
|
38
|
+
i++; continue
|
|
39
|
+
}
|
|
40
|
+
if (c === '"' || c === "'" || c === '`') { stack.push(c) }
|
|
41
|
+
i++
|
|
42
|
+
}
|
|
43
|
+
return stack.length > 0
|
|
44
|
+
}
|
|
45
|
+
|
|
17
46
|
function removeObjectKey(source, key) {
|
|
18
47
|
const keyRe = new RegExp(`^([ \\t]*)(${key})([ \\t]*:)`, 'gm')
|
|
19
48
|
let match
|
|
20
49
|
while ((match = keyRe.exec(source)) !== null) {
|
|
50
|
+
if (isInsideString(source, match.index)) { continue }
|
|
21
51
|
const removeStart = match.index
|
|
22
52
|
const afterColon = match.index + match[0].length
|
|
23
53
|
let pos = afterColon
|
|
@@ -181,6 +211,16 @@ test('server with deeply nested template literal in view is not affected',
|
|
|
181
211
|
'export default {\n view: (s, srv) => `<ul>${srv.items.map(i => `<li>${i}</li>`).join(\'\')}</ul>`\n}'
|
|
182
212
|
)
|
|
183
213
|
|
|
214
|
+
test('render: inside a template literal string (docs code example) is not stripped',
|
|
215
|
+
'export default {\n route: \'/auth\',\n view: () => `${highlight(`export default {\n render: (ctx) => ctx.user\n}`)}`\n}',
|
|
216
|
+
'export default {\n route: \'/auth\',\n view: () => `${highlight(`export default {\n render: (ctx) => ctx.user\n}`)}`\n}'
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
test('server: inside a double-quoted string is not stripped',
|
|
220
|
+
'export default {\n route: \'/docs\',\n view: () => "<pre>server: { data: async () => {} }</pre>"\n}',
|
|
221
|
+
'export default {\n route: \'/docs\',\n view: () => "<pre>server: { data: async () => {} }</pre>"\n}'
|
|
222
|
+
)
|
|
223
|
+
|
|
184
224
|
// ---------------------------------------------------------------------------
|
|
185
225
|
|
|
186
226
|
console.log(`\n${pass + fail} tests: ${pass} passed, ${fail} failed\n`)
|