Standardize monorepo pipeline: test, lint, typecheck across all workspaces
Previously only 2-3 workspaces participated in each turbo task. This expands test, lint, and typecheck to cover all 11 workspaces with parallel execution and caching. Test pipeline: - Move from single root vitest to per-workspace test scripts via turbo - Shared vitest config (vitest.shared.ts) with passWithNoTests - Per-workspace configs re-export the shared base - Mobile uses Jest (jest-expo + React Native Testing Library) - Add node-environment GPX tests verifying linkedom fallback - Add i18n mobile init tests - Exclude apps/mobile from root vitest (uses Jest separately) Lint pipeline: - Add eslint lint script to all 9 previously unlinted workspaces - Standardize all scripts to "eslint ." with shared root config - Add .expo/ to global ESLint ignores - Fix lint errors: unused imports in api/types, export parseGpx Typecheck pipeline: - Add "typecheck": "tsc" to all 8 packages - Add @types/node (catalog) to gpx and db packages - Fix mobile app.config.ts: remove deprecated experiments.monorepo and newArchEnabled (both default in Expo SDK 55) - Add allowImportingTsExtensions to mobile tsconfig Shared package compatibility (mobile-app Phase 1.3): - Add linkedom as explicit dependency to @trails-cool/gpx - Add initI18nMobile() export to @trails-cool/i18n - Confirm @trails-cool/types is pure interfaces (no DOM deps) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
423499d21d
commit
2ac4014521
41 changed files with 1787 additions and 36 deletions
|
|
@ -7,7 +7,16 @@
|
|||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trails-cool/types": "workspace:*"
|
||||
"@trails-cool/types": "workspace:*",
|
||||
"linkedom": "^0.18.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
85
packages/gpx/src/parse-node.test.ts
Normal file
85
packages/gpx/src/parse-node.test.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @vitest-environment node
|
||||
*
|
||||
* These tests run WITHOUT jsdom to verify GPX parsing and generation
|
||||
* work in environments without a native DOMParser (Node.js, React Native).
|
||||
* The linkedom fallback must handle all XML parsing.
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseGpxAsync } from "./parse.ts";
|
||||
import { generateGpx } from "./generate.ts";
|
||||
|
||||
const sampleGpx = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gpx version="1.1" creator="test" xmlns="http://www.topografix.com/GPX/1/1">
|
||||
<metadata><name>Test Route</name><desc>A test description</desc></metadata>
|
||||
<wpt lat="52.52" lon="13.405"><name>Berlin</name></wpt>
|
||||
<wpt lat="51.84" lon="12.243"><name>Dessau</name><type>overnight</type></wpt>
|
||||
<wpt lat="48.137" lon="11.576"><name>Munich</name></wpt>
|
||||
<trk>
|
||||
<trkseg>
|
||||
<trkpt lat="52.52" lon="13.405"><ele>34</ele></trkpt>
|
||||
<trkpt lat="51.84" lon="12.243"><ele>80</ele></trkpt>
|
||||
<trkpt lat="48.137" lon="11.576"><ele>519</ele></trkpt>
|
||||
</trkseg>
|
||||
</trk>
|
||||
</gpx>`;
|
||||
|
||||
describe("parseGpxAsync (node environment, no DOMParser)", () => {
|
||||
it("parses route name and description", async () => {
|
||||
const result = await parseGpxAsync(sampleGpx);
|
||||
expect(result.name).toBe("Test Route");
|
||||
expect(result.description).toBe("A test description");
|
||||
});
|
||||
|
||||
it("parses waypoints", async () => {
|
||||
const result = await parseGpxAsync(sampleGpx);
|
||||
expect(result.waypoints).toHaveLength(3);
|
||||
expect(result.waypoints[0]).toEqual({ lat: 52.52, lon: 13.405, name: "Berlin" });
|
||||
});
|
||||
|
||||
it("parses isDayBreak from overnight type", async () => {
|
||||
const result = await parseGpxAsync(sampleGpx);
|
||||
expect(result.waypoints[1]!.isDayBreak).toBe(true);
|
||||
expect(result.waypoints[0]!.isDayBreak).toBeUndefined();
|
||||
});
|
||||
|
||||
it("parses tracks with elevation", async () => {
|
||||
const result = await parseGpxAsync(sampleGpx);
|
||||
expect(result.tracks).toHaveLength(1);
|
||||
expect(result.tracks[0]).toHaveLength(3);
|
||||
expect(result.tracks[0]![0]!.ele).toBe(34);
|
||||
});
|
||||
|
||||
it("computes elevation gain and distance", async () => {
|
||||
const result = await parseGpxAsync(sampleGpx);
|
||||
expect(result.elevation.gain).toBe(485);
|
||||
expect(result.elevation.loss).toBe(0);
|
||||
expect(result.distance).toBeGreaterThan(400_000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("generateGpx + parseGpxAsync round-trip (node environment)", () => {
|
||||
it("round-trips waypoints, tracks, and metadata", async () => {
|
||||
const gpx = generateGpx({
|
||||
name: "Round Trip",
|
||||
description: "Testing",
|
||||
waypoints: [
|
||||
{ lat: 52.52, lon: 13.405, name: "Start" },
|
||||
{ lat: 51.0, lon: 12.0, name: "Camp", isDayBreak: true },
|
||||
{ lat: 48.137, lon: 11.576, name: "End" },
|
||||
],
|
||||
tracks: [[
|
||||
{ lat: 52.52, lon: 13.405, ele: 34 },
|
||||
{ lat: 51.0, lon: 12.0, ele: 200 },
|
||||
{ lat: 48.137, lon: 11.576, ele: 519 },
|
||||
]],
|
||||
});
|
||||
|
||||
const parsed = await parseGpxAsync(gpx);
|
||||
expect(parsed.name).toBe("Round Trip");
|
||||
expect(parsed.description).toBe("Testing");
|
||||
expect(parsed.waypoints).toHaveLength(3);
|
||||
expect(parsed.waypoints[1]!.isDayBreak).toBe(true);
|
||||
expect(parsed.tracks[0]).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* @vitest-environment jsdom
|
||||
*
|
||||
* Tests the browser DOMParser path. The linkedom/node path is covered
|
||||
* by parse-node.test.ts.
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseGpxAsync } from "./parse.ts";
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ async function getDOMParser(): Promise<typeof DOMParser> {
|
|||
return _LinkedDOMParser;
|
||||
}
|
||||
|
||||
function parseGpx(xml: string): GpxData {
|
||||
export function parseGpx(xml: string): GpxData {
|
||||
// Synchronous path for browser
|
||||
if (typeof DOMParser !== "undefined") {
|
||||
return parseGpxWithParser(new DOMParser(), xml);
|
||||
|
|
@ -26,7 +26,7 @@ function parseGpx(xml: string): GpxData {
|
|||
const { DOMParser: LP } = require("linkedom");
|
||||
return parseGpxWithParser(new LP() as unknown as DOMParser, xml);
|
||||
} catch {
|
||||
throw new Error("DOMParser not available — install linkedom for Node.js");
|
||||
throw new Error("DOMParser not available — install linkedom");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
"rootDir": "src",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
|
|||
1
packages/gpx/vitest.config.ts
Normal file
1
packages/gpx/vitest.config.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "../../vitest.shared.ts";
|
||||
Loading…
Add table
Add a link
Reference in a new issue