Repository: googlemaps/js-api-loader (ts)

Issue:
Support for Advanced markers  (AdvancedMarkerView)
This issue has already been mentioned [here](https://github.com/googlemaps/js-api-loader/issues/699),
 but Advanced markers function doesn't seem to work.

I used this code but got an error.

_code_
```
import { Loader } from '@googlemaps/js-api-loader';

const loader = new Loader({
  apiKey: "",
  version: "beta",
  libraries: ["marker"]
});
```

_error message_
```
TypeError: Cannot read properties of undefined (reading 'AdvancedMarkerView')
```

For more details, please click here.
[stackoverflow.com](https://stackoverflow.com/questions/76452357/typeerror-cannot-read-properties-of-undefined-reading-advancedmarkerview)


Pull request description:
fix: Support for Advanced markers (AdvancedMarkerView)
fixes #802

Patch:
diff --git a/examples/index.html b/examples/index.html
index 9d24ee3..522496c 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -48,6 +48,7 @@
           lng: 0,
         },
         zoom: 4,
+        mapId: "DEMO_MAP_ID"
       };
 
       const loader = new google.maps.plugins.loader.Loader({
@@ -59,8 +60,10 @@
       // Promise for a specific library
       loader
         .importLibrary('maps')
-        .then(({Map}) => {
-          new Map(document.getElementById("map"), mapOptions);
+        .then(async ({Map}) => {
+          const map = new Map(document.getElementById("map"), mapOptions);
+          const {AdvancedMarkerElement} = await loader.importLibrary('marker');
+          new AdvancedMarkerElement({map, position: mapOptions.center});
         })
         .catch((e) => {
           // do something
diff --git a/src/index.ts b/src/index.ts
index b0d486a..434609c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -468,6 +468,7 @@ export class Loader {
   public importLibrary(
     name: "visualization"
   ): Promise<google.maps.VisualizationLibrary>;
+  public importLibrary(name: Library): Promise<unknown>;
   public importLibrary(name: Library): Promise<unknown> {
     this.execute();
     return google.maps.importLibrary(name);
@@ -496,7 +497,7 @@ export class Loader {
       key: this.apiKey,
       channel: this.channel,
       client: this.client,
-      libraries: this.libraries,
+      libraries: this.libraries.length && this.libraries,
       v: this.version,
       mapIds: this.mapIds,
       language: this.language,
@@ -511,7 +512,7 @@ export class Loader {
 
     if (!window?.google?.maps?.importLibrary) {
       // tweaked copy of https://developers.google.com/maps/documentation/javascript/load-maps-js-api#dynamic-library-import
-      // which also sets the url, the id, and the nonce
+      // which also sets the base url, the id, and the nonce
       /* eslint-disable */
       ((g) => {
         // @ts-ignore
@@ -552,7 +553,17 @@ export class Loader {
       /* eslint-enable */
     }
 
-    this.importLibrary("core").then(
+    // While most libraries populate the global namespace when loaded via bootstrap params,
+    // this is not the case for "marker" when used with the inline bootstrap loader
+    // (and maybe others in the future). So ensure there is an importLibrary for each:
+    const libraryPromises = this.libraries.map((library) =>
+      this.importLibrary(library)
+    );
+    // ensure at least one library, to kick off loading...
+    if (!libraryPromises.length) {
+      libraryPromises.push(this.importLibrary("core"));
+    }
+    Promise.all(libraryPromises).then(
       () => this.callback(),
       (error) => {
         const event = new ErrorEvent("error", { error }); // for backwards compat


Test patch:
diff --git a/src/index.test.ts b/src/index.test.ts
index 496d615..1e4618d 100644
--- a/src/index.test.ts
+++ b/src/index.test.ts
@@ -107,7 +107,20 @@ test("setScript adds a script to head with valid src", async () => {
   const script = document.head.childNodes[0] as HTMLScriptElement;
 
   expect(script.src).toEqual(
-    "https://maps.googleapis.com/maps/api/js?libraries=&key=foo&callback=google.maps.__ib__"
+    "https://maps.googleapis.com/maps/api/js?libraries=core&key=foo&callback=google.maps.__ib__"
+  );
+});
+
+test("setScript adds a script to head with valid src with libraries", async () => {
+  const loader = new Loader({ apiKey: "foo", libraries: ["marker", "places"] });
+
+  loader["setScript"]();
+  await 0;
+
+  const script = document.head.childNodes[0] as HTMLScriptElement;
+
+  expect(script.src).toEqual(
+    "https://maps.googleapis.com/maps/api/js?libraries=marker%2Cplaces&key=foo&callback=google.maps.__ib__"
   );
 });
 
@@ -184,6 +197,7 @@ test("script onerror should retry", async () => {
 
   // wait for the first failure
   await 0;
+  await 0;
   expect(loader["errors"].length).toBe(1);
   // trigger the retry delay:
   jest.runAllTimers();
@@ -207,6 +221,7 @@ test("script onerror should reset retry mechanism with next loader", async () =>
   let rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
   // wait for the first first failure
   await 0;
+  await 0;
   expect(loader["errors"].length).toBe(1);
   // trigger the retry delay:
   jest.runAllTimers();
@@ -221,6 +236,7 @@ test("script onerror should reset retry mechanism with next loader", async () =>
 
   // wait for the second first failure
   await 0;
+  await 0;
   expect(loader["errors"].length).toBe(1);
   // trigger the retry delay:
   jest.runAllTimers();
@@ -241,6 +257,7 @@ test("script onerror should not reset retry mechanism with parallel loaders", as
   const rejection2 = expect(loader.load()).rejects.toBeInstanceOf(Error);
   // wait for the first first failure
   await 0;
+  await 0;
   jest.runAllTimers();
 
   await Promise.all([rejection1, rejection2]);
@@ -404,3 +421,29 @@ test("importLibrary resolves correctly", async () => {
   const core = await corePromise;
   expect(core).toEqual({ core: "fake" });
 });
+
+test("importLibrary can also set up bootstrap libraries (if bootstrap libraries empty)", async () => {
+  const loader = new Loader({ apiKey: "foo" });
+  loader.importLibrary("marker");
+  loader.importLibrary("places");
+
+  await 0;
+
+  const script = document.head.childNodes[0] as HTMLScriptElement;
+
+  expect(script.src).toEqual(
+    "https://maps.googleapis.com/maps/api/js?libraries=core%2Cmarker%2Cplaces&key=foo&callback=google.maps.__ib__"
+  );
+});
+
+test("importLibrary resolves correctly even with different bootstrap libraries", async () => {
+  window.google = { maps: {} } as any;
+  google.maps.importLibrary = async (name) => ({ [name]: "fake" } as any);
+
+  const loader = new Loader({ apiKey: "foo", libraries: ["places"] });
+  const corePromise = loader.importLibrary("core");
+
+  const core = await corePromise;
+  expect(core).toEqual({ core: "fake" });
+  expect(await loader.importLibrary("places")).toEqual({ places: "fake" });
+});