|
16 | 16 |
|
17 | 17 | package com.google.javascript.jscomp; |
18 | 18 |
|
| 19 | +import static com.google.common.truth.Truth.assertThat; |
19 | 20 | import static com.google.common.truth.Truth.assertWithMessage; |
20 | 21 |
|
21 | 22 | import com.google.common.base.Joiner; |
@@ -2249,6 +2250,209 @@ public void testAssociativeFoldConstantsWithVariables() { |
2249 | 2250 | test("alert(12 & x & 20);", "alert(x & 4);"); |
2250 | 2251 | } |
2251 | 2252 |
|
| 2253 | + @Test |
| 2254 | + public void testTemplateLiteralConcat() { |
| 2255 | + |
| 2256 | + // join at variables |
| 2257 | + test( |
| 2258 | + """ |
| 2259 | + const a = `${x}` + `${x}`; |
| 2260 | + """, |
| 2261 | + """ |
| 2262 | + const a = `${x}${x}`; |
| 2263 | + """); |
| 2264 | + test( |
| 2265 | + """ |
| 2266 | + const a = `${x}${y}` + `${x}${y}`; |
| 2267 | + """, |
| 2268 | + """ |
| 2269 | + const a = `${x}${y}${x}${y}`; |
| 2270 | + """); |
| 2271 | + |
| 2272 | + // join at variable and string |
| 2273 | + test( |
| 2274 | + """ |
| 2275 | + const a = `${x}` + `a${x}`; |
| 2276 | + """, |
| 2277 | + """ |
| 2278 | + const a =`${x}a${x}`; |
| 2279 | + """); |
| 2280 | + test( |
| 2281 | + """ |
| 2282 | + const a = `${x}` + ` ${x}`; |
| 2283 | + """, |
| 2284 | + """ |
| 2285 | + const a =`${x} ${x}`; |
| 2286 | + """); |
| 2287 | + |
| 2288 | + // join at string and variable |
| 2289 | + test( |
| 2290 | + """ |
| 2291 | + const a = `a${x}b` + `${x}c`; |
| 2292 | + """, |
| 2293 | + """ |
| 2294 | + const a = `a${x}b${x}c`; |
| 2295 | + """); |
| 2296 | + test( |
| 2297 | + """ |
| 2298 | + const a = `a${x} ` + `${x}b`; |
| 2299 | + """, |
| 2300 | + """ |
| 2301 | + const a = `a${x} ${x}b`; |
| 2302 | + """); |
| 2303 | + |
| 2304 | + // join at strings |
| 2305 | + test( |
| 2306 | + """ |
| 2307 | + const x = 'X'; |
| 2308 | + console.log(`a${x}b` + `c${x}d`); |
| 2309 | + """, |
| 2310 | + """ |
| 2311 | + const x = 'X'; |
| 2312 | + console.log(`a${x}bc${x}d`); |
| 2313 | + """); |
| 2314 | + |
| 2315 | + // complex joins |
| 2316 | + test( |
| 2317 | + """ |
| 2318 | + const a = `${foo() + bar()}` + `${baz()}`; |
| 2319 | + """, |
| 2320 | + """ |
| 2321 | + const a =`${foo() + bar()}${baz()}`; |
| 2322 | + """); |
| 2323 | + test( |
| 2324 | + """ |
| 2325 | + console.log(`<h1>${url}</h1>` + `<p>The URL is ${url}.</p>`); |
| 2326 | + """, |
| 2327 | + """ |
| 2328 | + console.log(`<h1>${url}</h1><p>The URL is ${url}.</p>`); |
| 2329 | + """); |
| 2330 | + test( |
| 2331 | + """ |
| 2332 | + console.log(`${(() => {return url;})()}</h1>` + `<p>The URL is ${url}.</p>`); |
| 2333 | + """, |
| 2334 | + """ |
| 2335 | + console.log(`${(() => {return url;})()}</h1><p>The URL is ${url}.</p>`); |
| 2336 | + """); |
| 2337 | + |
| 2338 | + // don't fold tagged template literals |
| 2339 | + testSame( |
| 2340 | + """ |
| 2341 | + const a = foo`${b}` + `${b}`; |
| 2342 | + """); |
| 2343 | + testSame( |
| 2344 | + """ |
| 2345 | + const a = foo`${b}` + bar`${b}`; |
| 2346 | + """); |
| 2347 | + } |
| 2348 | + |
| 2349 | + @Test |
| 2350 | + public void |
| 2351 | + testFoldAddTemplateLiterals_validateRawAndCookedStringForSpecialChars_andValidateChildren() { |
| 2352 | + // This is a bit tricky because there are two layers of escaping. |
| 2353 | + // Java sees "\\n" as "(\\)n" so it becomes "\n" for the javascript compiler. |
| 2354 | + // The compiler then interprets this as the special character "\n" |
| 2355 | + test("var x = `${a}\\n` + `\\t${b}`", "var x = `${a}\\n\\t${b}`"); |
| 2356 | + |
| 2357 | + // getJsRoot() returns the ROOT node for inputs only. |
| 2358 | + // The first child is the SCRIPT node containing the input code. |
| 2359 | + Node script = getLastCompiler().getJsRoot().getFirstChild(); |
| 2360 | + Node var = script.getFirstChild(); |
| 2361 | + Node name = var.getFirstChild(); |
| 2362 | + Node templateLit = name.getFirstChild(); |
| 2363 | + assertThat(templateLit.isTemplateLit()).isTrue(); |
| 2364 | + |
| 2365 | + // TEMPLATELIT should have 5 total children |
| 2366 | + assertThat(templateLit.getChildCount()).isEqualTo(5); |
| 2367 | + |
| 2368 | + // Index 0: TEMPLATELIT_STRING "" |
| 2369 | + @SuppressWarnings({"RhinoNodeGetFirstChild", "Simplification"}) |
| 2370 | + Node child0 = templateLit.getChildAtIndex(0); |
| 2371 | + assertThat(child0.isTemplateLitString()).isTrue(); |
| 2372 | + assertThat(child0.getCookedString()).isEmpty(); |
| 2373 | + assertThat(child0.getRawString()).isEmpty(); |
| 2374 | + |
| 2375 | + // Index 1: SUB a |
| 2376 | + @SuppressWarnings({"RhinoNodeGetSecondChild", "Simplification"}) |
| 2377 | + Node child1 = templateLit.getChildAtIndex(1); |
| 2378 | + assertThat(child1.isTemplateLitSub()).isTrue(); |
| 2379 | + assertThat(child1.getOnlyChild().isName()).isTrue(); |
| 2380 | + assertThat(child1.getOnlyChild().getString()).isEqualTo("a"); |
| 2381 | + |
| 2382 | + // Index 2: TEMPLATELIT_STRING "\n\t" |
| 2383 | + Node child2 = templateLit.getChildAtIndex(2); |
| 2384 | + assertThat(child2.isTemplateLitString()).isTrue(); |
| 2385 | + assertThat(child2.getCookedString()).isEqualTo("\n\t"); |
| 2386 | + assertThat(child2.getRawString()).isEqualTo("\\n\\t"); |
| 2387 | + |
| 2388 | + // Index 3: SUB b |
| 2389 | + Node child3 = templateLit.getChildAtIndex(3); |
| 2390 | + assertThat(child3.isTemplateLitSub()).isTrue(); |
| 2391 | + assertThat(child3.getOnlyChild().isName()).isTrue(); |
| 2392 | + assertThat(child3.getOnlyChild().getString()).isEqualTo("b"); |
| 2393 | + |
| 2394 | + // Index 4: TEMPLATELIT_STRING "" |
| 2395 | + Node child4 = templateLit.getChildAtIndex(4); |
| 2396 | + assertThat(child4.isTemplateLitString()).isTrue(); |
| 2397 | + assertThat(child4.getCookedString()).isEmpty(); |
| 2398 | + assertThat(child4.getRawString()).isEmpty(); |
| 2399 | + } |
| 2400 | + |
| 2401 | + @Test |
| 2402 | + public void |
| 2403 | + testFoldAddTemplateLiterals_validateRawAndCookedStringForLiteralBackslash_andValidateChildren() { |
| 2404 | + // This is a bit tricky because there are two layers of escaping. |
| 2405 | + // Java sees "\\\\n" as "(\\)(\\)n" so it becomes "\\n" for the javascript compiler. |
| 2406 | + // The compiler then interprets this as "(\\)n" so it becomes "\" followed by |
| 2407 | + // "n" NOT the special character "\n". |
| 2408 | + test( |
| 2409 | + "var x = `${foo()}\\\\` + `n\\t${()=> {return b;}}`", |
| 2410 | + "var x = `${foo()}\\\\n\\t${()=> {return b;}}`"); |
| 2411 | + |
| 2412 | + // getJsRoot() returns the ROOT node for inputs only. |
| 2413 | + // The first child is the SCRIPT node containing the input code. |
| 2414 | + Node script = getLastCompiler().getJsRoot().getFirstChild(); |
| 2415 | + Node var = script.getFirstChild(); |
| 2416 | + Node name = var.getFirstChild(); |
| 2417 | + Node templateLit = name.getFirstChild(); |
| 2418 | + assertThat(templateLit.isTemplateLit()).isTrue(); |
| 2419 | + |
| 2420 | + // TEMPLATELIT should have 5 children: |
| 2421 | + assertThat(templateLit.getChildCount()).isEqualTo(5); |
| 2422 | + |
| 2423 | + // Index 0: TEMPLATELIT_STRING "" |
| 2424 | + @SuppressWarnings({"RhinoNodeGetFirstChild", "Simplification"}) |
| 2425 | + Node child0 = templateLit.getChildAtIndex(0); |
| 2426 | + assertThat(child0.isTemplateLitString()).isTrue(); |
| 2427 | + assertThat(child0.getCookedString()).isEmpty(); |
| 2428 | + assertThat(child0.getRawString()).isEmpty(); |
| 2429 | + |
| 2430 | + // Index 1: SUB foo() |
| 2431 | + @SuppressWarnings({"RhinoNodeGetSecondChild", "Simplification"}) |
| 2432 | + Node child1 = templateLit.getChildAtIndex(1); |
| 2433 | + assertThat(child1.isTemplateLitSub()).isTrue(); |
| 2434 | + assertThat(child1.getOnlyChild().isCall()).isTrue(); |
| 2435 | + assertThat(child1.getOnlyChild().getOnlyChild().getString()).isEqualTo("foo"); |
| 2436 | + |
| 2437 | + // Index 2: TEMPLATELIT_STRING "\n\t" |
| 2438 | + Node child2 = templateLit.getChildAtIndex(2); |
| 2439 | + assertThat(child2.isTemplateLitString()).isTrue(); |
| 2440 | + assertThat(child2.getCookedString()).isEqualTo("\\n\t"); |
| 2441 | + assertThat(child2.getRawString()).isEqualTo("\\\\n\\t"); |
| 2442 | + |
| 2443 | + // Index 3: SUB ()=> {return b;} |
| 2444 | + Node child3 = templateLit.getChildAtIndex(3); |
| 2445 | + assertThat(child3.isTemplateLitSub()).isTrue(); |
| 2446 | + assertThat(child3.getOnlyChild().isFunction()).isTrue(); |
| 2447 | + assertThat(child3.getOnlyChild().getFirstChild().getString()).isEqualTo(""); |
| 2448 | + |
| 2449 | + // Index 4: TEMPLATELIT_STRING "" |
| 2450 | + Node child4 = templateLit.getChildAtIndex(4); |
| 2451 | + assertThat(child4.isTemplateLitString()).isTrue(); |
| 2452 | + assertThat(child4.getCookedString()).isEmpty(); |
| 2453 | + assertThat(child4.getRawString()).isEmpty(); |
| 2454 | + } |
| 2455 | + |
2252 | 2456 | private void foldBigIntTypes(String js, String expected) { |
2253 | 2457 | test( |
2254 | 2458 | "function f(/** @type {bigint} */ x) { " + js + " }", |
|
0 commit comments