cssVars.spec.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { compileStyle, parse } from '../src'
  2. import { mockId, compile, assertCode } from './util'
  3. describe('CSS vars injection', () => {
  4. test('generating correct code for nested paths', () => {
  5. const { content } = compile(
  6. `<script>const a = 1</script>\n` +
  7. `<style>div{
  8. color: v-bind(color);
  9. font-size: v-bind('font.size');
  10. }</style>`
  11. )
  12. expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
  13. "${mockId}-color": (_vm.color),
  14. "${mockId}-font_size": (_vm.font.size)
  15. })`)
  16. assertCode(content)
  17. })
  18. test('w/ normal <script> binding analysis', () => {
  19. const { content } = compile(
  20. `<script>
  21. export default {
  22. setup() {
  23. return {
  24. size: ref('100px')
  25. }
  26. }
  27. }
  28. </script>\n` +
  29. `<style>
  30. div {
  31. font-size: v-bind(size);
  32. }
  33. </style>`
  34. )
  35. expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
  36. "${mockId}-size": (_vm.size)
  37. })`)
  38. expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
  39. assertCode(content)
  40. })
  41. test('w/ <script setup> binding analysis', () => {
  42. const { content } = compile(
  43. `<script setup>
  44. import { defineProps, ref } from 'vue'
  45. const color = 'red'
  46. const size = ref('10px')
  47. defineProps({
  48. foo: String
  49. })
  50. </script>\n` +
  51. `<style>
  52. div {
  53. color: v-bind(color);
  54. font-size: v-bind(size);
  55. border: v-bind(foo);
  56. }
  57. </style>`
  58. )
  59. // should handle:
  60. // 1. local const bindings
  61. // 2. local potential ref bindings
  62. // 3. props bindings (analyzed)
  63. expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
  64. "${mockId}-color": (_setup.color),
  65. "${mockId}-size": (_setup.size),
  66. "${mockId}-foo": (_vm.foo)
  67. })`)
  68. expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
  69. assertCode(content)
  70. })
  71. test('should rewrite CSS vars in compileStyle', () => {
  72. const { code } = compileStyle({
  73. source: `.foo {
  74. color: v-bind(color);
  75. font-size: v-bind('font.size');
  76. }`,
  77. filename: 'test.css',
  78. id: 'data-v-test'
  79. })
  80. expect(code).toMatchInlineSnapshot(`
  81. ".foo[data-v-test] {
  82. color: var(--test-color);
  83. font-size: var(--test-font_size);
  84. }"
  85. `)
  86. })
  87. test('prod mode', () => {
  88. const { content } = compile(
  89. `<script>const a = 1</script>\n` +
  90. `<style>div{
  91. color: v-bind(color);
  92. font-size: v-bind('font.size');
  93. }</style>`,
  94. { isProd: true }
  95. )
  96. expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
  97. "4003f1a6": (_vm.color),
  98. "41b6490a": (_vm.font.size)
  99. }))}`)
  100. const { code } = compileStyle({
  101. source: `.foo {
  102. color: v-bind(color);
  103. font-size: v-bind('font.size');
  104. }`,
  105. filename: 'test.css',
  106. id: mockId,
  107. isProd: true
  108. })
  109. expect(code).toMatchInlineSnapshot(`
  110. ".foo[xxxxxxxx] {
  111. color: var(--4003f1a6);
  112. font-size: var(--41b6490a);
  113. }"
  114. `)
  115. })
  116. describe('codegen', () => {
  117. test('<script> w/ no default export', () => {
  118. assertCode(
  119. compile(
  120. `<script>const a = 1</script>\n` +
  121. `<style>div{ color: v-bind(color); }</style>`
  122. ).content
  123. )
  124. })
  125. test('<script> w/ default export', () => {
  126. assertCode(
  127. compile(
  128. `<script>export default { setup() {} }</script>\n` +
  129. `<style>div{ color: v-bind(color); }</style>`
  130. ).content
  131. )
  132. })
  133. test('<script> w/ default export in strings/comments', () => {
  134. assertCode(
  135. compile(
  136. `<script>
  137. // export default {}
  138. export default {}
  139. </script>\n` + `<style>div{ color: v-bind(color); }</style>`
  140. ).content
  141. )
  142. })
  143. test('w/ <script setup>', () => {
  144. assertCode(
  145. compile(
  146. `<script setup>const color = 'red'</script>\n` +
  147. `<style>div{ color: v-bind(color); }</style>`
  148. ).content
  149. )
  150. })
  151. //#4185
  152. test('should ignore comments', () => {
  153. const { content } = compile(
  154. `<script setup>const color = 'red';const width = 100</script>\n` +
  155. `<style>
  156. /* comment **/
  157. div{ /* color: v-bind(color); */ width:20; }
  158. div{ width: v-bind(width); }
  159. /* comment */
  160. </style>`
  161. )
  162. expect(content).not.toMatch(`"${mockId}-color": (_setup.color)`)
  163. expect(content).toMatch(`"${mockId}-width": (_setup.width)`)
  164. assertCode(content)
  165. })
  166. test('w/ <script setup> using the same var multiple times', () => {
  167. const { content } = compile(
  168. `<script setup>
  169. const color = 'red'
  170. </script>\n` +
  171. `<style>
  172. div {
  173. color: v-bind(color);
  174. }
  175. p {
  176. color: v-bind(color);
  177. }
  178. </style>`
  179. )
  180. // color should only be injected once, even if it is twice in style
  181. expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
  182. "${mockId}-color": (_setup.color)
  183. })`)
  184. assertCode(content)
  185. })
  186. test('should work with w/ complex expression', () => {
  187. const { content } = compile(
  188. `<script setup>
  189. let a = 100
  190. let b = 200
  191. let foo = 300
  192. </script>\n` +
  193. `<style>
  194. p{
  195. width: calc(v-bind(foo) - 3px);
  196. height: calc(v-bind('foo') - 3px);
  197. top: calc(v-bind(foo + 'px') - 3px);
  198. }
  199. div {
  200. color: v-bind((a + b) / 2 + 'px' );
  201. }
  202. div {
  203. color: v-bind ((a + b) / 2 + 'px' );
  204. }
  205. p {
  206. color: v-bind(((a + b)) / (2 * a));
  207. }
  208. </style>`
  209. )
  210. expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
  211. "${mockId}-foo": (_setup.foo),
  212. "${mockId}-foo____px_": (_setup.foo + 'px'),
  213. "${mockId}-_a___b____2____px_": ((_setup.a + _setup.b) / 2 + 'px'),
  214. "${mockId}-__a___b______2___a_": (((_setup.a + _setup.b)) / (2 * _setup.a))
  215. })`)
  216. assertCode(content)
  217. })
  218. // #6022
  219. test('should be able to parse incomplete expressions', () => {
  220. const { cssVars } = parse({
  221. source: `<script setup>let xxx = 1</script>
  222. <style scoped>
  223. label {
  224. font-weight: v-bind("count.toString(");
  225. font-weight: v-bind(xxx);
  226. }
  227. </style>`
  228. })
  229. expect(cssVars).toMatchObject([`count.toString(`, `xxx`])
  230. })
  231. })
  232. })