@@ -1489,15 +1489,23 @@ exports.Obj = class Obj extends Base
1489
1489
return yes for prop in @properties when prop instanceof Splat
1490
1490
no
1491
1491
1492
+ # Move rest property to the end of the list.
1493
+ # `{a, rest..., b} = obj` -> `{a, b, rest...} = obj`
1494
+ # `foo = ({a, rest..., b}) ->` -> `foo = {a, b, rest...}) ->`
1495
+ reorderProperties : ->
1496
+ props = @properties
1497
+ splatProps = (i for prop, i in props when prop instanceof Splat)
1498
+ props[splatProps[1 ]].error " multiple spread elements are disallowed" if splatProps ? .length > 1
1499
+ splatProp = props .splice splatProps[0 ], 1
1500
+ @objects = @properties = [].concat props, splatProp
1501
+
1492
1502
compileNode : (o ) ->
1503
+ @ reorderProperties () if @ hasSplat () and @lhs
1493
1504
props = @properties
1494
1505
if @generated
1495
1506
for node in props when node instanceof Value
1496
1507
node .error ' cannot have an implicit value in an implicit object'
1497
1508
1498
- # Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
1499
- return @ compileSpread o if @ hasSplat () and not @csx
1500
-
1501
1509
idt = o .indent += TAB
1502
1510
lastNode = @ lastNode @properties
1503
1511
@@ -1558,7 +1566,7 @@ exports.Obj = class Obj extends Base
1558
1566
else
1559
1567
# `{ [expression] }` output as `{ [expression]: expression }`.
1560
1568
prop = new Assign key, prop .base .value , ' object'
1561
- else if not prop .bareLiteral ? (IdentifierLiteral)
1569
+ else if not prop .bareLiteral ? (IdentifierLiteral) and prop not instanceof Splat
1562
1570
prop = new Assign prop, prop, ' object'
1563
1571
if indent then answer .push @ makeCode indent
1564
1572
answer .push prop .compileToFragments (o, LEVEL_TOP)...
@@ -1577,30 +1585,6 @@ exports.Obj = class Obj extends Base
1577
1585
prop = prop .unwrapAll ()
1578
1586
prop .eachName iterator if prop .eachName ?
1579
1587
1580
- # Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
1581
- # `obj2 = {a: 1, obj..., c: 3, d: 4}` → `obj2 = _extends({}, {a: 1}, obj, {c: 3, d: 4})`
1582
- compileSpread : (o ) ->
1583
- props = @properties
1584
- # Store object spreads.
1585
- splatSlice = []
1586
- propSlices = []
1587
- slices = []
1588
- addSlice = ->
1589
- slices .push new Obj propSlices if propSlices .length
1590
- slices .push splatSlice... if splatSlice .length
1591
- splatSlice = []
1592
- propSlices = []
1593
- for prop in props
1594
- if prop instanceof Splat
1595
- splatSlice .push new Value prop .name
1596
- addSlice ()
1597
- else
1598
- propSlices .push prop
1599
- addSlice ()
1600
- slices .unshift new Obj unless slices[0 ] instanceof Obj
1601
- _extends = new Value new Literal utility ' _extends' , o
1602
- (new Call _extends, slices).compileToFragments o
1603
-
1604
1588
compileCSXAttributes : (o ) ->
1605
1589
props = @properties
1606
1590
answer = []
@@ -2213,12 +2197,11 @@ exports.Assign = class Assign extends Base
2213
2197
# know that, so that those nodes know that they’re assignable as
2214
2198
# destructured variables.
2215
2199
@variable .base .lhs = yes
2216
- # Check if @variable contains Obj with splats.
2217
- hasSplat = @variable .contains (node) -> node instanceof Obj and node .hasSplat ()
2218
- return @ compileDestructuring o if not @variable .isAssignable () or @variable .isArray () and hasSplat
2219
- # Object destructuring. Can be removed once ES proposal hits Stage 4.
2220
- objDestructAnswer = @ compileObjectDestruct (o) if @variable .isObject () and hasSplat
2221
- return objDestructAnswer if objDestructAnswer
2200
+ unless @variable .isAssignable ()
2201
+ if @variable .isObject () and @variable .base .hasSplat ()
2202
+ return @ compileObjectDestruct o
2203
+ else
2204
+ return @ compileDestructuring o
2222
2205
2223
2206
return @ compileSplice o if @variable .isSplice ()
2224
2207
return @ compileConditional o if @context in [' ||=' , ' &&=' , ' ?=' ]
@@ -2289,92 +2272,18 @@ exports.Assign = class Assign extends Base
2289
2272
else
2290
2273
answer
2291
2274
2292
- # Check object destructuring variable for rest elements;
2293
- # can be removed once ES proposal hits Stage 4.
2275
+ # Object rest property is not assignable: `{{a}...}`
2294
2276
compileObjectDestruct : (o ) ->
2295
- # Returns a safe (cached) reference to the key for a given property
2296
- getPropKey = (prop ) ->
2297
- if prop instanceof Assign
2298
- [prop .variable , key ] = prop .variable .cache o
2299
- key
2300
- else
2301
- prop
2302
-
2303
- # Returns the name of a given property for use with excludeProps
2304
- # Property names are quoted (e.g. `a: b` -> 'a'), and everything else uses the key reference
2305
- # (e.g. `'a': b -> 'a'`, `"#{a}": b` -> <cached>`)
2306
- getPropName = (prop ) ->
2307
- key = getPropKey prop
2308
- cached = prop instanceof Assign and prop .variable isnt key
2309
- if cached or not key .isAssignable ()
2310
- key
2311
- else
2312
- new Literal " '#{ key .compileWithoutComments o} '"
2313
-
2314
- # Recursive function for searching and storing rest elements in objects.
2315
- # e.g. `{[properties...]} = source`.
2316
- traverseRest = (properties , source ) =>
2317
- restElements = []
2318
- restIndex = undefined
2319
- source = new Value source unless source .properties ?
2320
-
2321
- for prop, index in properties
2322
- nestedSourceDefault = nestedSource = nestedProperties = null
2323
- if prop instanceof Assign
2324
- # prop is `k: expr`, we need to check `expr` for nested splats
2325
- if prop .value .isObject ? ()
2326
- # prop is `k = {...} `
2327
- continue unless prop .context is ' object'
2328
- # prop is `k: {...}`
2329
- nestedProperties = prop .value .base .properties
2330
- else if prop .value instanceof Assign and prop .value .variable .isObject ()
2331
- # prop is `k: {...} = default`
2332
- nestedProperties = prop .value .variable .base .properties
2333
- [prop .value .value , nestedSourceDefault ] = prop .value .value .cache o
2334
- if nestedProperties
2335
- nestedSource = new Value source .base , source .properties .concat [new Access getPropKey prop]
2336
- nestedSource = new Value new Op ' ?' , nestedSource, nestedSourceDefault if nestedSourceDefault
2337
- restElements .push traverseRest (nestedProperties, nestedSource)...
2338
- else if prop instanceof Splat
2339
- prop .error " multiple rest elements are disallowed in object destructuring" if restIndex?
2340
- restIndex = index
2341
- restElements .push {
2342
- name : prop .name .unwrapAll ()
2343
- source
2344
- excludeProps : new Arr (getPropName p for p in properties when p isnt prop)
2345
- }
2346
-
2347
- if restIndex?
2348
- # Remove rest element from the properties after iteration
2349
- properties .splice restIndex, 1
2350
-
2351
- restElements
2352
-
2353
- # Cache the value for reuse with rest elements.
2354
- valueRefTemp =
2355
- if @value .shouldCache ()
2356
- new IdentifierLiteral o .scope .freeVariable ' ref' , reserve : false
2357
- else
2358
- @value .base
2359
-
2360
- # Find all rest elements.
2361
- restElements = traverseRest @variable .base .properties , valueRefTemp
2362
- return no unless restElements and restElements .length > 0
2363
-
2364
- [@value , valueRef ] = @value .cache o
2365
- result = new Block [@ ]
2366
-
2367
- for restElement in restElements
2368
- value = new Call new Value (new Literal utility ' objectWithoutKeys' , o), [restElement .source , restElement .excludeProps ]
2369
- result .push new Assign new Value (restElement .name ), value, null , param : if @param then ' alwaysDeclare' else null
2370
-
2371
- fragments = result .compileToFragments o
2372
- if o .level is LEVEL_TOP
2373
- # Remove leading tab and trailing semicolon
2374
- fragments .shift ()
2375
- fragments .pop ()
2376
-
2377
- fragments
2277
+ @variable .base .reorderProperties ()
2278
+ {properties : props } = @variable .base
2279
+ [... , splat ] = props
2280
+ splatProp = splat .name
2281
+ assigns = []
2282
+ refVal = new Value new IdentifierLiteral o .scope .freeVariable ' ref'
2283
+ props .splice - 1 , 1 , new Splat refVal
2284
+ assigns .push new Assign (new Value (new Obj props), @value ).compileToFragments o, LEVEL_LIST
2285
+ assigns .push new Assign (new Value (splatProp), refVal).compileToFragments o, LEVEL_LIST
2286
+ @ joinFragmentArrays assigns, ' , '
2378
2287
2379
2288
# Brief implementation of recursive pattern matching, when assigning array or
2380
2289
# object literals to a value. Peeks at their properties to assign inner names.
@@ -2409,12 +2318,19 @@ exports.Assign = class Assign extends Base
2409
2318
2410
2319
isSplat = splats ? .length > 0
2411
2320
isExpans = expans ? .length > 0
2412
- isObject = @variable .isObject ()
2413
- isArray = @variable .isArray ()
2414
2321
2415
2322
vvar = value .compileToFragments o, LEVEL_LIST
2416
2323
vvarText = fragmentsToText vvar
2417
2324
assigns = []
2325
+ pushAssign = (variable , val ) =>
2326
+ assigns .push new Assign (variable, val, null , param : @param , subpattern : yes ).compileToFragments o, LEVEL_LIST
2327
+
2328
+ if isSplat
2329
+ splatVar = objects[splats[0 ]].name .unwrap ()
2330
+ if splatVar instanceof Arr or splatVar instanceof Obj
2331
+ splatVarRef = new IdentifierLiteral o .scope .freeVariable ' ref'
2332
+ objects[splats[0 ]].name = splatVarRef
2333
+ splatVarAssign = -> pushAssign new Value (splatVar), splatVarRef
2418
2334
2419
2335
# At this point, there are several things to destructure. So the `fn()` in
2420
2336
# `{a, b} = fn()` must be cached, for example. Make vvar into a simple
@@ -2438,10 +2354,6 @@ exports.Assign = class Assign extends Base
2438
2354
# Helper which outputs `[].splice` code.
2439
2355
compSplice = slicer " splice"
2440
2356
2441
- # Check if `objects` array contains object spread (`{a, r...}`), e.g. `[a, b, {c, r...}]`.
2442
- hasObjSpreads = (objs ) ->
2443
- (i for obj, i in objs when obj .base instanceof Obj and obj .base .hasSplat ())
2444
-
2445
2357
# Check if `objects` array contains any instance of `Assign`, e.g. {a:1}.
2446
2358
hasObjAssigns = (objs ) ->
2447
2359
(i for obj, i in objs when obj instanceof Assign and obj .context is ' object' )
@@ -2451,15 +2363,14 @@ exports.Assign = class Assign extends Base
2451
2363
return yes for obj in objs when not obj .isAssignable ()
2452
2364
no
2453
2365
2454
- # `objects` are complex when there is object spread ({a...}), object assign ({a:1}),
2366
+ # `objects` are complex when there is object assign ({a:1}),
2455
2367
# unassignable object, or just a single node.
2456
2368
complexObjects = (objs ) ->
2457
- hasObjSpreads (objs). length or hasObjAssigns (objs).length or objIsUnassignable (objs) or olen is 1
2369
+ hasObjAssigns (objs).length or objIsUnassignable (objs) or olen is 1
2458
2370
2459
2371
# "Complex" `objects` are processed in a loop.
2460
2372
# Examples: [a, b, {c, r...}, d], [a, ..., {b, r...}, c, d]
2461
2373
loopObjects = (objs , vvar , vvarTxt ) =>
2462
- objSpreads = hasObjSpreads objs
2463
2374
for obj, i in objs
2464
2375
# `Elision` can be skipped.
2465
2376
continue if obj instanceof Elision
@@ -2478,20 +2389,19 @@ exports.Assign = class Assign extends Base
2478
2389
# `obj` is [a...], {a...} or a
2479
2390
vvar = switch
2480
2391
when obj instanceof Splat then new Value obj .name
2481
- when i in objSpreads then new Value obj .base
2482
2392
else obj
2483
2393
vval = switch
2484
2394
when obj instanceof Splat then compSlice (vvarTxt, i)
2485
2395
else new Value new Literal (vvarTxt), [new Index new NumberLiteral i]
2486
2396
message = isUnassignable vvar .unwrap ().value
2487
2397
vvar .error message if message
2488
- assigns . push new Assign ( vvar, vval, null , param : @param , subpattern : yes ). compileToFragments o, LEVEL_LIST
2398
+ pushAssign vvar, vval
2489
2399
2490
2400
# "Simple" `objects` can be split and compiled to arrays, [a, b, c] = arr, [a, b, c...] = arr
2491
2401
assignObjects = (objs , vvar , vvarTxt ) =>
2492
2402
vvar = new Value new Arr (objs, yes )
2493
2403
vval = if vvarTxt instanceof Value then vvarTxt else new Value new Literal (vvarTxt)
2494
- assigns . push new Assign ( vvar, vval, null , param : @param , subpattern : yes ). compileToFragments o, LEVEL_LIST
2404
+ pushAssign vvar, vval
2495
2405
2496
2406
processObjects = (objs , vvar , vvarTxt ) ->
2497
2407
if complexObjects objs
@@ -2528,6 +2438,7 @@ exports.Assign = class Assign extends Base
2528
2438
else
2529
2439
# There is no `Splat` or `Expansion` in `objects`.
2530
2440
processObjects objects, vvar, vvarText
2441
+ splatVarAssign? ()
2531
2442
assigns .push vvar unless top or @subpattern
2532
2443
fragments = @ joinFragmentArrays assigns, ' , '
2533
2444
if o .level < LEVEL_LIST then fragments else @ wrapInParentheses fragments
@@ -2689,7 +2600,7 @@ exports.Code = class Code extends Base
2689
2600
param .error ' an expansion parameter cannot be the only parameter in a function definition'
2690
2601
haveSplatParam = yes
2691
2602
if param .splat
2692
- if param .name instanceof Arr
2603
+ if param .name instanceof Arr or param . name instanceof Obj
2693
2604
# Splat arrays are treated oddly by ES; deal with them the legacy
2694
2605
# way in the function body. TODO: Should this be handled in the
2695
2606
# function parameter list, and if so, how?
@@ -2743,17 +2654,7 @@ exports.Code = class Code extends Base
2743
2654
if param .name instanceof Arr or param .name instanceof Obj
2744
2655
# This parameter is destructured.
2745
2656
param .name .lhs = yes
2746
- # Compile `foo({a, b...}) ->` to `foo(arg) -> {a, b...} = arg`.
2747
- # Can be removed once ES proposal hits Stage 4.
2748
- if param .name instanceof Obj and param .name .hasSplat ()
2749
- splatParamName = o .scope .freeVariable ' arg'
2750
- o .scope .parameter splatParamName
2751
- ref = new Value new IdentifierLiteral splatParamName
2752
- exprs .push new Assign new Value (param .name ), ref, null , param : ' alwaysDeclare'
2753
- # Compile `foo({a, b...} = {}) ->` to `foo(arg = {}) -> {a, b...} = arg`.
2754
- if param .value ? and not param .assignedInBody
2755
- ref = new Assign ref, param .value , null , param : yes
2756
- else unless param .shouldCache ()
2657
+ unless param .shouldCache ()
2757
2658
param .name .eachName (prop) ->
2758
2659
o .scope .parameter prop .value
2759
2660
else
@@ -3028,7 +2929,10 @@ exports.Splat = class Splat extends Base
3028
2929
3029
2930
children : [' name' ]
3030
2931
2932
+ shouldCache : -> no
2933
+
3031
2934
isAssignable : ->
2935
+ return no if @name instanceof Obj or @name instanceof Parens
3032
2936
@name .isAssignable () and (not @name .isAtomic or @name .isAtomic ())
3033
2937
3034
2938
assigns : (name ) ->
@@ -3883,33 +3787,14 @@ exports.If = class If extends Base
3883
3787
3884
3788
UTILITIES =
3885
3789
modulo : -> ' function(a, b) { return (+a % (b = +b) + b) % b; }'
3886
- objectWithoutKeys : -> "
3887
- function(o, ks) {
3888
- var res = {};
3889
- for (var k in o) ([].indexOf.call(ks, k) < 0 && {}.hasOwnProperty.call(o, k)) && (res[k] = o[k]);
3890
- return res;
3891
- }
3892
- "
3790
+
3893
3791
boundMethodCheck : -> "
3894
3792
function(instance, Constructor) {
3895
3793
if (!(instance instanceof Constructor)) {
3896
3794
throw new Error('Bound instance method accessed before binding');
3897
3795
}
3898
3796
}
3899
3797
"
3900
- _extends : -> "
3901
- Object.assign || function (target) {
3902
- for (var i = 1; i < arguments.length; i++) {
3903
- var source = arguments[i];
3904
- for (var key in source) {
3905
- if (Object.prototype.hasOwnProperty.call(source, key)) {
3906
- target[key] = source[key];
3907
- }
3908
- }
3909
- }
3910
- return target;
3911
- }
3912
- "
3913
3798
3914
3799
# Shortcuts to speed up the lookup time for native functions.
3915
3800
hasProp : -> ' {}.hasOwnProperty'
0 commit comments