vite-plugin-tsl-operator
Version:
A Vite plugin to let you use `+`, `-`, `*`, `/`, `%` with TSL Node in your Threejs project making the code more consise and easy to write, re-write & read.
1,071 lines (950 loc) • 32.5 kB
JavaScript
// test/tsl-operator-plugin.spec.js
import { describe, it, expect } from 'vitest'
import TSLOperatorPlugin from '../src/index.js'
// Helper to run the plugin in a shorter form
const run = (code, filename = 'test.js') => {
const result = TSLOperatorPlugin({logs:true}).transform(code, filename)
return result ? result.code : code
}
describe('Basic Arithmetic Operators', () => {
describe('Operator +', () => {
// 1 SUCCESS
it('1. transforms left + right => left.add(right)', () => {
const code = `Fn(() => left + right)`
const out = run(code)
expect(out).toContain('left.add(right)')
expect(out).not.toContain('+ right')
})
// 2 SUCCESS
it('2. transforms left + 1 => left.add(1)', () => {
const code = `Fn(() => left + 1)`
const out = run(code)
expect(out).toContain('left.add(1)')
expect(out).not.toContain('left + 1')
})
// 3 SUCCESS
it('3. transforms 1 + left => float(1).add(left)', () => {
const code = `Fn(() => 1 + left)`
const out = run(code)
expect(out).toContain('float(1).add(left)')
expect(out).not.toContain('1 + left')
})
})
describe('Operator -', () => {
// 4 SUCCESS
it('4. transforms left - right => left.sub(right)', () => {
const code = `Fn(() => left - right)`
const out = run(code)
expect(out).toContain('left.sub(right)')
expect(out).not.toContain('left - right')
})
// 5 SUCCESS
it('5. transforms left - 2 => left.sub(2)', () => {
const code = `Fn(() => left - 2)`
const out = run(code)
expect(out).toContain('left.sub(2)')
expect(out).not.toContain('left - 2')
})
// 6 SUCCESS
it('6. transforms 2 - left => float(2).sub(left)', () => {
const code = `Fn(() => 2 - left)`
const out = run(code)
expect(out).toContain('float(2).sub(left)')
expect(out).not.toContain('2 - left')
})
})
describe('Operator *', () => {
// 7 SUCCESS
it('7. transforms left * right => left.mul(right)', () => {
const code = `Fn(() => left * right)`
const out = run(code)
expect(out).toContain('left.mul(right)')
expect(out).not.toContain('* right')
})
// 8 SUCCESS
it('8. transforms left * 5 => left.mul(5)', () => {
const code = `Fn(() => left * 5)`
const out = run(code)
expect(out).toContain('left.mul(5)')
expect(out).not.toContain('left * 5')
})
// 9 SUCCESS
it('9. transforms 5 * left => float(5).mul(left)', () => {
const code = `Fn(() => 5 * left)`
const out = run(code)
expect(out).toContain('float(5).mul(left)')
expect(out).not.toContain('5 * left')
})
})
describe('Operator /', () => {
// 10 SUCCESS
it('10. transforms left / right => left.div(right)', () => {
const code = `Fn(() => left / right)`
const out = run(code)
expect(out).toContain('left.div(right)')
expect(out).not.toContain('left / right')
})
// 11 SUCCESS
it('11. transforms left / 3 => left.div(3)', () => {
const code = `Fn(() => left / 3)`
const out = run(code)
expect(out).toContain('left.div(3)')
expect(out).not.toContain('left / 3')
})
// 12 SUCCESS
it('12. transforms 3 / left => float(3).div(left)', () => {
const code = `Fn(() => 3 / left)`
const out = run(code)
expect(out).toContain('float(3).div(left)')
expect(out).not.toContain('3 / left')
})
})
describe('Operator %', () => {
// 13 SUCCESS
it('13. transforms left % right => left.mod(right)', () => {
const code = `Fn(() => left % right)`
const out = run(code)
expect(out).toContain('left.mod(right)')
expect(out).not.toContain('left % right')
})
// 14 SUCCESS
it('14. transforms left % 2 => left.mod(2)', () => {
const code = `Fn(() => left % 2)`
const out = run(code)
expect(out).toContain('left.mod(2)')
expect(out).not.toContain('left % 2')
})
// 15 SUCCESS
it('15. transforms 2 % left => float(2).mod(left)', () => {
const code = `Fn(() => 2 % left)`
const out = run(code)
expect(out).toContain('float(2).mod(left)')
expect(out).not.toContain('2 % left')
})
// Fn(()=>{
// return 1 - ( alpha * color.r % 3 )
// })
it('15Bis. transforms 1 - ( alpha * (color.r % 3) )', () => {
const code = `Fn(()=>{
return 1 - ( alpha * color.r % 3 )
})`
const out = run(code)
expect(out).toContain('float(1).sub(alpha.mul(color.r.mod(3)))')
})
})
})
describe('Operator Precedence & Parentheses', () => {
// 16 SUCCESS
it('16. keeps the correct precedence for 1 % 2 / 3 * 4 - 5 + 6 % 8 / 9', () => {
const code = `
Fn(() => {
return 1 % 2 / 3 * 4 - 5 + 6 % 8 / 9
})
`
const out = run(code)
expect(out).toContain('1 % 2 / 3 * 4 - 5 + 6 % 8 / 9')
})
// 17 SUCCESS
it('17. handles nested parentheses => (left + (right - 1)) * 2', () => {
const code = `Fn(() => (left + (right - 1)) * 2)`
const out = run(code)
expect(out).toContain('left.add(right.sub(1)).mul(2)')
})
// 18 SUCCESS
it('18 handles nested parentheses', () => {
const code = `Fn(() => (a + (b - c)) * d)`
const out = run(code)
expect(out).toContain('a.add(b.sub(c)).mul(d)')
})
// 19 SUCCESS
it('19 respects operator precedence', () => {
const code = `Fn(() => a + b * c + d / e)`
const out = run(code)
expect(out).toContain('a.add(b.mul(c)).add(d.div(e))')
})
// 20. SUCCESS
it('20. handles multiline expressions with line breaks', () => {
const code = `
Fn(() => {
return a
+ b * c
- d / e
})
`
const out = run(code)
expect(out.replace(/\s/g, '')).toContain('a.add(b.mul(c)).sub(d.div(e))')
})
})
describe('Unary Operations', () => {
// 21 SUCCESS
it('21. handles unary numeric with variable => -1 + x => float(-1).add(x)', () => {
const code = `Fn(() => -1 + x)`
const out = run(code)
expect(out).toContain('float(-1).add(x)')
})
// 22 SUCCESS
it('22. does not alter var a = ( -5 ) if not used with other ops', () => {
const code = `
Fn(() => {
let a = (-5)
return a
})
`
const out = run(code)
// It's purely numeric unary => no chain needed
expect(out).toContain('let a = -5')
expect(out).not.toContain('float(-5)')
})
// 23 SUCCESS
it('23. handles unary operators and function calls => -a * b + Math.abs(c)', () => {
const code = `Fn(() => -a * b + Math.abs(c))`
const out = run(code)
expect(out).toContain('a.mul(-1).mul(b).add(Math.abs(c))')
})
// 24 SUCCESS
it('24. handles multiple unary operators => -a + -b * -c', () => {
const code = `Fn(() => -a + -b * -c)`
const out = run(code)
expect(out).toContain('a.mul(-1).add(b.mul(-1).mul(c.mul(-1)))')
})
// 25 SUCCESS
it('25 handles unary operators', () => {
const code = `Fn(() => -a + -b * -c)`
const out = run(code)
expect(out).toContain('a.mul(-1).add(b.mul(-1).mul(c.mul(-1)))')
})
})
describe('Mixing with Math Constants & Functions', () => {
// 26 SUCCESS
it('26. does not transform Math.PI / 2 alone', () => {
const code = `Fn(() => Math.PI / 2)`
const out = run(code, 'mathpi.js')
expect(out).toContain('Math.PI / 2')
expect(out).not.toContain('Math.PI.div(')
})
it('26Bis1. does not transform Math.PI * 2 alone', () => {
const code = `Fn(() => Math.PI * 2)`
const out = run(code, 'mathpi.js')
expect(out).toContain('Math.PI * 2')
expect(out).not.toContain('Math.PI.mul(')
})
it('26Bis2. does not transform Math.PI % 2 alone', () => {
const code = `Fn(() => Math.PI % 2)`
const out = run(code, 'mathpi.js')
expect(out).toContain('Math.PI % 2')
expect(out).not.toContain('Math.PI.mod(')
})
it('26Bis3. does not transform Math.PI - 2 alone', () => {
const code = `Fn(() => Math.PI - 2)`
const out = run(code, 'mathpi.js')
expect(out).toContain('Math.PI - 2')
expect(out).not.toContain('Math.PI.sub(')
})
// 27 SUCCESS
it('27. transforms numeric + Math.PI => float(1).add(Math.PI)', () => {
const code = `Fn(() => 1 + Math.PI)`
const out = run(code)
expect(out).toContain('float(1).add(Math.PI)')
})
// 28 SUCCESS
it('28. mixes numeric with Math.PI => float(1).sub(Math.PI / 2)', () => {
const code = `Fn(() => 1 - (Math.PI / 2))`
const out = run(code)
expect(out).toContain('float(1).sub(Math.PI / 2)')
})
it('28Bis1. mixes numeric with Math.PI => float(1).sub(Math.PI * 2)', () => {
const code = `Fn(() => 1 - (Math.PI * 2))`
const out = run(code)
expect(out).toContain('float(1).sub(Math.PI * 2)')
})
// 29 SUCCESS
it('29. keeps pure numeric expressions intact', () => {
const code = `Fn(() => 1 - 2 * (3 + 4) / 5)`
const out = run(code)
expect(out).toContain('1 - 2 * (3 + 4) / 5')
expect(out).not.toContain('float(')
})
// 30 SUCCESS
it('30 handles Math functions correctly', () => {
const code = `Fn(() => Math.abs(a) + Math.sin(b) * sin(c) + d)`
const out = run(code)
expect(out).toContain('Math.abs(a).add(Math.sin(b).mul(sin(c))).add(d)')
})
})
describe('Complex Expressions & Chaining', () => {
// 31 SUCCESS
it('31. handles left - 2 + float(5).div(5)', () => {
const code = `Fn(() => left - 2 + float(5).div(5))`
const out = run(code)
expect(out).toContain('left.sub(2).add(float(5).div(5))')
})
// 32 SUCCESS
it('32. handles smoothstep(...) * .5 + .5 => smoothstep(...).mul(.5).add(.5)', () => {
const code = `
Fn(() => {
return smoothstep(0, 0.5, pos.y) * .5 + .5
})
`
const out = run(code)
expect(out).toContain('smoothstep(0, 0.5, pos.y).mul(.5).add(.5)')
})
// 33 SUCCESS
it('33. handles mix(...) calls alongside chainable ops', () => {
const code = `
Fn(() => {
let a = 1 - left * right
let b = smoothstep(0, 0.5, pos.y) * .5 + .5
return mix(a, b, color(0xff0000))
})
`
const out = run(code)
expect(out).toContain('float(1).sub(left.mul(right))')
expect(out).toContain('smoothstep(0, 0.5, pos.y).mul(.5).add(.5)')
expect(out).toContain('mix(a, b, color(0xff0000))')
})
// 34 SUCCESS
it('34. handles chain with left, right, x => left + right / x', () => {
const code = `Fn(() => left + right / x)`
const out = run(code)
expect(out).toContain('left.add(right.div(x))')
})
// 35 SUCCESS
it('35. handles multiple mod => left % 3 % 2 => left.mod(3).mod(2)', () => {
const code = `Fn(() => left % 3 % 2)`
const out = run(code)
expect(out).toContain('left.mod(3).mod(2)')
})
// 36 SUCCESS
it('36. handles float(2.5).mul(4) + 1 => float(2.5).mul(4).add(1)', () => {
const code = `Fn(() => float(2.5).mul(4) + 1)`
const out = run(code)
expect(out).toContain('float(2.5).mul(4).add(1)')
})
// 37 SUCCESS
it('37. correctly chains multiple operations => left * right - 2 / (3 + left)', () => {
const code = `Fn(() => left * right - 2 / (3 + left))`
const out = run(code)
expect(out).toContain('left.mul(right).sub(float(2).div(float(3).add(left)))')
})
// 38 SUCCESS
it('38. handles nested operations with multiple variables => (a + b) * (c - d)', () => {
const code = `Fn(() => (a + b) * (c - d))`
const out = run(code)
expect(out).toContain('a.add(b).mul(c.sub(d))')
})
// 40 SUCCESS
it('40. handles complex mixed operators => a * b + c / d - e % f', () => {
const code = `Fn(() => a * b + c / d - e % f)`
const out = run(code)
expect(out).toContain('a.mul(b).add(c.div(d)).sub(e.mod(f))')
})
// 41 SUCCESS
it('41. handles chained operations with parentheses => (a + b) * (c - (d / e))', () => {
const code = `Fn(() => (a + b) * (c - (d / e)))`
const out = run(code)
expect(out).toContain('a.add(b).mul(c.sub(d.div(e)))')
})
// 42 SUCCESS
it('42. handles multiple nested function calls => fn1(a) + fn2(b) * fn3(c)', () => {
const code = `Fn(() => fn1(a) + fn2(b) * fn3(c))`
const out = run(code)
expect(out).toContain('fn1(a).add(fn2(b).mul(fn3(c)))')
})
// 43 SUCCESS
it('43. handles complex mixed literals and variables => 2 * a + 3 / b - 4 % c', () => {
const code = `Fn(() => 2 * a + 3 / b - 4 % c)`
const out = run(code)
expect(out).toContain('float(2).mul(a).add(float(3).div(b)).sub(float(4).mod(c))')
})
// 44 SUCCESS
it('44. handles multiple chained method => a.mul(b).add(c).sub(d)', () => {
const code = `Fn(() => a * b + c - d)`
const out = run(code)
expect(out).toContain('a.mul(b).add(c).sub(d)')
})
// 45 SUCCESS
it('45. handles nested method and literals => a.mul(b.add(2)).sub(3)', () => {
const code = `Fn(() => a * (b + 2) - 3)`
const out = run(code)
expect(out).toContain('a.mul(b.add(2)).sub(3)')
})
// 46 SUCCESS
it('46. handles nested ternary operations => a ? b.add(c) : d.sub(e)', () => {
const code = `Fn(() => a ? b.add(c) : d.sub(e))`
const out = run(code)
expect(out).toContain('a ? b.add(c) : d.sub(e)')
})
// 47 SUCCESS
it('47. handles mixed literals and variables => 2 * a + 3 / b - 4 % c', () => {
const code = `Fn(() => 2 * a + 3 / b - 4 % c)`
const out = run(code)
expect(out).toContain('float(2).mul(a).add(float(3).div(b)).sub(float(4).mod(c))')
})
// 48. SUCCESS
it('48. handles nested arrow functions with arithmetic inside multiline function', () => {
const code = `
Fn(() => {
const calc = () => a + b
return calc() * 2
})
`
const out = run(code)
expect(out).toContain('const calc = () => a.add(b)')
expect(out).toContain('calc().mul(2)')
})
// 49 SUCCESS
it('49. handles expressions with comments between operators', () => {
const code = `Fn(() => a + /* plus comment */ b - /* minus comment */ c)`
const out = run(code)
expect(out.trim()).toContain('a.add(/* plus comment */b).sub(/* minus comment */c)')
})
// 50 SUCCESS
it('50. handles arithmetic in function arguments', () => {
const code = `Fn(() => someFunc(a + b - c))`
const out = run(code)
expect(out).toContain('someFunc(a.add(b).sub(c))')
})
})
describe('Mixed Code & Non-Transformed Segments', () => {
// 51 SUCCESS
it('51 does not alter non-TSL code', () => {
const code = `const x = a + b; Fn(() => x * c)`
const out = run(code)
expect(out).toContain('const x = a + b;')
expect(out).toContain('x.mul(c)')
})
// 52 SUCCESS
it('52. does not transform arithmetic inside string literals', () => {
const code = `Fn(() => "a + b should remain as is")`
const out = run(code)
expect(out).toContain('"a + b should remain as is"')
})
})
describe('Real-World Examples', () => {
// 53 SUCCESS
it('53. complex real-world example', () => {
const code = `
Fn(() => {
position.x.addAssign( (position.z * angleX + Math.PI / 2).cos() * powerX )
})
`
const out = run(code)
expect(out).toContain('position.x.addAssign(position.z.mul(angleX).add(Math.PI / 2).cos().mul(powerX))')
})
// 54 SUCCESS
it('54. handles complex fog mix example', () => {
const code = `
Fn( ()=>{
let c = output
let outsideRange = 2.2
let margin = 1.1
let right = smoothstep( -outsideRange, -outsideRange + margin, vBatchPosition.x )
let left = smoothstep( vBatchPosition.x, vBatchPosition.x + margin, outsideRange )
let outside = 1 - left * right
c = mix( c, fogColor, clamp( outside - vBatchPosition.y / 3 ) )
return applyFog( c, vBatchTransformed )
} )()
`
const out = run(code)
expect(out).toContain("let outsideRange = 2.2")
expect(out).toContain("smoothstep(float(outsideRange).mul(-1), float(outsideRange).mul(-1).add(margin), vBatchPosition.x)")
expect(out).toContain("let outside = float(1).sub(left.mul(right))")
expect(out).toContain("c = mix(c, fogColor, clamp(outside.sub(vBatchPosition.y.div(3))))")
expect(out).toContain("return applyFog(c, vBatchTransformed)")
})
// SUCCESS
it('54Bis1. handles complex normal transformation example', () => {
const code = `
Fn(() => {
const transformedNormal = normalLocal.div(vec3(bm[0].dot(bm[0]), bm[1].dot(bm[1]), bm[2].dot(bm[2])))
})
`
const out = run(code)
expect(out).toContain("const transformedNormal = normalLocal.div(vec3(bm[0].dot(bm[0]), bm[1].dot(bm[1]), bm[2].dot(bm[2]))")
})
// 54Bis2 FAILLED MINOR ISSUE : it add a space before toVar() casue the parenthese are changed from one line to the others..
// it('54Bis2. handles complex multiline matrix without redundant transforms', () => {
// const code = `
// Fn(() => {
// let batchingMatrix = mat4(
// textureLoad(matriceTexture, ivec2(x, y)),
// textureLoad(matriceTexture, ivec2(x.add(1), y)),
// textureLoad(matriceTexture, ivec2(x.add(2), y)),
// textureLoad(matriceTexture, ivec2(x.add(3), y))
// ).toVar()
// })
// `
// // Remove extra whitespace so formatting differences don’t cause false negatives
// const normalize = str => str.trim().replace(/\s+/g, ' ')
// const expected = normalize(
// `let batchingMatrix = mat4( textureLoad(matriceTexture, ivec2(x, y)), textureLoad(matriceTexture, ivec2(x.add(1), y)), textureLoad(matriceTexture, ivec2(x.add(2), y)), textureLoad(matriceTexture, ivec2(x.add(3), y))).toVar()`
// )
// const transformed = normalize(run(code))
// console.log('transformed', transformed)
// expect(transformed).toContain(expected)
// })
})
describe('TSLOperatorPlugin Edge Cases', () => {
it('55. idempotency: does not double transform already transformed code', () => {
const code = `Fn(() => left.add(right))`
const out = run(run(code))
expect(out).toContain('left.add(right)')
})
it('56. transforms arithmetic inside computed property keys', () => {
const code = `Fn(() => {
const obj = { [a + b]: c }
return obj
})`
const out = run(code)
expect(out).toContain('[a.add(b)]')
})
it('57. transforms arithmetic inside template literal interpolations', () => {
const code = 'Fn(() => `Value: ${a + b}`)'
const out = run(code)
expect(out).toContain('`Value: ${a.add(b)}`')
})
it('58. transforms arithmetic in array elements', () => {
const code = `Fn(() => [a + b, a - b])`
const out = run(code)
expect(out).toContain('a.add(b)')
expect(out).toContain('a.sub(b)')
})
it('59. dont transforms arithmetic in default parameter values', () => {
const code = `
const f = (x = a + b) => x
Fn(() => f())
`
const out = run(code)
expect(out).not.toContain('x = a.add(b)')
})
it('60. transforms arithmetic with unusual spacing and line breaks', () => {
const code = `
Fn(() => {
return a
+
b
-
c
})
`
const out = run(code)
expect(out.replace(/\s/g, '')).
toContain('a.add(b).sub(c)')
})
it('61. does not transform arithmetic inside string literals', () => {
const code = `Fn(() => "Sum: a + b should not change")`
const out = run(code)
expect(out).toContain('"Sum: a + b should not change"')
})
it('62. preserves grouping with unnecessary parentheses', () => {
const code = `Fn(() => (a + b))`
const out = run(code)
expect(out).toContain('a.add(b)')
})
it('63. transforms arithmetic in default values during object destructuring', () => {
const code = `Fn(() => {
const { x = a + b } = obj
return x
})`
const out = run(code)
expect(out).toContain('x = a.add(b)')
})
it('64. handles logical operators without interfering with arithmetic', () => {
const code = `Fn(() => {
return (a + b) && (c - d)
})`
const out = run(code)
expect(out).toContain('a.add(b)')
expect(out).toContain('c.sub(d)')
expect(out).toContain('&&')
})
it('65. transforms arithmetic in nested ternary conditions', () => {
const code = `Fn(() => a ? b + c : d - e)`
const out = run(code)
expect(out).toContain('b.add(c)')
expect(out).toContain('d.sub(e)')
})
it('66. handles expressions with multiple unary minus signs', () => {
const code = `Fn(() => - -a + - (b - c))`
const out = run(code)
expect(out).toContain('a.mul(-1).mul(-1).add(b.sub(c).mul(-1))')
})
it('67. transforms (a + b) * c % d => a.add(b).mul(c.mod(d))', () => {
const code = `Fn(() => (a + b) * c % d)`
const out = run(code)
expect(out).toContain('a.add(b).mul(c.mod(d))')
})
it('68. handles a * b * c % d => a.mul(b).mul(c.mod(d))', () => {
const code = `Fn(() => a * b * c % d)`
const out = run(code)
expect(out).toContain('a.mul(b).mul(c.mod(d))')
})
it('69. handles unary minus before `%`: -a % b => a.mul(-1).mod(b)', () => {
const code = `Fn(() => -a % b)`
const out = run(code)
expect(out).toContain('a.mul(-1).mod(b)')
})
it('70. leaves `%` in default params untouched', () => {
const code = `const f = (x = a % b) => x; Fn(() => f())`
const out = run(code)
expect(out).toContain('x = a % b')
})
it('71. transforms inside template literals', () => {
const code = 'Fn(() => `r=${x % y}`)'
const out = run(code)
expect(out).toContain('`r=${x.mod(y)}`')
})
// 72. handles a % b * c => a.mod(b).mul(c)
it('72. transforms a % b * c => a.mod(b).mul(c)', () => {
const code = `Fn(() => a % b * c)`
const out = run(code)
expect(out).toContain('a.mod(b).mul(c)')
})
// 73. transforms (a + b) * c % d => a.add(b).mul(c.mod(d))
it('73. transforms (a + b) * c % d => a.add(b).mul(c.mod(d))', () => {
const code = `Fn(() => (a + b) * c % d)`
const out = run(code)
expect(out).toContain('a.add(b).mul(c.mod(d))')
})
// 74. transforms a * (b + c) % (d - e) => a.mul(b.add(c).mod(d.sub(e)))
it('74. transforms a * (b + c) % (d - e) => a.mul(b.add(c).mod(d.sub(e)))', () => {
const code = `Fn(() => a * (b + c) % (d - e))`
const out = run(code)
expect(out).toContain('a.mul(b.add(c).mod(d.sub(e)))')
})
// 75. transforms 3 * x % 5 => float(3).mul(x.mod(5))
it('75. transforms 3 * x % 5 => float(3).mul(x.mod(5))', () => {
const code = `Fn(() => 3 * x % 5)`
const out = run(code)
expect(out).toContain('float(3).mul(x.mod(5))')
})
// 76. handles chaining a % b * c % d => a.mod(b).mul(c).mod(d)
it('76. transforms a % b * c % d => a.mod(b).mul(c).mod(d)', () => {
const code = `Fn(() => a % b * c % d)`
const out = run(code)
expect(out).toContain('a.mod(b).mul(c).mod(d)')
})
// 77. transforms computed prop {[a * b % c]: d} => {[a.mul(b.mod(c))]: d}
it('77. transforms computed prop {[a * b % c]: d} => {[a.mul(b.mod(c))]: d}', () => {
const code = `Fn(() => { const o = { [a * b % c]: d }; return o })`
const out = run(code)
expect(out).toContain('[a.mul(b.mod(c))]')
})
// 78. transforms in array elements [a * b % c, e] => [a.mul(b.mod(c)), e]
it('78. transforms in array [a * b % c, e] => [a.mul(b.mod(c)), e]', () => {
const code = `Fn(() => [a * b % c, e])`
const out = run(code)
expect(out).toContain('a.mul(b.mod(c))')
})
// 79. handles unary before modulo in args: foo(a * b % c, -d % e)
it('79. transforms foo(a * b % c, -d % e) => foo(a.mul(b.mod(c)), d.mul(-1).mod(e))', () => {
const code = `Fn(() => foo(a * b % c, -d % e))`
const out = run(code)
expect(out).toContain('foo(a.mul(b.mod(c)), d.mul(-1).mod(e))')
})
// 80. nested arrow with modulo: const inner = () => x * y % z
it('80. transforms nested arrow inner = () => x * y % z => inner = () => x.mul(y.mod(z))', () => {
const code = `
Fn(() => {
const inner = () => x * y % z
return inner()
})
`
const out = run(code)
expect(out).toContain('const inner = () => x.mul(y.mod(z))')
})
describe('Advanced Complex Use Cases', () => {
it('81. handles deeply nested mixed operations', () => {
const code = `Fn(() => {
const x = (((a + b * c) % (d - e)) * f / (g + h)) + (i % (j * k)) - (-l + m * (n % o))
return x
})`
const out = run(code)
expect(out).toContain(
'a.add(b.mul(c)).mod(d.sub(e)).mul(f).div(g.add(h)).add(i.mod(j.mul(k))).sub(l.mul(-1).add(m.mul(n.mod(o))))'
)
})
it('82. transforms foo((a + b) * (c % d)) => foo(a.add(b).mul(c.mod(d)))', () => {
const code = `Fn(() => foo((a + b) * (c % d)))`
const out = run(code)
expect(out).toContain('foo(a.add(b).mul(c.mod(d)))')
})
it('83. handles nested arrow function with modulo chain', () => {
const code = `
Fn(() => {
const inner = (x) => x % m * n + o
return inner(p)
})
`
const out = run(code)
expect(out).toContain('const inner = (x) => x.mod(m).mul(n).add(o)')
expect(out).toContain('return inner(p)')
})
it('84. transforms complex template literal with arithmetic', () => {
const code = 'Fn(() => `Result: ${((a - b) * c) % d}`)'
const out = run(code)
expect(out).toContain('`Result: ${a.sub(b).mul(c.mod(d))}`')
})
it('85. handles unary minus and nested modulo correctly', () => {
const code = `Fn(() => -((x % y) * z))`
const out = run(code)
expect(out).toContain('x.mod(y).mul(z).mul(-1)')
})
})
it('86. handles arithmetic in destructuring default, computed props, and nested ternary', () => {
const code = `Fn(() => {
const { a = b + c } = obj
const o = { [d * e % f]: g }
return h ? i - j * k % l : m + n
})`
const out = run(code)
expect(out).toContain('a = b.add(c)')
expect(out).toContain('[d.mul(e.mod(f))]')
expect(out).toContain('h ? i.sub(j.mul(k.mod(l))) : m.add(n)')
})
it('87. transforms (a + b).toVar() => a.add(b).toVar()', () => {
const code = `Fn(() => (a + b).toVar())`
const out = run(code)
expect(out).toContain('a.add(b).toVar()')
})
it('88. transforms (a - b).toConst() => a.sub(b).toConst()', () => {
const code = `Fn(() => (a - b).toConst())`
const out = run(code)
expect(out).toContain('a.sub(b).toConst()')
})
it('89. transforms uniform(vec2(1 + 2)) => uniform(vec2(float(1).add(2)))', () => {
const code = `Fn(() => uniform(vec2(1 + 2)))`
const out = run(code)
expect(out).toContain('uniform(vec2(float(1).add(2)))')
})
it('90. transforms array(vec2(3 + 4), vec2(5 % 2)) => array(vec2(float(3).add(4)), vec2(5.mod(2)))', () => {
const code = `Fn(() => array(vec2(3 + 4), vec2(5 % 2)))`
const out = run(code)
expect(out).toContain('array(vec2(float(3).add(4)), vec2(float(5).mod(2)))')
})
})
describe('Compound Assignment Operators', () => {
// +=
it('transforms x += y => x.addAssign(y)', () => {
const out = run(`Fn(() => { let x = a; x += b; return x })`)
expect(out).toContain('x.addAssign(b)')
expect(out).not.toContain('+= b')
})
// -=
it('transforms x -= y => x.subAssign(y)', () => {
const out = run(`Fn(() => { let x = a; x -= b; return x })`)
expect(out).toContain('x.subAssign(b)')
expect(out).not.toContain('-= b')
})
// *=
it('transforms x *= y => x.mulAssign(y)', () => {
const out = run(`Fn(() => { let x = a; x *= b; return x })`)
expect(out).toContain('x.mulAssign(b)')
expect(out).not.toContain('*= b')
})
// /=
it('transforms x /= y => x.divAssign(y)', () => {
const out = run(`Fn(() => { let x = a; x /= b; return x })`)
expect(out).toContain('x.divAssign(b)')
expect(out).not.toContain('/= b')
})
// %=
it('transforms x %= y => x.modAssign(y)', () => {
const out = run(`Fn(() => { let x = a; x %= b; return x })`)
expect(out).toContain('x.modAssign(b)')
expect(out).not.toContain('%= b')
})
})
describe('If / Else Statements', () => {
// 91
it('91. transforms arithmetic inside if block => x = a.add(b)', () => {
const code = `
Fn(() => {
let x = 0
if(flag){
x = a + b
}
return x
})
`
const out = run(code)
expect(out).toContain('x = a.add(b)')
})
// 92
it('92. transforms arithmetic inside else block => x = c.sub(d)', () => {
const code = `
Fn(() => {
let x
if(cond){
x = a + b
} else {
x = c - d
}
return x
})
`
const out = run(code)
expect(out).toContain('x = a.add(b)')
expect(out).toContain('x = c.sub(d)')
})
// 93
it('93. transforms arithmetic in all branches of nested if/else', () => {
const code = `
Fn(() => {
let y
if(a > b){
y = c * d
} else if(a < b){
y = e / f
} else {
y = g % h
}
return y
})
`
const out = run(code)
expect(out).toContain('y = c.mul(d)')
expect(out).toContain('y = e.div(f)')
expect(out).toContain('y = g.mod(h)')
})
// 94
it('94. keeps relational condition intact but transforms inside', () => {
const code = `
Fn(() => {
if(a > b){
return a + b
}
return c
})
`
const out = run(code)
expect(out).toContain('if(a > b)')
expect(out).toContain('return a.add(b)')
})
// 95
it('95. transforms arithmetic in ternary within if block', () => {
const code = `
Fn(() => {
let z = 0
if(flag){
z = a ? b + c : d - e
}
return z
})
`
const out = run(code)
expect(out).toContain('z = a ? b.add(c) : d.sub(e)')
})
})
/* -------------------------------------------------------
TSL If / Else specific tests (continue numbering)
---------------------------------------------------- */
describe('TSL If / Else', () => {
// 96
it('96. transforms arithmetic inside If branch => x = a.add(b)', () => {
const code = `
Fn(() => {
let x = 0
If(flag, () => {
x = a + b
})
return x
})
`
const out = run(code)
expect(out).toContain('x = a.add(b)')
})
// 97
it('97. transforms arithmetic inside Else branch => x = c.sub(d)', () => {
const code = `
Fn(() => {
let x = 0
If(flag, () => {
x = a + b
}).Else(() => {
x = c - d
})
return x
})
`
const out = run(code)
expect(out).toContain('x = a.add(b)')
expect(out).toContain('x = c.sub(d)')
})
// 98
it('98. transforms arithmetic inside ElseIf branch => y = e.mul(f)', () => {
const code = `
Fn(() => {
let y = 0
If(cond1, () => {
y = c * d
}).ElseIf(cond2, () => {
y = e * f
}).Else(() => {
y = g % h
})
return y
})
`
const out = run(code)
expect(out).toContain('y = c.mul(d)')
expect(out).toContain('y = e.mul(f)')
expect(out).toContain('y = g.mod(h)')
})
// 99
it('99. transforms arithmetic inside If *condition* => (a * b).greaterThan(c)', () => {
const code = `
Fn(() => {
If((a * b).greaterThan(c), () => {
return a
})
})
`
const out = run(code)
expect(out).toContain('a.mul(b).greaterThan(c)')
})
// 100
it('100. transforms compound assignments inside If / Else', () => {
const code = `
Fn(() => {
let z = 0
If(useAdd, () => {
z += p
}).Else(() => {
z *= q
})
return z
})
`
const out = run(code)
expect(out).toContain('z.addAssign(p)')
expect(out).toContain('z.mulAssign(q)')
})
// 101
it('101. idempotency: re‑running the plugin keeps TSL If code stable', () => {
const code = `
Fn(() => {
If(flag, () => {
value = a + b
})
})
`
const once = run(code)
const twice = run(once)
expect(twice).toContain('value = a.add(b)') // still transformed
expect((twice.match(/a\.add\(b\)/g) || []).length).toBe(1) // not duplicated
})
// 102
it('102. transforms arithmetic in computed props inside If branch', () => {
const code = `
Fn(() => {
If(show, () => {
const obj = { [m - n]: o }
return obj
})
})
`
const out = run(code)
expect(out).toContain('[m.sub(n)]')
})
})