Skip to content

Commit 96527b0

Browse files
committed
feature: CommentArray
1 parent 41766cb commit 96527b0

File tree

6 files changed

+296
-11
lines changed

6 files changed

+296
-11
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"dependencies": {
5858
"core-util-is": "^1.0.2",
5959
"esprima": "^4.0.1",
60+
"has-own-prop": "^2.0.0",
6061
"repeat-string": "^1.6.1"
6162
}
6263
}

src/array.js

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
const hasOwnProperty = require('has-own-prop')
2+
3+
const PREFIX_BEFORE = 'before'
4+
const PREFIX_AFTER_PROP = 'after-prop'
5+
const PREFIX_AFTER_COLON = 'after-colon'
6+
const PREFIX_AFTER_VALUE = 'after-value'
7+
8+
const SYMBOL_PREFIXES = [
9+
PREFIX_BEFORE,
10+
PREFIX_AFTER_PROP,
11+
PREFIX_AFTER_COLON,
12+
PREFIX_AFTER_VALUE
13+
]
14+
15+
const COLON = ':'
16+
const UNDEFINED = undefined
17+
18+
const symbol = (prefix, key) => Symbol.for(prefix + COLON + key)
19+
20+
const assign_comments = (
21+
target, source, target_key, source_key, prefix, remove_source
22+
) => {
23+
const source_prop = symbol(prefix, source_key)
24+
if (!hasOwnProperty(source, source_prop)) {
25+
return
26+
}
27+
28+
const target_prop = target_key === source_key
29+
? source_prop
30+
: symbol(prefix, target_key)
31+
32+
target[target_prop] = source[source_prop]
33+
34+
if (remove_source) {
35+
delete source[source_prop]
36+
}
37+
}
38+
39+
// Assign keys and comments
40+
const assign = (target, source, keys) => {
41+
keys.forEach(key => {
42+
if (!hasOwnProperty(source, key)) {
43+
return
44+
}
45+
46+
target[key] = source[key]
47+
SYMBOL_PREFIXES.forEach(prefix => {
48+
assign_comments(target, source, key, key, prefix)
49+
})
50+
})
51+
52+
return target
53+
}
54+
55+
const swap_comments = (array, from, to) => {
56+
if (from === to) {
57+
return
58+
}
59+
60+
SYMBOL_PREFIXES.forEach(prefix => {
61+
const target_prop = symbol(prefix, to)
62+
if (!hasOwnProperty(array, target_prop)) {
63+
assign_comments(array, array, from, to, prefix)
64+
return
65+
}
66+
67+
const comments = array[target_prop]
68+
assign_comments(array, array, from, to, prefix)
69+
array[symbol(prefix, from)] = comments
70+
})
71+
}
72+
73+
const reverse_comments = array => {
74+
const {length} = array
75+
let i = 0
76+
const max = length / 2
77+
78+
for (; i < max; i ++) {
79+
swap_comments(array, i, length - i - 1)
80+
}
81+
}
82+
83+
const move_comment = (target, source, i, offset, remove) => {
84+
SYMBOL_PREFIXES.forEach(prefix => {
85+
assign_comments(target, source, i + offset, i, prefix, remove)
86+
})
87+
}
88+
89+
const move_comments = (
90+
// `Array` target array
91+
target,
92+
// `Array` source array
93+
source,
94+
// `number` start index
95+
start,
96+
// `number` number of indexes to move
97+
count,
98+
// `number` offset to move
99+
offset,
100+
// `boolean` whether should remove the comments from source
101+
remove
102+
) => {
103+
if (offset > 0) {
104+
let i = count
105+
// | count | offset |
106+
// source: -------------
107+
// target: -------------
108+
// | remove |
109+
// => remove === offset
110+
111+
// From [count - 1, 0]
112+
while (i -- > 0) {
113+
move_comment(target, source, start + i, offset, remove && i < offset)
114+
}
115+
return
116+
}
117+
118+
let i = 0
119+
const min_remove = count + offset
120+
// | remove | count |
121+
// -------------
122+
// -------------
123+
// | offset |
124+
125+
// From [0, count - 1]
126+
while (i < count) {
127+
const ii = i ++
128+
move_comment(target, source, start + ii, offset, remove && i >= min_remove)
129+
}
130+
}
131+
132+
class CommentArray extends Array {
133+
// - deleteCount + items.length
134+
splice (begin, deleteCount, ...items) {
135+
const {length} = this
136+
137+
const ret = super.splice(begin, deleteCount, ...items)
138+
139+
// > If deleteCount is 0 or negative, no elements are removed.
140+
// > In this case, you should specify at least one new element (see below).
141+
if (deleteCount <= 0) {
142+
return ret
143+
}
144+
145+
if (deleteCount === UNDEFINED) {
146+
deleteCount = length - begin
147+
} else {
148+
deleteCount = Math.min(length - begin, deleteCount)
149+
}
150+
151+
const {
152+
length: item_length
153+
} = items
154+
155+
// itemsToDelete: -
156+
// itemsToAdd: +
157+
// | dc | count |
158+
// =======-------------============
159+
// =======++++++============
160+
// | il |
161+
const offset = item_length - deleteCount
162+
const start = begin + deleteCount
163+
const count = length - start
164+
165+
move_comments(this, this, start, count, offset, true)
166+
167+
return ret
168+
}
169+
170+
slice (begin, before) {
171+
const array = new CommentArray(...super.slice(begin, before))
172+
if (begin < 0 && before === UNDEFINED) {
173+
return new CommentArray()
174+
}
175+
176+
if (before === UNDEFINED) {
177+
before = this.length
178+
179+
if (begin < 0) {
180+
begin += before
181+
}
182+
}
183+
184+
move_comments(array, this, begin, before - begin, begin)
185+
return array
186+
}
187+
188+
unshift (...items) {
189+
const ret = super.unshift(...items)
190+
const {length} = items
191+
192+
if (length > 0) {
193+
move_comments(this, this, length, this.length, length, true)
194+
}
195+
196+
return ret
197+
}
198+
199+
shift () {
200+
const ret = super.shift()
201+
const {length} = this
202+
203+
move_comments(this, this, 1, length - 1, - 1, true)
204+
205+
return ret
206+
}
207+
208+
reverse () {
209+
super.reverse()
210+
211+
reverse_comments(this)
212+
213+
return this
214+
}
215+
216+
pop () {
217+
const ret = super.pop()
218+
219+
// Removes comments
220+
const {length} = this
221+
SYMBOL_PREFIXES.forEach(prefix => {
222+
const prop = symbol(prefix, length)
223+
delete this[prop]
224+
})
225+
226+
return ret
227+
}
228+
}
229+
230+
module.exports = {
231+
CommentArray,
232+
assign,
233+
234+
PREFIX_BEFORE,
235+
PREFIX_AFTER_PROP,
236+
PREFIX_AFTER_COLON,
237+
PREFIX_AFTER_VALUE,
238+
239+
COLON,
240+
UNDEFINED
241+
}

src/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
const {parse, tokenize} = require('./parse')
22
const stringify = require('./stringify')
3+
const {CommentArray} = require('./array')
34

45
module.exports = {
56
parse,
67
stringify,
7-
tokenize
8+
tokenize,
9+
10+
CommentArray
811
}

src/parse.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@
22

33
const esprima = require('esprima')
44

5+
const {
6+
CommentArray,
7+
8+
PREFIX_BEFORE,
9+
PREFIX_AFTER_PROP,
10+
PREFIX_AFTER_COLON,
11+
PREFIX_AFTER_VALUE,
12+
COLON,
13+
UNDEFINED
14+
} = require('./array')
15+
516
const tokenize = code => esprima.tokenize(code, {
617
comment: true,
718
loc: true
819
})
920

10-
const UNDEFINED = undefined
11-
1221
const previous_hosts = []
1322
let comments_host = null
1423
let unassigned_comments = null
@@ -46,18 +55,13 @@ const free = () => {
4655
}
4756

4857
const PREFIX_BEFORE_ALL = 'before-all'
49-
const PREFIX_BEFORE = 'before'
50-
const PREFIX_AFTER_PROP = 'after-prop'
51-
const PREFIX_AFTER_COLON = 'after-colon'
52-
const PREFIX_AFTER_VALUE = 'after-value'
5358
const PREFIX_AFTER = 'after'
5459
const PREFIX_AFTER_ALL = 'after-all'
5560

5661
const BRACKET_OPEN = '['
5762
const BRACKET_CLOSE = ']'
5863
const CURLY_BRACKET_OPEN = '{'
5964
const CURLY_BRACKET_CLOSE = '}'
60-
const COLON = ':'
6165
const COMMA = ','
6266
const EMPTY = ''
6367
const MINUS = '-'
@@ -243,7 +247,7 @@ const parse_object = () => {
243247
}
244248

245249
const parse_array = () => {
246-
const array = []
250+
const array = new CommentArray()
247251
set_comments_host(array)
248252
set_prop(UNDEFINED, true)
249253

test/array.test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const test = require('ava')
2+
const {isFunction} = require('core-util-is')
3+
const {parse, stringify} = require('../src')
4+
5+
const a1 = `[
6+
// 0
7+
0,
8+
// 1
9+
1,
10+
// 2
11+
2
12+
]`
13+
14+
const CASES = [
15+
[
16+
'splice',
17+
a1,
18+
array => array.splice(0, 1),
19+
(t, ret, str) => {
20+
t.deepEqual(ret, [0])
21+
t.is(str, `[
22+
// 1
23+
1,
24+
// 2
25+
2
26+
]`)
27+
}
28+
]
29+
]
30+
31+
CASES.forEach(([d, a, run, expect]) => {
32+
test(d, t => {
33+
const parsed = parse(a)
34+
const ret = run(parsed)
35+
36+
expect(t, [...ret], stringify(parsed, null, 2), parsed, ret)
37+
})
38+
})

test/parse.test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
const test = require('ava')
42

53
const parser = require('../src')

0 commit comments

Comments
 (0)