diff --git a/crates/turbopack-ecmascript/src/analyzer/builtin.rs b/crates/turbopack-ecmascript/src/analyzer/builtin.rs index 9c39c0486c81a..781aaf79fd433 100644 --- a/crates/turbopack-ecmascript/src/analyzer/builtin.rs +++ b/crates/turbopack-ecmascript/src/analyzer/builtin.rs @@ -412,6 +412,21 @@ pub fn replace_builtin(value: &mut JsValue) -> bool { } _ => {} } + + // matching calls on strings like `"dayjs/locale/".concat(userLocale, ".js")` + if obj.is_string() == Some(true) { + if let Some(str) = prop.as_str() { + // The String.prototype.concat method + if str == "concat" { + let mut values = vec![take(obj)]; + values.extend(take(args)); + + *value = JsValue::concat(values); + return true; + } + } + } + // without special handling, we convert it into a normal call like // `(obj.prop)(arg1, arg2, ...)` *value = JsValue::call( diff --git a/crates/turbopack-tests/tests/execution/turbopack/dynamic-requests/basic/input/index.js b/crates/turbopack-tests/tests/execution/turbopack/dynamic-requests/basic/input/index.js index ddb11fcf2fabd..9d272d883f96b 100644 --- a/crates/turbopack-tests/tests/execution/turbopack/dynamic-requests/basic/input/index.js +++ b/crates/turbopack-tests/tests/execution/turbopack/dynamic-requests/basic/input/index.js @@ -2,11 +2,14 @@ import * as a from "./dir/a.js"; import * as b from "./dir/b.ts"; const requireTemplate = (key) => require(`./dir/${key}`); -const requireConcat = (key) => require("./dir/" + key); +const requireAdd = (key) => require("./dir/" + key); +const requireConcat = (key) => require("./dir/".concat(key)); const importTemplate = (key) => import(`./dir/${key}`); const importTemplateSuffix = (key) => import(`./dir/${key}.js`); -const importConcat = (key) => import("./dir/" + key); -const importConcatSuffix = (key) => import("./dir/" + key + ".js"); +const importAdd = (key) => import("./dir/" + key); +const importAddSuffix = (key) => import("./dir/" + key + ".js"); +const importConcat = (key) => import("./dir/".concat(key)); +const importConcatSuffix = (key) => import("./dir/".concat(key, ".js")); it("should support dynamic requests in require with template literals", () => { expect(requireTemplate("a.js")).toBe(a); @@ -15,6 +18,13 @@ it("should support dynamic requests in require with template literals", () => { expect(requireTemplate("d.js")).toBe("d"); }); +it("should support dynamic requests in require with addition", () => { + expect(requireAdd("a.js")).toBe(a); + expect(requireAdd("b.ts")).toBe(b); + expect(requireAdd("c.module.css")).toHaveProperty("class"); + expect(requireAdd("d.js")).toBe("d"); +}); + it("should support dynamic requests in require with concatenation", () => { expect(requireConcat("a.js")).toBe(a); expect(requireConcat("b.ts")).toBe(b); @@ -37,6 +47,13 @@ it("should support dynamic requests in import with template literals and suffix" ); }); +it("should support dynamic requests in import with addition", async () => { + await expect(importAdd("a.js")).resolves.toBe(a); + await expect(importAdd("b.ts")).resolves.toBe(b); + await expect(importAdd("c.module.css")).resolves.toHaveProperty("class"); + await expect(importAdd("d.js")).resolves.toHaveProperty("default", "d"); +}); + it("should support dynamic requests in import with concatenation", async () => { await expect(importConcat("a.js")).resolves.toBe(a); await expect(importConcat("b.ts")).resolves.toBe(b); @@ -44,6 +61,11 @@ it("should support dynamic requests in import with concatenation", async () => { await expect(importConcat("d.js")).resolves.toHaveProperty("default", "d"); }); +it("should support dynamic requests in import with addition and suffix", async () => { + await expect(importAddSuffix("a")).resolves.toBe(a); + await expect(importAddSuffix("d")).resolves.toHaveProperty("default", "d"); +}); + it("should support dynamic requests in import with concatenation and suffix", async () => { await expect(importConcatSuffix("a")).resolves.toBe(a); await expect(importConcatSuffix("d")).resolves.toHaveProperty("default", "d"); @@ -51,18 +73,24 @@ it("should support dynamic requests in import with concatenation and suffix", as it("should throw an error when requesting a non-existent file", async () => { expect(() => requireTemplate("e.js")).toThrowError(); + expect(() => requireAdd("e.js")).toThrowError(); expect(() => requireConcat("e.js")).toThrowError(); await expect(importTemplate("e.js")).rejects.toThrowError(); + await expect(importAdd("e.js")).rejects.toThrowError(); await expect(importConcat("e.js")).rejects.toThrowError(); }); it("should support dynamic requests without the extension", async () => { expect(requireTemplate("a")).toBe(a); + expect(requireAdd("a")).toBe(a); expect(requireConcat("a")).toBe(a); expect(requireTemplate("d")).toBe("d"); + expect(requireAdd("d")).toBe("d"); expect(requireConcat("d")).toBe("d"); await expect(importTemplate("a")).resolves.toBe(a); await expect(importTemplate("d")).resolves.toHaveProperty("default", "d"); + await expect(importAdd("a")).resolves.toBe(a); + await expect(importAdd("d")).resolves.toHaveProperty("default", "d"); await expect(importConcat("a")).resolves.toBe(a); await expect(importConcat("d")).resolves.toHaveProperty("default", "d"); }); @@ -70,6 +98,8 @@ it("should support dynamic requests without the extension", async () => { it("should not support dynamic requests with double extension", async () => { await expect(importTemplateSuffix("a.js")).rejects.toThrowError(); await expect(importTemplateSuffix("d.js")).rejects.toThrowError(); + await expect(importAddSuffix("a.js")).rejects.toThrowError(); + await expect(importAddSuffix("d.js")).rejects.toThrowError(); await expect(importConcatSuffix("a.js")).rejects.toThrowError(); await expect(importConcatSuffix("d.js")).rejects.toThrowError(); });