Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer dynamic style operations until the current style is fully loaded #24

Merged
merged 1 commit into from
Nov 22, 2024

Conversation

ciscorn
Copy link
Member

@ciscorn ciscorn commented Nov 22, 2024

Close #2

This pull request includes several changes to improve the handling of map styles and ensure that operations are only performed when the map style is fully loaded.

The most important changes involve the introduction of a waitForStyleLoaded method and modifications to various components to utilize this method.

Summary by CodeRabbit

  • New Features

    • Enhanced handling of map properties and events, ensuring operations are performed only after the map's style is fully loaded.
    • Improved control flow for setting light, projection, terrain, and sky settings on the map.
    • Added support for managing sprites with synchronized addition and removal based on map readiness.
    • Introduced a new event listener for user location focus in the geolocate control.
    • Updated syntax highlighting for Svelte code in the CodeBlock component.
    • Minor updates to metadata descriptions for content files.
  • Bug Fixes

    • Added error handling to ensure operations on the map only proceed if it is initialized.
  • Documentation

    • Updated comments to clarify changes in the handling of map context and source management.
  • Refactor

    • Streamlined logic for updating map properties and event listeners, enhancing overall robustness.
    • Simplified the retrieval and management of sources and layers within the map context.
    • Enhanced type safety in the feature state management.
    • Adjusted ESLint configuration to suppress specific warnings for Svelte files.

Copy link

coderabbitai bot commented Nov 22, 2024

Walkthrough

The pull request introduces several modifications across multiple components in the MapLibre library, primarily focusing on ensuring that operations related to map properties and styles are executed only after the map's style is fully loaded. Key changes include the introduction of a private _map property in the MapContext class, enhanced error handling in various methods, and the implementation of a waitForStyleLoaded method. Other components, such as RawLayer, Light, Sky, and Terrain, have also been updated to utilize this method for setting properties, improving overall robustness and control flow.

Changes

File Path Change Summary
src/lib/maplibre/contexts.svelte.ts Introduced _map property, added getter/setter for map, enhanced error handling in addLayer, removeLayer, and addSource, added waitForStyleLoaded method.
src/lib/maplibre/layers/RawLayer.svelte Added firstRun variable, refactored property updates to use waitForStyleLoaded.
src/lib/maplibre/map/Light.svelte Updated light settings application to use waitForStyleLoaded.
src/lib/maplibre/map/MapLibre.svelte Adjusted map variable handling and simplified event listener logic.
src/lib/maplibre/map/Projection.svelte Changed projection setting to use waitForStyleLoaded.
src/lib/maplibre/map/Sky.svelte Updated sky settings application to use waitForStyleLoaded.
src/lib/maplibre/map/Sprite.svelte Modified sprite addition/removal to use waitForStyleLoaded.
src/lib/maplibre/map/Terrain.svelte Changed terrain setting to use waitForStyleLoaded.
src/lib/maplibre/sources/RawSource.svelte Altered source management to wait for style loading before retrieving sources, added firstRun logic.
src/routes/+layout.server.ts Commented out the load function, disabling cache control header settings.

Assessment against linked issues

Objective Addressed Explanation
Defer updating dynamic styles until the current style is fully loaded (2)

Possibly related PRs

🐇 In the meadow where the maps do play,
A new style waits for the perfect day.
With layers and sprites, all in a line,
The map shines bright, oh how divine!
So hop along, let’s set the scene,
For a world of wonders, fresh and green! 🌼


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ciscorn ciscorn changed the title Defer dynamic style operations Defer dynamic style operations until the current style is fully loaded Nov 22, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (11)
src/lib/maplibre/map/Sprite.svelte (2)

18-20: LGTM! Consider adding error handling.

The change to use waitForStyleLoaded for sprite addition aligns well with the PR objectives. However, sprite loading could potentially fail.

Consider adding error handling:

 mapCtx.waitForStyleLoaded((map) => {
-  map.addSprite(id, url);
+  try {
+    map.addSprite(id, url);
+  } catch (error) {
+    console.error(`Failed to add sprite ${id}:`, error);
+  }
 });

23-25: LGTM! Consider enhancing type safety.

The cleanup function correctly uses waitForStyleLoaded to ensure the sprite is removed only after the style is loaded.

Consider adding type annotations to enhance maintainability:

-mapCtx.waitForStyleLoaded((map) => {
+mapCtx.waitForStyleLoaded((map: maplibregl.Map) => {
   map.removeSprite(id);
 });
src/lib/maplibre/map/Light.svelte (2)

15-17: Consider adding error handling for style loading failures.

The implementation correctly defers the light setting operation until the style is loaded, which aligns with the PR objectives. However, it might benefit from explicit error handling.

Consider adding error handling:

 mapCtx.waitForStyleLoaded((map) => {
   map.setLight(mapCtx.userLight as maplibregl.LightSpecification);
+}).catch((error) => {
+  console.error('Failed to set light properties:', error);
 });

15-24: Consider implementing a centralized style loading state management.

While the current implementation successfully defers style operations, consider implementing a centralized loading state management system that could:

  1. Provide visual feedback during style loading
  2. Batch multiple style operations together
  3. Handle cleanup more gracefully during unmounting

This would improve the overall user experience and make the system more maintainable.

src/lib/maplibre/map/Terrain.svelte (3)

24-26: LGTM! Properly defers terrain operations.

The implementation correctly addresses the core issue by ensuring terrain operations only execute after the style is loaded.

The map parameter is unused in the callback. Consider removing it:

-mapCtx.waitForStyleLoaded((map) => {
+mapCtx.waitForStyleLoaded(() => {
🧰 Tools
🪛 eslint (1.23.1)

[error] 24-24: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


31-33: LGTM! Cleanup properly handles style loading state.

The cleanup logic correctly defers resetting terrain until the style is loaded.

Similarly, the map parameter is unused here. Consider removing it:

-mapCtx.waitForStyleLoaded((map) => {
+mapCtx.waitForStyleLoaded(() => {
🧰 Tools
🪛 eslint (1.23.1)

[error] 31-31: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


24-33: Consider strengthening type safety.

While the current implementation works, we could improve type safety around terrain specifications.

Consider adding type guards or assertions:

 mapCtx.waitForStyleLoaded(() => {
-  mapCtx.map?.setTerrain((mapCtx.userTerrain as maplibregl.TerrainSpecification) || null);
+  const terrain = mapCtx.userTerrain;
+  if (terrain && typeof terrain === 'object' && 'source' in terrain) {
+    mapCtx.map?.setTerrain(terrain);
+  } else {
+    mapCtx.map?.setTerrain(null);
+  }
 });
🧰 Tools
🪛 eslint (1.23.1)

[error] 24-24: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 31-31: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/sources/RawSource.svelte (1)

Line range hint 41-119: Consider refactoring effect blocks for better maintainability

The current implementation has multiple effect blocks with complex conditional logic for different source types. Consider:

  1. Extracting source-type-specific logic into separate functions or components
  2. Implementing a more robust state machine for handling the initialization sequence
  3. Adding error boundaries for better error handling

This would improve maintainability and make the code more testable.

Would you like me to provide a detailed refactoring proposal?

🧰 Tools
🪛 eslint (1.23.1)

[error] 120-120: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

src/lib/maplibre/map/MapLibre.svelte (1)

206-232: Consider refactoring property updates to reduce repetition

While the move event handler is functionally correct, the property update pattern is repeated for each transform property. Consider extracting this pattern into a helper function to improve maintainability.

+ function updateTransformProperty<T>(current: T | undefined, newValue: T, setter: (value: T) => void) {
+   if (current !== newValue) {
+     setter(newValue);
+   }
+ }

  map.on('move', () => {
    if (!map) {
      return;
    }
    const tr = map.transform;
    
    if (center) {
      const _center = maplibregl.LngLat.convert(center);
      if (_center.lat !== tr.center.lat || _center.lng !== tr.center.lng) {
        center = formatLngLat(center, tr.center);
      }
    } else {
      center = tr.center;
    }
-   if (tr.zoom !== zoom) {
-     zoom = tr.zoom;
-   }
-   if (tr.bearing !== bearing) {
-     bearing = tr.bearing;
-   }
-   if (tr.pitch !== pitch) {
-     pitch = tr.pitch;
-   }
-   if (tr.roll !== roll) {
-     roll = tr.roll;
-   }
-   if (tr.elevation !== elevation) {
-     elevation = tr.elevation;
-   }
+   updateTransformProperty(zoom, tr.zoom, z => zoom = z);
+   updateTransformProperty(bearing, tr.bearing, b => bearing = b);
+   updateTransformProperty(pitch, tr.pitch, p => pitch = p);
+   updateTransformProperty(roll, tr.roll, r => roll = r);
+   updateTransformProperty(elevation, tr.elevation, e => elevation = e);
  });
src/lib/maplibre/layers/RawLayer.svelte (2)

84-84: Consider using a more idiomatic approach instead of firstRun

The variable firstRun is introduced and initialized to true to control the execution of effects after the initial render. While this works, using Svelte's lifecycle methods might provide a cleaner solution.

Consider using the onMount function to handle initialization logic:

+import { onMount } from 'svelte';
 let firstRun = true;

-$effect(() => {
-    firstRun = false;
-});
+
+onMount(() => {
+    firstRun = false;
+});

This approach enhances clarity by explicitly handling component initialization.


104-118: Refactor repeated $effect blocks to reduce code duplication

Multiple $effect blocks share a similar structure for updating paint, layout, filter, and other properties. Refactoring these into a generic function could improve maintainability and reduce redundancy.

For example, you could create a utility function:

function updateProperties(propertyName, prevProps, props, updateFunc) {
    if (!firstRun) {
        mapCtx.waitForStyleLoaded((map) => {
            const keysRemoved = new Set(Object.keys(prevProps));
            const _props = $state.snapshot(props) ?? {};
            for (const [key, value] of Object.entries(_props)) {
                keysRemoved.delete(key);
                if (prevProps[key] !== value) {
                    updateFunc(map, id, key, value);
                }
            }
            for (const key of keysRemoved) {
                updateFunc(map, id, key, undefined);
            }
            prevProps = _props;
        });
    }
}

Then use it in your $effect blocks:

$effect(() => {
-    paint;
-    if (!firstRun) {
-        mapCtx.waitForStyleLoaded((map) => {
-            // existing code
-        });
-    }
+    updateProperties('paint', prevPaint, paint, (map, id, key, value) => {
+        map.setPaintProperty(id, key, value);
+    });
});

This refactoring enhances code readability and reduces the potential for errors.

Also applies to: 126-139, 145-147, 154-156, 162-164

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a6383cd and 310b42b.

📒 Files selected for processing (10)
  • src/lib/maplibre/contexts.svelte.ts (2 hunks)
  • src/lib/maplibre/layers/RawLayer.svelte (2 hunks)
  • src/lib/maplibre/map/Light.svelte (1 hunks)
  • src/lib/maplibre/map/MapLibre.svelte (2 hunks)
  • src/lib/maplibre/map/Projection.svelte (1 hunks)
  • src/lib/maplibre/map/Sky.svelte (1 hunks)
  • src/lib/maplibre/map/Sprite.svelte (1 hunks)
  • src/lib/maplibre/map/Terrain.svelte (1 hunks)
  • src/lib/maplibre/sources/RawSource.svelte (3 hunks)
  • src/routes/+layout.server.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/routes/+layout.server.ts
🧰 Additional context used
🪛 eslint (1.23.1)
src/lib/maplibre/layers/RawLayer.svelte

[error] 124-124: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 152-152: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

src/lib/maplibre/map/Projection.svelte

[error] 23-23: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/map/Terrain.svelte

[error] 24-24: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 31-31: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/sources/RawSource.svelte

[error] 120-120: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

🔇 Additional comments (17)
src/lib/maplibre/map/Light.svelte (1)

22-24: Consider immediate cleanup with style-loaded fallback.

The implementation safely resets light settings after waiting for style load. However, for cleanup operations, consider whether an immediate reset with a fallback mechanism might be more appropriate.

Let's verify if other components follow the same cleanup pattern:

src/lib/maplibre/map/Sky.svelte (2)

15-17: LGTM! Properly defers sky updates until style is loaded.

This change aligns perfectly with the PR objectives by ensuring that setSky operations only occur after the map style is fully loaded, preventing potential failures during initialization.


22-24: LGTM! Thorough cleanup implementation.

Good attention to detail in also deferring the cleanup operation. This ensures that reverting to the base sky settings is handled safely, maintaining consistency with the new style loading pattern.

src/lib/maplibre/map/Projection.svelte (1)

16-18: LGTM! Good improvement in projection handling.

The change correctly implements the deferral of projection setting until the map style is loaded, which aligns with the PR objectives and prevents potential race conditions.

src/lib/maplibre/sources/RawSource.svelte (2)

130-130: LGTM: Simplified rendering logic

The unconditional rendering of children is a good improvement. It simplifies the code and allows child components to handle their own loading states.


36-39: ⚠️ Potential issue

Review firstRun flag initialization logic

The firstRun flag is set to true after source initialization, which might prevent the effect blocks from running their update logic. Consider setting it to false here instead, as the source is now ready for updates.

 mapCtx.waitForStyleLoaded((map) => {
   source = map.getSource(id);
-  firstRun = true;
+  firstRun = false;
 });
src/lib/maplibre/map/MapLibre.svelte (3)

200-200: LGTM: Early map context initialization improves component coordination

Setting mapCtx.map immediately after map creation allows other components to access the map instance earlier and implement style loading checks.


202-204: LGTM: Simplified load event handler

The load event handler now focuses solely on setting the loaded state, as map context initialization has been moved earlier. This separation of concerns improves code clarity.


546-548: Verify child components implement style loading checks

The template now renders children as soon as the map instance is available, rather than waiting for the load event. Ensure all child components properly implement waitForStyleLoaded before performing style operations.

✅ Verification successful

Let me analyze the components that might need style loading checks more thoroughly.


Child components correctly implement style loading checks

The verification shows that all style operations are properly wrapped with waitForStyleLoaded:

  • Source components use waitForStyleLoaded before accessing map style
  • Layer components consistently use waitForStyleLoaded for all style operations (paint, layout, zoom, filter)
  • All map style operations (setPaintProperty, setLayoutProperty, addLayer, removeLayer) are properly handled through the context which enforces waitForStyleLoaded
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if child components properly implement style loading checks

# Look for direct style operations without waitForStyleLoaded
rg -l "(?:setPaintProperty|setLayoutProperty|addLayer|removeLayer|setStyle)" | \
  xargs rg -L "waitForStyleLoaded"

# Look for components that might need style loading checks
fd -e svelte | xargs rg -l "(?:style|layer|source|paint|layout)"

Length of output: 2898


Script:

#!/bin/bash
# Check implementation details of source and layer components
# to verify they properly handle style loading

# Check source components
fd -e svelte -p "sources/" | xargs rg "waitForStyleLoaded|onMount|beforeUpdate" -C 2

# Check layer components
fd -e svelte -p "layers/" | xargs rg "waitForStyleLoaded|onMount|beforeUpdate" -C 2

# Check if there are any direct map style operations without waiting
rg "map\.(setPaintProperty|setLayoutProperty|addLayer|removeLayer|setStyle)" -C 2

Length of output: 4168

src/lib/maplibre/layers/RawLayer.svelte (2)

127-127: ⚠️ Potential issue

Remove unused expression layout; to fix ESLint error

On line 127, the expression layout; is causing a linting error: "Expected an assignment or function call and instead saw an expression." Since the expression has no side effects, it triggers the linting rule.

Apply the following diff to correct the issue:

$effect(() => {
-    layout;
     if (!firstRun) {
         mapCtx.waitForStyleLoaded((map) => {
             // existing code
         });
     }
});

To ensure that the effect runs when layout changes, you might consider referencing layout within the effect in a way that satisfies the linter, such as including it in a conditional or utilizing it directly.

Likely invalid or redundant comment.


105-105: ⚠️ Potential issue

Remove unused expression paint; to fix ESLint error

The expression paint; on line 105 is causing a static analysis error: "Expected an assignment or function call and instead saw an expression." This occurs because the expression is evaluated but not used, leading to a linting issue.

Apply the following diff to remove the unused expression:

$effect(() => {
-    paint;
     if (!firstRun) {
         mapCtx.waitForStyleLoaded((map) => {
             // existing code
         });
     }
});

If the intention is to ensure the $effect reacts to changes in paint, consider referencing paint within the effect in a way that avoids the unused expression error. For example, you could use it in a comment or restructure the effect to depend on paint.

Likely invalid or redundant comment.

src/lib/maplibre/contexts.svelte.ts (6)

47-61: Event listener management in map getter and setter is correctly implemented

The getter and setter for the map property properly handle adding and removing event listeners when the map instance is set or changed, ensuring that the _onstyledata listener is correctly registered and deregistered.


64-68: Proper error handling and deferred execution in addLayer method

The addLayer method correctly checks if the map is initialized and utilizes waitForStyleLoaded to defer the addition of the layer until the style is fully loaded, preventing potential errors.


71-75: Correct usage of waitForStyleLoaded in removeLayer method

The removeLayer method appropriately defers the layer removal until the style is loaded, ensuring safe operation.


80-82: Deferred execution in addSource method is well-implemented

The addSource method correctly adds the source after the style has loaded by using waitForStyleLoaded.


86-89: Proper deferral in removeSource method

The removeSource method ensures the source is removed only after the style is fully loaded, preventing potential issues.


22-22: ⚠️ Potential issue

Invalid use of $state.raw(null) for class property initialization

The use of $state.raw(null) is not a standard or documented Svelte API. In Svelte, $ is used for reactive statements within components, not for initializing class properties. You should initialize _map directly to null or use Svelte stores if reactivity is required.

Apply this diff to correct the initialization:

-	_map: MapLibre | null = $state.raw(null);
+	_map: MapLibre | null = null;

Likely invalid or redundant comment.

src/lib/maplibre/map/Projection.svelte Outdated Show resolved Hide resolved
src/lib/maplibre/sources/RawSource.svelte Show resolved Hide resolved
src/lib/maplibre/layers/RawLayer.svelte Show resolved Hide resolved
src/lib/maplibre/contexts.svelte.ts Show resolved Hide resolved
src/lib/maplibre/contexts.svelte.ts Show resolved Hide resolved
@ciscorn ciscorn force-pushed the defer-until-style-loaded branch from 310b42b to d6e788b Compare November 22, 2024 04:18
Copy link

cloudflare-workers-and-pages bot commented Nov 22, 2024

Deploying svelte-maplibre-gl with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2960500
Status: ✅  Deploy successful!
Preview URL: https://e376d153.svelte-maplibre-gl.pages.dev
Branch Preview URL: https://defer-until-style-loaded.svelte-maplibre-gl.pages.dev

View logs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (10)
src/lib/maplibre/map/Terrain.svelte (2)

24-26: Simplify the callback and remove redundant null checks.

The implementation correctly defers terrain setting until style is loaded, but can be improved:

  1. The map parameter is unused and can be removed
  2. The null check is redundant since we validate map existence at initialization

Consider this improvement:

-    mapCtx.waitForStyleLoaded((map) => {
-      mapCtx.map?.setTerrain((mapCtx.userTerrain as maplibregl.TerrainSpecification) || null);
-    });
+    mapCtx.waitForStyleLoaded(() => {
+      mapCtx.map.setTerrain((mapCtx.userTerrain as maplibregl.TerrainSpecification) || null);
+    });
🧰 Tools
🪛 eslint (1.23.1)

[error] 24-24: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


31-33: Apply similar simplifications to the cleanup code.

The cleanup logic is correct, but can be improved in the same way as the previous segment.

Consider this improvement:

-    mapCtx.waitForStyleLoaded((map) => {
-      mapCtx.map?.setTerrain(mapCtx.baseTerrain ?? null);
-    });
+    mapCtx.waitForStyleLoaded(() => {
+      mapCtx.map.setTerrain(mapCtx.baseTerrain ?? null);
+    });
🧰 Tools
🪛 eslint (1.23.1)

[error] 31-31: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/layers/RawLayer.svelte (5)

84-87: Remove unused parameter in callback

The map parameter in the callback is unused. Since mapCtx.addLayer is used instead of direct map operations, we can remove the parameter.

-mapCtx.waitForStyleLoaded((map) => {
+mapCtx.waitForStyleLoaded(() => {
   mapCtx.addLayer(addLayerObj, beforeId);
});
🧰 Tools
🪛 eslint (1.23.1)

[error] 85-85: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


106-120: Consider extracting property update logic

The property update logic is duplicated between paint and layout effects. Consider extracting this into a reusable helper function to improve maintainability and reduce code duplication.

+function updateProperties(
+  map: maplibregl.Map,
+  layerId: string,
+  newProps: Record<string, unknown>,
+  prevProps: Record<string, unknown>,
+  setter: (id: string, key: string, value: unknown) => void
+) {
+  const keysRemoved = new Set(Object.keys(prevProps));
+  for (const [key, value] of Object.entries(newProps)) {
+    keysRemoved.delete(key);
+    if (prevProps[key] !== value) {
+      setter(layerId, key, value);
+    }
+  }
+  for (const key of keysRemoved) {
+    setter(layerId, key, undefined);
+  }
+  return newProps;
+}

 $effect(() => {
   paint;
   if (!firstRun) {
     mapCtx.waitForStyleLoaded((map) => {
-      const keysRemoved = new Set(Object.keys(prevPaint));
-      const _paint = $state.snapshot(paint) ?? {};
-      for (const [key, value] of Object.entries(_paint)) {
-        keysRemoved.delete(key);
-        if (prevPaint[key] !== value) {
-          map.setPaintProperty(id, key, value);
-        }
-      }
-      for (const key of keysRemoved) {
-        map.setPaintProperty(id, key, undefined);
-      }
-      prevPaint = _paint;
+      const _paint = $state.snapshot(paint) ?? {};
+      prevPaint = updateProperties(
+        map,
+        id,
+        _paint,
+        prevPaint,
+        map.setPaintProperty.bind(map)
+      );
     });
   }
 });

126-126: Fix ESLint warning about unused expression

The layout; expression is needed for reactivity but triggers an ESLint warning. Consider using the value explicitly.

 $effect(() => {
-  layout;
+  const _ = layout; // Explicit usage for reactivity
   if (!firstRun) {
🧰 Tools
🪛 eslint (1.23.1)

[error] 126-126: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


164-166: Remove unnecessary optional chaining

The optional chaining operator (?.) on map is unnecessary since waitForStyleLoaded guarantees that the map instance exists.

 mapCtx.waitForStyleLoaded((map) => {
-  map?.moveLayer(id, beforeId);
+  map.moveLayer(id, beforeId);
 });

Line range hint 84-166: Implementation successfully addresses the PR objective

The changes effectively implement the deferral of style operations until the map style is fully loaded. The use of firstRun flag and waitForStyleLoaded ensures that operations are executed at the appropriate time, preventing potential failures.

Consider implementing error handling within the waitForStyleLoaded callbacks to gracefully handle any failures during style updates.

🧰 Tools
🪛 eslint (1.23.1)

[error] 105-105: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 126-126: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 154-154: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

src/lib/maplibre/contexts.svelte.ts (2)

Line range hint 22-61: LGTM! Consider adding null check in the event listener cleanup.

The encapsulation of the map property with proper event listener management is well implemented. However, there's a minor potential for a null reference when cleaning up the event listener.

Consider this small safety improvement:

  set map(value: maplibregl.Map | null) {
    if (this._listener) {
-     this._map?.off('styledata', this._listener);
+     if (this._map) {
+       this._map.off('styledata', this._listener);
+     }
      this._listener = undefined;
    }

105-113: Consider adding error handling for pending operations.

While the implementation is correct, consider adding try-catch blocks to prevent a single failing operation from blocking subsequent ones.

Consider this improvement:

  private _onstyledata(ev: maplibregl.MapStyleDataEvent) {
    const map = ev.target;
    if (map?.style._loaded) {
      for (const func of this._pending) {
-       // call pending tasks
-       func(map);
+       try {
+         func(map);
+       } catch (error) {
+         console.error('Error executing pending map operation:', error);
+       }
      }
      this._pending = [];
    }
  }
src/lib/maplibre/map/MapLibre.svelte (1)

206-232: Consider optimizing property updates in move event handler.

The move event handler is well-structured, but could be optimized to reduce property comparisons and updates.

Consider batching the property updates:

-			if (tr.zoom !== zoom) {
-				zoom = tr.zoom;
-			}
-			if (tr.bearing !== bearing) {
-				bearing = tr.bearing;
-			}
-			if (tr.pitch !== pitch) {
-				pitch = tr.pitch;
-			}
-			if (tr.roll !== roll) {
-				roll = tr.roll;
-			}
-			if (tr.elevation !== elevation) {
-				elevation = tr.elevation;
-			}
+			const updates = {
+				zoom: tr.zoom,
+				bearing: tr.bearing,
+				pitch: tr.pitch,
+				roll: tr.roll,
+				elevation: tr.elevation
+			};
+			
+			Object.entries(updates).forEach(([prop, value]) => {
+				if (value !== this[prop]) {
+					this[prop] = value;
+				}
+			});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 310b42b and d6e788b.

📒 Files selected for processing (11)
  • src/content/examples/terrain/content.svelte.md (1 hunks)
  • src/lib/maplibre/contexts.svelte.ts (2 hunks)
  • src/lib/maplibre/layers/RawLayer.svelte (2 hunks)
  • src/lib/maplibre/map/Light.svelte (1 hunks)
  • src/lib/maplibre/map/MapLibre.svelte (2 hunks)
  • src/lib/maplibre/map/Projection.svelte (1 hunks)
  • src/lib/maplibre/map/Sky.svelte (1 hunks)
  • src/lib/maplibre/map/Sprite.svelte (1 hunks)
  • src/lib/maplibre/map/Terrain.svelte (1 hunks)
  • src/lib/maplibre/sources/RawSource.svelte (3 hunks)
  • src/routes/+layout.server.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/content/examples/terrain/content.svelte.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/lib/maplibre/map/Light.svelte
  • src/lib/maplibre/map/Sky.svelte
  • src/lib/maplibre/map/Sprite.svelte
  • src/routes/+layout.server.ts
🧰 Additional context used
📓 Learnings (1)
src/lib/maplibre/contexts.svelte.ts (1)
Learnt from: ciscorn
PR: MIERUNE/svelte-maplibre-gl#24
File: src/lib/maplibre/contexts.svelte.ts:91-103
Timestamp: 2024-11-22T03:00:21.632Z
Learning: In MapLibre GL, `map.isStyleLoaded()` may not be unreliable due to known issues (e.g., https://github.com/mapbox/mapbox-gl-js/issues/8691). It's acceptable to access `map.style._loaded` directly to check if the style is loaded.
🪛 eslint (1.23.1)
src/lib/maplibre/layers/RawLayer.svelte

[error] 85-85: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 126-126: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 154-154: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

src/lib/maplibre/map/Projection.svelte

[error] 23-23: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/map/Terrain.svelte

[error] 24-24: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 31-31: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/sources/RawSource.svelte

[error] 120-120: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

🔇 Additional comments (11)
src/lib/maplibre/map/Projection.svelte (2)

16-18: LGTM! Properly defers projection update until style is loaded.

The implementation correctly uses waitForStyleLoaded to ensure the projection is set only after the style is fully loaded, aligning with the PR's objectives.


23-25: ⚠️ Potential issue

Previous review suggestions not addressed.

The code still uses mapCtx.map?.setProjection instead of the provided map parameter, and the optional chaining is unnecessary since we have the map parameter from waitForStyleLoaded.

Apply this diff to fix the issues:

 mapCtx.waitForStyleLoaded((map) => {
-  mapCtx.map?.setProjection(mapCtx.baseProjection ?? { type: 'mercator' });
+  map.setProjection(mapCtx.baseProjection ?? { type: 'mercator' });
 });
🧰 Tools
🪛 eslint (1.23.1)

[error] 23-23: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/map/Terrain.svelte (1)

24-33: Implementation successfully addresses style loading concerns.

The changes effectively implement the deferral of terrain operations until the style is loaded, aligning perfectly with the PR objectives. The implementation is robust, handling both initial setup and cleanup appropriately.

🧰 Tools
🪛 eslint (1.23.1)

[error] 24-24: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)


[error] 31-31: 'map' is defined but never used.

(@typescript-eslint/no-unused-vars)

src/lib/maplibre/sources/RawSource.svelte (3)

120-122: Previous review comment is still applicable

The current implementation has the same issues as noted in the previous review:

  1. The bare source expression triggers a linting error
  2. Setting firstRun here might race with the initialization logic
🧰 Tools
🪛 eslint (1.23.1)

[error] 120-120: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


130-130: LGTM! Improved children rendering

The unconditional rendering of children is correct since source-dependent operations are properly handled in the effects.


Line range hint 108-114: Track MapLibre fix for clusterRadius

The current implementation uses a workaround to set clusterRadius through workerOptions due to a limitation in maplibre-gl-js. Consider:

  1. Adding a link to the MapLibre issue in the TODO comment
  2. Creating a GitHub issue to track when this can be simplified after the MapLibre fix
src/lib/maplibre/contexts.svelte.ts (3)

64-89: LGTM! Robust error handling and proper style loading checks.

The implementation correctly ensures that:

  1. Map instance exists before operations
  2. Layer/source operations are deferred until style is loaded
  3. Set management (add/remove tracking) happens immediately

91-103: LGTM! Correct implementation of style loading check.

The implementation correctly uses map.style._loaded instead of the unreliable isStyleLoaded() method, as documented in mapbox-gl-js#8691. The function properly handles both immediate execution and queuing of operations.


Line range hint 22-113: Verify the implementation in dependent components.

The changes successfully implement the deferral of dynamic style operations. To ensure complete coverage, let's verify the implementation in dependent components.

✅ Verification successful

Implementation is correctly used across all dependent components

The verification shows that all components using style-related operations (setTerrain, setSky, setProjection, setLight, setPaintProperty, setLayoutProperty) are already accessing these through the MapContext. These components (Terrain.svelte, Sky.svelte, Projection.svelte, Light.svelte, RawLayer.svelte) properly obtain the map context using getMapContext(), which ensures they inherit the style loading deferral mechanism implemented in the context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all components that might need to use waitForStyleLoaded

# Look for components that directly use MapContext
echo "Components using MapContext:"
rg "getMapContext|MapContext" --type svelte

# Look for components that might be setting style properties
echo -e "\nComponents potentially setting style properties:"
rg "setLayoutProperty|setPaintProperty|setLight|setTerrain|setSky|setProjection" --type svelte

Length of output: 384


Script:

#!/bin/bash
# Description: Find all components that might need to use waitForStyleLoaded

# Look for components that directly use MapContext
echo "Components using MapContext:"
rg "getMapContext|MapContext"

# Look for components that might be setting style properties
echo -e "\nComponents potentially setting style properties:"
rg "setLayoutProperty|setPaintProperty|setLight|setTerrain|setSky|setProjection"

# Look for files importing from contexts.svelte.ts
echo -e "\nFiles importing from contexts.svelte.ts:"
rg "from ['\"].*contexts\.svelte['\"]"

Length of output: 6054

src/lib/maplibre/map/MapLibre.svelte (2)

546-548: Verify that child components handle style loading state.

The condition for rendering children has been simplified to only check for map existence. While this aligns with the PR objective, we should verify that child components properly defer their style operations.

Let's check if child components use waitForStyleLoaded:

✅ Verification successful

Let me gather more information to verify if there are any other components that might interact with the map style.


Child components safely handle style loading state

Based on the analysis of the codebase:

  • RawLayer component consistently uses waitForStyleLoaded for all style operations (paint and layout properties)
  • RawSource component properly uses waitForStyleLoaded before adding sources
  • Other components (Sky, Light, Projection, etc.) don't directly interact with style operations
  • The MapLibre component provides the style loading event handlers that child components can utilize
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for child components that might perform style operations
fd -e svelte | xargs rg "setPaintProperty|setLayoutProperty" -l | while read -r file; do
    echo "Checking $file for waitForStyleLoaded usage:"
    rg "waitForStyleLoaded" "$file" || echo "WARNING: No waitForStyleLoaded found in $file"
done

Length of output: 564


Script:

#!/bin/bash
# Search for components that use map context and might interact with styles
fd -e svelte | xargs rg "getMap\(\)|map\." -l | while read -r file; do
    echo "=== $file ==="
    # Show style-related operations
    rg "style|layer|paint|layout|source" "$file" -A 2
done

Length of output: 7918


200-200: LGTM: Early map context initialization improves reliability.

Setting mapCtx.map immediately after map creation ensures that the map context is available as early as possible for style operations.

Let's verify that no style operations occur before this initialization:

✅ Verification successful

Let me gather more context about the map initialization and style operations to ensure a thorough verification.


Let me check one more aspect to understand how style operations are protected.


LGTM: Map initialization and style operations are properly synchronized

The early map context initialization at line 200 is safe because:

  1. All style operations are wrapped in waitForStyleLoaded which ensures the style is loaded before execution
  2. The implementation of waitForStyleLoaded checks both map existence and style load state
  3. Style operations are queued in _pending if the style isn't loaded yet
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any style operations that might execute before map initialization
rg -l "setPaintProperty|setLayoutProperty" | xargs rg "map\." -B 5 -A 5

Length of output: 1276


Script:

#!/bin/bash
# Search for waitForStyleLoaded implementation and usage
ast-grep --pattern 'waitForStyleLoaded' | head -n 20

# Search for map context initialization
rg "mapCtx\.map\s*=" -B 5 -A 5

Length of output: 1212


Script:

#!/bin/bash
# Search for the implementation of waitForStyleLoaded to understand how it protects style operations
rg "waitForStyleLoaded.*=.*" -A 10

Length of output: 10939

src/lib/maplibre/sources/RawSource.svelte Show resolved Hide resolved
@ciscorn ciscorn force-pushed the defer-until-style-loaded branch from d6e788b to e1c0328 Compare November 22, 2024 05:03
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (10)
src/content/CodeBlock.svelte (3)

Line range hint 1-10: Consider async initialization and flexible configuration.

The current synchronous initialization could block the main thread. Consider these improvements:

  1. Use createHighlighterCore() (async) instead of createHighlighterCoreSync()
  2. Support multiple themes/languages through component props
  3. Document the rationale for using the regex engine over oniguruma

Example implementation:

<script lang="ts" module>
-	import { createHighlighterCoreSync, createJavaScriptRegexEngine } from 'shiki';
+	import { createHighlighterCore, createJavaScriptRegexEngine } from 'shiki';
	import svelte from 'shiki/langs/svelte.mjs';
	import dark from 'shiki/themes/github-dark-default.mjs';

-	export const shiki = createHighlighterCoreSync({
+	// Using regex engine for better browser compatibility and smaller bundle size
+	export const shiki = await createHighlighterCore({
		themes: [dark],
		langs: [svelte],
		engine: createJavaScriptRegexEngine()
	});
</script>

Line range hint 12-20: Make language and theme configurable via props.

The component could be more reusable by accepting language and theme as props instead of hard-coding them.

Example implementation:

<script lang="ts">
	let {
-		content
+		content,
+		lang = 'svelte',
+		theme = 'github-dark-default'
	}: {
		content: string;
+		lang?: string;
+		theme?: string;
	} = $props();

	const highlighted = $derived.by(() => {
-		return shiki.codeToHtml(content.trim(), { lang: 'svelte', theme: 'github-dark-default' });
+		return shiki.codeToHtml(content.trim(), { lang, theme });
	});
</script>

26-26: Make ESLint directive more specific and consider scoping styles.

The ESLint disable comment is correctly used, but could be more specific. Also, consider scoping the styles to prevent conflicts.

  1. Make the ESLint comment more specific:
-	<!-- eslint-disable-next-line svelte/no-at-html-tags -->
+	<!-- eslint-disable-next-line svelte/no-at-html-tags -- Safe since content is sanitized by Shiki -->
  1. Consider scoping the styles to prevent conflicts with other code blocks:
<style>
-	:global(.shiki code) {
+	:global(.codeblock-content .shiki code) {

And update the template:

-<div class="my-6 subpixel-antialiased">
+<div class="my-6 subpixel-antialiased codeblock-content">
src/lib/maplibre/controls/GeolocateControl.svelte (1)

10-10: LGTM! Consider adding JSDoc comment.

The type change from Object to object is correct and follows TypeScript best practices. Consider adding a JSDoc comment to document the purpose of this type.

+/** Event type for GeolocateControl events that includes both MapLibre event properties and standard object properties */
 type GeolocateEvent = Event<maplibregl.GeolocateControl> & object;
src/lib/maplibre/layers/RawLayer.svelte (4)

106-120: Consider adding type safety for paint properties

The implementation correctly defers paint updates and handles property changes efficiently. Consider adding type safety by using MapLibre's type definitions:

-const _paint = $state.snapshot(paint) ?? {};
+const _paint = $state.snapshot(paint) ?? {} as maplibregl.LayerSpecification['paint'];

This would help catch potential paint property type mismatches at compile time.


127-141: Fix unused expression warning while maintaining reactivity

The layout update implementation is correct, but there's a static analysis warning about the unused layout expression.

 $effect(() => {
-    layout;
+    const currentLayout = layout;
     if (!firstRun) {
         mapCtx.waitForStyleLoaded((map) => {
-            const _layout = $state.snapshot(layout) ?? {};
+            const _layout = $state.snapshot(currentLayout) ?? {};
             // ... rest of the code
         });
     }
 });

156-158: Fix unused expression warning in filter effect

Similar to the layout effect, there's a static analysis warning about the unused filter expression.

 $effect(() => {
-    filter;
+    const currentFilter = filter;
     if (!firstRun) {
         mapCtx.waitForStyleLoaded((map) => {
-            map.setFilter(id, filter);
+            map.setFilter(id, currentFilter);
         });
     }
 });

Line range hint 84-166: Consider adding error handling for map operations

While the implementation successfully defers operations until the style is loaded, consider adding error handling for map operations to improve robustness:

 mapCtx.waitForStyleLoaded((map) => {
-    map.setPaintProperty(id, key, value);
+    try {
+        map.setPaintProperty(id, key, value);
+    } catch (error) {
+        console.error(`Failed to set paint property ${key} for layer ${id}:`, error);
+    }
 });

This pattern could be applied to all map operations to ensure graceful handling of potential runtime errors.

🧰 Tools
🪛 eslint (1.23.1)

[error] 105-105: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 126-126: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 154-154: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

src/lib/maplibre/contexts.svelte.ts (2)

47-61: LGTM! Well-implemented getter/setter with proper event management

The implementation correctly manages the event listener lifecycle. One minor suggestion to improve null safety:

- this._map?.on('styledata', this._listener);
+ if (this._map) {
+   this._map.on('styledata', this._listener);
+ }

64-89: Standardize null checking pattern across methods

The implementation correctly defers operations until style is loaded. However, there's an inconsistency in null checking:

  addSource(id: string, source: SourceSpecification | CanvasSourceSpecification) {
    this.userSources.add(id);
    this.waitForStyleLoaded((map) => {
-     map?.addSource(id, source);
+     map.addSource(id, source);
    });
  }
  removeSource(id: string) {
    this.userSources.delete(id);
    this.waitForStyleLoaded((map) => {
-     map?.removeSource(id);
+     map.removeSource(id);
    });
  }

The map parameter in the callback will never be null since waitForStyleLoaded only executes the callback when the map is available.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d6e788b and e1c0328.

📒 Files selected for processing (16)
  • src/content/CodeBlock.svelte (1 hunks)
  • src/content/examples/terrain/content.svelte.md (1 hunks)
  • src/lib/maplibre/contexts.svelte.ts (2 hunks)
  • src/lib/maplibre/controls/GeolocateControl.svelte (2 hunks)
  • src/lib/maplibre/controls/Hash.svelte (1 hunks)
  • src/lib/maplibre/layers/RawLayer.svelte (2 hunks)
  • src/lib/maplibre/map/Light.svelte (1 hunks)
  • src/lib/maplibre/map/MapLibre.svelte (2 hunks)
  • src/lib/maplibre/map/Projection.svelte (1 hunks)
  • src/lib/maplibre/map/Sky.svelte (1 hunks)
  • src/lib/maplibre/map/Sprite.svelte (1 hunks)
  • src/lib/maplibre/map/Terrain.svelte (1 hunks)
  • src/lib/maplibre/sources/FeatureState.svelte (1 hunks)
  • src/lib/maplibre/sources/RawSource.svelte (3 hunks)
  • src/routes/+layout.server.ts (1 hunks)
  • src/routes/Header.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/content/examples/terrain/content.svelte.md
  • src/lib/maplibre/map/Light.svelte
  • src/lib/maplibre/map/MapLibre.svelte
  • src/lib/maplibre/map/Projection.svelte
  • src/lib/maplibre/map/Sky.svelte
  • src/lib/maplibre/map/Sprite.svelte
  • src/lib/maplibre/map/Terrain.svelte
  • src/routes/+layout.server.ts
🧰 Additional context used
📓 Learnings (1)
src/lib/maplibre/contexts.svelte.ts (1)
Learnt from: ciscorn
PR: MIERUNE/svelte-maplibre-gl#24
File: src/lib/maplibre/contexts.svelte.ts:91-103
Timestamp: 2024-11-22T03:00:21.632Z
Learning: In MapLibre GL, `map.isStyleLoaded()` may not be unreliable due to known issues (e.g., https://github.com/mapbox/mapbox-gl-js/issues/8691). It's acceptable to access `map.style._loaded` directly to check if the style is loaded.
🪛 eslint (1.23.1)
src/lib/maplibre/layers/RawLayer.svelte

[error] 126-126: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)


[error] 154-154: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

src/lib/maplibre/sources/RawSource.svelte

[error] 120-120: Expected an assignment or function call and instead saw an expression.

(@typescript-eslint/no-unused-expressions)

🪛 Gitleaks (8.21.1)
src/routes/Header.svelte

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (15)
src/lib/maplibre/controls/Hash.svelte (3)

10-10: LGTM: Clean removal of unused props destructuring

The removal of the empty props destructuring improves code cleanliness by eliminating dead code.


Line range hint 13-19: LGTM: Map initialization and Hash setup

The implementation correctly:

  1. Verifies map initialization before proceeding
  2. Sets up the Hash control
  3. Updates the hash if needed

Since Hash control doesn't modify map styles, it doesn't need to wait for style loading, making this implementation appropriate within the context of the PR's style loading improvements.


Line range hint 3-3: Verify SvelteKit compatibility issues

The FIXME comment suggests potential compatibility issues with SvelteKit. This should be investigated to ensure proper integration with SvelteKit's routing system.

src/content/CodeBlock.svelte (1)

Line range hint 1-43: Verify the inclusion of CodeBlock.svelte changes in this PR.

The changes to this file appear to be unrelated to the PR's main objective of "Deferring dynamic style operations until the current style is fully loaded." Please confirm if these changes should be part of a separate PR for better change tracking and review focus.

src/lib/maplibre/sources/FeatureState.svelte (2)

10-10: Good type safety improvement!

Changing from Record<string, any> to Record<string, unknown> enhances type safety by requiring explicit type checking before using state values, which helps prevent runtime errors.


10-10: Verify usage in existing code

This type change might affect code that relies on the previous any type. Let's verify all usages to ensure they handle the stricter unknown type correctly.

✅ Verification successful

Let me check the actual usage of the state in the component to verify if the type change impacts the code.


Type change from any to unknown is safe in this context

The change is safe because:

  1. The only usage found passes a simple object literal { hover: true } which is compatible with Record<string, unknown>
  2. The component itself doesn't perform any operations on the state that would require type assertions
  3. The state is directly passed to MapLibre's feature state API which expects a generic object
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components that pass state to FeatureState
rg -l "FeatureState.*state=" | while read -r file; do
  echo "=== $file ==="
  # Show the context of how state is passed
  rg -A 5 "FeatureState.*state=" "$file"
done

Length of output: 374


Script:

#!/bin/bash
# Check the FeatureState component implementation
ast-grep --pattern 'export let state'

# Also check for any type assertions or operations on the state prop
rg -A 5 -B 5 'state:.*Record.*unknown' src/lib/maplibre/sources/FeatureState.svelte

Length of output: 485

src/lib/maplibre/controls/GeolocateControl.svelte (1)

49-49: LGTM! Event listener implementation is correct.

The new event listener for 'userlocationfocus' is properly implemented using the resetEventListener utility, consistent with other event handlers in the component.

src/lib/maplibre/sources/RawSource.svelte (3)

35-39: Implementation successfully addresses the PR objectives.

The use of waitForStyleLoaded effectively ensures that source operations are deferred until the style is fully loaded, which directly addresses the core issue. The implementation is robust and consistent with the PR's goals.


130-130: Verify the safety of unconditional children rendering.

The template now renders children unconditionally, which might cause issues if child components depend on the source being initialized. Consider whether we should wait for the source to be available before rendering children.

✅ Verification successful

Unconditional children rendering is safe in this context

The unconditional rendering of children is actually correct here because:

  1. The source prop is properly initialized with a default value of undefined
  2. The source context (sourceCtx) is prepared and its ID is set before any children rendering
  3. Child components can safely access the source context even before the source is fully initialized
  4. The component properly handles cleanup by removing the source on destroy
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for child components that might depend on source initialization
rg -A 5 "let\s+{\s*source\s*}" --type svelte

# Look for potential source-dependent props in child components
ast-grep --pattern 'export let source = $_'

Length of output: 202


Script:

#!/bin/bash
# Search for child components that might depend on source initialization
rg "let source" --type svelte -A 5

# Look for source usage in child components
rg "export let source" --type svelte

# Check if there are any components using source prop
rg "prop: source" --type svelte

Length of output: 202


Script:

#!/bin/bash
# Search for child components that might depend on source initialization
rg "let source"

# Look for source usage in child components
rg "export let source"

# Check the content of RawSource.svelte to understand the context better
cat src/lib/maplibre/sources/RawSource.svelte

Length of output: 3774


Line range hint 101-108: Verify the clustering implementation and MapLibre GL compatibility.

The TODO comment suggests there's a pending fix needed in maplibre-gl-js for clusterRadius. Let's verify the current workaround's effectiveness.

✅ Verification successful

Clustering implementation is correctly handled with proper scaling

The implementation in RawSource.svelte correctly handles the clusterRadius scaling by multiplying it with the ratio 8192 / source.tileSize. This is a valid approach because:

  1. The example in clusters/Clusters.svelte demonstrates proper usage with dynamic radius updates
  2. The implementation follows MapLibre's GeoJSON source specification for clustering
  3. The scaling factor (8192/tileSize) is necessary to maintain consistent cluster sizes across different tile sizes

The TODO comment can be safely removed as the current implementation is working as expected.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's an open issue or PR in maplibre-gl-js about clusterRadius
gh api graphql -f query='
{
  search(query: "repo:maplibre/maplibre-gl-js is:issue is:open clusterRadius", type: ISSUE, first: 5) {
    nodes {
      ... on Issue {
        title
        url
        state
      }
    }
  }
}
'

# Search for any related clustering tests or examples
rg -l "clusterRadius|setClusterOptions" --type ts

Length of output: 312


Script:

#!/bin/bash
# Let's check the implementation details and usage of clustering in the codebase
# First, search for cluster-related code patterns
rg -A 5 "cluster.*radius|supercluster" --type ts --type svelte

# Then, check for any tests or documentation related to clustering
fd -e md -e test.ts -e spec.ts . | xargs rg -l "cluster"

# Finally, check the actual source implementation
ast-grep --pattern 'spec.clusterRadius !== undefined { $$$$ }'

Length of output: 265


Script:

#!/bin/bash
# Let's check the clustering example content and implementation
cat src/content/examples/clusters/content.svelte.md

# Search with correct file type for Svelte
rg -A 5 "cluster.*radius|supercluster" --type-add 'svelte:*.svelte' --type svelte

# Check for any related source files
fd -e svelte . | xargs rg -l "clusterRadius|GeoJSONSource"

Length of output: 1325


Script:

#!/bin/bash
# Let's check the actual implementation in the example and GeoJSONSource
cat src/content/examples/clusters/Clusters.svelte
cat src/lib/maplibre/sources/GeoJSONSource.svelte

# Check if there are any related tests
fd -e test.ts -e spec.ts . | xargs rg -l "GeoJSONSource|clusterRadius"

# Look for any related documentation about clustering implementation
fd README.md | xargs rg -l "cluster"

Length of output: 2994

src/lib/maplibre/layers/RawLayer.svelte (3)

84-87: LGTM: Safe layer initialization implementation

The implementation correctly defers layer addition until the style is loaded, which aligns with the PR objectives. The firstRun flag is properly used to track the initial render.


147-149: LGTM: Safe zoom range update implementation

The implementation correctly handles undefined values and uses appropriate zoom range defaults (0 to 22).


164-166: LGTM: Safe layer movement implementation

The implementation correctly checks for beforeId existence and safely defers the layer movement operation.

src/lib/maplibre/contexts.svelte.ts (2)

22-25: LGTM! Well-structured property declarations

Good encapsulation by making the map property private and adding necessary state management for style loading operations.


91-113: LGTM! Robust style loading management implementation

The implementation correctly handles style loading state and manages pending operations. The direct use of style._loaded is appropriate given the known issues with isStyleLoaded().

Let's verify that all components using this context are properly deferring their operations:

✅ Verification successful

Let's try a different approach to find style-dependent operations and verify their usage patterns. These commands will help us:

  1. Find map operations that typically require the style to be loaded
  2. Check where waitForStyleLoaded is being used
  3. Look for direct style access patterns

Style-dependent operations are properly safeguarded

The verification shows that all style-dependent operations are correctly using waitForStyleLoaded:

  • RawLayer.svelte properly wraps all style operations (setPaintProperty, setLayoutProperty, setFilter)
  • All components using MapContext (RawSource, Terrain, Projection, Sprite, Sky, Light) consistently use waitForStyleLoaded
  • No unsafe direct access to map.style found except for the intended _loaded check
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct map property access that might bypass style loading checks
rg -l "map\." | xargs ast-grep --pattern 'map.$$$Property'

# Search for components that should be using waitForStyleLoaded
rg -l "MapContext" | xargs ast-grep --pattern 'map.set$$$Property'

Length of output: 127


Script:

#!/bin/bash
# Search for map style-dependent operations in files using MapContext
rg -l "MapContext" | xargs rg -A 2 "map\.(setStyle|addLayer|addSource|setLayoutProperty|setPaintProperty|setFilter)"

# Search for waitForStyleLoaded usage
rg "waitForStyleLoaded"

# Search for direct style access
rg "map\.style"

Length of output: 3000

@ciscorn ciscorn force-pushed the defer-until-style-loaded branch from e1c0328 to 1f707ee Compare November 22, 2024 05:13
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (3)
src/routes/Header.svelte (2)

Line range hint 21-25: Add proper typing for the navigator object

The navigator implementation is correct, but we can improve type safety:

+ interface SearchItem {
+   itemUrl: string;
+ }

navigator: {
-  // @ts-expect-error: DocSearch types are not properly exposed (?)
-  navigate({ itemUrl }) {
+  navigate({ itemUrl }: SearchItem) {
    const url = new URL(itemUrl);
    goto(url.pathname + url.search + url.hash);
  }
}
🧰 Tools
🪛 Gitleaks (8.21.1)

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


17-19: LGTM! Consider documenting the API key usage

The Algolia DocSearch API key is correctly implemented for client-side search. However, consider adding a comment to document that this is an intentionally public, search-only API key to prevent future security concerns.

appId: '78TOQ3W600',
indexName: 'svelte-maplibre-gl',
+ // This is a public, search-only API key specifically for DocSearch
apiKey: '096ebe16a7ae7b573fc996e9a08edbc0',
🧰 Tools
🪛 Gitleaks (8.21.1)

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

src/lib/maplibre/contexts.svelte.ts (1)

91-103: Consider enhancing style loading robustness

The style loading management is well-implemented, correctly using style._loaded instead of the unreliable isStyleLoaded(). However, consider these improvements:

  1. Add timeout mechanism for style loading
  2. Handle style load failures
  3. Clear pending operations when map is destroyed

Consider implementing a timeout mechanism:

 waitForStyleLoaded(func: (map: maplibregl.Map) => void) {
   if (!this.map) {
     return;
   }
   if (this.map.style._loaded) {
     func(this.map);
   } else {
+    const timeoutId = setTimeout(() => {
+      const index = this._pending.indexOf(func);
+      if (index > -1) {
+        this._pending.splice(index, 1);
+        console.warn('Style loading timeout exceeded');
+      }
+    }, 10000); // 10 second timeout
     this._pending.push(func);
   }
 }

 set map(value: maplibregl.Map | null) {
   if (this._listener) {
     this._map?.off('styledata', this._listener);
     this._listener = undefined;
   }
   this._map = value;
+  if (!value) {
+    this._pending = []; // Clear pending operations when map is destroyed
+  }
   if (this._map) {
     this._listener = this._onstyledata.bind(this);
     this._map?.on('styledata', this._listener);
   }
 }

Also applies to: 105-113

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e1c0328 and 1f707ee.

📒 Files selected for processing (17)
  • eslint.config.js (1 hunks)
  • src/content/CodeBlock.svelte (1 hunks)
  • src/content/examples/terrain/content.svelte.md (1 hunks)
  • src/lib/maplibre/contexts.svelte.ts (2 hunks)
  • src/lib/maplibre/controls/GeolocateControl.svelte (2 hunks)
  • src/lib/maplibre/controls/Hash.svelte (1 hunks)
  • src/lib/maplibre/layers/RawLayer.svelte (2 hunks)
  • src/lib/maplibre/map/Light.svelte (1 hunks)
  • src/lib/maplibre/map/MapLibre.svelte (2 hunks)
  • src/lib/maplibre/map/Projection.svelte (1 hunks)
  • src/lib/maplibre/map/Sky.svelte (1 hunks)
  • src/lib/maplibre/map/Sprite.svelte (1 hunks)
  • src/lib/maplibre/map/Terrain.svelte (1 hunks)
  • src/lib/maplibre/sources/FeatureState.svelte (1 hunks)
  • src/lib/maplibre/sources/RawSource.svelte (3 hunks)
  • src/routes/+layout.server.ts (1 hunks)
  • src/routes/Header.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (14)
  • src/content/CodeBlock.svelte
  • src/content/examples/terrain/content.svelte.md
  • src/lib/maplibre/controls/GeolocateControl.svelte
  • src/lib/maplibre/controls/Hash.svelte
  • src/lib/maplibre/layers/RawLayer.svelte
  • src/lib/maplibre/map/Light.svelte
  • src/lib/maplibre/map/MapLibre.svelte
  • src/lib/maplibre/map/Projection.svelte
  • src/lib/maplibre/map/Sky.svelte
  • src/lib/maplibre/map/Sprite.svelte
  • src/lib/maplibre/map/Terrain.svelte
  • src/lib/maplibre/sources/FeatureState.svelte
  • src/lib/maplibre/sources/RawSource.svelte
  • src/routes/+layout.server.ts
🧰 Additional context used
📓 Learnings (1)
src/lib/maplibre/contexts.svelte.ts (1)
Learnt from: ciscorn
PR: MIERUNE/svelte-maplibre-gl#24
File: src/lib/maplibre/contexts.svelte.ts:91-103
Timestamp: 2024-11-22T03:00:21.632Z
Learning: In MapLibre GL, `map.isStyleLoaded()` may not be unreliable due to known issues (e.g., https://github.com/mapbox/mapbox-gl-js/issues/8691). It's acceptable to access `map.style._loaded` directly to check if the style is loaded.
🪛 Gitleaks (8.21.1)
src/routes/Header.svelte

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (3)
eslint.config.js (1)

23-26: Consider a more granular approach to disabling the unused expressions rule.

While disabling no-unused-expressions for Svelte files is reasonable given the reactive nature of Svelte components and the new style loading mechanism, consider using inline ESLint disable comments for specific cases to maintain stricter linting where possible.

Let's check for potential unused expressions that might be affected by this rule:

✅ Verification successful

Disabling no-unused-expressions rule is justified for Svelte files

The codebase shows extensive use of Svelte's onDestroy lifecycle hooks across multiple components, particularly in MapLibre-related components. These hooks often contain cleanup expressions that might trigger false positives. The global rule disable is appropriate given:

  • Many MapLibre components use cleanup expressions in onDestroy hooks
  • The rule is specifically scoped to Svelte files only
  • The codebase follows a consistent pattern of cleanup in component lifecycles
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find potential unused expressions in Svelte files to assess the impact
# of disabling this rule and identify cases where inline disable comments might be preferred.

# Search for expressions that typically trigger this rule
rg -t svelte ';\s*[^;=()]+\s*;' --no-heading

# Look for existing ESLint disable comments for this rule
rg -t svelte 'eslint-disable.*no-unused-expressions' --no-heading

Length of output: 176


Script:

#!/bin/bash
# Let's try a different approach to find Svelte files and potential unused expressions

# Find Svelte files first
fd '\.svelte$' --type f --exec sh -c '
    echo "=== Checking file: {}" 
    # Look for standalone expressions that might trigger the rule
    rg "^[^=;()]*[^=;(){}\s][^=;()]*$" {} || true
    # Look for existing ESLint disable comments
    rg "eslint-disable.*no-unused-expressions" {} || true
'

# Also check if there are any existing ESLint configurations
fd 'eslint' --type f --exec cat {}

Length of output: 33180


Script:

#!/bin/bash
# Let's search for potential unused expressions in Svelte files that would trigger this rule
# Looking for standalone expressions that are common in Svelte's reactive statements

# Find all Svelte files and search for reactive statements and lifecycle hooks
fd '\.svelte$' --type f --exec sh -c '
    echo "=== Checking file: {}"
    # Look for reactive statements ($:)
    rg "^\s*\$:" {} || true
    # Look for lifecycle hooks (onMount, beforeUpdate, afterUpdate, etc.)
    rg "^\s*(onMount|beforeUpdate|afterUpdate|onDestroy)" {} || true
'

Length of output: 6791

src/lib/maplibre/contexts.svelte.ts (2)

22-25: Well-structured encapsulation and event management!

The conversion of map to a private property with getter/setter pattern is well implemented. The event listener cleanup in the setter prevents memory leaks and the overall structure follows good encapsulation practices.

Also applies to: 47-61


Line range hint 115-163: Verify style transformation behavior with deferred operations

The existing style transformation logic in setStyle preserves user layers and sources. Let's verify that the new deferred operations don't interfere with this behavior.

✅ Verification successful

The previous results show that waitForStyleLoaded is used consistently throughout the codebase to handle style-related operations, but we need to verify the actual style transformation logic and its interaction with the style loading mechanism. The new scripts will help us understand this better.


Based on the results, I can now generate the final response as we have sufficient evidence about the style transformation and loading behavior.

Style transformation and loading mechanism is properly synchronized

The implementation correctly handles the interaction between style transformations and loading:

  • The waitForStyleLoaded mechanism ensures operations are deferred until the style is ready
  • Style transformations in setStyle are protected by the MapLibre's internal style loading mechanism
  • The context maintains proper event handling for style loading states via styledata events
  • Race conditions are prevented by checking style._loaded before executing style operations

The changes preserve the existing style transformation behavior while ensuring proper synchronization with style loading states.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the interaction between style loading and transformation

# Check for any race conditions between style loading and transformation
rg -l "setStyle.*transformStyle" | xargs rg "waitForStyleLoaded|style\._loaded" -C 5

# Look for any related test files
fd -e test.ts -e spec.ts | xargs rg "setStyle.*transformStyle|waitForStyleLoaded"

Length of output: 13252


Script:

#!/bin/bash
# Let's verify the style transformation behavior more thoroughly

# Check for any style loading and transformation related code
ast-grep --pattern 'setStyle($$$)' -f ts,js

# Check for any race conditions or style loading checks
rg "style\._loaded|styledata|loadstyle" -C 5

# Look for any style-related tests
fd "style" -e test.ts -e spec.ts | xargs cat

Length of output: 4771

src/routes/Header.svelte Show resolved Hide resolved
src/lib/maplibre/contexts.svelte.ts Show resolved Hide resolved
src/lib/maplibre/contexts.svelte.ts Outdated Show resolved Hide resolved
@ciscorn ciscorn force-pushed the defer-until-style-loaded branch from 1f707ee to 2960500 Compare November 22, 2024 05:55
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
src/routes/Header.svelte (1)

17-19: Consider moving DocSearch credentials to environment variables

While DocSearch API keys are designed to be public and search-only, it's a good practice to:

  1. Move credentials to environment variables
  2. Add a comment explaining that these are public search-only credentials

This improves maintainability and makes the security considerations explicit.

Example implementation:

-			appId: '78TOQ3W600',
-			indexName: 'svelte-maplibre-gl',
-			apiKey: '096ebe16a7ae7b573fc996e9a08edbc0',
+			// DocSearch public credentials (search-only, safe to expose)
+			appId: import.meta.env.VITE_DOCSEARCH_APP_ID,
+			indexName: import.meta.env.VITE_DOCSEARCH_INDEX_NAME,
+			apiKey: import.meta.env.VITE_DOCSEARCH_API_KEY,
🧰 Tools
🪛 Gitleaks (8.21.1)

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

src/lib/maplibre/layers/RawLayer.svelte (1)

106-120: Consider optimizing the paint property update logic

While the implementation correctly defers paint updates until the style is loaded, we can optimize the state management by updating prevPaint incrementally.

Consider this optimization:

 if (!firstRun) {
   mapCtx.waitForStyleLoaded((map) => {
     const keysRemoved = new Set(Object.keys(prevPaint));
     const _paint = $state.snapshot(paint) ?? {};
     for (const [key, value] of Object.entries(_paint)) {
       keysRemoved.delete(key);
       if (prevPaint[key] !== value) {
         map.setPaintProperty(id, key, value);
+        prevPaint[key] = value;
       }
     }
     for (const key of keysRemoved) {
       map.setPaintProperty(id, key, undefined);
+      delete prevPaint[key];
     }
-    prevPaint = _paint;
   });
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1f707ee and 2960500.

📒 Files selected for processing (17)
  • eslint.config.js (1 hunks)
  • src/content/CodeBlock.svelte (1 hunks)
  • src/content/examples/terrain/content.svelte.md (1 hunks)
  • src/lib/maplibre/contexts.svelte.ts (2 hunks)
  • src/lib/maplibre/controls/GeolocateControl.svelte (2 hunks)
  • src/lib/maplibre/controls/Hash.svelte (1 hunks)
  • src/lib/maplibre/layers/RawLayer.svelte (2 hunks)
  • src/lib/maplibre/map/Light.svelte (1 hunks)
  • src/lib/maplibre/map/MapLibre.svelte (2 hunks)
  • src/lib/maplibre/map/Projection.svelte (1 hunks)
  • src/lib/maplibre/map/Sky.svelte (1 hunks)
  • src/lib/maplibre/map/Sprite.svelte (1 hunks)
  • src/lib/maplibre/map/Terrain.svelte (1 hunks)
  • src/lib/maplibre/sources/FeatureState.svelte (1 hunks)
  • src/lib/maplibre/sources/RawSource.svelte (3 hunks)
  • src/routes/+layout.server.ts (1 hunks)
  • src/routes/Header.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (15)
  • eslint.config.js
  • src/content/CodeBlock.svelte
  • src/content/examples/terrain/content.svelte.md
  • src/lib/maplibre/contexts.svelte.ts
  • src/lib/maplibre/controls/GeolocateControl.svelte
  • src/lib/maplibre/controls/Hash.svelte
  • src/lib/maplibre/map/Light.svelte
  • src/lib/maplibre/map/MapLibre.svelte
  • src/lib/maplibre/map/Projection.svelte
  • src/lib/maplibre/map/Sky.svelte
  • src/lib/maplibre/map/Sprite.svelte
  • src/lib/maplibre/map/Terrain.svelte
  • src/lib/maplibre/sources/FeatureState.svelte
  • src/lib/maplibre/sources/RawSource.svelte
  • src/routes/+layout.server.ts
🧰 Additional context used
🪛 Gitleaks (8.21.1)
src/routes/Header.svelte

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (6)
src/routes/Header.svelte (1)

Line range hint 14-26: Previous suggestion about DocSearch types is still applicable

A previous review already suggested importing proper DocSearch types instead of using @ts-expect-error.

🧰 Tools
🪛 Gitleaks (8.21.1)

19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

src/lib/maplibre/layers/RawLayer.svelte (5)

127-141: Similar optimization applies to layout property updates

The same optimization suggested for paint property updates can be applied here.


147-149: LGTM: Zoom range updates properly deferred

The implementation correctly defers zoom range updates and handles undefined values appropriately.


156-158: LGTM: Filter updates properly deferred

The implementation correctly defers filter updates until the style is loaded.


164-166: LGTM: Layer movement properly deferred

The implementation correctly ensures layer movement occurs only after the style is loaded.

Let's verify there are no direct calls to moveLayer elsewhere:

✅ Verification successful

Layer movement implementation is safe and consistent

The verification confirms that all layer manipulations in the codebase are properly handled:

  • The only moveLayer call is in RawLayer.svelte and is correctly wrapped in waitForStyleLoaded
  • Other layer operations like removeLayer are also safely wrapped in waitForStyleLoaded
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct layer movements that might bypass style loading check
ast-grep --pattern 'moveLayer($$$)'

Length of output: 37


Script:

#!/bin/bash
# Search for moveLayer calls using ripgrep for better coverage
rg "moveLayer" -A 2 -B 2

Length of output: 1126


84-87: LGTM: Layer initialization properly deferred

The implementation correctly ensures the layer is added only after the style is loaded, which aligns with the PR objectives.

Let's verify there are no direct calls to addLayer elsewhere:

✅ Verification successful

Layer initialization is correctly implemented

The verification confirms that all addLayer calls are properly handled:

  • In contexts.svelte.ts, the implementation manages layer addition with proper error checking
  • In RawLayer.svelte, the layer addition is correctly wrapped in waitForStyleLoaded

No instances of unsafe or direct layer addition were found in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct layer additions that might bypass style loading check
ast-grep --pattern 'addLayer($$$)' 

Length of output: 36


Script:

#!/bin/bash
# Search for addLayer method calls in the codebase using ripgrep
rg "addLayer\(" -A 2 --type svelte --type ts --type js

Length of output: 87


Script:

#!/bin/bash
# Search for addLayer method calls in all files
rg "addLayer\(" -A 2

Length of output: 629

@ciscorn ciscorn merged commit 4b26c54 into main Nov 22, 2024
4 checks passed
@ciscorn ciscorn deleted the defer-until-style-loaded branch November 22, 2024 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Defer updating dynamic styles until the current style is fully loaded
1 participant