@versatiles/svg-renderer 1.2.0 → 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8014,6 +8014,10 @@ class Color {
8014
8014
  get rgb() {
8015
8015
  return `#${d2h(this.values[0])}${d2h(this.values[1])}${d2h(this.values[2])}`;
8016
8016
  }
8017
+ /** The red, green and blue components as bytes, for painting pixels directly. */
8018
+ get rgbBytes() {
8019
+ return [this.values[0], this.values[1], this.values[2]];
8020
+ }
8017
8021
  get opacity() {
8018
8022
  return this.values[3] / 255;
8019
8023
  }
@@ -8232,6 +8236,133 @@ function formatNum(tenths) {
8232
8236
  return (negative ? '-' : '') + String(whole) + '.' + String(frac);
8233
8237
  }
8234
8238
 
8239
+ /**
8240
+ * Where a symbol sits relative to its point, shared by every backend so the two cannot
8241
+ * drift apart. The text vocabulary is SVG's (`text-anchor` / `dominant-baseline`); the
8242
+ * canvas backend translates it to `textAlign` / `textBaseline`.
8243
+ */
8244
+ function mapTextAnchor(anchor) {
8245
+ switch (anchor) {
8246
+ case 'left':
8247
+ return ['start', 'central'];
8248
+ case 'right':
8249
+ return ['end', 'central'];
8250
+ case 'top':
8251
+ return ['middle', 'text-before-edge'];
8252
+ case 'bottom':
8253
+ return ['middle', 'text-after-edge'];
8254
+ case 'top-left':
8255
+ return ['start', 'text-before-edge'];
8256
+ case 'top-right':
8257
+ return ['end', 'text-before-edge'];
8258
+ case 'bottom-left':
8259
+ return ['start', 'text-after-edge'];
8260
+ case 'bottom-right':
8261
+ return ['end', 'text-after-edge'];
8262
+ default:
8263
+ return ['middle', 'central'];
8264
+ }
8265
+ }
8266
+ function mapIconAnchor(anchor, w, h) {
8267
+ switch (anchor) {
8268
+ case 'left':
8269
+ return [0, -h / 2];
8270
+ case 'right':
8271
+ return [-w, -h / 2];
8272
+ case 'top':
8273
+ return [-w / 2, 0];
8274
+ case 'bottom':
8275
+ return [-w / 2, -h];
8276
+ case 'top-left':
8277
+ return [0, 0];
8278
+ case 'top-right':
8279
+ return [-w, 0];
8280
+ case 'bottom-left':
8281
+ return [0, -h];
8282
+ case 'bottom-right':
8283
+ return [-w, -h];
8284
+ default:
8285
+ return [-w / 2, -h / 2];
8286
+ }
8287
+ }
8288
+
8289
+ /** Size of a raster tile image in user units, when drawn as a mesh of triangles on the globe. */
8290
+ const RASTER_TILE_UNITS = 256;
8291
+ /** How far (pixels) a raster tile image reaches beyond the tile border in the seam underlay. */
8292
+ const RASTER_TILE_BLEED_PX = 1;
8293
+ /** How far (pixels) each raster triangle is grown beyond its edges to overlap its neighbours. */
8294
+ const RASTER_TRIANGLE_OVERLAP_PX = 0.5;
8295
+ /**
8296
+ * The affine transform [a, b, c, d, e, f] mapping the source triangle (in tile units 0..1,
8297
+ * scaled to RASTER_TILE_UNITS) exactly onto the target triangle, or undefined if either is
8298
+ * degenerate.
8299
+ */
8300
+ function affineFromTriangles(source, target) {
8301
+ const k = RASTER_TILE_UNITS;
8302
+ const [[u0, v0], [u1, v1], [u2, v2]] = source.map(([u, v]) => [u * k, v * k]);
8303
+ const [[x0, y0], [x1, y1], [x2, y2]] = target;
8304
+ const du1 = u1 - u0;
8305
+ const dv1 = v1 - v0;
8306
+ const du2 = u2 - u0;
8307
+ const dv2 = v2 - v0;
8308
+ const det = du1 * dv2 - du2 * dv1;
8309
+ if (Math.abs(det) < 1e-12)
8310
+ return undefined;
8311
+ const dx1 = x1 - x0;
8312
+ const dx2 = x2 - x0;
8313
+ const dy1 = y1 - y0;
8314
+ const dy2 = y2 - y0;
8315
+ // Screen area below a hundredth of a pixel: nothing to draw.
8316
+ if (Math.abs(dx1 * dy2 - dx2 * dy1) < 0.02)
8317
+ return undefined;
8318
+ const a = (dx1 * dv2 - dx2 * dv1) / det;
8319
+ const c = (du1 * dx2 - du2 * dx1) / det;
8320
+ const b = (dy1 * dv2 - dy2 * dv1) / det;
8321
+ const d = (du1 * dy2 - du2 * dy1) / det;
8322
+ return [a, b, c, d, x0 - a * u0 - c * v0, y0 - b * u0 - d * v0];
8323
+ }
8324
+ function isOnTileBorder(source) {
8325
+ return source.some(([u, v]) => u === 0 || u === 1 || v === 0 || v === 1);
8326
+ }
8327
+ /**
8328
+ * Moves the source corners of a triangle that lie on the tile border inwards, by the
8329
+ * equivalent of RASTER_TILE_BLEED_PX on screen, so that the image edge lies beyond the border.
8330
+ * (Used for the underlay that closes the seams between tiles, see `#drawRasterMeshes`.)
8331
+ */
8332
+ function bleedAtTileBorder(source, target) {
8333
+ const [[u0, v0], [u1, v1], [u2, v2]] = source;
8334
+ const [[x0, y0], [x1, y1], [x2, y2]] = target;
8335
+ const sourceArea = Math.abs((u1 - u0) * (v2 - v0) - (u2 - u0) * (v1 - v0));
8336
+ const targetArea = Math.abs((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0));
8337
+ if (sourceArea === 0 || targetArea === 0)
8338
+ return source;
8339
+ // Pixels per tile unit, and the bleed in tile units, kept within the triangle.
8340
+ const scale = Math.sqrt(targetArea / sourceArea);
8341
+ const size = Math.max(Math.abs(u1 - u0), Math.abs(u2 - u0), Math.abs(v1 - v0), Math.abs(v2 - v0));
8342
+ const bleed = Math.min(RASTER_TILE_BLEED_PX / scale, size / 4);
8343
+ const inset = (t) => (t === 0 ? bleed : t === 1 ? 1 - bleed : t);
8344
+ return source.map(([u, v]) => [inset(u), inset(v)]);
8345
+ }
8346
+ /**
8347
+ * Moves every edge of a triangle outwards by `distance`, by scaling it around its incenter.
8348
+ * Growth is limited for very thin triangles, whose corners would otherwise shoot far out.
8349
+ */
8350
+ function growTriangle(triangle, distance) {
8351
+ const [[x0, y0], [x1, y1], [x2, y2]] = triangle;
8352
+ const a = Math.hypot(x2 - x1, y2 - y1);
8353
+ const b = Math.hypot(x2 - x0, y2 - y0);
8354
+ const c = Math.hypot(x1 - x0, y1 - y0);
8355
+ const perimeter = a + b + c;
8356
+ const area = Math.abs((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)) / 2;
8357
+ if (perimeter === 0 || area === 0)
8358
+ return triangle;
8359
+ const inradius = (2 * area) / perimeter;
8360
+ const cx = (a * x0 + b * x1 + c * x2) / perimeter;
8361
+ const cy = (a * y0 + b * y1 + c * y2) / perimeter;
8362
+ const scale = 1 + distance / Math.max(inradius, 2 * distance);
8363
+ return triangle.map(([x, y]) => [cx + (x - cx) * scale, cy + (y - cy) * scale]);
8364
+ }
8365
+
8235
8366
  // line-blur approximates MapLibre's edge-feather blur, which keeps an opaque core
8236
8367
  // and feathers over ~`blur` px with a hard cutoff. feGaussianBlur alone conserves
8237
8368
  // total ink and leaves soft, infinite tails, so we combine three corrections,
@@ -8805,82 +8936,6 @@ function strokeAttr(color, width) {
8805
8936
  function formatScaled(v) {
8806
8937
  return formatNum(Math.round(v * 10));
8807
8938
  }
8808
- /** Size of a raster tile image in user units, when drawn as a mesh of triangles on the globe. */
8809
- const RASTER_TILE_UNITS = 256;
8810
- /** How far (pixels) a raster tile image reaches beyond the tile border in the seam underlay. */
8811
- const RASTER_TILE_BLEED_PX = 1;
8812
- /** How far (pixels) each raster triangle is grown beyond its edges to overlap its neighbours. */
8813
- const RASTER_TRIANGLE_OVERLAP_PX = 0.5;
8814
- /**
8815
- * The affine transform [a, b, c, d, e, f] mapping the source triangle (in tile units 0..1,
8816
- * scaled to RASTER_TILE_UNITS) exactly onto the target triangle, or undefined if either is
8817
- * degenerate.
8818
- */
8819
- function affineFromTriangles(source, target) {
8820
- const k = RASTER_TILE_UNITS;
8821
- const [[u0, v0], [u1, v1], [u2, v2]] = source.map(([u, v]) => [u * k, v * k]);
8822
- const [[x0, y0], [x1, y1], [x2, y2]] = target;
8823
- const du1 = u1 - u0;
8824
- const dv1 = v1 - v0;
8825
- const du2 = u2 - u0;
8826
- const dv2 = v2 - v0;
8827
- const det = du1 * dv2 - du2 * dv1;
8828
- if (Math.abs(det) < 1e-12)
8829
- return undefined;
8830
- const dx1 = x1 - x0;
8831
- const dx2 = x2 - x0;
8832
- const dy1 = y1 - y0;
8833
- const dy2 = y2 - y0;
8834
- // Screen area below a hundredth of a pixel: nothing to draw.
8835
- if (Math.abs(dx1 * dy2 - dx2 * dy1) < 0.02)
8836
- return undefined;
8837
- const a = (dx1 * dv2 - dx2 * dv1) / det;
8838
- const c = (du1 * dx2 - du2 * dx1) / det;
8839
- const b = (dy1 * dv2 - dy2 * dv1) / det;
8840
- const d = (du1 * dy2 - du2 * dy1) / det;
8841
- return [a, b, c, d, x0 - a * u0 - c * v0, y0 - b * u0 - d * v0];
8842
- }
8843
- function isOnTileBorder(source) {
8844
- return source.some(([u, v]) => u === 0 || u === 1 || v === 0 || v === 1);
8845
- }
8846
- /**
8847
- * Moves the source corners of a triangle that lie on the tile border inwards, by the
8848
- * equivalent of RASTER_TILE_BLEED_PX on screen, so that the image edge lies beyond the border.
8849
- * (Used for the underlay that closes the seams between tiles, see `#drawRasterMeshes`.)
8850
- */
8851
- function bleedAtTileBorder(source, target) {
8852
- const [[u0, v0], [u1, v1], [u2, v2]] = source;
8853
- const [[x0, y0], [x1, y1], [x2, y2]] = target;
8854
- const sourceArea = Math.abs((u1 - u0) * (v2 - v0) - (u2 - u0) * (v1 - v0));
8855
- const targetArea = Math.abs((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0));
8856
- if (sourceArea === 0 || targetArea === 0)
8857
- return source;
8858
- // Pixels per tile unit, and the bleed in tile units, kept within the triangle.
8859
- const scale = Math.sqrt(targetArea / sourceArea);
8860
- const size = Math.max(Math.abs(u1 - u0), Math.abs(u2 - u0), Math.abs(v1 - v0), Math.abs(v2 - v0));
8861
- const bleed = Math.min(RASTER_TILE_BLEED_PX / scale, size / 4);
8862
- const inset = (t) => (t === 0 ? bleed : t === 1 ? 1 - bleed : t);
8863
- return source.map(([u, v]) => [inset(u), inset(v)]);
8864
- }
8865
- /**
8866
- * Moves every edge of a triangle outwards by `distance`, by scaling it around its incenter.
8867
- * Growth is limited for very thin triangles, whose corners would otherwise shoot far out.
8868
- */
8869
- function growTriangle(triangle, distance) {
8870
- const [[x0, y0], [x1, y1], [x2, y2]] = triangle;
8871
- const a = Math.hypot(x2 - x1, y2 - y1);
8872
- const b = Math.hypot(x2 - x0, y2 - y0);
8873
- const c = Math.hypot(x1 - x0, y1 - y0);
8874
- const perimeter = a + b + c;
8875
- const area = Math.abs((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)) / 2;
8876
- if (perimeter === 0 || area === 0)
8877
- return triangle;
8878
- const inradius = (2 * area) / perimeter;
8879
- const cx = (a * x0 + b * x1 + c * x2) / perimeter;
8880
- const cy = (a * y0 + b * y1 + c * y2) / perimeter;
8881
- const scale = 1 + distance / Math.max(inradius, 2 * distance);
8882
- return triangle.map(([x, y]) => [cx + (x - cx) * scale, cy + (y - cy) * scale]);
8883
- }
8884
8939
  function formatUnit(v) {
8885
8940
  return (Math.round(v * 100000) / 100000).toString();
8886
8941
  }
@@ -8894,50 +8949,6 @@ function formatPoint(p) {
8894
8949
  const [x, y] = roundXY(p[0], p[1]);
8895
8950
  return formatNum(x) + ',' + formatNum(y);
8896
8951
  }
8897
- function mapTextAnchor(anchor) {
8898
- switch (anchor) {
8899
- case 'left':
8900
- return ['start', 'central'];
8901
- case 'right':
8902
- return ['end', 'central'];
8903
- case 'top':
8904
- return ['middle', 'text-before-edge'];
8905
- case 'bottom':
8906
- return ['middle', 'text-after-edge'];
8907
- case 'top-left':
8908
- return ['start', 'text-before-edge'];
8909
- case 'top-right':
8910
- return ['end', 'text-before-edge'];
8911
- case 'bottom-left':
8912
- return ['start', 'text-after-edge'];
8913
- case 'bottom-right':
8914
- return ['end', 'text-after-edge'];
8915
- default:
8916
- return ['middle', 'central'];
8917
- }
8918
- }
8919
- function mapIconAnchor(anchor, w, h) {
8920
- switch (anchor) {
8921
- case 'left':
8922
- return [0, -h / 2];
8923
- case 'right':
8924
- return [-w, -h / 2];
8925
- case 'top':
8926
- return [-w / 2, 0];
8927
- case 'bottom':
8928
- return [-w / 2, -h];
8929
- case 'top-left':
8930
- return [0, 0];
8931
- case 'top-right':
8932
- return [-w, 0];
8933
- case 'bottom-left':
8934
- return [0, -h];
8935
- case 'bottom-right':
8936
- return [-w, -h];
8937
- default:
8938
- return [-w / 2, -h / 2];
8939
- }
8940
- }
8941
8952
  function escapeXml(s) {
8942
8953
  return s
8943
8954
  .replace(/&/g, '&amp;')
@@ -11358,7 +11369,12 @@ function resolveTokens(text, properties) {
11358
11369
  return '';
11359
11370
  });
11360
11371
  }
11361
- async function renderMap(job) {
11372
+ /**
11373
+ * Draws the map described by `job` onto its renderer and returns that renderer, so a
11374
+ * caller can take the result in whatever form the backend provides (see
11375
+ * {@link renderMap} for the SVG string, or the PNG entry point for an image buffer).
11376
+ */
11377
+ async function drawMap(job) {
11362
11378
  job.projection ??= Projection.fromStyle({
11363
11379
  width: job.renderer.width,
11364
11380
  height: job.renderer.height,
@@ -11370,7 +11386,10 @@ async function renderMap(job) {
11370
11386
  if (clipCircle)
11371
11387
  job.renderer.setClipCircle?.(clipCircle);
11372
11388
  await render(job);
11373
- return job.renderer.getString();
11389
+ return job.renderer;
11390
+ }
11391
+ async function renderMap(job) {
11392
+ return (await drawMap(job)).getString();
11374
11393
  }
11375
11394
  function getFeatures(layerFeatures, layerStyle) {
11376
11395
  return layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source);
@@ -11483,7 +11502,7 @@ async function render(job) {
11483
11502
  case 'raster':
11484
11503
  {
11485
11504
  const tiles = await getRasterTiles(job, layerStyle.source);
11486
- renderer.drawRasterTiles(layerId, tiles, {
11505
+ await renderer.drawRasterTiles(layerId, tiles, {
11487
11506
  opacity: getPaint('raster-opacity'),
11488
11507
  hueRotate: getPaint('raster-hue-rotate'),
11489
11508
  brightnessMin: getPaint('raster-brightness-min'),
@@ -11535,7 +11554,7 @@ async function render(job) {
11535
11554
  if (symbolFeatures.length === 0)
11536
11555
  continue;
11537
11556
  // Render icons first (underneath text)
11538
- renderer.drawIcons(`${layerId}-icons`, symbolFeatures.flatMap((feature) => {
11557
+ await renderer.drawIcons(`${layerId}-icons`, symbolFeatures.flatMap((feature) => {
11539
11558
  const iconImage = getLayout('icon-image', feature);
11540
11559
  const iconName = iconImage != null
11541
11560
  ? resolveTokens(iconImage.toString(), feature.properties)
@@ -11599,6 +11618,61 @@ async function render(job) {
11599
11618
  }
11600
11619
  }
11601
11620
 
11621
+ /**
11622
+ * Renders a MapLibre style to an SVG image.
11623
+ *
11624
+ * Fetches the vector and raster tiles the view needs, then draws every layer as SVG
11625
+ * elements: fills and lines as paths, raster tiles and sprites as embedded images. The
11626
+ * result is a single self-contained SVG document — it references no external files, so
11627
+ * it can be saved, inlined into HTML or opened in a vector editor as is.
11628
+ *
11629
+ * Works in Node.js (22 or later) and in the browser. The same function is exported by
11630
+ * `@versatiles/svg-renderer`, `@versatiles/png-renderer` and
11631
+ * `@versatiles/maplibre-svg-export`; the examples import it from the first.
11632
+ *
11633
+ * @example Render a map in Node.js
11634
+ * ```ts
11635
+ * import { renderToSVG } from '@versatiles/svg-renderer';
11636
+ * import { inlineSources, osm } from '@versatiles/style';
11637
+ * import { writeFile } from 'node:fs/promises';
11638
+ *
11639
+ * // inlineSources() resolves the style's TileJSON sources into tile URLs,
11640
+ * // which the renderer needs. Without it the map comes out empty.
11641
+ * const style = await inlineSources(osm({ theme: 'colorful' }));
11642
+ *
11643
+ * const svg = await renderToSVG({
11644
+ * style,
11645
+ * width: 800,
11646
+ * height: 600,
11647
+ * lon: 13.4, // Berlin
11648
+ * lat: 52.52,
11649
+ * zoom: 12,
11650
+ * });
11651
+ *
11652
+ * await writeFile('berlin.svg', svg);
11653
+ * ```
11654
+ *
11655
+ * @example Render a hosted style in the browser
11656
+ * ```ts
11657
+ * import { renderToSVG } from '@versatiles/svg-renderer';
11658
+ *
11659
+ * // This hosted style already lists its tile URLs, so it can be used directly.
11660
+ * const style = await fetch(
11661
+ * 'https://tiles.versatiles.org/assets/styles/colorful/style.json',
11662
+ * ).then((response) => response.json());
11663
+ *
11664
+ * document.body.innerHTML = await renderToSVG({ style, width: 800, height: 600, zoom: 3 });
11665
+ * ```
11666
+ *
11667
+ * @example Include labels and icons
11668
+ * ```ts
11669
+ * const svg = await renderToSVG({ style, lon: 13.4, lat: 52.52, zoom: 14, renderLabels: true });
11670
+ * ```
11671
+ *
11672
+ * @param options - What to render and how large. Only `style` is required.
11673
+ * @returns The SVG document, as a string beginning with `<svg`.
11674
+ * @throws If `width` or `height` is not positive.
11675
+ */
11602
11676
  async function renderToSVG(options) {
11603
11677
  const width = options.width ?? 1024;
11604
11678
  const height = options.height ?? 1024;
package/package.json CHANGED
@@ -1,118 +1,40 @@
1
1
  {
2
2
  "name": "@versatiles/svg-renderer",
3
- "version": "1.2.0",
4
- "description": "Renders vector maps as SVG.",
3
+ "version": "2.0.0-rc.0",
4
+ "description": "Renders MapLibre styles as SVG, in Node.js or the browser.",
5
5
  "keywords": [
6
6
  "svg",
7
7
  "map",
8
8
  "renderer",
9
9
  "vector-tiles",
10
- "versatiles"
10
+ "versatiles",
11
+ "maplibre"
11
12
  ],
12
- "homepage": "https://github.com/versatiles-org/versatiles-svg-renderer",
13
+ "homepage": "https://github.com/versatiles-org/versatiles-svg-renderer/tree/main/packages/svg-renderer",
13
14
  "bugs": "https://github.com/versatiles-org/versatiles-svg-renderer/issues",
14
15
  "repository": {
15
16
  "type": "git",
16
- "url": "git+https://github.com/versatiles-org/versatiles-svg-renderer.git"
17
+ "url": "git+https://github.com/versatiles-org/versatiles-svg-renderer.git",
18
+ "directory": "packages/svg-renderer"
17
19
  },
18
- "main": "dist/index.cjs",
19
- "module": "dist/index.js",
20
- "types": "dist/index.d.ts",
21
- "unpkg": "dist/maplibre-svg-export.umd.js",
22
- "jsdelivr": "dist/maplibre-svg-export.umd.js",
20
+ "author": "Michael Kreil <versatiles@michael-kreil.de>",
21
+ "license": "MIT",
22
+ "type": "module",
23
23
  "exports": {
24
24
  ".": {
25
- "import": {
26
- "types": "./dist/index.d.ts",
27
- "default": "./dist/index.js"
28
- },
29
- "require": {
30
- "types": "./dist/index.d.ts",
31
- "default": "./dist/index.cjs"
32
- }
33
- },
34
- "./maplibre": {
35
- "script": "./dist/maplibre-svg-export.umd.js",
36
- "import": {
37
- "types": "./dist/maplibre-svg-export.d.ts",
38
- "default": "./dist/maplibre-svg-export.js"
39
- }
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
40
27
  }
41
28
  },
42
29
  "files": [
43
30
  "dist",
44
31
  "!dist/**/*.map"
45
32
  ],
46
- "scripts": {
47
- "build": "rm -rf dist && rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript && rm -rf dist/types",
48
- "build-analyze": "npm run build && tsx scripts/analyze-bundle.ts",
49
- "check": "npm run format && npm run lint && npm run typecheck && npm run build && npm run test && npm run test:e2e",
50
- "demo": "tsx src/demo.ts",
51
- "dev": "tsx dev/serve.ts",
52
- "dev:render-comparison": "tsx dev/render-comparison.ts",
53
- "doc:graph": "vrt deps-graph | vrt doc-insert README.md '## Dependency Graph'",
54
- "doc:typescript": "npx vrt doc-typescript -f html -o ./doc-typescript",
55
- "format": "prettier --write --log-level warn .",
56
- "lint": "eslint --color .",
57
- "prepack": "npm run doc:graph && npm run build",
58
- "release": "npx vrt release-npm",
59
- "start": "tsx src/index.ts",
60
- "test": "vitest run",
61
- "test:coverage": "vitest run --coverage",
62
- "test:e2e:maplibre": "tsx e2e/maplibre-control.ts",
63
- "test:e2e:screenshots": "tsx e2e/screenshots.ts",
64
- "test:e2e": "vitest run --config vitest.e2e.config.ts && npm run test:e2e:maplibre && npm run test:e2e:screenshots",
65
- "typecheck": "tsc --noEmit && tsc --noEmit -p e2e/tsconfig.json && tsc --noEmit -p dev/tsconfig.json",
66
- "upgrade": "vrt deps-upgrade"
67
- },
68
33
  "sideEffects": false,
69
- "type": "module",
70
- "author": "Michael Kreil <versatiles@michael-kreil.de>",
71
- "license": "MIT",
72
34
  "engines": {
73
35
  "node": ">= 22"
74
36
  },
75
- "peerDependencies": {
76
- "maplibre-gl": ">=3.0.0"
77
- },
78
- "peerDependenciesMeta": {
79
- "maplibre-gl": {
80
- "optional": true
81
- }
82
- },
83
- "devDependencies": {
84
- "@eslint/js": "^10.0.1",
85
- "@mapbox/vector-tile": "^3.0.0",
86
- "@maplibre/maplibre-gl-style-spec": "^26.4.4",
87
- "@rollup/plugin-node-resolve": "^16.0.3",
88
- "@rollup/plugin-typescript": "^12.3.0",
89
- "@types/node": "^26.6.2",
90
- "@types/pbf": "^3.0.5",
91
- "@types/pngjs": "^6.0.5",
92
- "@versatiles/release-tool": "^2.15.0",
93
- "@versatiles/style": "^6.0.0",
94
- "@vitest/coverage-v8": "^5.0.1",
95
- "eslint": "^10.11.0",
96
- "geojson": "^0.5.0",
97
- "jsdom": "^30.1.0",
98
- "maplibre-gl": "^6.10.0",
99
- "npm-check-updates": "^23.1.0",
100
- "pbf": "^5.1.2",
101
- "pixelmatch": "^7.2.0",
102
- "playwright": "^1.63.0",
103
- "pngjs": "^7.0.0",
104
- "prettier": "^3.9.8",
105
- "rollup": "^4.63.4",
106
- "rollup-plugin-dts": "^6.5.1",
107
- "rollup-plugin-visualizer": "^7.1.1",
108
- "tslib": "^2.8.1",
109
- "tsx": "^4.23.13",
110
- "typescript": "^6.0.3",
111
- "typescript-eslint": "^8.70.0",
112
- "vitest": "^5.0.1"
113
- },
114
- "allowScripts": {
115
- "esbuild": true,
116
- "fsevents": true
37
+ "dependencies": {
38
+ "@types/geojson": "^7946.0.16"
117
39
  }
118
40
  }