@unvt/charites 2.0.2 → 2.1.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/cli/serve.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { Command } from 'commander';
2
+ import { createServer } from 'vite';
3
+ import { fileURLToPath } from 'url';
4
+ import path from 'path';
2
5
  import { serve } from '../commands/serve.js';
3
6
  import { error } from '../lib/error.js';
4
7
  const program = new Command();
@@ -9,6 +12,7 @@ program
9
12
  .option('-i, --sprite-input [<icon input directory>]', 'directory path of icon source to build icons. The default <icon source> is `icons/`')
10
13
  .option('--sdf', 'Allows to use SDF sprite in charites')
11
14
  .option('--port [port]', 'Specify custom port')
15
+ .option('--vite-port [vitePort]', 'Specify custom port for vite server')
12
16
  .option('--no-open', "Don't open the preview in the default browser")
13
17
  .action(async (source, serveOptions) => {
14
18
  const options = program.opts();
@@ -16,11 +20,25 @@ program
16
20
  options.spriteInput = serveOptions.spriteInput;
17
21
  options.open = serveOptions.open;
18
22
  options.sdf = serveOptions.sdf;
23
+ options.vitePort = serveOptions.vitePort;
19
24
  try {
25
+ await startProviderDevServer(options);
20
26
  await serve(source, program.opts());
21
27
  }
22
28
  catch (e) {
23
29
  error(e);
24
30
  }
25
31
  });
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = path.dirname(__filename);
34
+ async function startProviderDevServer(options) {
35
+ const config = {
36
+ root: path.resolve(__dirname, '../../provider'),
37
+ server: {
38
+ port: parseInt(options.vitePort ?? '5137'),
39
+ },
40
+ };
41
+ const viteServer = await createServer(config);
42
+ await viteServer.listen();
43
+ }
26
44
  export default program;
@@ -3,11 +3,12 @@ import fs from 'fs';
3
3
  import os from 'os';
4
4
  import http from 'http';
5
5
  import open from 'open';
6
- import { WebSocketServer } from 'ws';
6
+ // tweak to fix WebSocketServer is not a constructor
7
+ const ws = await import('ws');
8
+ const WebSocketServer = ws.default.WebSocketServer || ws.WebSocketServer;
7
9
  import watch from 'node-watch';
8
10
  import { parser } from '../lib/yaml-parser.js';
9
11
  import { validateStyle } from '../lib/validate-style.js';
10
- import { providerDir } from '../lib/defaultValues.js';
11
12
  import { buildSprite } from '../lib/build-sprite.js';
12
13
  export async function serve(source, options) {
13
14
  let port = process.env.PORT || 8080;
@@ -53,12 +54,6 @@ export async function serve(source, options) {
53
54
  return;
54
55
  }
55
56
  switch (url) {
56
- case '/':
57
- res.statusCode = 200;
58
- res.setHeader('Content-Type', 'text/html; charset=UTF-8');
59
- const content = fs.readFileSync(path.join(providerDir, 'index.html'), 'utf-8');
60
- res.end(content);
61
- break;
62
57
  case '/style.json':
63
58
  let style;
64
59
  try {
@@ -76,24 +71,37 @@ export async function serve(source, options) {
76
71
  res.setHeader('Cache-Control', 'no-store');
77
72
  res.end(JSON.stringify(style));
78
73
  break;
79
- case '/app.css':
80
- res.statusCode = 200;
81
- res.setHeader('Content-Type', 'text/css; charset=UTF-8');
82
- const css = fs.readFileSync(path.join(providerDir, 'app.css'), 'utf-8');
83
- res.end(css);
84
- break;
85
- case `/app.js`:
86
- res.statusCode = 200;
87
- res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
88
- const app = fs.readFileSync(path.join(providerDir, 'app.js'), 'utf-8');
89
- const js = app.replace('___PORT___', `${port}`);
90
- res.end(js);
91
- break;
92
- default:
74
+ case '/sprite.json':
75
+ case '/sprite@2x.json':
76
+ case '/sprite.png':
77
+ case '/sprite@2x.png':
93
78
  res.statusCode = 404;
94
79
  res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
95
80
  res.end('Not found');
96
81
  break;
82
+ default:
83
+ const vitePort = parseInt(options.vitePort || '5137');
84
+ const proxyReq = http.request({
85
+ hostname: 'localhost',
86
+ port: vitePort,
87
+ path: url,
88
+ method: req.method,
89
+ headers: req.headers,
90
+ }, (viteRes) => {
91
+ res.writeHead(viteRes.statusCode || 500, viteRes.headers);
92
+ viteRes.pipe(res, {
93
+ end: true,
94
+ });
95
+ });
96
+ req.pipe(proxyReq, {
97
+ end: true,
98
+ });
99
+ proxyReq.on('error', (err) => {
100
+ console.error('Error with proxy request:', err);
101
+ res.statusCode = 500;
102
+ res.end('Bad gateway: failed to proxy to Vite dev server');
103
+ });
104
+ break;
97
105
  }
98
106
  });
99
107
  server.listen(port, () => {
@@ -126,7 +134,7 @@ export async function serve(source, options) {
126
134
  // Nothing to do
127
135
  }
128
136
  });
129
- ws.on('close', () => {
137
+ wss.on('close', () => {
130
138
  watcher.close();
131
139
  });
132
140
  });
@@ -1,6 +1,6 @@
1
- import maplibreStyleSpec from '@maplibre/maplibre-gl-style-spec';
1
+ import { validateStyleMin, } from '@maplibre/maplibre-gl-style-spec';
2
2
  export function validateStyle(style) {
3
- const result = maplibreStyleSpec.validateStyleMin(style);
3
+ const result = validateStyleMin(style);
4
4
  const errors = [];
5
5
  for (let i = 0; i < result.length; i++) {
6
6
  const msg = result[i].message;
@@ -27,9 +27,12 @@ class IncFileTag {
27
27
  }
28
28
  const INC_PATH_TYPE = new YAML.Type('tag:yaml.org,2002:inc/file', {
29
29
  kind: 'scalar',
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
31
  resolve: (data) => data,
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
33
  construct: (data) => new IncFileTag(data),
32
34
  instanceOf: IncFileTag,
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
36
  represent: (tag) => tag.value,
34
37
  });
35
38
  const INC_PATH_OUTPUT_SCHEMA = YAML.DEFAULT_SCHEMA.extend([INC_PATH_TYPE]);
@@ -1,3 +1,3 @@
1
- export * from './tilejson';
2
- export * from './metadatajson';
3
- export * from './vector_layers';
1
+ export * from './tilejson.js';
2
+ export * from './metadatajson.js';
3
+ export * from './vector_layers.js';
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -9,7 +9,7 @@ Charites - Documentation
9
9
  :Date: 2024-08-23
10
10
  :Copyright: CC-BY-SA
11
11
  :Organization: The United Nations Vector Tile Toolkit
12
- :Version: 2.0.2
12
+ :Version: 2.1.0
13
13
  :Abstract: This document contains the complete documentation of Charites, an application to style vector tiles easily
14
14
 
15
15
  .. meta::
@@ -27,7 +27,7 @@ Checking version
27
27
  .. code-block:: bash
28
28
 
29
29
  $ charites --version
30
- 0.3.0
30
+ 2.1.0
31
31
 
32
32
 
33
33
  Inititalize `style.yml`
@@ -95,12 +95,18 @@ Realtime editor on browser
95
95
  source> is `icons/`
96
96
  --sdf Allows to use SDF sprite in charites
97
97
  --port [port] Specify custom port
98
+ --vite-port [vitePort] Specify custom port for vite server
99
+ --no-open Don't open the preview in the default browser
98
100
  -h, --help display help for command
99
101
 
100
- Charites has three options for `serve` command.
102
+ Charites has fifth options for `serve` command.
101
103
 
102
104
  - ``--sprite-input`` - If you are building icon spritesheets with Charites, you can specify the directory of SVG files to compile here. See the ``build`` command for more information.
103
105
 
104
106
  - ``--sdf`` - if this option is used together with ``--sprite-input``, the viewer will generate SDF sprite. If the option is not specified, non SDF sprite will be generated.
105
107
 
106
108
  - ``--port`` - Set http server's port number. When not specified, the default is 8080.
109
+
110
+ - ``--vite-port`` - Set Vite server's port number. When not specified, the default is 5137.
111
+
112
+ - ``--no-open`` - If this option is used, the preview will not be opened in the default browser. This is useful when you want to use the preview in a headless environment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unvt/charites",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "charites": "dist/cli.js"
@@ -12,51 +12,59 @@
12
12
  "test": "npm run build && NODE_OPTIONS='--experimental-specifier-resolution=node' mocha --timeout 5000 'test/*.ts'",
13
13
  "test:watch": "npm test -- --watch --watch-files src/**/*.ts --watch-files test/**/*.ts",
14
14
  "test:e2e": "npx playwright test",
15
- "command": "./node_modules/.bin/ts-node ./src/cli.ts"
15
+ "command": "tsx ./src/cli.ts"
16
16
  },
17
+ "workspaces": [
18
+ "provider"
19
+ ],
17
20
  "author": "",
18
21
  "license": "MIT",
19
22
  "dependencies": {
20
- "@maplibre/maplibre-gl-style-spec": "^20.3.1",
21
- "@types/geojson": "^7946.0.14",
22
- "@types/jsonminify": "^0.4.3",
23
+ "@maplibre/maplibre-gl-style-spec": "^23.1.0",
23
24
  "@unvt/sprite-one": "^0.1.1",
24
- "axios": "^1.7.5",
25
- "commander": "^12.1.0",
26
- "glob": "^11.0.0",
25
+ "@watergis/maplibre-gl-legend": "^2.0.5",
26
+ "axios": "^1.8.4",
27
+ "commander": "^13.1.0",
28
+ "glob": "^11.0.1",
27
29
  "js-yaml": "^4.1.0",
28
30
  "jsonminify": "^0.4.2",
31
+ "maplibre-gl": "^5.3.0",
29
32
  "node-watch": "^0.7.4",
30
33
  "open": "^10.1.0",
31
- "ws": "^8.18.0"
34
+ "pmtiles": "^4.3.0",
35
+ "vite": "^6.2.5",
36
+ "ws": "^8.18.1"
32
37
  },
33
38
  "devDependencies": {
34
- "@eslint/eslintrc": "^3.1.0",
35
- "@eslint/js": "^9.9.1",
36
- "@playwright/test": "^1.46.1",
37
- "@types/chai": "^4.3.19",
38
- "@types/chai-as-promised": "^7.1.8",
39
+ "@eslint/eslintrc": "^3.3.1",
40
+ "@eslint/js": "^9.24.0",
41
+ "@playwright/test": "^1.15.1",
42
+ "@types/chai": "^5.2.1",
43
+ "@types/chai-as-promised": "^8.0.2",
39
44
  "@types/fs-extra": "^11.0.4",
45
+ "@types/geojson": "^7946.0.16",
40
46
  "@types/js-yaml": "^4.0.9",
47
+ "@types/jsonminify": "^0.4.3",
41
48
  "@types/mapbox__point-geometry": "^0.1.4",
42
- "@types/mocha": "^10.0.7",
43
- "@types/node": "^22.5.0",
44
- "@types/ws": "^8.5.12",
45
- "@typescript-eslint/eslint-plugin": "^8.3.0",
46
- "@typescript-eslint/parser": "^8.3.0",
47
- "chai": "^5.1.1",
48
- "chai-as-promised": "^8.0.0",
49
- "eslint": "^9.9.1",
50
- "eslint-config-prettier": "^9.1.0",
51
- "eslint-plugin-prettier": "^5.2.1",
52
- "fs-extra": "^11.2.0",
49
+ "@types/mocha": "^10.0.10",
50
+ "@types/node": "^22.14.0",
51
+ "@types/ws": "^8.18.1",
52
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
53
+ "@typescript-eslint/parser": "^8.29.1",
54
+ "chai": "^5.2.0",
55
+ "chai-as-promised": "^8.0.1",
56
+ "eslint": "^9.24.0",
57
+ "eslint-config-prettier": "^10.1.1",
58
+ "eslint-plugin-prettier": "^5.2.6",
59
+ "fs-extra": "^11.3.0",
53
60
  "kill-port-process": "^3.2.1",
54
- "mocha": "^10.7.3",
61
+ "mocha": "^11.1.0",
55
62
  "node-abort-controller": "^3.1.1",
56
- "prettier": "^3.3.3",
63
+ "prettier": "^3.5.3",
57
64
  "ts-node": "^10.9.2",
58
65
  "tsconfig-paths": "^4.2.0",
59
- "typescript": "^5.5.4"
66
+ "tsx": "^4.19.3",
67
+ "typescript": "^5.8.3"
60
68
  },
61
69
  "type": "module"
62
70
  }
@@ -1,13 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html id="charites-default">
3
3
  <head>
4
- <link rel="stylesheet" href="/app.css" />
5
- <link href='https://cdn.jsdelivr.net/npm/maplibre-gl/dist/maplibre-gl.css' rel='stylesheet' />
6
- <link href='https://cdn.jsdelivr.net/npm/@watergis/maplibre-gl-legend@2.0.5/dist/maplibre-gl-legend.css' rel='stylesheet' />
7
4
  <title>Charites Live Preview</title>
8
- <script src='https://cdn.jsdelivr.net/npm/maplibre-gl/dist/maplibre-gl.js'></script>
9
- <script src="https://cdn.jsdelivr.net/npm/@watergis/maplibre-gl-legend@2.0.5/dist/maplibre-gl-legend.umd.js"></script>
10
- <script src="https://cdn.jsdelivr.net/npm/pmtiles@4.3.0/dist/pmtiles.js"></script>
11
5
  </head>
12
6
  <body>
13
7
  <div class="overlay">
@@ -18,11 +12,6 @@
18
12
  <label><input type="checkbox" id="showPadding">show Padding</label>
19
13
  </div>
20
14
  <div id="map"></div>
21
- <script type="text/javascript">
22
- let protocol = new pmtiles.Protocol()
23
- maplibregl.addProtocol('pmtiles', protocol.tile)
24
- </script>
25
- <script type="text/javascript" src="/shared.js"></script>
26
- <script type="text/javascript" src="/app.js"></script>
15
+ <script type="module" src="./src/main.ts"></script>
27
16
  </body>
28
17
  </html>
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@unvt/provider",
3
+ "version": "0.0.1",
4
+ "private": true
5
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -14,4 +14,4 @@ html, body, #map
14
14
  margin: 5px;
15
15
  z-index: 90;
16
16
  background-color: rgba(255, 255, 255, 0.6);
17
- }
17
+ }
@@ -0,0 +1,137 @@
1
+ import {
2
+ Map,
3
+ MapOptions,
4
+ NavigationControl,
5
+ addProtocol,
6
+ IControl,
7
+ } from 'maplibre-gl'
8
+ import 'maplibre-gl/dist/maplibre-gl.css'
9
+ import { MaplibreLegendControl } from '@watergis/maplibre-gl-legend'
10
+ import '@watergis/maplibre-gl-legend/dist/maplibre-gl-legend.css'
11
+ import { Protocol } from 'pmtiles'
12
+ import './app.css'
13
+
14
+ const protocol = new Protocol()
15
+ addProtocol('pmtiles', protocol.tile)
16
+
17
+ class Charites {
18
+ constructor() {}
19
+
20
+ initializeWebSocket = (map: Map) => {
21
+ const isHttps = window.location.protocol === 'https:'
22
+ const wsProtocol = isHttps ? 'wss' : 'ws'
23
+ const socket = new WebSocket(`${wsProtocol}://${window.location.host}`)
24
+
25
+ socket.addEventListener('message', (message) => {
26
+ const data = JSON.parse(message.data)
27
+ if (data.event === 'styleUpdate') {
28
+ console.log('styleUpdate', data)
29
+ map.setStyle(`//${window.location.host}/style.json`)
30
+ } else if (data.event === 'spriteUpdate') {
31
+ map.setStyle(`//${window.location.host}/style.json`, {
32
+ diff: false,
33
+ })
34
+ }
35
+ })
36
+ }
37
+
38
+ parseMapStyle = async () => {
39
+ const mapStyleUrl = `//${window.location.host}/style.json`
40
+ const mapStyleRes = await fetch(mapStyleUrl)
41
+ const mapStyleJson = await mapStyleRes.json()
42
+
43
+ // detect center & zoom from map style json
44
+ let center = mapStyleJson.hasOwnProperty('center')
45
+ ? mapStyleJson.center
46
+ : undefined
47
+ let zoom = mapStyleJson.hasOwnProperty('zoom')
48
+ ? mapStyleJson.zoom
49
+ : undefined
50
+
51
+ // detect center & zoom from tile json
52
+ if (center === undefined || zoom === undefined) {
53
+ for (const sourceName in mapStyleJson.sources) {
54
+ if (
55
+ mapStyleJson.sources[sourceName].type === 'vector' &&
56
+ mapStyleJson.sources[sourceName].hasOwnProperty('url')
57
+ ) {
58
+ const mapTileUrl = mapStyleJson.sources[sourceName].url
59
+ const mapTileRes = await fetch(mapTileUrl)
60
+ const mapTileJson = await mapTileRes.json()
61
+ if (center === undefined) {
62
+ const bounds = mapTileJson.bounds
63
+ center = mapTileJson.center
64
+ if (!center && bounds) {
65
+ center = [
66
+ (bounds[0] + bounds[2]) / 2,
67
+ (bounds[1] + bounds[3]) / 2,
68
+ ]
69
+ }
70
+ }
71
+ if (zoom === undefined) {
72
+ zoom = (mapTileJson.minzoom + mapTileJson.maxzoom) / 2
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ return {
79
+ style: mapStyleUrl,
80
+ center,
81
+ zoom,
82
+ }
83
+ }
84
+
85
+ setupDebugCheckbox = (map: Map) => {
86
+ const properties = [
87
+ 'showTileBoundaries',
88
+ 'showCollisionBoxes',
89
+ 'showPadding',
90
+ ]
91
+ for (const property of properties) {
92
+ const control = document.getElementById(property) as HTMLInputElement
93
+ const clickHandler = () => {
94
+ const checked = control.checked
95
+ map[property] = checked
96
+ }
97
+ clickHandler()
98
+ control.addEventListener('click', clickHandler)
99
+ }
100
+ }
101
+ }
102
+
103
+ const init = async () => {
104
+ const charites = new Charites()
105
+ const { style, center, zoom } = await charites.parseMapStyle()
106
+ const options: MapOptions = {
107
+ container: 'map',
108
+ hash: true,
109
+ maxPitch: 85,
110
+ style,
111
+ }
112
+ if (center) {
113
+ options.center = center
114
+ }
115
+ if (zoom) {
116
+ options.zoom = zoom
117
+ }
118
+ const map = new Map(options)
119
+
120
+ charites.initializeWebSocket(map)
121
+
122
+ map.addControl(new NavigationControl(), 'top-right')
123
+ map.addControl(
124
+ new MaplibreLegendControl(
125
+ {},
126
+ {
127
+ showDefault: true,
128
+ showCheckbox: true,
129
+ onlyRendered: true,
130
+ reverseOrder: true,
131
+ },
132
+ ) as unknown as IControl,
133
+ 'bottom-left',
134
+ )
135
+ charites.setupDebugCheckbox(map)
136
+ }
137
+ init()
package/src/cli/serve.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { Command } from 'commander'
2
+ import { createServer, InlineConfig } from 'vite'
3
+ import { fileURLToPath } from 'url'
4
+ import path from 'path'
2
5
  import { serve, serveOptions } from '../commands/serve.js'
3
6
  import { error } from '../lib/error.js'
4
7
 
@@ -13,6 +16,7 @@ program
13
16
  )
14
17
  .option('--sdf', 'Allows to use SDF sprite in charites')
15
18
  .option('--port [port]', 'Specify custom port')
19
+ .option('--vite-port [vitePort]', 'Specify custom port for vite server')
16
20
  .option('--no-open', "Don't open the preview in the default browser")
17
21
  .action(async (source: string, serveOptions: serveOptions) => {
18
22
  const options: serveOptions = program.opts()
@@ -20,11 +24,26 @@ program
20
24
  options.spriteInput = serveOptions.spriteInput
21
25
  options.open = serveOptions.open
22
26
  options.sdf = serveOptions.sdf
27
+ options.vitePort = serveOptions.vitePort
23
28
  try {
29
+ await startProviderDevServer(options)
24
30
  await serve(source, program.opts())
25
31
  } catch (e) {
26
32
  error(e)
27
33
  }
28
34
  })
29
35
 
36
+ const __filename = fileURLToPath(import.meta.url)
37
+ const __dirname = path.dirname(__filename)
38
+ async function startProviderDevServer(options: serveOptions) {
39
+ const config: InlineConfig = {
40
+ root: path.resolve(__dirname, '../../provider'),
41
+ server: {
42
+ port: parseInt(options.vitePort ?? '5137'),
43
+ },
44
+ }
45
+
46
+ const viteServer = await createServer(config)
47
+ await viteServer.listen()
48
+ }
30
49
  export default program
@@ -3,16 +3,18 @@ import fs from 'fs'
3
3
  import os from 'os'
4
4
  import http from 'http'
5
5
  import open from 'open'
6
- import { WebSocketServer } from 'ws'
6
+ // tweak to fix WebSocketServer is not a constructor
7
+ const ws = await import('ws')
8
+ const WebSocketServer = ws.default.WebSocketServer || ws.WebSocketServer
7
9
  import watch from 'node-watch'
8
10
 
9
11
  import { parser } from '../lib/yaml-parser.js'
10
12
  import { validateStyle } from '../lib/validate-style.js'
11
- import { providerDir } from '../lib/defaultValues.js'
12
13
  import { buildSprite } from '../lib/build-sprite.js'
13
14
 
14
15
  export interface serveOptions {
15
16
  port?: string
17
+ vitePort?: string
16
18
  spriteInput?: string
17
19
  open?: boolean
18
20
  sdf?: boolean
@@ -71,15 +73,6 @@ export async function serve(source: string, options: serveOptions) {
71
73
  }
72
74
 
73
75
  switch (url) {
74
- case '/':
75
- res.statusCode = 200
76
- res.setHeader('Content-Type', 'text/html; charset=UTF-8')
77
- const content = fs.readFileSync(
78
- path.join(providerDir, 'index.html'),
79
- 'utf-8',
80
- )
81
- res.end(content)
82
- break
83
76
  case '/style.json':
84
77
  let style
85
78
  try {
@@ -98,24 +91,40 @@ export async function serve(source: string, options: serveOptions) {
98
91
  res.setHeader('Cache-Control', 'no-store')
99
92
  res.end(JSON.stringify(style))
100
93
  break
101
- case '/app.css':
102
- res.statusCode = 200
103
- res.setHeader('Content-Type', 'text/css; charset=UTF-8')
104
- const css = fs.readFileSync(path.join(providerDir, 'app.css'), 'utf-8')
105
- res.end(css)
106
- break
107
- case `/app.js`:
108
- res.statusCode = 200
109
- res.setHeader('Content-Type', 'application/javascript; charset=UTF-8')
110
- const app = fs.readFileSync(path.join(providerDir, 'app.js'), 'utf-8')
111
- const js = app.replace('___PORT___', `${port}`)
112
- res.end(js)
113
- break
114
- default:
94
+ case '/sprite.json':
95
+ case '/sprite@2x.json':
96
+ case '/sprite.png':
97
+ case '/sprite@2x.png':
115
98
  res.statusCode = 404
116
99
  res.setHeader('Content-Type', 'text/plain; charset=UTF-8')
117
100
  res.end('Not found')
118
101
  break
102
+ default:
103
+ const vitePort = parseInt(options.vitePort || '5137')
104
+ const proxyReq = http.request(
105
+ {
106
+ hostname: 'localhost',
107
+ port: vitePort,
108
+ path: url,
109
+ method: req.method,
110
+ headers: req.headers,
111
+ },
112
+ (viteRes) => {
113
+ res.writeHead(viteRes.statusCode || 500, viteRes.headers)
114
+ viteRes.pipe(res, {
115
+ end: true,
116
+ })
117
+ },
118
+ )
119
+ req.pipe(proxyReq, {
120
+ end: true,
121
+ })
122
+ proxyReq.on('error', (err) => {
123
+ console.error('Error with proxy request:', err)
124
+ res.statusCode = 500
125
+ res.end('Bad gateway: failed to proxy to Vite dev server')
126
+ })
127
+ break
119
128
  }
120
129
  })
121
130
 
@@ -129,11 +138,11 @@ export async function serve(source: string, options: serveOptions) {
129
138
 
130
139
  const wss = new WebSocketServer({ server })
131
140
 
132
- wss.on('connection', (ws) => {
141
+ wss.on('connection', (ws: WebSocket) => {
133
142
  const watcher = watch(
134
143
  path.dirname(sourcePath),
135
144
  { recursive: true, filter: /\.yml$|\.svg$/i },
136
- (event, file) => {
145
+ (event: string, file: string) => {
137
146
  console.log(`${(event || '').toUpperCase()}: ${file}`)
138
147
  try {
139
148
  if (file?.toLowerCase().endsWith('.yml')) {
@@ -159,7 +168,7 @@ export async function serve(source: string, options: serveOptions) {
159
168
  }
160
169
  },
161
170
  )
162
- ws.on('close', () => {
171
+ wss.on('close', () => {
163
172
  watcher.close()
164
173
  })
165
174
  })
@@ -1,4 +1,4 @@
1
- import { VectorLayer } from '../../types'
1
+ import { VectorLayer } from '../../types/vector_layers.js'
2
2
  import {
3
3
  StyleSpecification,
4
4
  SourceSpecification,
@@ -1,9 +1,10 @@
1
- import maplibreStyleSpec, {
1
+ import {
2
+ validateStyleMin,
2
3
  StyleSpecification,
3
4
  } from '@maplibre/maplibre-gl-style-spec'
4
5
 
5
6
  export function validateStyle(style: StyleSpecification): void {
6
- const result = maplibreStyleSpec.validateStyleMin(style)
7
+ const result = validateStyleMin(style)
7
8
 
8
9
  const errors = []
9
10
  for (let i = 0; i < result.length; i++) {
@@ -43,10 +43,13 @@ class IncFileTag {
43
43
 
44
44
  const INC_PATH_TYPE = new YAML.Type('tag:yaml.org,2002:inc/file', {
45
45
  kind: 'scalar',
46
- resolve: (data) => data,
47
- construct: (data) => new IncFileTag(data),
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ resolve: (data: any) => data,
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ construct: (data: any) => new IncFileTag(data),
48
50
  instanceOf: IncFileTag,
49
- represent: (tag) => (tag as IncFileTag).value,
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ represent: (tag: any) => (tag as IncFileTag).value,
50
53
  })
51
54
  const INC_PATH_OUTPUT_SCHEMA = YAML.DEFAULT_SCHEMA.extend([INC_PATH_TYPE])
52
55
 
@@ -1,3 +1,3 @@
1
- export * from './tilejson'
2
- export * from './metadatajson'
3
- export * from './vector_layers'
1
+ export * from './tilejson.js'
2
+ export * from './metadatajson.js'
3
+ export * from './vector_layers.js'
@@ -1,4 +1,4 @@
1
- import { VectorLayer } from './vector_layers'
1
+ import { VectorLayer } from './vector_layers.js'
2
2
 
3
3
  /**
4
4
  * Definition of TileJSON v3.0.0
package/tsconfig.json CHANGED
@@ -13,13 +13,21 @@
13
13
  "experimentalDecorators": true,
14
14
  "emitDecoratorMetadata": true,
15
15
  "resolveJsonModule": true,
16
- "module": "ESNext",
16
+ "allowSyntheticDefaultImports": true,
17
+ "skipLibCheck": true,
18
+ "module": "Preserve",
17
19
  "moduleResolution": "node",
18
20
  "outDir": "./dist",
19
21
  "baseUrl": ".",
20
22
  "paths": {
21
23
  "*": ["node_modules/*", "src/types/*"]
22
- }
24
+ },
25
+ "types": [
26
+ "mocha",
27
+ "chai",
28
+ "node"
29
+ ],
30
+ "typeRoots": ["./node_modules/@types", "./src/types"]
23
31
  },
24
32
  "include": ["src/**/*"],
25
33
  "exclude": ["node_modules", "dist"]
@@ -1,6 +0,0 @@
1
- import { fileURLToPath } from 'url';
2
- import { dirname } from 'path';
3
- import path from 'path';
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = dirname(__filename);
6
- export const providerDir = path.join(path.dirname(path.dirname(__dirname)), 'provider');
package/provider/app.js DELETED
@@ -1,112 +0,0 @@
1
- ;(async () => {
2
- window._charites = {
3
- initializeWebSocket: function (map) {
4
- const isHttps = window.location.protocol === 'https:'
5
- const wsProtocol = isHttps ? 'wss' : 'ws'
6
- const socket = new WebSocket(`${wsProtocol}://${window.location.host}`)
7
-
8
- socket.addEventListener('message', (message) => {
9
- const data = JSON.parse(message.data)
10
- if (data.event === 'styleUpdate') {
11
- map.setStyle(`//${window.location.host}/style.json`)
12
- } else if (data.event === 'spriteUpdate') {
13
- map.setStyle(`//${window.location.host}/style.json`, {
14
- diff: false,
15
- })
16
- }
17
- })
18
- },
19
- parseMapStyle: async function () {
20
- const mapStyleUrl = `//${window.location.host}/style.json`
21
- const mapStyleRes = await fetch(mapStyleUrl)
22
- const mapStyleJson = await mapStyleRes.json()
23
-
24
- // detect center & zoom from map style json
25
- let center = mapStyleJson.hasOwnProperty('center')
26
- ? mapStyleJson.center
27
- : undefined
28
- let zoom = mapStyleJson.hasOwnProperty('zoom')
29
- ? mapStyleJson.zoom
30
- : undefined
31
-
32
- // detect center & zoom from tile json
33
- if (center === undefined || zoom === undefined) {
34
- for (const sourceName in mapStyleJson.sources) {
35
- if (
36
- mapStyleJson.sources[sourceName].type === 'vector' &&
37
- mapStyleJson.sources[sourceName].hasOwnProperty('url')
38
- ) {
39
- const mapTileUrl = mapStyleJson.sources[sourceName].url
40
- const mapTileRes = await fetch(mapTileUrl)
41
- const mapTileJson = await mapTileRes.json()
42
- if (center === undefined) {
43
- const bounds = mapTileJson.bounds
44
- center = mapTileJson.center
45
- if (!center && bounds) {
46
- center = [
47
- (bounds[0] + bounds[2]) / 2,
48
- (bounds[1] + bounds[3]) / 2,
49
- ]
50
- }
51
- }
52
- if (zoom === undefined) {
53
- zoom = (mapTileJson.minzoom + mapTileJson.maxzoom) / 2
54
- }
55
- }
56
- }
57
- }
58
-
59
- return {
60
- style: mapStyleUrl,
61
- center,
62
- zoom,
63
- }
64
- },
65
- setupDebugCheckboxes: function (map) {
66
- const properties = [
67
- 'showTileBoundaries',
68
- 'showCollisionBoxes',
69
- 'showPadding',
70
- ]
71
- for (const property of properties) {
72
- const control = document.getElementById(property)
73
- const clickHandler = function () {
74
- const checked = control.checked
75
- map[property] = checked
76
- }
77
- clickHandler()
78
- control.addEventListener('click', clickHandler)
79
- }
80
- },
81
- }
82
-
83
- const { style, center, zoom } = await window._charites.parseMapStyle()
84
- const options = {
85
- container: 'map',
86
- hash: true,
87
- maxPitch: 85,
88
- style,
89
- }
90
- if (center) options.center = center
91
- if (zoom) options.zoom = zoom
92
- const map = new maplibregl.Map(options)
93
-
94
- window._charites.initializeWebSocket(map)
95
-
96
- map.addControl(new maplibregl.NavigationControl(), 'top-right')
97
-
98
- map.addControl(
99
- new MaplibreLegendControl.MaplibreLegendControl(
100
- {},
101
- {
102
- showDefault: true,
103
- showCheckbox: true,
104
- onlyRendered: true,
105
- reverseOrder: true,
106
- },
107
- ),
108
- 'bottom-left',
109
- )
110
-
111
- window._charites.setupDebugCheckboxes(map)
112
- })()
@@ -1,10 +0,0 @@
1
- import { fileURLToPath } from 'url'
2
- import { dirname } from 'path'
3
- import path from 'path'
4
-
5
- const __filename = fileURLToPath(import.meta.url)
6
- const __dirname = dirname(__filename)
7
- export const providerDir = path.join(
8
- path.dirname(path.dirname(__dirname)),
9
- 'provider',
10
- )