- webpack config separation for development and production

- load only the necessary classes from leaflet
This commit is contained in:
Arno Kaimbacher 2021-08-25 17:52:09 +02:00
parent 7857e2c5bb
commit bc44385846
13 changed files with 883 additions and 209 deletions

View file

@ -125,3 +125,26 @@ Install dependencies
npm install --save rxjs
npm install --save-dev webpack-merge
========================= cretae html file for production
https://webpack.js.org/plugins/html-webpack-plugin/
npm install --save-dev html-webpack-plugin
Basic Usage:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [new HtmlWebpackPlugin()],
};
This will generate a file dist/index.html containing the following:

772
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,8 @@
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prod": "rm -rf dist && webpack --progress --mode=production",
"dev": "webpack serve --mode development --content-base=./ --hot --progress --port 8080"
"prod": "rm -rf dist && webpack --progress --config webpack.production.js",
"dev": "webpack serve --config webpack.development.js --content-base=./ --hot --progress --port 8080"
},
"keywords": [
"geomonitoring",
@ -29,6 +29,7 @@
"dotenv": "^10.0.0",
"file-loader": "^6.2.0",
"html-loader": "^2.1.2",
"html-webpack-plugin": "^5.3.2",
"img-loader": "^4.0.0",
"mini-css-extract-plugin": "^2.1.0",
"raw-loader": "^4.0.2",
@ -37,7 +38,8 @@
"url-loader": "^4.1.1",
"webpack": "^5.47.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@angular/core": "^12.1.4",
@ -52,7 +54,7 @@
"babel-plugin-transform-typescript-metadata": "^0.3.2",
"core-js": "^3.16.0",
"leaflet": "^1.7.1",
"rxjs": "^6.6.7",
"rxjs": "^7.3.0",
"zone.js": "^0.11.4"
}
}

View file

@ -1,4 +1,5 @@
<app-map [mapId]="'map'" [mapOptions]="mapOptions" [serviceUrl]="providerUrl" [baseMaps]="baseMaps" (mapInitializedEvent)="mapInitialized($event)"></app-map>
<app-map [mapId]="'map'" [mapOptions]="mapOptions" [serviceUrl]="providerUrl" [baseMaps]="baseMaps"
(onSelected)="onStationSelected($event)" (onMapInitializedEvent)="onMapInitialized($event)"></app-map>
<!-- <span>{{ name }} app is running!</span> -->
<!-- <div>

View file

@ -4,9 +4,10 @@ import '../styles.css';
import { ParameterFilter, Phenomenon, Station } from '@helgoland/core';
import { GeoSearchOptions, LayerOptions } from '@helgoland/map';
import * as L from 'leaflet';
import { Marker, MapOptions, Control, icon, LatLngBoundsExpression } from 'leaflet';
// optional, to adapt leaflet markers
L.Marker.prototype.options.icon = L.icon({
Marker.prototype.options.icon = icon({
iconRetinaUrl: 'assets/img/marker-icon-2x.png',
iconUrl: 'assets/img/marker-icon.png',
shadowUrl: 'assets/img/marker-shadow.png',
@ -39,25 +40,25 @@ export class AppComponent {
// public providerUrl = 'https://geo.irceline.be/sos/api/v2/';
public providerUrl = 'https://geomon.geologie.ac.at/52n-sos-webapp/api/';
public fitBounds: L.LatLngBoundsExpression = [[49.5, 3.27], [51.5, 5.67]];
public fitBounds: LatLngBoundsExpression = [[ 9.47996951665, 46.4318173285], [16.9796667823, 49.0390742051]];
// public zoomControlOptions: L.Control.ZoomOptions = { position: 'topleft' };
public avoidZoomToSelection = false;
public baseMaps: Map<string, LayerOptions> = new Map<string, LayerOptions>();
public overlayMaps: Map<string, LayerOptions> = new Map<string, LayerOptions>();
public layerControlOptions: L.Control.LayersOptions = { position: 'bottomleft' };
public layerControlOptions: Control.LayersOptions = { position: 'bottomleft' };
public cluster = false;
public loadingStations: boolean;
public stationFilter: ParameterFilter = {
// phenomenon: '8'
};
public statusIntervals = false;
public mapOptions: L.MapOptions = { dragging: true, zoomControl: false };
public mapOptions: MapOptions = { dragging: true, zoomControl: false };
public onStationSelected(station: Station) {
private onStationSelected(station: Station) {
console.log('Clicked station: ' + station.properties.label);
}
mapInitialized(newItem: string) {
private onMapInitialized(newItem: string) {
// alert(newItem);
}
}

View file

@ -4,10 +4,6 @@ import { BrowserModule } from "@angular/platform-browser";
import { AppComponent } from './app.component';
import { MapComponent } from './map/map.component';
// import {
// HelgolandServicesConnector
// } from '@helgoland/core';
import { HttpClientModule, HttpClient } from '@angular/common/http'; //for http requests
import { MarkerService } from './services/marker.service';
import { DatasetApiService } from "./services/dataset-api.service";
@ -16,10 +12,10 @@ import { HttpService } from "./services/http.service";
// siehe https://52north.github.io/helgoland-toolbox/additional-documentation/how-tos/integrate-a-map-component.html
// https://52north.github.io/helgoland-toolbox/components/LocateControlComponent.html#source
import {
DatasetApiInterface, ApiV3InterfaceService, SplittedDataDatasetApiInterface, DatasetApiV3Connector, HelgolandServicesConnector
} from '@helgoland/core';
import { HelgolandCoreModule } from "@helgoland/core";
// import {
// DatasetApiInterface, ApiV3InterfaceService, SplittedDataDatasetApiInterface, DatasetApiV3Connector, HelgolandServicesConnector
// } from '@helgoland/core';
// import { HelgolandCoreModule } from "@helgoland/core";
// import 'core-js';
@ -27,7 +23,7 @@ import { HelgolandCoreModule } from "@helgoland/core";
// declarations: The components, directives, and pipes that belong to this NgModule.
declarations: [AppComponent, MapComponent],
// imports: Other modules whose exported classes are needed by component templates declared in this NgModule.
imports: [BrowserModule, HttpClientModule, HelgolandCoreModule],
imports: [BrowserModule, HttpClientModule],
providers: [
MarkerService, HttpService, DatasetApiService,
// {
@ -36,11 +32,11 @@ import { HelgolandCoreModule } from "@helgoland/core";
// deps: [HttpXhrBackend]
// },
{
provide: HelgolandServicesConnector,
useClass: DatasetApiV3Connector,
deps: [HttpClient]
},
// {
// provide: HelgolandServicesConnector,
// useClass: DatasetApiV3Connector,
// deps: [HttpClient]
// },
],
// bootstrap: The main application view, called the root component, which hosts all other application views.

View file

@ -70,7 +70,7 @@ export abstract class BaseMapComponent implements OnChanges, OnInit {
* Informs when initialization is done with map id.
*/
@Output()
public mapInitializedEvent: EventEmitter<string> = new EventEmitter();
public onMapInitializedEvent: EventEmitter<string> = new EventEmitter();
protected oldBaseLayer: L.Control.LayersObject = {};
protected map: L.Map;
@ -103,7 +103,7 @@ export abstract class BaseMapComponent implements OnChanges, OnInit {
zoom: 3
});
this.mapInitializedEvent.emit(this.mapId);
this.onMapInitializedEvent.emit(this.mapId);
if (this.baseMaps && this.baseMaps.size > 0) {
this.baseMaps.forEach((layerOptions, key) => this.addBaseMap(layerOptions));
} else {

View file

@ -1,24 +1,10 @@
// https://github.com/52North/helgoland-toolbox/blob/c2c2eda20353c469a7aa4a6a118a810723af6622/libs/map/src/lib/selector/station-map-selector/station-map-selector.component.ts
import { Component, AfterViewInit, OnChanges, SimpleChanges, EventEmitter, Input, Output } from '@angular/core';
import * as L from 'leaflet';
import { Map, Control, FeatureGroup, geoJSON, circleMarker, FitBoundsOptions, LatLngBoundsExpression } from 'leaflet';
import { MarkerService } from '../services/marker.service';
import { DatasetApiService } from '../services/dataset-api.service';
import { BaseMapComponent } from './base-map.component';
// const iconRetinaUrl = 'assets/marker-icon-2x.png';
// const iconUrl = 'assets/marker-icon.png';
// const shadowUrl = 'assets/marker-shadow.png';
// const iconDefault = L.icon({
// iconRetinaUrl,
// iconUrl,
// shadowUrl,
// iconSize: [25, 41],
// iconAnchor: [12, 41],
// popupAnchor: [1, -34],
// tooltipAnchor: [16, -28],
// shadowSize: [41, 41]
// });
// L.Marker.prototype.options.icon = iconDefault;
@Component({
selector: 'app-map',
templateUrl: './map.component.html',
@ -41,17 +27,22 @@ export class MapComponent
@Input()
public serviceUrl: string;
// @Output()
// public onSelected: EventEmitter<T> = new EventEmitter<T>();
@Output()
public onSelected: EventEmitter<any> = new EventEmitter<any>();
@Output()
public onContentLoadingEvent: EventEmitter<boolean> = new EventEmitter();
/**
* @input Additional configuration for the marker zooming (https://leafletjs.com/reference-1.3.4.html#fitbounds-options)
*/
@Input()
public fitBoundsMarkerOptions: FitBoundsOptions;
protected oldBaseLayer: L.Control.LayersObject = {};
protected map: L.Map;
protected zoomControl: L.Control.Zoom;
protected markerFeatureGroup: L.FeatureGroup;
protected oldBaseLayer: Control.LayersObject = {};
protected map: Map;
protected zoomControl: Control.Zoom;
protected markerFeatureGroup: FeatureGroup;
// constructor() { }
constructor(
@ -69,20 +60,27 @@ export class MapComponent
// this.markerService.makeCapitalCircleMarkers(this.map);
this.datasetApiService.getStations('https://geomon.geologie.ac.at/52n-sos-webapp/api/')
.subscribe((res) => {
this.markerFeatureGroup = L.featureGroup();
this.markerFeatureGroup = new FeatureGroup();
if (res instanceof Array && res.length > 0) {
res.forEach((entry) => {
res.forEach((station) => {
//const marker = this.createDefaultGeometry(entry);
// const marker = L.geoJSON(entry.geometry);
const marker = L.geoJSON(entry.geometry, {
// const marker = geoJSON(entry.geometry);
const marker = geoJSON(station.geometry, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng);
return circleMarker(latlng);
}
})
if (marker) { this.markerFeatureGroup.addLayer(marker); }
// if (marker) { this.markerFeatureGroup.addLayer(marker); }
marker && this.markerFeatureGroup.addLayer(marker);
if (marker) {
marker.on('click', () => {
this.onSelected.emit(station);
});
}
});
this.markerFeatureGroup.addTo(this.map);
// this.zoomToMarkerBounds(this.markerFeatureGroup.getBounds());
this.zoomToMarkerBounds(this.markerFeatureGroup.getBounds());
} else {
// this.onNoResultsFound.emit(true);
}
@ -108,4 +106,14 @@ export class MapComponent
// }
}
/**
* Zooms to the given bounds
*
* @protected
* @param bounds where to zoom
*/
protected zoomToMarkerBounds(bounds: LatLngBoundsExpression) {
this.map.fitBounds(bounds, this.fitBoundsMarkerOptions || {});
}
}

View file

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';
import { Map, circleMarker } from 'leaflet';
// @Injectable({
// // declares that this service should be created
@ -33,7 +33,7 @@ export class MarkerService {
// });
// }
makeCapitalCircleMarkers(map: L.Map): void {
makeCapitalCircleMarkers(map: Map): void {
this.http.get(this.capitalsUrl).subscribe((res: any) => {
let maxPop = Math.max(...res.features.map(x => x.properties.population), 0);
@ -42,7 +42,7 @@ export class MarkerService {
const lon = c.geometry.coordinates[0];
const lat = c.geometry.coordinates[1];
// const circle = L.circleMarker([lat, lon]);
let circle = L.circleMarker([lat, lon], {
let circle = circleMarker([lat, lon], {
radius: MarkerService.scaledRadius(c.properties.population, maxPop)
});
circle.addTo(map);

40
src/index.tmp.html Normal file
View file

@ -0,0 +1,40 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= htmlWebpackPlugin.options.title || 'Webpack App'%></title>
<meta name="description" content="A simple HTML5 Template for new projects.">
<meta name="author" content="Arno Kaimbacher">
<meta property="og:title" content="GeoMon Viewer">
<meta property="og:type" content="website">
<meta property="og:url" content="https://geomon.geologie.ac.at/52n-sos-webapp/">
<meta property="og:description" content="A simple HTML5 Template for new projects.">
<meta property="og:image" content="image.png">
<link rel="icon" href="src/favicon.ico">
<!-- <link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png"> -->
<!-- <link rel="stylesheet" href="./node_modules/leaflet//dist/leaflet.css"> -->
</head>
<body>
<!-- your content here... -->
<app-component></app-component>
<!-- <script src="dist/polyfills.js" defer></script> -->
<!-- <script src="dist/main.js"></script> -->
</body>
</html>

View file

@ -8,8 +8,8 @@ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
/**
* flag Used to check if the environment is production or not
*/
const isProduction = false; //(process.env.NODE_ENV === 'production');
const devMode = (process.env.NODE_ENV !== 'production');
const isProduction = (process.env.NODE_ENV === 'production');
// const devMode = (process.env.NODE_ENV !== 'production');
/**
* Include hash to filenames for cache busting - only at production
@ -33,14 +33,6 @@ module.exports = {
publicPath: '/dist/',
// sourceMapFilename: "[name].js.map"
},
devServer: {
contentBase: path.resolve(__dirname),
publicPath: "/", //should provide the path of the served js , img , etc...
writeToDisk: true,
compress: true,
port: 8080,
open: true,
},
// resolve: {
// alias: {
@ -126,15 +118,6 @@ module.exports = {
},
}
},
{
test: /\.(css|scss)$/,
// include: helpers.root('src', 'app'),
@ -178,35 +161,35 @@ module.exports = {
resolve: {
extensions: ['*', '.js', '.jsx', '.tsx', '.ts'],
},
devtool: 'inline-source-map',
// devtool: 'inline-source-map',
stats: {
colors: true
},
// optimization: {
// minimize: isProduction,
// minimizer: [
// new TerserPlugin({
// // cache: true,
// parallel: true,
// // sourceMap: true, // Must be set to true if using source-maps in production
// extractComments: true,
// terserOptions: {
optimization: {
minimize: isProduction,
minimizer: [
new TerserPlugin({
// cache: true,
parallel: true,
// sourceMap: true, // Must be set to true if using source-maps in production
extractComments: true,
terserOptions: {
// compress: {
// directives: false,
// // drop_console: true,
// // drop_debugger: true,
// // keep_classnames: false,
// // keep_fnames: false,
// },
// mangle: true, // Note `mangle.properties` is `false` by default.
compress: {
directives: false,
// drop_console: true,
// drop_debugger: true,
// keep_classnames: false,
// keep_fnames: false,
// }
// })
// ],
// },
},
mangle: true, // Note `mangle.properties` is `false` by default.
keep_classnames: false,
keep_fnames: false,
}
})
],
},
plugins: [
new MiniCssExtractPlugin({ // Make sure MiniCssExtractPlugin instance is included in array before the PurifyCSSPlugin
@ -217,12 +200,6 @@ module.exports = {
filename: '[name].css',
chunkFilename: '[chunkhash].css',
}),
// new webpack.LoaderOptionsPlugin({
// htmlLoader: {
// minimize: false
// }
// })
]
};

17
webpack.development.js Normal file
View file

@ -0,0 +1,17 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: path.resolve(__dirname),
publicPath: "/", //should provide the path of the served js , img , etc...
writeToDisk: true,
compress: false,
port: 8080,
open: false,
stats: 'errors-only',
},
});

15
webpack.production.js Normal file
View file

@ -0,0 +1,15 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
plugins: [
new HtmlWebpackPlugin({
template: './src/index.tmp.html',
title: 'sensor viewer',
})
],
});