@@ -15,18 +15,32 @@ import {
1515interface UtilsSettings {
1616 commonFiles : string [ ] ;
1717 browser : { fileName : string ; files : string [ ] } ;
18- node : { fileName : string ; files : string [ ] } ;
1918}
2019
2120interface VariantConfig {
2221 name : string ;
2322 files : string [ ] ;
2423 fileName : string ;
2524 suffix : string ;
26- wrapUmd : boolean ;
2725}
2826
27+ interface ParsedRegion {
28+ name : string ;
29+ data : unknown ;
30+ }
31+
32+ type ParseFn = (
33+ input : { shp : ArrayBuffer ; dbf : ArrayBuffer } ,
34+ options : { precision : number } ,
35+ ) => unknown ;
36+
2937const USE_STRICT_HEADER = '"use strict";\n\n' ;
38+ const DEBUG_SUFFIX = '.debug' ;
39+ const DEFAULT_PRECISION = 4 ;
40+
41+ function toArrayBuffer ( buffer : Buffer ) : ArrayBuffer {
42+ return buffer . buffer . slice ( buffer . byteOffset , buffer . byteOffset + buffer . byteLength ) ;
43+ }
3044
3145async function buildUtilsVariant (
3246 variant : VariantConfig ,
@@ -40,12 +54,10 @@ async function buildUtilsVariant(
4054 const content = await readFileText ( filePath ) ;
4155 contents . push ( content ) ;
4256 }
43- let bundle = contents . join ( '\n' ) ;
57+ const concatenated = contents . join ( '\n' ) ;
4458
45- if ( variant . wrapUmd ) {
46- const compiled = _ . template ( utilsTemplate ) ;
47- bundle = compiled ( { data : bundle } ) ;
48- }
59+ const compiled = _ . template ( utilsTemplate ) ;
60+ let bundle = compiled ( { data : concatenated } ) ;
4961
5062 bundle = ensureTrailingNewline ( normalizeEol ( bundle ) ) ;
5163
@@ -58,32 +70,24 @@ async function buildUtils(
5870 settingsFile : string ,
5971 utilsTemplatePath : string ,
6072 outDir : string ,
61- ) : Promise < { nodeUtilPath : string } > {
73+ ) : Promise < { debugBundlePath : string } > {
6274 const settingsPath = path . join ( sourceDir , settingsFile ) ;
6375 const settings : UtilsSettings = JSON . parse ( await readFileText ( settingsPath ) ) ;
6476 const utilsTemplate = await readFileText ( utilsTemplatePath ) ;
6577
78+ const browserFiles = [ ...settings . commonFiles , ...settings . browser . files ] ;
6679 const variants : VariantConfig [ ] = [
6780 {
6881 name : 'browser-debug' ,
69- files : [ ... settings . commonFiles , ... settings . browser . files ] ,
82+ files : browserFiles ,
7083 fileName : settings . browser . fileName ,
71- suffix : '.debug' ,
72- wrapUmd : true ,
84+ suffix : DEBUG_SUFFIX ,
7385 } ,
7486 {
7587 name : 'browser-prod' ,
76- files : [ ... settings . commonFiles , ... settings . browser . files ] ,
88+ files : browserFiles ,
7789 fileName : settings . browser . fileName ,
7890 suffix : '' ,
79- wrapUmd : true ,
80- } ,
81- {
82- name : 'node' ,
83- files : [ ...settings . commonFiles , ...settings . node . files ] ,
84- fileName : settings . node . fileName ,
85- suffix : '' ,
86- wrapUmd : false ,
8791 } ,
8892 ] ;
8993
@@ -94,12 +98,56 @@ async function buildUtils(
9498 await buildUtilsVariant ( variant , sourceDir , utilsTemplate , outDir ) ;
9599 }
96100
97- const nodeUtilPath = path . join ( outDir , `${ settings . node . fileName } .js` ) ;
98- return { nodeUtilPath } ;
101+ const debugBundlePath = path . join ( outDir , `${ settings . browser . fileName } ${ DEBUG_SUFFIX } .js` ) ;
102+ return { debugBundlePath } ;
103+ }
104+
105+ function parseShapefiles ( parse : ParseFn , sourcesDir : string , precision : number ) : ParsedRegion [ ] {
106+ const shpFiles = fs
107+ . readdirSync ( sourcesDir )
108+ . filter ( ( f ) => path . extname ( f ) . toLowerCase ( ) === '.shp' )
109+ . map ( ( f ) => path . basename ( f , '.shp' ) ) ;
110+
111+ const regions : ParsedRegion [ ] = [ ] ;
112+ for ( const name of shpFiles ) {
113+ const shpBuffer = fs . readFileSync ( path . join ( sourcesDir , `${ name } .shp` ) ) ;
114+ const dbfBuffer = fs . readFileSync ( path . join ( sourcesDir , `${ name } .dbf` ) ) ;
115+
116+ const data = parse (
117+ { shp : toArrayBuffer ( shpBuffer ) , dbf : toArrayBuffer ( dbfBuffer ) } ,
118+ { precision } ,
119+ ) ;
120+
121+ if ( ! data ) {
122+ throw new Error (
123+ `Vectormap: parse() returned no data for "${ name } ". `
124+ + `Check that "${ name } .shp" and "${ name } .dbf" are valid shapefiles.` ,
125+ ) ;
126+ }
127+
128+ regions . push ( { name, data } ) ;
129+ }
130+
131+ return regions ;
132+ }
133+
134+ async function writeRegionModules (
135+ regions : ParsedRegion [ ] ,
136+ dataTemplate : string ,
137+ outDir : string ,
138+ ) : Promise < void > {
139+ const compiled = _ . template ( dataTemplate ) ;
140+
141+ for ( const { name, data } of regions ) {
142+ const rawData = `${ name } = ${ JSON . stringify ( data ) } ;` ;
143+ let wrapped = USE_STRICT_HEADER + compiled ( { data : rawData } ) ;
144+ wrapped = ensureTrailingNewline ( normalizeEol ( wrapped ) ) ;
145+ await writeFileText ( path . join ( outDir , `${ name } .js` ) , wrapped ) ;
146+ }
99147}
100148
101149async function buildData (
102- nodeUtilPath : string ,
150+ debugBundlePath : string ,
103151 sourcesDir : string ,
104152 sourcesSettingsFile : string ,
105153 dataTemplatePath : string ,
@@ -108,43 +156,25 @@ async function buildData(
108156) : Promise < void > {
109157 const dataTemplate = await readFileText ( dataTemplatePath ) ;
110158
111- const resolvedUtilPath = path . resolve ( nodeUtilPath ) ;
159+ const resolvedUtilPath = path . resolve ( debugBundlePath ) ;
112160 delete require . cache [ require . resolve ( resolvedUtilPath ) ] ;
113- const { processFiles } = require ( resolvedUtilPath ) ;
161+ const { parse } = require ( resolvedUtilPath ) as { parse : ParseFn } ;
114162
115163 const resolvedSourcesDir = path . resolve ( projectRoot , sourcesDir ) ;
116164 const resolvedOutDir = path . resolve ( projectRoot , outDir ) ;
165+ const resolvedSettingsPath = path . resolve ( resolvedSourcesDir , sourcesSettingsFile ) ;
117166
118- await ensureDir ( resolvedOutDir ) ;
167+ delete require . cache [ require . resolve ( resolvedSettingsPath ) ] ;
168+ const sourcesSettings = require ( resolvedSettingsPath ) as { precision ?: number } ;
169+ const precision =
170+ sourcesSettings . precision !== undefined && sourcesSettings . precision >= 0
171+ ? Math . round ( sourcesSettings . precision )
172+ : DEFAULT_PRECISION ;
119173
120- await new Promise < void > ( ( resolve , reject ) => {
121- try {
122- processFiles (
123- resolvedSourcesDir ,
124- {
125- output : resolvedOutDir ,
126- settings : path . resolve ( resolvedSourcesDir , sourcesSettingsFile ) ,
127- } ,
128- ( ) => {
129- resolve ( ) ;
130- } ,
131- ) ;
132- } catch ( error ) {
133- reject ( error ) ;
134- }
135- } ) ;
136-
137- const files = fs . readdirSync ( resolvedOutDir ) . filter ( ( f ) => f . endsWith ( '.js' ) ) ;
138- const compiled = _ . template ( dataTemplate ) ;
174+ await ensureDir ( resolvedOutDir ) ;
139175
140- for ( const file of files ) {
141- const filePath = path . join ( resolvedOutDir , file ) ;
142- const rawData = await readFileText ( filePath ) ;
143- let wrapped = compiled ( { data : rawData } ) ;
144- wrapped = USE_STRICT_HEADER + wrapped ;
145- wrapped = ensureTrailingNewline ( normalizeEol ( wrapped ) ) ;
146- await writeFileText ( filePath , wrapped ) ;
147- }
176+ const regions = parseShapefiles ( parse , resolvedSourcesDir , precision ) ;
177+ await writeRegionModules ( regions , dataTemplate , resolvedOutDir ) ;
148178}
149179
150180const runExecutor : PromiseExecutor < VectormapExecutorSchema > = async ( options , context ) => {
@@ -167,17 +197,16 @@ const runExecutor: PromiseExecutor<VectormapExecutorSchema> = async (options, co
167197
168198 try {
169199 logger . verbose ( 'Phase 1: Building vectormap utilities...' ) ;
170- const { nodeUtilPath } = await buildUtils (
200+ const { debugBundlePath } = await buildUtils (
171201 resolvedSourceDir ,
172202 settingsFile ,
173203 resolvedUtilsTemplatePath ,
174204 resolvedUtilsOutDir ,
175205 ) ;
176206
177207 logger . verbose ( 'Phase 2: Building vectormap data...' ) ;
178-
179208 await buildData (
180- nodeUtilPath ,
209+ debugBundlePath ,
181210 sourcesDir ,
182211 sourcesSettingsFile ,
183212 resolvedDataTemplatePath ,
0 commit comments