Reduce bundle size of apps that import @turf/helpers (#2623)

* Remove GeojsonEquality library from turf-helpers/lib/ in favour of the third party geojson-equality-ts (which is essentially a copy of the code we're removing from turf-helpers/lib/). This allows us to remove the dependency on deep-equal which was causing an unintended increase in bundle size for users of turf-helpers. Updated boolean-equal and boolean-overlap to use geojson-equality-ts directly.

* Removing keepNames from tsup config.

---------

Co-authored-by: Tim Welch <tim.j.welch@gmail.com>
This commit is contained in:
James Beard 2024-06-25 22:57:25 +10:00 committed by GitHub
parent 36167829d9
commit 0ce6ecca05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 25 additions and 1326 deletions

View File

@ -1,5 +1,5 @@
import { Feature, Geometry } from "geojson";
import { GeojsonEquality } from "@turf/helpers";
import { geojsonEquality } from "geojson-equality-ts";
import { cleanCoords } from "@turf/clean-coords";
import { getGeom } from "@turf/invariant";
@ -45,8 +45,9 @@ function booleanEqual(
const type2 = getGeom(feature2).type;
if (type1 !== type2) return false;
const equality = new GeojsonEquality({ precision: precision });
return equality.compare(cleanCoords(feature1), cleanCoords(feature2));
return geojsonEquality(cleanCoords(feature1), cleanCoords(feature2), {
precision,
});
}
export { booleanEqual };

View File

@ -74,6 +74,7 @@
"@turf/clean-coords": "workspace:^",
"@turf/helpers": "workspace:^",
"@turf/invariant": "workspace:^",
"geojson-equality-ts": "^1.0.2",
"tslib": "^2.6.2"
}
}

View File

@ -3,7 +3,7 @@ import { segmentEach } from "@turf/meta";
import { getGeom } from "@turf/invariant";
import { lineOverlap } from "@turf/line-overlap";
import { lineIntersect } from "@turf/line-intersect";
import { GeojsonEquality } from "@turf/helpers";
import { geojsonEquality } from "geojson-equality-ts";
/**
* Compares two geometries of the same dimension and returns true if their intersection set results in a geometry
@ -49,8 +49,8 @@ function booleanOverlap(
if (type1 === "Point") throw new Error("Point geometry not supported");
// features must be not equal
const equality = new GeojsonEquality({ precision: 6 });
if (equality.compare(feature1 as any, feature2 as any)) return false;
if (geojsonEquality(feature1 as any, feature2 as any, { precision: 6 }))
return false;
let overlap = 0;

View File

@ -75,6 +75,7 @@
"@turf/line-intersect": "workspace:^",
"@turf/line-overlap": "workspace:^",
"@turf/meta": "workspace:^",
"geojson-equality-ts": "^1.0.2",
"tslib": "^2.6.2"
}
}

View File

@ -17,7 +17,6 @@ import {
import { Id } from "./lib/geojson.js";
export * from "./lib/geojson.js";
export * from "./lib/geojson-equality.js";
// TurfJS Combined Types
export type Coord = Feature<Point> | Point | Position;

View File

@ -1,198 +0,0 @@
import {
Feature,
LineString,
Position,
GeoJSON,
Point,
Polygon,
GeometryCollection,
FeatureCollection,
MultiLineString,
MultiPoint,
MultiPolygon,
} from "geojson";
import equal from "deep-equal";
/**
* GeoJSON equality checking utility.
* Adapted from https://github.com/geosquare/geojson-equality
*
* @memberof helpers
* @type {Class}
*/
export class GeojsonEquality {
private precision: number;
private direction = false;
private compareProperties = true;
constructor(opts?: {
precision?: number;
direction?: boolean;
compareProperties?: boolean;
}) {
this.precision = 10 ** -(opts?.precision ?? 17);
this.direction = opts?.direction ?? false;
this.compareProperties = opts?.compareProperties ?? true;
}
compare(g1: GeoJSON, g2: GeoJSON): boolean {
if (g1.type !== g2.type) {
return false;
}
if (!sameLength(g1, g2)) {
return false;
}
switch (g1.type) {
case "Point":
return this.compareCoord(g1.coordinates, (g2 as Point).coordinates);
case "LineString":
return this.compareLine(g1.coordinates, (g2 as LineString).coordinates);
case "Polygon":
return this.comparePolygon(g1, g2 as Polygon);
case "GeometryCollection":
return this.compareGeometryCollection(g1, g2 as GeometryCollection);
case "Feature":
return this.compareFeature(g1, g2 as Feature);
case "FeatureCollection":
return this.compareFeatureCollection(g1, g2 as FeatureCollection);
default:
if (g1.type.startsWith("Multi")) {
const g1s = explode(g1);
const g2s = explode(
g2 as MultiLineString | MultiPoint | MultiPolygon
);
return g1s.every((g1part) =>
g2s.some((g2part) => this.compare(g1part as any, g2part as any))
);
}
}
return false;
}
private compareCoord(c1: Position, c2: Position) {
return (
c1.length === c2.length &&
c1.every((c, i) => Math.abs(c - c2[i]) < this.precision)
);
}
private compareLine(
path1: Position[],
path2: Position[],
ind = 0,
isPoly = false
): boolean {
if (!sameLength(path1, path2)) {
return false;
}
const p1 = path1;
let p2 = path2;
if (isPoly && !this.compareCoord(p1[0], p2[0])) {
// fix start index of both to same point
const startIndex = this.fixStartIndex(p2, p1);
if (!startIndex) {
return false;
} else {
p2 = startIndex;
}
}
// for linestring ind =0 and for polygon ind =1
const sameDirection = this.compareCoord(p1[ind], p2[ind]);
if (this.direction || sameDirection) {
return this.comparePath(p1, p2);
} else {
if (this.compareCoord(p1[ind], p2[p2.length - (1 + ind)])) {
return this.comparePath(p1.slice().reverse(), p2);
}
return false;
}
}
private fixStartIndex(sourcePath: Position[], targetPath: Position[]) {
//make sourcePath first point same as of targetPath
let correctPath,
ind = -1;
for (let i = 0; i < sourcePath.length; i++) {
if (this.compareCoord(sourcePath[i], targetPath[0])) {
ind = i;
break;
}
}
if (ind >= 0) {
correctPath = ([] as Position[]).concat(
sourcePath.slice(ind, sourcePath.length),
sourcePath.slice(1, ind + 1)
);
}
return correctPath;
}
private comparePath(p1: Position[], p2: Position[]) {
return p1.every((c, i) => this.compareCoord(c, p2[i]));
}
private comparePolygon(g1: Polygon, g2: Polygon) {
if (this.compareLine(g1.coordinates[0], g2.coordinates[0], 1, true)) {
const holes1 = g1.coordinates.slice(1, g1.coordinates.length);
const holes2 = g2.coordinates.slice(1, g2.coordinates.length);
return holes1.every((h1) =>
holes2.some((h2) => this.compareLine(h1, h2, 1, true))
);
}
return false;
}
private compareGeometryCollection(
g1: GeometryCollection,
g2: GeometryCollection
) {
return (
sameLength(g1.geometries, g2.geometries) &&
this.compareBBox(g1, g2) &&
g1.geometries.every((g, i) => this.compare(g, g2.geometries[i]))
);
}
private compareFeature(g1: Feature, g2: Feature) {
return (
g1.id === g2.id &&
(this.compareProperties ? equal(g1.properties, g2.properties) : true) &&
this.compareBBox(g1, g2) &&
this.compare(g1.geometry, g2.geometry)
);
}
private compareFeatureCollection(
g1: FeatureCollection,
g2: FeatureCollection
) {
return (
sameLength(g1.features, g2.features) &&
this.compareBBox(g1, g2) &&
g1.features.every((f, i) => this.compare(f, g2.features[i]))
);
}
private compareBBox(g1: GeoJSON, g2: GeoJSON): boolean {
return (
Boolean(!g1.bbox && !g2.bbox) ||
(g1.bbox && g2.bbox ? this.compareCoord(g1.bbox, g2.bbox) : false)
);
}
}
function sameLength(g1: any, g2: any) {
return g1.coordinates
? g1.coordinates.length === g2.coordinates.length
: g1.length === g2.length;
}
function explode(g: MultiLineString | MultiPoint | MultiPolygon) {
return g.coordinates.map((part) => ({
type: g.type.replace("Multi", ""),
coordinates: part,
}));
}

View File

@ -59,7 +59,6 @@
},
"devDependencies": {
"@types/benchmark": "^2.1.5",
"@types/deep-equal": "^1.0.4",
"@types/tape": "^4.2.32",
"benchmark": "^2.1.4",
"npm-run-all": "^4.1.5",
@ -69,7 +68,6 @@
"typescript": "^5.2.2"
},
"dependencies": {
"deep-equal": "^2.2.3",
"tslib": "^2.6.2"
}
}

File diff suppressed because it is too large Load Diff

22
pnpm-lock.yaml generated
View File

@ -1146,6 +1146,9 @@ importers:
'@turf/invariant':
specifier: workspace:^
version: link:../turf-invariant
geojson-equality-ts:
specifier: ^1.0.2
version: 1.0.2
tslib:
specifier: ^2.6.2
version: 2.6.2
@ -1251,6 +1254,9 @@ importers:
'@turf/meta':
specifier: workspace:^
version: link:../turf-meta
geojson-equality-ts:
specifier: ^1.0.2
version: 1.0.2
tslib:
specifier: ^2.6.2
version: 2.6.2
@ -3154,9 +3160,6 @@ importers:
packages/turf-helpers:
dependencies:
deep-equal:
specifier: ^2.2.3
version: 2.2.3
tslib:
specifier: ^2.6.2
version: 2.6.2
@ -3164,9 +3167,6 @@ importers:
'@types/benchmark':
specifier: ^2.1.5
version: 2.1.5
'@types/deep-equal':
specifier: ^1.0.4
version: 1.0.4
'@types/tape':
specifier: ^4.2.32
version: 4.13.4
@ -9064,6 +9064,10 @@ packages:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true
/@types/geojson@7946.0.14:
resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==}
dev: false
/@types/geojson@7946.0.8:
resolution: {integrity: sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==}
@ -12086,6 +12090,12 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/geojson-equality-ts@1.0.2:
resolution: {integrity: sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==}
dependencies:
'@types/geojson': 7946.0.14
dev: false
/geojson-polygon-self-intersections@1.2.1:
resolution: {integrity: sha512-/QM1b5u2d172qQVO//9CGRa49jEmclKEsYOQmWP9ooEjj63tBM51m2805xsbxkzlEELQ2REgTf700gUhhlegxA==}
dependencies:

View File

@ -9,7 +9,6 @@ const baseOptions: Options = {
sourcemap: true,
target: "es2017",
tsconfig: "./tsconfig.json",
keepNames: true,
// treeshake: true, causes "chunk.default" warning, breaks CJS exports?
cjsInterop: true,
splitting: true,