mapshaper 0.7.19 → 0.7.21

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/bin/mapshaper-gui CHANGED
@@ -14,6 +14,7 @@ var defaultPort = 5555,
14
14
  .option('-n, --name <name(s)>', 'rename input layer or layers')
15
15
  .option('-c, --commands <string>', 'console commands to run initially')
16
16
  .option('-t, --target <name>', 'name of layer to select initially')
17
+ .option('-w, --watch', 'watch input files and reload the browser on change')
17
18
  .addOption(new commander.Option('-b, --blurb <text>',
18
19
  'replace the default blurb on the import screen').hideHelp())
19
20
  .argument('[file...]', 'data files to load')
@@ -30,9 +31,26 @@ var defaultPort = 5555,
30
31
  port = parseInt(opts.port, 10) || defaultPort,
31
32
  dataFiles = expandShapefiles(program.args),
32
33
  probeCount = 0,
33
- sessionId = null;
34
+ sessionId = null,
35
+ sseClients = [],
36
+ reloadTimer = null;
34
37
  validateFiles(dataFiles);
35
38
 
39
+ if (opts.watch && dataFiles.length > 0) {
40
+ dataFiles.forEach(function(file) {
41
+ if (isUrl(file)) return;
42
+ fs.watch(file, function() {
43
+ // debounce: editors often write files in multiple steps
44
+ clearTimeout(reloadTimer);
45
+ reloadTimer = setTimeout(function() {
46
+ sseClients.forEach(function(res) {
47
+ try { res.write('event: reload\ndata: changed\n\n'); } catch(e) {}
48
+ });
49
+ }, 150);
50
+ });
51
+ });
52
+ }
53
+
36
54
  process.on('uncaughtException', function(err) {
37
55
  // added 'code' for Node.js v16
38
56
  if (err.errno === 'EADDRINUSE' || err.code === 'EADDRINUSE') {
@@ -99,6 +117,18 @@ function startServer(port) {
99
117
  serveFile(getDataFilePath(uri), response);
100
118
  } else if (uri.indexOf('/save') === 0) {
101
119
  saveContent(request, response);
120
+ } else if (uri == '/sse' && opts.watch) {
121
+ // Server-Sent Events endpoint for file-watch reload notifications
122
+ response.writeHead(200, {
123
+ 'Content-Type': 'text/event-stream',
124
+ 'Cache-Control': 'no-cache',
125
+ 'Connection': 'keep-alive'
126
+ });
127
+ response.write('data: connected\n\n');
128
+ sseClients.push(response);
129
+ request.on('close', function() {
130
+ sseClients = sseClients.filter(function(c) { return c !== response; });
131
+ });
102
132
  } else {
103
133
  // serve a file from the web root. Translate directory paths (any URI
104
134
  // ending in '/') to their index.html so that links like '/docs/' work
@@ -106,7 +136,21 @@ function startServer(port) {
106
136
  if (uri.endsWith('/')) {
107
137
  uri += 'index.html';
108
138
  }
109
- serveFile(getAssetFilePath(uri), response);
139
+ if (opts.watch && uri == '/index.html') {
140
+ // inject SSE reload script into index.html
141
+ var htmlPath = getAssetFilePath('/index.html');
142
+ fs.readFile(htmlPath, 'utf8', function(err, content) {
143
+ if (err) return serve404(response);
144
+ var script = '<script>(function(){' +
145
+ 'var es=new EventSource("/sse");' +
146
+ 'es.addEventListener("reload",function(){location.reload();});' +
147
+ '})();</script>';
148
+ content = content.replace('</body>', script + '\n</body>');
149
+ serveContent(content, response, 'text/html');
150
+ });
151
+ } else {
152
+ serveFile(getAssetFilePath(uri), response);
153
+ }
110
154
  }
111
155
  }).listen(port, 'localhost', function() {
112
156
  if (process.env.MAPSHAPER_GUI_NO_OPEN) return;
@@ -165,7 +209,7 @@ function serveContent(content, response, mimeType) {
165
209
  }
166
210
  response.setHeader('Cache-Control', 'no-cache');
167
211
  response.writeHead(200);
168
- response.write(content, "binary");
212
+ response.write(content, Buffer.isBuffer(content) ? undefined : "utf8");
169
213
  response.end();
170
214
  }
171
215