utils.spec.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { TransformContext } from '../src'
  2. import { Position } from '../src/ast'
  3. import {
  4. getInnerRange,
  5. advancePositionWithClone,
  6. isMemberExpressionNode,
  7. isMemberExpressionBrowser,
  8. toValidAssetId
  9. } from '../src/utils'
  10. function p(line: number, column: number, offset: number): Position {
  11. return { column, line, offset }
  12. }
  13. describe('advancePositionWithClone', () => {
  14. test('same line', () => {
  15. const pos = p(1, 1, 0)
  16. const newPos = advancePositionWithClone(pos, 'foo\nbar', 2)
  17. expect(newPos.column).toBe(3)
  18. expect(newPos.line).toBe(1)
  19. expect(newPos.offset).toBe(2)
  20. })
  21. test('same line', () => {
  22. const pos = p(1, 1, 0)
  23. const newPos = advancePositionWithClone(pos, 'foo\nbar', 4)
  24. expect(newPos.column).toBe(1)
  25. expect(newPos.line).toBe(2)
  26. expect(newPos.offset).toBe(4)
  27. })
  28. test('multiple lines', () => {
  29. const pos = p(1, 1, 0)
  30. const newPos = advancePositionWithClone(pos, 'foo\nbar\nbaz', 10)
  31. expect(newPos.column).toBe(3)
  32. expect(newPos.line).toBe(3)
  33. expect(newPos.offset).toBe(10)
  34. })
  35. })
  36. describe('getInnerRange', () => {
  37. const loc1 = {
  38. source: 'foo\nbar\nbaz',
  39. start: p(1, 1, 0),
  40. end: p(3, 3, 11)
  41. }
  42. test('at start', () => {
  43. const loc2 = getInnerRange(loc1, 0, 4)
  44. expect(loc2.start).toEqual(loc1.start)
  45. expect(loc2.end.column).toBe(1)
  46. expect(loc2.end.line).toBe(2)
  47. expect(loc2.end.offset).toBe(4)
  48. })
  49. test('at end', () => {
  50. const loc2 = getInnerRange(loc1, 4)
  51. expect(loc2.start.column).toBe(1)
  52. expect(loc2.start.line).toBe(2)
  53. expect(loc2.start.offset).toBe(4)
  54. expect(loc2.end).toEqual(loc1.end)
  55. })
  56. test('in between', () => {
  57. const loc2 = getInnerRange(loc1, 4, 3)
  58. expect(loc2.start.column).toBe(1)
  59. expect(loc2.start.line).toBe(2)
  60. expect(loc2.start.offset).toBe(4)
  61. expect(loc2.end.column).toBe(4)
  62. expect(loc2.end.line).toBe(2)
  63. expect(loc2.end.offset).toBe(7)
  64. })
  65. })
  66. describe('isMemberExpression', () => {
  67. function commonAssertions(fn: (str: string) => boolean) {
  68. // should work
  69. expect(fn('obj.foo')).toBe(true)
  70. expect(fn('obj[foo]')).toBe(true)
  71. expect(fn('obj[arr[0]]')).toBe(true)
  72. expect(fn('obj[arr[ret.bar]]')).toBe(true)
  73. expect(fn('obj[arr[ret[bar]]]')).toBe(true)
  74. expect(fn('obj[arr[ret[bar]]].baz')).toBe(true)
  75. expect(fn('obj[1 + 1]')).toBe(true)
  76. expect(fn(`obj[x[0]]`)).toBe(true)
  77. expect(fn('obj[1][2]')).toBe(true)
  78. expect(fn('obj[1][2].foo[3].bar.baz')).toBe(true)
  79. expect(fn(`a[b[c.d]][0]`)).toBe(true)
  80. expect(fn('obj?.foo')).toBe(true)
  81. expect(fn('foo().test')).toBe(true)
  82. // strings
  83. expect(fn(`a['foo' + bar[baz]["qux"]]`)).toBe(true)
  84. // multiline whitespaces
  85. expect(fn('obj \n .foo \n [bar \n + baz]')).toBe(true)
  86. expect(fn(`\n model\n.\nfoo \n`)).toBe(true)
  87. // should fail
  88. expect(fn('a \n b')).toBe(false)
  89. expect(fn('obj[foo')).toBe(false)
  90. expect(fn('objfoo]')).toBe(false)
  91. expect(fn('obj[arr[0]')).toBe(false)
  92. expect(fn('obj[arr0]]')).toBe(false)
  93. expect(fn('123[a]')).toBe(false)
  94. expect(fn('a + b')).toBe(false)
  95. expect(fn('foo()')).toBe(false)
  96. expect(fn('a?b:c')).toBe(false)
  97. expect(fn(`state['text'] = $event`)).toBe(false)
  98. }
  99. test('browser', () => {
  100. commonAssertions(isMemberExpressionBrowser)
  101. })
  102. test('node', () => {
  103. const ctx = { expressionPlugins: ['typescript'] } as any as TransformContext
  104. const fn = (str: string) => isMemberExpressionNode(str, ctx)
  105. commonAssertions(fn)
  106. // TS-specific checks
  107. expect(fn('foo as string')).toBe(true)
  108. expect(fn(`foo.bar as string`)).toBe(true)
  109. expect(fn(`foo['bar'] as string`)).toBe(true)
  110. expect(fn(`foo[bar as string]`)).toBe(true)
  111. expect(fn(`foo() as string`)).toBe(false)
  112. expect(fn(`a + b as string`)).toBe(false)
  113. })
  114. })
  115. test('toValidAssetId', () => {
  116. expect(toValidAssetId('foo', 'component')).toBe('_component_foo')
  117. expect(toValidAssetId('p', 'directive')).toBe('_directive_p')
  118. expect(toValidAssetId('div', 'filter')).toBe('_filter_div')
  119. expect(toValidAssetId('foo-bar', 'component')).toBe('_component_foo_bar')
  120. expect(toValidAssetId('test-测试-1', 'component')).toBe(
  121. '_component_test_2797935797_1'
  122. )
  123. })