diff --git a/.monorepolint.config.mjs b/.monorepolint.config.mjs index 5ce247918..013f83f83 100644 --- a/.monorepolint.config.mjs +++ b/.monorepolint.config.mjs @@ -16,6 +16,7 @@ const PACKAGES = []; // packages that aren't @turf/turf const MAIN_PACKAGE = "@turf/turf"; const TAPE_PACKAGES = []; // packages that have tape tests +const NODE_TEST_PACKAGES = []; // projects that use node's native test runner const TYPES_PACKAGES = []; // packages that have types tests const TSTYCHE_PACKAGES = []; // packages that use tstyche for type tests. @@ -32,8 +33,16 @@ for (const pk of await fs.readdir(packagesPath)) { PACKAGES.push(name); - if (existsSync(path.join(pk, "test.js"))) { - TAPE_PACKAGES.push(name); + if (existsSync(path.join(packagesPath, pk, "test.ts"))) { + const testFileContents = await fs.readFile( + path.join(packagesPath, pk, "test.ts"), + "utf-8" + ); + if (testFileContents.includes(`from "tape"`)) { + TAPE_PACKAGES.push(name); + } else { + NODE_TEST_PACKAGES.push(name); + } } if (existsSync(path.join(packagesPath, pk, "types.ts"))) { @@ -198,11 +207,23 @@ export default { scripts: { bench: "tsx bench.ts", "test:tape": "tsx test.ts", + "test:node": REMOVE, }, }, includePackages: TAPE_PACKAGES, }), + packageScript({ + options: { + scripts: { + bench: "node bench.ts", + "test:node": "node --test", + "test:tape": REMOVE, + }, + }, + includePackages: NODE_TEST_PACKAGES, + }), + packageScript({ options: { scripts: { @@ -233,20 +254,43 @@ export default { }, }, includePackages: PACKAGES, + excludePackages: NODE_TEST_PACKAGES, }), requireDependency({ options: { - dependencies: { - tslib: "catalog:", + devDependencies: { + tape: "catalog:", + "@types/tape": "catalog:", + }, + }, + includePackages: TAPE_PACKAGES, + }), + + requireDependency({ + options: { + devDependencies: { + "@types/benchmark": REMOVE, + "@types/tape": REMOVE, + benchmark: REMOVE, + "load-json-file": REMOVE, + tape: REMOVE, + tsx: REMOVE, + "write-json-file": REMOVE, }, + }, + includePackages: NODE_TEST_PACKAGES, + }), + + requireDependency({ + options: { devDependencies: { "@types/benchmark": "catalog:", - "@types/tape": "catalog:", - typescript: "catalog:", + benchmark: "catalog:", }, }, includePackages: PACKAGES, + excludePackages: NODE_TEST_PACKAGES, }), requireDependency({ diff --git a/package.json b/package.json index a339ee93c..649d7d512 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,10 @@ "@monorepolint/config": "0.6.0-alpha.6", "@monorepolint/core": "0.6.0-alpha.6", "@monorepolint/rules": "0.6.0-alpha.6", + "@types/benchmark": "^2.1.5", "@types/node": "22.15.3", "acorn": "^8.14.1", + "benchmark": "^2.1.4", "camelcase": "^8.0.0", "d3-queue": "*", "decamelize": "^6.0.0", diff --git a/packages/turf-center/bench.ts b/packages/turf-center/bench.ts index aed38d199..a9abd3f42 100644 --- a/packages/turf-center/bench.ts +++ b/packages/turf-center/bench.ts @@ -1,48 +1,12 @@ -import path from "path"; -import { fileURLToPath } from "url"; -import { glob } from "glob"; -import { loadJsonFileSync } from "load-json-file"; -import Benchmark from "benchmark"; -import { center } from "./index.js"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -const fixtures = glob - .sync(path.join(__dirname, "test", "in", "*.geojson")) - .map((input) => { - return { - name: path.parse(input).name, - geojson: loadJsonFileSync(input), - }; - }); - -/** - * Single Process Benchmark - * - * feature-collection: 0.445ms - * imbalanced-polygon: 0.051ms - * linestring: 0.027ms - * point: 0.011ms - * polygon: 0.013ms - */ -for (const { name, geojson } of fixtures) { - console.time(name); - center(geojson); - console.timeEnd(name); -} - -/** - * Benchmark Results - * - * feature-collection x 2,786,700 ops/sec ±1.50% (83 runs sampled) - * imbalanced-polygon x 1,364,145 ops/sec ±3.33% (76 runs sampled) - * linestring x 4,104,106 ops/sec ±4.16% (81 runs sampled) - * point x 4,901,692 ops/sec ±5.23% (81 runs sampled) - * polygon x 2,862,759 ops/sec ±1.14% (86 runs sampled) - */ -const suite = new Benchmark.Suite("turf-center"); -for (const { name, geojson } of fixtures) { - suite.add(name, () => center(geojson)); -} - -suite.on("cycle", (e) => console.log(String(e.target))).run(); +import { center } from "./index.ts"; +import { benchFixtures } from "../../support/benchFixtures.mts"; + +// Benchmark Results +// feature-collection.geojson x 27,241,658 ops/sec ±0.34% (99 runs sampled) +// imbalanced-polygon.geojson x 14,679,583 ops/sec ±0.27% (98 runs sampled) +// linestring.geojson x 34,199,495 ops/sec ±0.52% (95 runs sampled) +// point.geojson x 52,230,993 ops/sec ±0.70% (96 runs sampled) +// points-with-weights.geojson x 24,802,237 ops/sec ±0.33% (100 runs sampled) +// polygon-without-weights.geojson x 18,423,881 ops/sec ±0.28% (100 runs sampled) +// polygon.geojson x 24,990,920 ops/sec ±0.45% (99 runs sampled) +await benchFixtures("turf-center", (input) => center(input)); diff --git a/packages/turf-center/index.ts b/packages/turf-center/index.ts index f23c8697c..7b3bd7e05 100644 --- a/packages/turf-center/index.ts +++ b/packages/turf-center/index.ts @@ -1,6 +1,6 @@ -import { BBox, Feature, GeoJsonProperties, Point } from "geojson"; +import type { BBox, Feature, GeoJsonProperties, Point } from "geojson"; import { bbox } from "@turf/bbox"; -import { point, Id, AllGeoJSON } from "@turf/helpers"; +import { point, type Id, type AllGeoJSON } from "@turf/helpers"; /** * Takes a {@link Feature} or {@link FeatureCollection} and returns the absolute center point of all features. diff --git a/packages/turf-center/package.json b/packages/turf-center/package.json index 26ed436da..3413441b5 100644 --- a/packages/turf-center/package.json +++ b/packages/turf-center/package.json @@ -46,24 +46,17 @@ "dist" ], "scripts": { - "bench": "tsx bench.ts", + "bench": "node bench.ts", "build": "tsup --config ../../tsup.config.ts", "test": "pnpm run /test:.*/", - "test:tape": "tsx test.ts", + "test:node": "node --test", "test:types": "tsc --esModuleInterop --module node16 --moduleResolution node16 --noEmit --strict types.ts" }, "devDependencies": { "@turf/bbox-polygon": "workspace:*", "@turf/meta": "workspace:*", - "@types/benchmark": "catalog:", - "@types/tape": "catalog:", - "benchmark": "catalog:", - "load-json-file": "^7.0.1", - "tape": "catalog:", "tsup": "catalog:", - "tsx": "catalog:", - "typescript": "catalog:", - "write-json-file": "^6.0.0" + "typescript": "catalog:" }, "dependencies": { "@turf/bbox": "workspace:*", diff --git a/packages/turf-center/test.ts b/packages/turf-center/test.ts index 2896523e4..42d8d6201 100644 --- a/packages/turf-center/test.ts +++ b/packages/turf-center/test.ts @@ -1,61 +1,47 @@ -import test from "tape"; -import { glob } from "glob"; -import path from "path"; -import { fileURLToPath } from "url"; -import { loadJsonFileSync } from "load-json-file"; -import { writeJsonFileSync } from "write-json-file"; -import { bboxPolygon } from "@turf/bbox-polygon"; -import { bbox } from "@turf/bbox"; -import { featureEach, coordEach } from "@turf/meta"; -import { lineString, featureCollection } from "@turf/helpers"; -import { center } from "./index.js"; +import test from "node:test"; +import center from "./index.ts"; +import { featureCollection, lineString } from "@turf/helpers"; +import { coordEach, featureEach } from "@turf/meta"; +import bboxPolygon from "@turf/bbox-polygon"; +import bbox from "@turf/bbox"; +import type { Geometry } from "geojson"; +import { testFixtures } from "../../support/testFixtures.mts"; +import assert from "assert"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); +await test("center fixtures", async (t) => { + await testFixtures(t, (geojson) => { + const options = geojson.options || {}; + options.properties = { "marker-symbol": "star", "marker-color": "#F00" }; + const centered = center(geojson, options); -test("turf-center", (t) => { - glob - .sync(path.join(__dirname, "test", "in", "*.geojson")) - .forEach((filepath) => { - const geojson = loadJsonFileSync(filepath); - const options = geojson.options || {}; - options.properties = { "marker-symbol": "star", "marker-color": "#F00" }; - const centered = center(geojson, options); + // Display Results + const results = featureCollection([centered]); + featureEach(geojson, (feature) => results.features.push(feature)); + const extent = bboxPolygon(bbox(geojson)); + extent.properties = { + stroke: "#00F", + "stroke-width": 1, + "fill-opacity": 0, + }; + coordEach(extent, (coord) => + results.features.push( + lineString([coord, centered.geometry.coordinates], { + stroke: "#00F", + "stroke-width": 1, + }) + ) + ); + results.features.push(extent); - // Display Results - const results = featureCollection([centered]); - featureEach(geojson, (feature) => results.features.push(feature)); - const extent = bboxPolygon(bbox(geojson)); - extent.properties = { - stroke: "#00F", - "stroke-width": 1, - "fill-opacity": 0, - }; - coordEach(extent, (coord) => - results.features.push( - lineString([coord, centered.geometry.coordinates], { - stroke: "#00F", - "stroke-width": 1, - }) - ) - ); - results.features.push(extent); - - const out = filepath.replace( - path.join("test", "in"), - path.join("test", "out") - ); - if (process.env.REGEN) writeJsonFileSync(out, results); - t.deepEqual(results, loadJsonFileSync(out), path.parse(filepath).name); - }); - t.end(); + return results; + }); }); -test("turf-center -- properties", (t) => { +test("turf-center -- properties", () => { const line = lineString([ [0, 0], [1, 1], ]); const pt = center(line, { properties: { foo: "bar" } }); - t.equal(pt.properties.foo, "bar", "translate properties"); - t.end(); + assert.strictEqual(pt.properties.foo, "bar", "translate properties"); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 757550c40..2cc56153f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,12 +59,18 @@ importers: '@monorepolint/rules': specifier: 0.6.0-alpha.6 version: 0.6.0-alpha.6 + '@types/benchmark': + specifier: ^2.1.5 + version: 2.1.5 '@types/node': specifier: 22.15.3 version: 22.15.3 acorn: specifier: ^8.14.1 version: 8.15.0 + benchmark: + specifier: ^2.1.4 + version: 2.1.4 camelcase: specifier: ^8.0.0 version: 8.0.0 @@ -1686,33 +1692,12 @@ importers: '@turf/meta': specifier: workspace:* version: link:../turf-meta - '@types/benchmark': - specifier: 'catalog:' - version: 2.1.5 - '@types/tape': - specifier: 'catalog:' - version: 5.8.1 - benchmark: - specifier: 'catalog:' - version: 2.1.4 - load-json-file: - specifier: ^7.0.1 - version: 7.0.1 - tape: - specifier: 'catalog:' - version: 5.9.0 tsup: specifier: 'catalog:' version: 8.4.0(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.2) - tsx: - specifier: 'catalog:' - version: 4.19.4 typescript: specifier: 'catalog:' version: 5.8.3 - write-json-file: - specifier: ^6.0.0 - version: 6.0.0 packages/turf-center-mean: dependencies: diff --git a/support/benchFixtures.mts b/support/benchFixtures.mts new file mode 100644 index 000000000..b111054a5 --- /dev/null +++ b/support/benchFixtures.mts @@ -0,0 +1,15 @@ +import path from "path"; +import { readdir, readFile } from "node:fs/promises"; +import Benchmark from "benchmark"; + +export async function benchFixtures(pkgName: string, fn: (input: any) => void) { + const suite = new Benchmark.Suite(pkgName); + const fixturesPath = path.join(process.cwd(), "test", "in"); + for (const file of await readdir(fixturesPath)) { + const inputPath = path.join(fixturesPath, file); + const inputData = JSON.parse(await readFile(inputPath, "utf-8")); + suite.add(file, () => fn(inputData)); + } + + suite.on("cycle", (e: any) => console.log(String(e.target))).run(); +} diff --git a/support/testFixtures.mts b/support/testFixtures.mts new file mode 100644 index 000000000..f568cfd84 --- /dev/null +++ b/support/testFixtures.mts @@ -0,0 +1,26 @@ +import assert from "node:assert"; +import { readdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import type { TestContext } from "node:test"; + +export async function testFixtures(t: TestContext, fn: (input: any) => any) { + const dirs = { + in: path.join(process.cwd(), "test", "in"), + out: path.join(process.cwd(), "test", "out"), + }; + + for (const file of await readdir(dirs.in)) { + await t.test(file, async () => { + const inputPath = path.join(dirs.in, file); + const outputPath = path.join(dirs.out, file); + const inputData = JSON.parse(await readFile(inputPath, "utf-8")); + const result = fn(inputData); + if (process.env.REGEN) { + await writeFile(outputPath, JSON.stringify(result, null, 2)); + } else { + const expected = JSON.parse(await readFile(outputPath, "utf-8")); + assert.deepStrictEqual(result, expected); + } + }); + } +} diff --git a/tsconfig.shared.json b/tsconfig.shared.json index 81cb45d27..68fcbf085 100644 --- a/tsconfig.shared.json +++ b/tsconfig.shared.json @@ -8,6 +8,7 @@ "moduleResolution": "node16", "importHelpers": true, "skipLibCheck": true, - "erasableSyntaxOnly": true + "erasableSyntaxOnly": true, + "rewriteRelativeImportExtensions": true } }