kopia lustrzana https://github.com/FacilMap/facilmap
WIP finished: Migrated to Vue 3, Bootstrap 5, ESM, Vite (only minor issues remaining)
rodzic
44c16a2f76
commit
f4555ee628
|
@ -1,4 +1,4 @@
|
|||
FROM node:15.12-alpine
|
||||
FROM node:21-alpine
|
||||
MAINTAINER Candid Dauth <cdauth@cdauth.eu>
|
||||
|
||||
CMD yarn run server
|
||||
|
@ -21,6 +21,6 @@ RUN cd .. && yarn install
|
|||
RUN cd .. && yarn run build
|
||||
|
||||
USER root
|
||||
RUN chown -R root:root /opt/facilmap && chown -R facilmap:facilmap /opt/facilmap/server/node_modules/.cache
|
||||
RUN chown -R root:root /opt/facilmap && mkdir -p /opt/facilmap/server/node_modules/.cache && chown -R facilmap:facilmap /opt/facilmap/server/node_modules/.cache
|
||||
|
||||
USER facilmap
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/rollup-plugin-auto-external": "^2.0.5",
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-dts": "^3.6.3"
|
||||
|
|
|
@ -369,6 +369,7 @@ export default class Client {
|
|||
|
||||
async createPad(data: PadData<CRU.CREATE>): Promise<void> {
|
||||
const obj = await this._emit("createPad", data);
|
||||
this._set(this.state, 'serverError', undefined);
|
||||
this._set(this.state, 'readonly', false);
|
||||
this._set(this.state, 'writable', 2);
|
||||
this._receiveMultiple(obj);
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { defineConfig } from "vite";
|
||||
import dtsPlugin from "vite-plugin-dts";
|
||||
import autoExternalPlugin from "rollup-plugin-auto-external";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
dtsPlugin({ rollupTypes: true }),
|
||||
autoExternalPlugin()
|
||||
dtsPlugin({ rollupTypes: true })
|
||||
],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
|
@ -14,6 +12,9 @@ export default defineConfig({
|
|||
entry: './src/client.ts',
|
||||
fileName: () => 'facilmap-client.mjs',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => !id.startsWith("./") && !id.startsWith("../") && /* resolved internal modules */ !id.startsWith("/")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -64,4 +64,58 @@ class ReactiveClient extends Client {
|
|||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### React
|
||||
|
||||
```javascript
|
||||
class ObservableClient extends Client {
|
||||
_observers = new Set();
|
||||
|
||||
subscribe(callback) {
|
||||
this._observers.add(callback);
|
||||
return () => {
|
||||
this._observers.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
_triggerObservers() {
|
||||
for (const observer of this._observers) {
|
||||
observer();
|
||||
}
|
||||
}
|
||||
|
||||
_set(object, key, value) {
|
||||
object[key] = value;
|
||||
this._triggerObservers();
|
||||
}
|
||||
|
||||
_delete(object, key) {
|
||||
delete object[key];
|
||||
this._triggerObservers();
|
||||
}
|
||||
}
|
||||
|
||||
function useClientObserver(client, selector) {
|
||||
React.useSyncExternalStore(
|
||||
(callback) => client.subscribe(callback),
|
||||
() => selector(client)
|
||||
);
|
||||
}
|
||||
|
||||
const MarkerInfo = ({ client, markerId }) => {
|
||||
const marker = useClientObserver(client, (client) => client.markers[markerId]);
|
||||
return (
|
||||
<div>
|
||||
Marker name: {marker?.name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Keep in mind that React’s `useSyncExternalStore` will rerender the component if the resulting _object reference_ changes.
|
||||
This means one the one hand that you cannot use this example implementation on a higher up object of the client (such as
|
||||
`client` itself or `client.markers`), as their identity never changes, causing your component to never rerender. And on
|
||||
the other hand that you should avoid using it on objects created in the selector (such as returning
|
||||
`[client.padData.id, client.padData.name]` in order to get multiple values at once), as it will cause your component to
|
||||
rerender every time the selector is called.
|
|
@ -1,33 +1,58 @@
|
|||
# Overview
|
||||
|
||||
The FacilMap frontend is a [Vue.js](https://vuejs.org/) app that provides the main FacilMap UI.
|
||||
The FacilMap frontend is a [Vue.js](https://vuejs.org/) app that provides the main FacilMap UI. You can use it to integrate a modified or extended version of the FacilMap UI or its individual components into your app. If you just want to embed the whole FacilMap UI without any modifications, it is easier to [embed it as an iframe](../embed.md).
|
||||
|
||||
The FacilMap frontend is available as the [facilmap-frontend](https://www.npmjs.com/package/facilmap-frontend) package on NPM.
|
||||
|
||||
Right now there is no documentation of the individual UI components, nor are they designed to be reusable or have a stable interface. Use them at your own risk and have a look at the [source code](https://github.com/FacilMap/facilmap/tree/main/frontend/src/lib) to get an idea how to use them.
|
||||
|
||||
## Setup
|
||||
|
||||
The FacilMap frontend uses [Bootstrap-Vue](https://bootstrap-vue.org/), but does not install it by default to provide bigger flexibility. When using the FacilMap frontend, make sure to have it [set up](https://bootstrap-vue.org/docs#using-module-bundlers):
|
||||
The FacilMap frontend is published as an ES module. It is meant to be used as part of an app that is built using a bundler, rather than importing it directly into a HTML file.
|
||||
|
||||
The frontend heavily relies on Vue, Bootstrap and other large libraries. These are not part of the bundle, but are imported using `import` JavaScript statements. This avoids duplicating these dependencies if you also use them elsewhere in your app.
|
||||
|
||||
To get FacilMap into your app, install the NPM package using `npm install -S facilmap-frontend` or `yarn add facilmap-frontend` and then import the components that you need.
|
||||
|
||||
```javascript
|
||||
import Vue from "vue";
|
||||
import { BootstrapVue } from "bootstrap-vue";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import "bootstrap-vue/dist/bootstrap-vue.css";
|
||||
import { FacilMap } from "facilmap-frontend";
|
||||
```
|
||||
|
||||
Vue.use(BootstrapVue);
|
||||
The FacilMap UI uses a slightly adjusted version of [Bootstrap 5](https://getbootstrap.com/) for styling. To avoid duplication if you want to integrate FacilMap into an app that is already using Bootstrap, the Bootstrap CSS is not part of the main export. If you want to use FacilMap’s default Bootstrap styles, import them separately from `facilmap-frontend/bootstrap.css`:
|
||||
|
||||
```javascript
|
||||
import "facilmap-frontend/bootstrap.css";
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
The FacilMap server renders a static HTML file, which already contains some metadata about the map (such as the map title and the search engines policy configured for the particular map). It then renders a Vue.js app that renders the FacilMap UI using the [`FacilMap`](./facilmap.md) component. It sets the props and listens to the events of the FacilMap app in a way that the URL and document title are updated as the user opens or closes collaborative maps or their metadata changes.
|
||||
The [`<FacilMap>`](./facilmap.md) component renders the whole frontend, including a component that provides a connection to the FacilMap server. If you want to render the whole FacilMap UI, simply render that component rather than rendering all the components of the UI individually.
|
||||
|
||||
The FacilMap frontend makes heavy use of [provide/inject](https://vuejs.org/v2/api/#provide-inject) feature of Vue.js. Most FacilMap components require the presence of certain injected objects to work. When rendering the `FacilMap` component, the following component hierarchy is created: `FacilMap` (provides `Context`) → `ClientProvider` (provides `Client`) → `LeafletMap` (provides `MapComponents` and `MapContext`) → any UI components. The injected objects have the following purpose:
|
||||
* `Context`: A reactive object that contains general details about the context in which this FacilMap runs, such as the props that were passed to the `FacilMap` component and the currently opened map ID.
|
||||
* `Client`: A reactive instance of the FacilMap client.
|
||||
* `MapComponents`: A non-reactive object that contains all the Leaflet components of the map.
|
||||
* `MapContext`: A reactive object that contains information about the current state of some Leaflet components, for example the current position of the map. It also acts as an event emitter that is used for communication between different components of the UI.
|
||||
```vue
|
||||
<FacilMap
|
||||
baseUrl="https://facilmap.org/"
|
||||
serverUrl="https://facilmap.org/"
|
||||
:padId="undefined"
|
||||
></FacilMap>
|
||||
```
|
||||
|
||||
By passing child components to the `FacilMap` component, you can yourself make use of these injected objects when building extensions for FacilMap. When using class components with [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator), you can inject these objects by using the `InjectContext()`, `InjectMapComponents()`, … decorators. Otherwise, you can inject them by using `inject: [CONTEXT_INJECT_KEY, MAP_COMPONENTS_INJECT_KEY, …]`.
|
||||
The `<FacilMapContextProvider>` component provides a reactive object that acts as the central hub for all components to provide their public state (for example the facilmap-client object, the current map ID, the current selection, the current map view) and their public API (for example to open a search box tab, to open a map, to set route destinations). All components that need to communicate with each other use this context for that. This means that if you want to render individual UI components, you need to make sure that they are rendered within a `<FacilMapContextProvider>` (or within a `<FacilMap>`, which renders the context provider for you). It also means that if you want to add your own custom UI components, they will benefit greatly from accessing the context.
|
||||
|
||||
The context is both injected and exposed by both the `<FacilMap>` and the `<FacilMapContextProvider>` component. To access the injected context, import the `injectContextRequired` function from `facilmap-frontend` and call it in the setup function of your component. For this, the component must be a child of `<FacilMap>` or `<FacilMapContextProvider>`. To access the exposed context, use a ref:
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { FacilMap } from "facilmap-frontend";
|
||||
|
||||
const facilMapRef = ref<InstanceType<typeof FacilMap>>();
|
||||
|
||||
// Access the context as facilMapRef.value.context
|
||||
</script>
|
||||
<template>
|
||||
<FacilMap ref="facilMapRef"></FacilMap>
|
||||
</template>
|
||||
```
|
||||
|
||||
For now there is no documentation of the context object. To get an idea of its API, have a look at its [source code](https://github.com/FacilMap/facilmap/blob/main/frontend/src/lib/components/facil-map-context-provider/facil-map-context.ts).
|
||||
|
||||
## Styling
|
||||
|
||||
|
|
|
@ -3,53 +3,51 @@
|
|||
The `FacilMap` component renders a complete FacilMap UI. It can be used like this in a Vue.js app:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<FacilMap base-url="/" server-url="https://facilmap.org/" pad-id="my-map"></FacilMap>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { FacilMap } from "facilmap-frontend";
|
||||
|
||||
export default {
|
||||
components: { FacilMap }
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FacilMap
|
||||
baseUrl="https://facilmap.org/"
|
||||
serverUrl="https://facilmap.org/"
|
||||
padId="my-map"
|
||||
></FacilMap>
|
||||
</template>
|
||||
```
|
||||
|
||||
In a non-Vue.js app, it can be embedded like this:
|
||||
|
||||
```javascript
|
||||
import { FacilMap } from "facilmap-frontend";
|
||||
import Vue from "vue";
|
||||
import Vue, { createApp, defineComponent, h } from "vue";
|
||||
|
||||
new Vue({
|
||||
el: "#facilmap", // A selector whose DOM element will be replaced with the FacilMap component
|
||||
components: { FacilMap },
|
||||
render: (h) => (
|
||||
h("FacilMap", {
|
||||
props: {
|
||||
baseUrl: "/",
|
||||
serverUrl: "https://facilmap.org/",
|
||||
padId: "my-map"
|
||||
}
|
||||
})
|
||||
)
|
||||
});
|
||||
createApp(defineComponent({
|
||||
setup() {
|
||||
return () => h(FacilMap, {
|
||||
baseUrl: "https://facilmap.org/",
|
||||
serverUrl: "https://facilmap.org/",
|
||||
padId: "my-map"
|
||||
});
|
||||
}
|
||||
})).mount(document.getElementById("facilmap")!); // A DOM element that be replaced with the FacilMap component
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
Note that all of these props are reactive and can be changed while the map is open.
|
||||
|
||||
* `baseUrl` (string, required): Collaborative maps should be reachable under `${baseUrl}${mapId}`, while the general map should be available under `${baseUrl}`. For the default FacilMap installation, `baseUrl` would be `https://facilmap.org/` (or simply `/`). It needs to end with a slash. It is used to create the map URL for example in the map settings or when switching between different maps (only in interactive mode).
|
||||
* `baseUrl` (string, required): Collaborative maps should be reachable under `${baseUrl}${mapId}`, while the general map should be available under `${baseUrl}`. For the default FacilMap installation, `baseUrl` would be `https://facilmap.org/`. It needs to end with a slash. It is used to create the map URL for example in the map settings or when switching between different maps (only in interactive mode).
|
||||
* `serverUrl` (string, required): The URL under which the FacilMap server is running, for example `https://facilmap.org/`. This is invisible to the user.
|
||||
* `padId` (string, optional): The ID of the collaborative map that should be opened. If this is undefined, no map is opened. This is reactive, when a new value is passed, a new map is opened. Note that the map ID may change as the map is open, either because the ID of the map is changed in the map settings, or because the user navigates to a different map (only in interactive mode). Use `:padId.sync` to get a [two-way binding](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier) (or listen to the `update:padId` event).
|
||||
* `toolbox` (boolean, optional): Whether the toolbox should be shown. Default is `true`.
|
||||
* `search` (boolean, optional): Whether the search box should be shown. Default is `true`.
|
||||
* `autofocus` (boolean, optional): Whether the search field should be focused. Default is `false`.
|
||||
* `legend` (boolean, optional): Whether the legend should be shown (if it is available). Default is `true`.
|
||||
* `interactive` (boolean, optional): Whether [interactive mode](../embed.md#interactive-mode) should be enabled. Default is `true`.
|
||||
* `linkLogo` (boolean, optional): If `true`, the FacilMap logo will be a link that opens the map in a new window. Default is `false`.
|
||||
* `updateHash` (boolean, optional): Whether `location.hash` should be synchonised with the current map view. Default is `false`.
|
||||
* `padId` (string or undefined, required): The ID of the collaborative map that should be opened. If this is undefined, no map is opened. This is reactive, when a new value is passed, a new map is opened. Note that the map ID may change as the map is open, either because the ID of the map is changed in the map settings, or because the user navigates to a different map (only in interactive mode). Use `v-model:padId` to get a [two-way binding](https://vuejs.org/guide/essentials/forms.html) (or listen to the `update:padId` event).
|
||||
* `settings` (object, optional): An object with the following properties:
|
||||
* `toolbox` (boolean, optional): Whether the toolbox should be shown. Default is `true`.
|
||||
* `search` (boolean, optional): Whether the search box should be shown. Default is `true`.
|
||||
* `autofocus` (boolean, optional): Whether the search field should be focused. Default is `false`.
|
||||
* `legend` (boolean, optional): Whether the legend should be shown (if it is available). Default is `true`.
|
||||
* `interactive` (boolean, optional): Whether [interactive mode](../embed.md#interactive-mode) should be enabled. Default is `true`.
|
||||
* `linkLogo` (boolean, optional): If `true`, the FacilMap logo will be a link that opens the map in a new window. Default is `false`.
|
||||
* `updateHash` (boolean, optional): Whether `location.hash` should be synchonised with the current map view. Default is `false`.
|
||||
|
||||
## Events
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
**facilmap-frontend** is the Vue.js frontend for [FacilMap](https://github.com/facilmap/facilmap).
|
||||
|
||||
Some information on how to use it can be found in the [developer documentation](https://docs.facilmap.org/developers/frontend/).
|
|
@ -3,12 +3,12 @@ import { dirname } from "path";
|
|||
import { fileURLToPath } from "url";
|
||||
|
||||
const root = dirname(fileURLToPath(import.meta.url));
|
||||
const dist = `${root}/dist`;
|
||||
const dist = `${root}/dist/app`;
|
||||
|
||||
export const paths = {
|
||||
root,
|
||||
dist,
|
||||
base: '/app/',
|
||||
base: '/_app/',
|
||||
mapEntry: "src/map/map.ts",
|
||||
mapEjs: `${root}/src/map/map.ejs`,
|
||||
tableEntry: "src/table/table.ts",
|
||||
|
|
|
@ -17,15 +17,18 @@
|
|||
"url": "https://github.com/FacilMap/facilmap.git"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "./dist/frontend.js",
|
||||
"typings": "./dist/src/lib/index.d.ts",
|
||||
"main": "./dist/lib/facilmap-frontend.mjs",
|
||||
"typings": "./dist/lib/facilmap-frontend.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
"static",
|
||||
"iframe-test.html",
|
||||
"README.md",
|
||||
"tsconfig.json"
|
||||
"tsconfig.json",
|
||||
"build.js",
|
||||
"build.d.ts",
|
||||
"public"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "yarn build:lib && yarn build:app",
|
||||
|
@ -65,7 +68,6 @@
|
|||
"p-debounce": "^4.0.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"popper-max-size-modifier": "^0.2.0",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"tablesorter": "^2.31.3",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.0",
|
||||
|
|
|
@ -14,35 +14,33 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<FacilMap
|
||||
:baseUrl="serverUrl"
|
||||
:serverUrl="serverUrl"
|
||||
v-model:padId="padId1"
|
||||
@update:padName="padName1 = $event"
|
||||
ref="map1Ref"
|
||||
>
|
||||
<template #before>
|
||||
<div>{{padId1}} | {{padName1}}</div>
|
||||
<div>
|
||||
#{{map1Ref?.context?.components.map?.hash}}
|
||||
</div>
|
||||
</template>
|
||||
</FacilMap>
|
||||
<FacilMap
|
||||
:baseUrl="serverUrl"
|
||||
:serverUrl="serverUrl"
|
||||
v-model:padId="padId1"
|
||||
@update:padName="padName1 = $event"
|
||||
ref="map1Ref"
|
||||
>
|
||||
<template #before>
|
||||
<div>{{padId1}} | {{padName1}}</div>
|
||||
<div>
|
||||
#{{map1Ref?.context?.components.map?.hash}}
|
||||
</div>
|
||||
</template>
|
||||
</FacilMap>
|
||||
|
||||
<FacilMap
|
||||
:baseUrl="serverUrl"
|
||||
:serverUrl="serverUrl"
|
||||
v-model:padId="padId2"
|
||||
@update:padName="padName2 = $event"
|
||||
ref="map2Ref"
|
||||
>
|
||||
<template #before>
|
||||
<div>{{padId2}} | {{padName2}}</div>
|
||||
<div>
|
||||
#{{map2Ref?.context?.components.map?.hash}}
|
||||
</div>
|
||||
</template>
|
||||
</FacilMap>
|
||||
</div>
|
||||
<FacilMap
|
||||
:baseUrl="serverUrl"
|
||||
:serverUrl="serverUrl"
|
||||
v-model:padId="padId2"
|
||||
@update:padName="padName2 = $event"
|
||||
ref="map2Ref"
|
||||
>
|
||||
<template #before>
|
||||
<div>{{padId2}} | {{padName2}}</div>
|
||||
<div>
|
||||
#{{map2Ref?.context?.components.map?.hash}}
|
||||
</div>
|
||||
</template>
|
||||
</FacilMap>
|
||||
</template>
|
|
@ -25,6 +25,7 @@
|
|||
width: 50%;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="./example.ts"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { createApp, defineComponent, h } from "vue";
|
||||
import ExampleRoot from "./example-root.vue";
|
||||
import "../lib/bootstrap.scss";
|
||||
|
||||
const Root = defineComponent({
|
||||
setup() {
|
||||
return h(ExampleRoot);
|
||||
return () => h(ExampleRoot);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -9,11 +9,9 @@
|
|||
import type { ClientContext } from "./facil-map-context-provider/client-context";
|
||||
import { injectContextRequired } from "./facil-map-context-provider/facil-map-context-provider.vue";
|
||||
|
||||
class ReactiveClient extends Client {
|
||||
_makeReactive<O extends object>(obj: O) {
|
||||
return reactive(obj) as O;
|
||||
}
|
||||
};
|
||||
function isPadNotFoundError(serverError: Client["serverError"]): boolean {
|
||||
return !!serverError?.message?.includes("does not exist");
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -32,9 +30,6 @@
|
|||
"update:padId": [padId: string | undefined];
|
||||
}>();
|
||||
|
||||
const createId = ref<string>();
|
||||
const counter = ref(1);
|
||||
|
||||
function openPad(padId: string | undefined): void {
|
||||
emit("update:padId", padId);
|
||||
}
|
||||
|
@ -50,15 +45,26 @@
|
|||
toasts.hideToast(`fm${context.id}-client-connecting`);
|
||||
toasts.hideToast(`fm${context.id}-client-error`);
|
||||
toasts.hideToast(`fm${context.id}-client-deleted`);
|
||||
createId.value = undefined;
|
||||
if (props.padId)
|
||||
toasts.showToast(`fm${context.id}-client-connecting`, "Loading", "Loading map…", { spinner: true, noCloseButton: true });
|
||||
else
|
||||
toasts.showToast(`fm${context.id}-client-connecting`, "Connecting", "Connecting to server…", { spinner: true, noCloseButton: true });
|
||||
|
||||
const newClient: ClientContext = Object.assign(new ReactiveClient(props.serverUrl, props.padId), {
|
||||
openPad
|
||||
});
|
||||
class CustomClient extends Client implements ClientContext {
|
||||
_makeReactive<O extends object>(obj: O) {
|
||||
return reactive(obj) as O;
|
||||
}
|
||||
|
||||
openPad(padId: string | undefined) {
|
||||
openPad(padId);
|
||||
}
|
||||
|
||||
get isCreatePad() {
|
||||
return context.settings.interactive && isPadNotFoundError(super.serverError);
|
||||
}
|
||||
}
|
||||
|
||||
const newClient = new CustomClient(props.serverUrl, props.padId);
|
||||
connectingClient.value = newClient;
|
||||
|
||||
let lastPadId: PadId | undefined = undefined;
|
||||
|
@ -119,9 +125,7 @@
|
|||
});
|
||||
});
|
||||
|
||||
if (newClient.serverError?.message?.includes("does not exist") && context.settings.interactive) {
|
||||
createId.value = newClient.padId!;
|
||||
} else if (newClient.serverError) {
|
||||
if (newClient.serverError && !newClient.isCreatePad) {
|
||||
toasts.showErrorToast(`fm${context.id}-client-error`, "Error opening map", newClient.serverError, {
|
||||
noCloseButton: true,
|
||||
actions: context.settings.interactive ? [
|
||||
|
@ -139,7 +143,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
counter.value++;
|
||||
connectingClient.value = undefined;
|
||||
client.value?.disconnect();
|
||||
client.value = newClient;
|
||||
|
@ -150,6 +153,12 @@
|
|||
});
|
||||
|
||||
context.provideComponent("client", client);
|
||||
|
||||
function handleCreateDialogHide() {
|
||||
if (client.value?.isCreatePad) {
|
||||
client.value.openPad(undefined);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -165,9 +174,9 @@
|
|||
/>
|
||||
|
||||
<PadSettingsDialog
|
||||
v-if="createId"
|
||||
is-create
|
||||
no-cancel
|
||||
:proposed-admin-id="createId"
|
||||
v-if="client?.isCreatePad"
|
||||
isCreate
|
||||
:proposedAdminId="client.padId"
|
||||
@hide="handleCreateDialogHide"
|
||||
></PadSettingsDialog>
|
||||
</template>
|
|
@ -1,5 +1,7 @@
|
|||
import type Client from "facilmap-client";
|
||||
|
||||
export type ClientContext = Client & {
|
||||
/** If this is a true, it means that the current pad ID was not found and a create dialog is shown for it. */
|
||||
get isCreatePad(): boolean;
|
||||
openPad(padId: string | undefined): void;
|
||||
};
|
|
@ -13,7 +13,7 @@
|
|||
import LineInfoTab from "./line-info/line-info-tab.vue";
|
||||
import MultipleInfoTab from "./multiple-info/multiple-info-tab.vue";
|
||||
import OverpassInfoTab from "./overpass-info/overpass-info-tab.vue";
|
||||
import FacilMapContext from "./facil-map-context-provider/facil-map-context-provider.vue";
|
||||
import FacilMapContextProvider from "./facil-map-context-provider/facil-map-context-provider.vue";
|
||||
import type { FacilMapSettings } from "./facil-map-context-provider/facil-map-context";
|
||||
import ClientProvider from "./client-provider.vue";
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
const contextRef = ref<InstanceType<typeof FacilMapContext>>();
|
||||
const contextRef = ref<InstanceType<typeof FacilMapContextProvider>>();
|
||||
const context = toRef(() => contextRef.value?.context);
|
||||
const client = toRef(() => context.value?.components.client);
|
||||
|
||||
|
@ -59,7 +59,7 @@
|
|||
|
||||
<template>
|
||||
<div class="fm-facilmap">
|
||||
<FacilMapContext
|
||||
<FacilMapContextProvider
|
||||
:baseUrl="props.baseUrl"
|
||||
:settings="props.settings"
|
||||
ref="contextRef"
|
||||
|
@ -93,7 +93,7 @@
|
|||
|
||||
<slot></slot>
|
||||
</LeafletMap>
|
||||
</FacilMapContext>
|
||||
</FacilMapContextProvider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -70,8 +70,8 @@
|
|||
<slot v-if="mapContext" name="after"></slot>
|
||||
</div>
|
||||
|
||||
<div class="fm-leaflet-map-disabled-cover" v-show="client.padId && (client.disconnected || client.serverError || client.deleted)"></div>
|
||||
<div class="fm-leaflet-map-loading" v-show="!loaded && !client.serverError" :class="{ 'fatal-error': !!fatalError }">
|
||||
<div class="fm-leaflet-map-disabled-cover" v-show="client.padId && (client.disconnected || (client.serverError && !client.isCreatePad) || client.deleted)"></div>
|
||||
<div class="fm-leaflet-map-loading" v-show="!loaded && !client.serverError && !client.isCreatePad" :class="{ 'fatal-error': !!fatalError }">
|
||||
{{fatalError || 'Loading...'}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import type { CRU, PadData } from "facilmap-types";
|
||||
import { padIdValidator, type CRU, type PadData } from "facilmap-types";
|
||||
import { computed, ref } from "vue";
|
||||
import { getUniqueId, validateRequired } from "../../utils/utils";
|
||||
import copyToClipboard from "copy-to-clipboard";
|
||||
|
@ -38,9 +38,14 @@
|
|||
const touched = ref(false);
|
||||
|
||||
function validatePadId(id: string) {
|
||||
if (id.includes("/")) {
|
||||
return "May not contain a slash.";
|
||||
} else if (idProps.some((p) => p !== props.idProp && props.padData[p] === id)) {
|
||||
if (id) {
|
||||
const result = padIdValidator.safeParse(id);
|
||||
if (!result.success) {
|
||||
return result.error.format()._errors.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (idProps.some((p) => p !== props.idProp && props.padData[p] === id)) {
|
||||
return "The same link cannot be used for different access levels.";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
hide: [];
|
||||
hidden: [];
|
||||
}>();
|
||||
|
||||
|
@ -63,6 +64,7 @@
|
|||
await client.value.createPad(padData.value as PadData<CRU.CREATE>);
|
||||
else
|
||||
await client.value.editPad(padData.value);
|
||||
console.log('created');
|
||||
modalRef.value?.modal.hide();
|
||||
} catch (err) {
|
||||
toasts.showErrorToast(`fm${context.id}-pad-settings-error`, props.isCreate ? "Error creating map" : "Error saving map settings", err);
|
||||
|
@ -105,6 +107,7 @@
|
|||
:okLabel="props.isCreate ? 'Create' : undefined"
|
||||
ref="modalRef"
|
||||
@submit="$event.waitUntil(save())"
|
||||
@hide="emit('hide')"
|
||||
@hidden="emit('hidden')"
|
||||
>
|
||||
<template v-if="padData">
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
}): Promise<AlertResult> {
|
||||
return await new Promise<AlertResult>((resolve) => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add("fm-alert-container");
|
||||
document.body.appendChild(el);
|
||||
const app = createApp(defineComponent({
|
||||
setup() {
|
||||
|
@ -145,4 +146,10 @@
|
|||
>
|
||||
<slot>{{props.message}}</slot>
|
||||
</ModalDialog>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.fm-alert-container {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
|
@ -5,7 +5,7 @@
|
|||
import { makeTextColour } from "facilmap-utils";
|
||||
import { arrowNavigation } from "../../utils/ui";
|
||||
import { type StyleValue, computed, nextTick, ref } from "vue";
|
||||
import { type Validator } from "./validated-form/validated-field.vue";
|
||||
import type { Validator } from "./validated-form/validated-field.vue";
|
||||
|
||||
function normalizeData(value: string) {
|
||||
return ColorMixin.data.apply({ modelValue: value }).val;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { type SlotsType, computed, defineComponent, h, ref, shallowRef, useSlots, watch, watchEffect } from "vue";
|
||||
import { maxSizeModifiers, type ButtonSize, type ButtonVariant, useMaxBreakpoint } from "../../utils/bootstrap";
|
||||
import { Dropdown } from "bootstrap";
|
||||
import Dropdown from "bootstrap/js/dist/dropdown";
|
||||
import vLinkDisabled from "../../utils/link-disabled";
|
||||
import type { TooltipPlacement } from "../../utils/tooltip";
|
||||
import AttributePreservingElement from "./attribute-preserving-element.vue";
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
onHidden: () => {
|
||||
emit("hidden");
|
||||
},
|
||||
static: computed(() => isSubmitting.value || props.isBusy || props.noCancel || props.isModified)
|
||||
static: computed(() => isSubmitting.value || props.isBusy || props.noCancel || props.isModified),
|
||||
noEscape: computed(() => isSubmitting.value || props.isBusy || props.noCancel)
|
||||
});
|
||||
|
||||
useUnloadHandler(() => props.isModified);
|
||||
|
@ -85,8 +86,6 @@
|
|||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
ref="modalRef"
|
||||
:data-bs-backdrop="isSubmitting || props.isBusy || props.noCancel || props.isModified ? 'static' : 'true'"
|
||||
:data-bs-keyboard="isSubmitting || props.isBusy || props.noCancel || props.isModified ? 'false' : 'true'"
|
||||
>
|
||||
<div class="modal-dialog modal-dialog-scrollable">
|
||||
<ValidatedForm
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { computed, ref, toRef, watch, watchEffect } from "vue";
|
||||
import { Popover, Tooltip } from "bootstrap";
|
||||
import Popover from "bootstrap/js/dist/popover";
|
||||
import Tooltip from "bootstrap/js/dist/tooltip";
|
||||
import { useResizeObserver } from "../../utils/vue";
|
||||
import { getUniqueId, useDomEventListener } from "../../utils/utils";
|
||||
import { useDomEventListener } from "../../utils/utils";
|
||||
|
||||
/**
|
||||
* Like Bootstrap Popover, but uses an existing popover element rather than creating a new one. This way, the popover
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { createApp, nextTick, onScopeDispose, reactive, ref } from "vue";
|
||||
import { Toast } from "bootstrap";
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import { createApp, nextTick, onScopeDispose, reactive, ref, type App } from "vue";
|
||||
import Toast from "bootstrap/js/dist/toast";
|
||||
import Toasts from "./toasts.vue";
|
||||
import { mapRef } from "../../../utils/vue";
|
||||
import { getUniqueId } from "../../../utils/utils";
|
||||
|
@ -40,12 +42,20 @@
|
|||
}
|
||||
|
||||
export const toastContainer = document.createElement("div");
|
||||
toastContainer.classList.add("fm-toast-container");
|
||||
document.body.appendChild(toastContainer);
|
||||
|
||||
let app: App | undefined;
|
||||
const appMountP = Promise.resolve().then(() => {
|
||||
createApp(Toasts).mount(toastContainer);
|
||||
app = createApp(Toasts);
|
||||
app.mount(toastContainer);
|
||||
}).catch((err) => {
|
||||
console.error("Error rendering toast container", err);
|
||||
});
|
||||
import.meta.hot?.dispose(() => {
|
||||
app?.unmount();
|
||||
toastContainer.remove();
|
||||
});
|
||||
|
||||
const toasts = ref<ToastInstance[]>([]);
|
||||
const toastRefs = reactive(new Map<ToastInstance, HTMLElement>());
|
||||
|
@ -204,6 +214,10 @@
|
|||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.fm-toast-container {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.fm-toasts {
|
||||
z-index: 10002;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,19 @@
|
|||
import "./bootstrap.scss";
|
||||
import "./styles.scss";
|
||||
|
||||
// Bootstrap import, see https://getbootstrap.com/docs/5.3/customize/optimize/#lean-javascript
|
||||
import 'bootstrap/js/dist/alert';
|
||||
import 'bootstrap/js/dist/button';
|
||||
import 'bootstrap/js/dist/carousel';
|
||||
// import 'bootstrap/js/dist/collapse';
|
||||
import 'bootstrap/js/dist/dropdown';
|
||||
import 'bootstrap/js/dist/modal';
|
||||
// import 'bootstrap/js/dist/offcanvas';
|
||||
import 'bootstrap/js/dist/popover';
|
||||
// import 'bootstrap/js/dist/scrollspy';
|
||||
import 'bootstrap/js/dist/tab';
|
||||
import 'bootstrap/js/dist/toast';
|
||||
import 'bootstrap/js/dist/tooltip';
|
||||
|
||||
import { registerDeobfuscationHandlers } from "../utils/obfuscate";
|
||||
|
||||
registerDeobfuscationHandlers();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Carousel } from "bootstrap";
|
||||
import Carousel from "bootstrap/js/dist/carousel";
|
||||
import { type Ref, reactive, readonly, watch } from "vue";
|
||||
|
||||
export interface CarouselContext {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Modal } from "bootstrap";
|
||||
import Modal from "bootstrap/js/dist/modal";
|
||||
import { type Ref, shallowRef, watch, watchEffect, reactive, readonly } from "vue";
|
||||
|
||||
export interface ModalConfig {
|
||||
|
@ -8,8 +8,10 @@ export interface ModalConfig {
|
|||
onHide?: (event: Modal.Event) => void;
|
||||
/** Will be called after the fade-out animation when the modal is closed. */
|
||||
onHidden?: (event: Modal.Event) => void;
|
||||
/** If true, the modal will not be closed by clicking the backdrop or pressing Escape. */
|
||||
/** If true, the modal can not be closed by clicking the backdrop. */
|
||||
static?: Ref<boolean>;
|
||||
/** If true, the modal can not be closed by pressing Escape. */
|
||||
noEscape?: Ref<boolean>;
|
||||
}
|
||||
|
||||
export interface ModalActions {
|
||||
|
@ -19,7 +21,7 @@ export interface ModalActions {
|
|||
/**
|
||||
* Enables a Bootstrap modal dialog on the element that is saved in the returned {@link ModalActions#ref}.
|
||||
*/
|
||||
export function useModal(modalRef: Ref<HTMLElement | undefined>, { onShown, onHide, onHidden, static: isStatic }: ModalConfig): Readonly<ModalActions> {
|
||||
export function useModal(modalRef: Ref<HTMLElement | undefined>, { onShown, onHide, onHidden, static: isStatic, noEscape }: ModalConfig): Readonly<ModalActions> {
|
||||
const modal = shallowRef<Modal>();
|
||||
|
||||
const handleShown = (e: Event) => {
|
||||
|
@ -79,7 +81,7 @@ export function useModal(modalRef: Ref<HTMLElement | undefined>, { onShown, onHi
|
|||
if (modal.value) {
|
||||
const config = (modal.value as any)._config as Modal.Options;
|
||||
config.backdrop = isStatic?.value ? "static" : true;
|
||||
config.keyboard = !isStatic?.value;
|
||||
config.keyboard = !noEscape?.value;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Tooltip } from "bootstrap";
|
||||
import Tooltip from "bootstrap/js/dist/tooltip";
|
||||
import type { Directive } from "vue";
|
||||
|
||||
declare global {
|
||||
|
|
|
@ -59,7 +59,8 @@
|
|||
@keyframes spinner-border {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
</style>
|
||||
<style type="text/css">
|
||||
<% for (const path of styles) { -%>
|
||||
@import url("<%=paths.base%><%=path%>");
|
||||
<% } -%>
|
||||
|
@ -68,7 +69,7 @@
|
|||
<script type="module" src="<%=paths.base%><%=path%>"></script>
|
||||
<% } -%>
|
||||
<% for (const path of preloadScripts) { -%>
|
||||
<link rel="modulepreload" src="<%=paths.base%><%=path%>"/>
|
||||
<link rel="modulepreload" href="<%=paths.base%><%=path%>"/>
|
||||
<% } -%>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { createApp, defineComponent, h, ref, watch } from "vue";
|
|||
import { FacilMap } from "../lib";
|
||||
import { decodeQueryString, encodeQueryString } from "facilmap-utils";
|
||||
import decodeURIComponent from "decode-uri-component";
|
||||
import "../lib/bootstrap.scss"; // Not imported in lib/index.ts because we don't want it to be bundled
|
||||
|
||||
// Dereferrer
|
||||
$(document).on("click", "a", function() {
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
h2[aria-expanded=true] svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
th,td {
|
||||
box-sizing: content-box !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glyphicon:before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.glyphicon-chevron-down {
|
||||
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226.812%22%3E%3Cpath%20d%3D%22M10%201.807L5.005%206.812%200%201.807%201.798%200l3.207%203.206L8.21%200z%22%2F%3E%3C%2Fsvg%3E") no-repeat center;
|
||||
}
|
||||
|
||||
.glyphicon-chevron-up {
|
||||
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226.812%22%3E%3Cpath%20d%3D%22M10%205.005L5.005%200%200%205.005l1.798%201.807%203.207-3.206L8.21%206.812z%22%2F%3E%3C%2Fsvg%3E") no-repeat center;
|
||||
}
|
|
@ -17,10 +17,22 @@
|
|||
<%
|
||||
}
|
||||
-%>
|
||||
<link rel="icon" href="../../static/favicon.svg">
|
||||
<link rel="mask-icon" href="../../static/favicon.svg" color="#00272a">
|
||||
<link rel="apple-touch-icon" href="../../static/app-180.png">
|
||||
<link rel="manifest" href="../../static/manifest.json">
|
||||
<link rel="icon" href="<%=paths.base%>static/favicon.svg">
|
||||
<link rel="mask-icon" href="<%=paths.base%>favicon.svg" color="#00272a">
|
||||
<link rel="apple-touch-icon" href="<%=paths.base%>static/app-180.png">
|
||||
<link rel="manifest" href="<%=paths.base%>static/manifest.json">
|
||||
|
||||
<style type="text/css">
|
||||
<% for (const path of styles) { -%>
|
||||
@import url("<%=paths.base%><%=path%>");
|
||||
<% } -%>
|
||||
</style>
|
||||
<% for (const path of scripts) { -%>
|
||||
<script type="module" src="<%=paths.base%><%=path%>"></script>
|
||||
<% } -%>
|
||||
<% for (const path of preloadScripts) { -%>
|
||||
<link rel="modulepreload" href="<%=paths.base%><%=path%>"/>
|
||||
<% } -%>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
|
@ -28,7 +40,7 @@
|
|||
<%
|
||||
for(let type of Object.values(types)) {
|
||||
-%>
|
||||
<h2 role="button" data-toggle="collapse" data-target="#type-<%=type.id%>" aria-expanded="true" aria-controls="type-<%=type.id%>"><svg class="hidden-print" viewbox="0 0 11 15" height="15"><path d="M10.195 7.5l-7.5 7.5L0 12.305 4.805 7.5 0 2.695 2.695 0z"/></svg> <%=type.name%></h2>
|
||||
<h2 role="button" data-bs-toggle="collapse" data-bs-target="#type-<%=type.id%>" aria-expanded="true" aria-controls="type-<%=type.id%>"><svg class="d-print-none" viewbox="0 0 11 15" height="15"><path d="M10.195 7.5l-7.5 7.5L0 12.305 4.805 7.5 0 2.695 2.695 0z"/></svg> <%=type.name%></h2>
|
||||
<div id="type-<%=type.id%>" class="collapse show">
|
||||
<table class="table table-striped table-bordered table-condensed tablesorter" data-sortlist="[[0,0]]">
|
||||
<thead>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
// Bootstrap import, see https://getbootstrap.com/docs/5.3/customize/optimize/
|
||||
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "bootstrap/scss/variables-dark";
|
||||
@import "bootstrap/scss/maps";
|
||||
@import "bootstrap/scss/mixins";
|
||||
@import "bootstrap/scss/utilities";
|
||||
|
||||
@import "bootstrap/scss/root";
|
||||
@import "bootstrap/scss/reboot";
|
||||
//@import "bootstrap/scss/type";
|
||||
//@import "bootstrap/scss/images";
|
||||
@import "bootstrap/scss/containers";
|
||||
//@import "bootstrap/scss/grid";
|
||||
@import "bootstrap/scss/tables";
|
||||
//@import "bootstrap/scss/forms";
|
||||
//@import "bootstrap/scss/buttons";
|
||||
@import "bootstrap/scss/transitions"; // Needed for collapse
|
||||
//@import "bootstrap/scss/dropdown";
|
||||
//@import "bootstrap/scss/button-group";
|
||||
//@import "bootstrap/scss/nav";
|
||||
//@import "bootstrap/scss/navbar";
|
||||
//@import "bootstrap/scss/card";
|
||||
//@import "bootstrap/scss/accordion";
|
||||
//@import "bootstrap/scss/breadcrumb";
|
||||
//@import "bootstrap/scss/pagination";
|
||||
//@import "bootstrap/scss/badge";
|
||||
//@import "bootstrap/scss/alert";
|
||||
//@import "bootstrap/scss/progress";
|
||||
//@import "bootstrap/scss/list-group";
|
||||
//@import "bootstrap/scss/close";
|
||||
//@import "bootstrap/scss/toasts";
|
||||
//@import "bootstrap/scss/modal";
|
||||
//@import "bootstrap/scss/tooltip";
|
||||
//@import "bootstrap/scss/popover";
|
||||
//@import "bootstrap/scss/carousel";
|
||||
//@import "bootstrap/scss/spinners";
|
||||
//@import "bootstrap/scss/offcanvas";
|
||||
//@import "bootstrap/scss/placeholders";
|
||||
|
||||
//@import "bootstrap/scss/helpers";
|
||||
|
||||
@import "bootstrap/scss/utilities/api"; // Needed for .d-print-none
|
||||
|
||||
h2[aria-expanded=true] svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
th,td {
|
||||
box-sizing: content-box !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glyphicon:before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.glyphicon-chevron-down {
|
||||
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226.812%22%3E%3Cpath%20d%3D%22M10%201.807L5.005%206.812%200%201.807%201.798%200l3.207%203.206L8.21%200z%22%2F%3E%3C%2Fsvg%3E") no-repeat center;
|
||||
}
|
||||
|
||||
.glyphicon-chevron-up {
|
||||
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226.812%22%3E%3Cpath%20d%3D%22M10%205.005L5.005%200%200%205.005l1.798%201.807%203.207-3.206L8.21%206.812z%22%2F%3E%3C%2Fsvg%3E") no-repeat center;
|
||||
}
|
|
@ -2,10 +2,24 @@ import $ from "jquery";
|
|||
import "tablesorter/dist/js/jquery.tablesorter";
|
||||
import "tablesorter/dist/js/widgets/widget-uitheme.min.js";
|
||||
import "tablesorter/dist/js/widgets/widget-resizable.min.js";
|
||||
import "tablesorter/dist/css/theme.bootstrap_3.min.css";
|
||||
import "./table.css";
|
||||
import "tablesorter/dist/css/theme.bootstrap_4.min.css";
|
||||
import "./table.scss";
|
||||
import { registerDeobfuscationHandlers } from "../utils/obfuscate";
|
||||
|
||||
// Bootstrap import, see https://getbootstrap.com/docs/5.3/customize/optimize/#lean-javascript
|
||||
// import 'bootstrap/js/dist/alert';
|
||||
// import 'bootstrap/js/dist/button';
|
||||
// import 'bootstrap/js/dist/carousel';
|
||||
import 'bootstrap/js/dist/collapse';
|
||||
// import 'bootstrap/js/dist/dropdown';
|
||||
// import 'bootstrap/js/dist/modal';
|
||||
// import 'bootstrap/js/dist/offcanvas';
|
||||
// import 'bootstrap/js/dist/popover';
|
||||
// import 'bootstrap/js/dist/scrollspy';
|
||||
// import 'bootstrap/js/dist/tab';
|
||||
// import 'bootstrap/js/dist/toast';
|
||||
// import 'bootstrap/js/dist/tooltip';
|
||||
|
||||
// Dereferrer
|
||||
$(document).on("click", "a", function() {
|
||||
const el = $(this);
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { defineConfig } from "vite";
|
||||
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
||||
//import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
||||
import dtsPlugin from "vite-plugin-dts";
|
||||
import autoExternalPlugin from "rollup-plugin-auto-external";
|
||||
import vuePlugin from "@vitejs/plugin-vue";
|
||||
|
||||
export default defineConfig(({ mode }) => ({
|
||||
plugins: [
|
||||
cssInjectedByJsPlugin(),
|
||||
//cssInjectedByJsPlugin(),
|
||||
dtsPlugin({ rollupTypes: true }),
|
||||
autoExternalPlugin(),
|
||||
vuePlugin()
|
||||
],
|
||||
build: {
|
||||
outDir: "./dist/lib",
|
||||
sourcemap: true,
|
||||
minify: false,
|
||||
lib: {
|
||||
|
@ -17,6 +18,9 @@ export default defineConfig(({ mode }) => ({
|
|||
name: 'facilmap-frontend',
|
||||
fileName: () => 'facilmap-frontend.mjs',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => !id.startsWith("./") && !id.startsWith("../") && /* resolved internal modules */ !id.startsWith("/")
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
import { defineConfig } from "vite";
|
||||
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
||||
import { fileURLToPath } from "url";
|
||||
import { paths } from "./build.js";
|
||||
import vuePlugin from "@vitejs/plugin-vue";
|
||||
|
||||
export default defineConfig({
|
||||
base: paths.base,
|
||||
//appType: "custom",
|
||||
plugins: [
|
||||
cssInjectedByJsPlugin(),
|
||||
vuePlugin()
|
||||
],
|
||||
assetsInclude: [
|
||||
"**/*.ejs"
|
||||
],
|
||||
build: {
|
||||
outDir: "dist/app",
|
||||
manifest: true,
|
||||
target: ["es2022", "chrome89", "edge89", "safari15", "firefox89", "opera75"],
|
||||
rollupOptions: {
|
||||
input: {
|
||||
map: fileURLToPath(new URL('./src/map/map.ts', import.meta.url)),
|
||||
//table: fileURLToPath(new URL('./src/table/table.ts', import.meta.url)),
|
||||
//example: fileURLToPath(new URL('./src/example/example.html', import.meta.url)),
|
||||
table: fileURLToPath(new URL('./src/table/table.ts', import.meta.url)),
|
||||
example: fileURLToPath(new URL('./src/example/example.html', import.meta.url)),
|
||||
},
|
||||
external: [
|
||||
"jsdom" // Only actually imported in backend environment
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
import webpack, { Configuration } from "webpack";
|
||||
import copyPlugin from "copy-webpack-plugin";
|
||||
import htmlPlugin from "html-webpack-plugin";
|
||||
import { compile, CompilerOptions } from "vue-template-compiler";
|
||||
import svgToMiniDataURI from "mini-svg-data-uri";
|
||||
import nodeExternals from "webpack-node-externals";
|
||||
//import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
|
||||
function includeHotMiddleware(entry: string | string[], isDev: boolean): string | string[] {
|
||||
if(!isDev)
|
||||
return entry;
|
||||
|
||||
if(!Array.isArray(entry))
|
||||
entry = [ entry ];
|
||||
|
||||
return [ "webpack-hot-middleware/client" ].concat(entry);
|
||||
}
|
||||
|
||||
module.exports = (env: any, argv: any): Configuration[] => {
|
||||
const isDev = argv.mode == "development" || !!process.env.FM_DEV;
|
||||
const path = __dirname + "/dist/";
|
||||
|
||||
const base: Configuration = {
|
||||
resolve: {
|
||||
alias: {
|
||||
vue: "vue/dist/vue.runtime.esm.js"
|
||||
},
|
||||
extensions: ['.ts', '.wasm', '.mjs', '.js', '.json']
|
||||
},
|
||||
mode: isDev ? "development" : "production",
|
||||
devtool: isDev ? "eval-source-map" : "source-map", // Change to inline-cheap-source-map to work with dependency source maps
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.js$/, enforce: "pre", use: ["source-map-loader"] },
|
||||
{
|
||||
resource: /\.ts$/,
|
||||
use: {
|
||||
loader: "ts-loader",
|
||||
options: { onlyCompileBundledFiles: true }
|
||||
}
|
||||
},
|
||||
{ test: /\.css$/, use: [ "style-loader", "css-loader" ] },
|
||||
{ test: /\.scss$/, use: [
|
||||
"style-loader",
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
modules: "global"
|
||||
}
|
||||
},
|
||||
"sass-loader"
|
||||
]},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|ttf)$/,
|
||||
type: 'asset/inline',
|
||||
exclude: [
|
||||
`${__dirname}/static/favicon-180.png`
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(svg)$/,
|
||||
type: 'asset/inline',
|
||||
generator: {
|
||||
dataUrl: (content: any) => {
|
||||
content = content.toString();
|
||||
return svgToMiniDataURI(content);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(html|ejs)$/,
|
||||
loader: "html-loader",
|
||||
options: {
|
||||
sources: {
|
||||
list: [
|
||||
{ tag: "img", attribute: "src", type: "src" },
|
||||
{ tag: "link", attribute: "href", type: "src", filter: (tag: any, attr: any, attrs: any) => attrs.some((a: any) => a.name == "rel" && ["icon"].includes(a.value)) },
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: "vue-template-loader",
|
||||
options: {
|
||||
transformAssetUrls: {
|
||||
img: 'src'
|
||||
},
|
||||
compiler: {
|
||||
compile: (template: string, options: CompilerOptions) => compile(template, { ...options, whitespace: "condense" })
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
//new BundleAnalyzerPlugin(),
|
||||
]
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
...base,
|
||||
name: "app",
|
||||
entry: {
|
||||
map: includeHotMiddleware(__dirname + "/src/map/map.ts", isDev),
|
||||
table: includeHotMiddleware(__dirname + "/src/table/table.ts", isDev),
|
||||
example: includeHotMiddleware(__dirname + "/src/example/example.ts", isDev),
|
||||
},
|
||||
output: {
|
||||
filename: "frontend-[name]-[chunkhash].js",
|
||||
path
|
||||
},
|
||||
plugins: [
|
||||
...base.plugins!,
|
||||
new htmlPlugin({
|
||||
template: `${__dirname}/src/map/map.ejs`,
|
||||
filename: "map.ejs",
|
||||
chunks: ["map"]
|
||||
}),
|
||||
new htmlPlugin({
|
||||
template: `${__dirname}/src/table/table.ejs`,
|
||||
filename: "table.ejs",
|
||||
chunks: ["table"]
|
||||
}),
|
||||
new htmlPlugin({
|
||||
template: `${__dirname}/src/example/example.html`,
|
||||
filename: "example.html",
|
||||
chunks: ["example"]
|
||||
}),
|
||||
new copyPlugin({
|
||||
patterns: [
|
||||
"app-180.png",
|
||||
"app-512.png",
|
||||
"deref.html",
|
||||
"favicon-64.png",
|
||||
"favicon.ico",
|
||||
"favicon.svg",
|
||||
"manifest.json",
|
||||
"sw.js",
|
||||
"opensearch.xml"
|
||||
].map((file) => ({ from: `${__dirname}/static/${file}` }))
|
||||
}),
|
||||
...(isDev ? [
|
||||
new webpack.HotModuleReplacementPlugin()
|
||||
] : [
|
||||
//new BundleAnalyzerPlugin(),
|
||||
]),
|
||||
],
|
||||
devServer: {
|
||||
publicPath: "/dist",
|
||||
//hotOnly: true,
|
||||
disableHostCheck: true,
|
||||
writeToDisk: true,
|
||||
injectClient: false, // https://github.com/webpack/webpack-dev-server/issues/2484
|
||||
port: 8082
|
||||
}
|
||||
},
|
||||
{
|
||||
...base,
|
||||
name: "lib",
|
||||
entry: __dirname + "/src/lib/index.ts",
|
||||
output: {
|
||||
filename: "frontend.js",
|
||||
path
|
||||
},
|
||||
optimization: {
|
||||
minimize: false
|
||||
},
|
||||
externalsType: "commonjs2",
|
||||
externals: nodeExternals({
|
||||
additionalModuleDirs: [
|
||||
`${__dirname}/../node_modules`
|
||||
],
|
||||
allowlist: /\.css$/
|
||||
})
|
||||
}
|
||||
];
|
||||
};
|
|
@ -62,7 +62,6 @@
|
|||
"@types/leaflet.markercluster": "^1.5.4",
|
||||
"@types/lodash-es": "^4.17.11",
|
||||
"@types/node-fetch": "^2.6.9",
|
||||
"@types/rollup-plugin-auto-external": "^2.0.5",
|
||||
"@types/yauzl": "^2.10.3",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"fast-glob": "^3.3.2",
|
||||
|
@ -71,7 +70,6 @@
|
|||
"node-fetch": "^3.3.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup": "3",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"svgo": "^3.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Plugin } from "rollup";
|
||||
import type { Plugin } from "rollup";
|
||||
import glob from "fast-glob";
|
||||
import { readFile } from "fs/promises";
|
||||
import { createRequire } from "module";
|
||||
|
@ -8,15 +8,15 @@ const require = createRequire(import.meta.url);
|
|||
|
||||
export default function iconPlugin(): Plugin {
|
||||
return {
|
||||
name: 'custom:icons',
|
||||
name: 'virtual:icons',
|
||||
resolveId: (id) => {
|
||||
if (id === 'custom:icons') {
|
||||
if (id === 'virtual:icons') {
|
||||
return id;
|
||||
}
|
||||
},
|
||||
load: async (id) => {
|
||||
if (id === 'custom:icons') {
|
||||
const icons/*: Record<string, Record<string, string>>*/ = {};
|
||||
if (id === 'virtual:icons') {
|
||||
const icons: Record<string, Record<string, string>> = {};
|
||||
for (const path of await glob('./assets/icons/*/*.svg')) {
|
||||
const [set, fname] = path.split("/").slice(-2);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type Client from "facilmap-client";
|
||||
import { type ClientEvents } from "facilmap-client";
|
||||
import type { ClientEvents } from "facilmap-client";
|
||||
import type { EventHandler } from "facilmap-types";
|
||||
import { Handler, LatLng, LatLngBounds, Map } from "leaflet";
|
||||
import { leafletToFmBbox } from "./utils/leaflet";
|
||||
|
|
|
@ -3,7 +3,7 @@ import { FeatureGroup, latLng, Layer, type LayerOptions } from "leaflet";
|
|||
import MarkerLayer from "../markers/marker-layer";
|
||||
import { getSymbolForTags } from "../utils/icons";
|
||||
import { tooltipOptions } from "../utils/leaflet";
|
||||
import { type OverpassPreset } from "./overpass-presets";
|
||||
import type { OverpassPreset } from "./overpass-presets";
|
||||
import { getOverpassElements, isOverpassQueryEmpty, type OverpassElement } from "./overpass-utils";
|
||||
|
||||
declare module "leaflet" {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type Client from "facilmap-client";
|
||||
import { type RouteWithTrackPoints } from "facilmap-client";
|
||||
import type { RouteWithTrackPoints } from "facilmap-client";
|
||||
import { Map, type PolylineOptions } from "leaflet";
|
||||
import { type HighlightableLayerOptions, HighlightablePolyline } from "leaflet-highlightable-layers";
|
||||
import { trackPointsToLatLngArray } from "../utils/leaflet";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
declare module "custom:icons" {
|
||||
declare module "virtual:icons" {
|
||||
const rawIcons: Record<string, Record<string, string>>;
|
||||
export default rawIcons;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { type FilterFunc as _TypeExtensions_FilterFunc } from "facilmap-utils";
|
||||
import type { FilterFunc as _TypeExtensions_FilterFunc } from "facilmap-utils";
|
||||
|
||||
// These should really go in other places, but due to https://github.com/microsoft/rushstack/issues/1709, we append
|
||||
// this file manually to the declarations output during the vite build. Hence the import names need to be unique.
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { Shape, Symbol } from "facilmap-types";
|
|||
import { makeTextColour, quoteHtml } from "facilmap-utils";
|
||||
import { icon, type Icon } from "leaflet";
|
||||
import { memoize } from "lodash-es";
|
||||
import rawIcons from "custom:icons";
|
||||
import rawIcons from "virtual:icons";
|
||||
|
||||
export const symbolList = Object.keys(rawIcons).map((key) => Object.keys(rawIcons[key])).flat();
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { defineConfig } from "vite";
|
||||
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
||||
import dtsPlugin from "vite-plugin-dts";
|
||||
import autoExternalPlugin from "rollup-plugin-auto-external";
|
||||
import iconsPlugin from "./rollup-icons";
|
||||
import { appendFile, readFile } from "fs/promises";
|
||||
|
||||
|
@ -18,7 +17,6 @@ export default defineConfig({
|
|||
await appendFile("./dist/facilmap-leaflet.d.ts", filterFile);
|
||||
},
|
||||
}),
|
||||
autoExternalPlugin(),
|
||||
iconsPlugin()
|
||||
],
|
||||
build: {
|
||||
|
@ -29,6 +27,9 @@ export default defineConfig({
|
|||
name: 'facilmap-leaflet',
|
||||
fileName: () => 'facilmap-leaflet.mjs',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => !id.startsWith("./") && !id.startsWith("../") && id !== "virtual:icons" && /* resolved internal modules */ !id.startsWith("/")
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
*
|
|
@ -27,7 +27,7 @@
|
|||
"scripts": {
|
||||
"build": "vite build",
|
||||
"clean": "rimraf dist",
|
||||
"server": "DOTENV_CONFIG_PATH=../config.env facilmap-server",
|
||||
"server": "DOTENV_CONFIG_PATH=../config.env node ./bin/facilmap-server.js",
|
||||
"ts-server": "DOTENV_CONFIG_PATH=../config.env node ./esrun.js",
|
||||
"dev-server": "FM_DEV=true DOTENV_CONFIG_PATH=../config.env node ./esrun.js",
|
||||
"test": "vitest run",
|
||||
|
@ -81,7 +81,6 @@
|
|||
"cpy-cli": "^5.0.0",
|
||||
"debug": "^4.3.4",
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { type AssociationOptions, Model, type ModelAttributeColumnOptions, type ModelCtor, type WhereOptions, DataTypes, type FindOptions, Op, Sequelize, type ModelStatic, type InferAttributes, type InferCreationAttributes, type CreationAttributes } from "sequelize";
|
||||
import { type Line, type Marker, type PadId, type ID, type Type, type Bbox, CRU } from "facilmap-types";
|
||||
import type { Line, Marker, PadId, ID, Type, Bbox, CRU } from "facilmap-types";
|
||||
import Database from "./database.js";
|
||||
import { cloneDeep, isEqual } from "lodash-es";
|
||||
import { calculateRouteForLine } from "../routing/routing.js";
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { BboxWithZoom, ID, Latitude, Longitude, PadId, Point, Route, RouteM
|
|||
import { type BboxWithExcept, createModel, getPosType, getVirtualLatType, getVirtualLonType, makeBboxCondition } from "./helpers.js";
|
||||
import { calculateRouteForLine } from "../routing/routing.js";
|
||||
import { omit } from "lodash-es";
|
||||
import { type Point as GeoJsonPoint } from "geojson";
|
||||
import type { Point as GeoJsonPoint } from "geojson";
|
||||
|
||||
const updateTimes: Record<string, number> = {};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { type CreationOptional, DataTypes, type ForeignKey, type InferAttributes
|
|||
import type { CRU, Field, ID, PadId, Type } from "facilmap-types";
|
||||
import Database from "./database.js";
|
||||
import { createModel, getDefaultIdType, makeNotNullForeignKey, validateColour } from "./helpers.js";
|
||||
import { type PadModel } from "./pad.js";
|
||||
import type { PadModel } from "./pad.js";
|
||||
|
||||
export interface TypeModel extends Model<InferAttributes<TypeModel>, InferCreationAttributes<TypeModel>> {
|
||||
id: CreationOptional<ID>;
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
import { promiseAuto } from "../utils/utils.js";
|
||||
import { render } from "ejs";
|
||||
import type { ID, Line, Marker, PadId, Type } from "facilmap-types";
|
||||
import type { ID, PadId } from "facilmap-types";
|
||||
import { compileExpression } from "facilmap-utils";
|
||||
import * as utils from "facilmap-utils";
|
||||
import Database from "../database/database.js";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { paths } from "facilmap-frontend/build.js";
|
||||
|
||||
type TypeWithObjects = Type & {
|
||||
markers: Marker[],
|
||||
lines: Line[]
|
||||
}
|
||||
import { renderTable, type TypeWithObjects } from "../frontend.js";
|
||||
|
||||
export function createTable(database: Database, padId: PadId, filter: string | undefined, hide: string[]): Promise<string> {
|
||||
const filterFunc = compileExpression(filter);
|
||||
|
@ -42,19 +34,16 @@ export function createTable(database: Database, padId: PadId, filter: string | u
|
|||
if(filterFunc(line, types[line.typeId]))
|
||||
types[line.typeId].lines.push(line);
|
||||
}
|
||||
},
|
||||
|
||||
template: readFile(paths.tableEjs).then((t) => t.toString())
|
||||
}
|
||||
}).then((results) => {
|
||||
for(const i of Object.keys(results.types) as unknown as ID[]) {
|
||||
if(results.types[i].markers.length == 0 && results.types[i].lines.length == 0)
|
||||
delete results.types[i];
|
||||
}
|
||||
|
||||
return render(results.template, {
|
||||
return renderTable({
|
||||
padData: results.padData,
|
||||
types: results.types,
|
||||
utils,
|
||||
hide
|
||||
})
|
||||
});
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import type { Manifest } from "vite";
|
||||
import { paths, serve } from "facilmap-frontend/build.js";
|
||||
import { readFile } from "fs/promises";
|
||||
import type { ID, Line, Marker, PadData, Type } from "facilmap-types";
|
||||
import * as ejs from "ejs";
|
||||
import * as utils from "facilmap-utils";
|
||||
import { Router, type RequestHandler } from "express";
|
||||
import { static as expressStatic } from "express";
|
||||
|
||||
export const isDevMode = !!process.env.FM_DEV;
|
||||
|
||||
async function getManifest(): Promise<Manifest> {
|
||||
const manifest = await readFile(paths.manifest);
|
||||
return JSON.parse(manifest.toString());
|
||||
}
|
||||
|
||||
interface Scripts {
|
||||
scripts: string[];
|
||||
preloadScripts: string[];
|
||||
styles: string[];
|
||||
}
|
||||
|
||||
export type TypeWithObjects = Type & {
|
||||
markers: Marker[];
|
||||
lines: Line[];
|
||||
}
|
||||
|
||||
async function getScripts(entry: "mapEntry" | "tableEntry"): Promise<Scripts> {
|
||||
if (isDevMode) {
|
||||
return {
|
||||
scripts: ["@vite/client", paths[entry]],
|
||||
preloadScripts: [],
|
||||
styles: []
|
||||
};
|
||||
} else {
|
||||
const manifest = await getManifest();
|
||||
|
||||
let referencedChunks = [paths[entry]];
|
||||
for (let i = 0; i < referencedChunks.length; i++) {
|
||||
const chunk = manifest[referencedChunks[i]];
|
||||
for (const reference of [
|
||||
...chunk.imports ?? [],
|
||||
...chunk.dynamicImports ?? []
|
||||
]) {
|
||||
if (!referencedChunks.includes(reference)) {
|
||||
referencedChunks.push(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scripts = referencedChunks.map((c) => manifest[c].file);
|
||||
const styles = referencedChunks.map((c) => manifest[c].css ?? []);
|
||||
|
||||
return {
|
||||
scripts: scripts.slice(0, 1),
|
||||
preloadScripts: scripts.slice(1),
|
||||
styles: styles.flat()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface RenderMapParams {
|
||||
padData: Pick<PadData, "name" | "description" | "searchEngines"> | undefined;
|
||||
isReadOnly: boolean;
|
||||
}
|
||||
|
||||
export async function renderMap(params: RenderMapParams): Promise<string> {
|
||||
const [template, injections] = await Promise.all([
|
||||
readFile(paths.mapEjs).then((t) => t.toString()),
|
||||
getScripts("mapEntry")
|
||||
]);
|
||||
|
||||
return ejs.render(template, {
|
||||
config: {},
|
||||
...injections,
|
||||
paths,
|
||||
...params
|
||||
});
|
||||
}
|
||||
|
||||
export async function renderTable(params: {
|
||||
padData: PadData | undefined;
|
||||
types: Record<ID, TypeWithObjects>;
|
||||
hide: string[];
|
||||
}): Promise<string> {
|
||||
const [template, injections] = await Promise.all([
|
||||
readFile(paths.tableEjs).then((t) => t.toString()),
|
||||
getScripts("tableEntry")
|
||||
]);
|
||||
|
||||
return ejs.render(template, {
|
||||
...injections,
|
||||
paths,
|
||||
utils,
|
||||
...params
|
||||
});
|
||||
}
|
||||
|
||||
export async function getStaticFrontendMiddleware(): Promise<RequestHandler> {
|
||||
if (isDevMode) {
|
||||
const devServer = await serve({
|
||||
server: {
|
||||
middlewareMode: true,
|
||||
/* hmr: {
|
||||
protocol: 'ws',
|
||||
host: '127.0.0.1'
|
||||
} */
|
||||
//origin: "http://localhost:40829"
|
||||
},
|
||||
appType: "custom"
|
||||
});
|
||||
|
||||
return devServer.middlewares;
|
||||
} else {
|
||||
const router = Router();
|
||||
router.use(`${paths.base}assets/`, (req, res, next) => {
|
||||
res.setHeader('Cache-Control', 'public, max-age=315576000, immutable'); // 10 years
|
||||
next();
|
||||
});
|
||||
router.use(paths.base, expressStatic(paths.dist));
|
||||
return router;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import { distanceToDegreesLat, distanceToDegreesLon } from "./utils/geo.js";
|
||||
import md5 from "md5-file";
|
||||
import { schedule } from "node-cron";
|
||||
import { open, Reader, type Response } from "maxmind";
|
||||
import { open, type Reader, type Response } from "maxmind";
|
||||
import { createWriteStream } from "fs";
|
||||
import { rename } from "node:fs/promises";
|
||||
import { rename } from "fs/promises";
|
||||
import https from "https";
|
||||
import zlib from "zlib";
|
||||
import config from "./config.js";
|
||||
import { IncomingMessage } from "http";
|
||||
import type { IncomingMessage } from "http";
|
||||
import type { Bbox } from "facilmap-types";
|
||||
import { fileURLToPath } from "url";
|
||||
import { fileExists } from "./utils/utils";
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { promiseProps } from "./utils/utils.js";
|
||||
import { asyncIteratorToArray } from "./utils/streams.js";
|
||||
import { isInBbox } from "./utils/geo.js";
|
||||
import { Server, Socket as SocketIO } from "socket.io";
|
||||
import { Server, type Socket as SocketIO } from "socket.io";
|
||||
import domain from "domain";
|
||||
import { exportLineToGpx } from "./export/gpx.js";
|
||||
import { find } from "./search.js";
|
||||
import { geoipLookup } from "./geoip.js";
|
||||
import { cloneDeep, isEqual, omit } from "lodash-es";
|
||||
import Database, { type DatabaseEvents } from "./database/database.js";
|
||||
import { Server as HttpServer } from "http";
|
||||
import type { Server as HttpServer } from "http";
|
||||
import { type Bbox, type BboxWithZoom, type EventHandler, type EventName, type MapEvents, type MultipleEvents, type PadData, type PadId, type RequestData, type RequestName, type ResponseData, Writable, requestDataValidators } from "facilmap-types";
|
||||
import { calculateRoute, prepareForBoundingBox } from "./routing/routing.js";
|
||||
import type { RouteWithId } from "./database/route.js";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { access } from "node:fs/promises";
|
||||
import { access } from "fs/promises";
|
||||
|
||||
const LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
import compression from "compression";
|
||||
import * as ejs from "ejs";
|
||||
import express, { type Request, type Response, static as expressStatic, type NextFunction } from "express";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { createServer, Server as HttpServer } from "http";
|
||||
import express, { type Request, type Response, type NextFunction } from "express";
|
||||
import { createServer, type Server as HttpServer } from "http";
|
||||
import type { PadId } from "facilmap-types";
|
||||
import { createTable } from "./export/table.js";
|
||||
import Database from "./database/database";
|
||||
import { exportGeoJson } from "./export/geojson.js";
|
||||
import { exportGpx } from "./export/gpx.js";
|
||||
import domainMiddleware from "express-domain-middleware";
|
||||
import { paths, serve } from "facilmap-frontend/build.js";
|
||||
import type { Manifest } from "vite";
|
||||
import { Readable, Writable } from "stream";
|
||||
|
||||
const isDevMode = !!process.env.FM_DEV;
|
||||
|
||||
async function getManifest(): Promise<Manifest> {
|
||||
const manifest = await readFile(paths.manifest);
|
||||
return JSON.parse(manifest.toString());
|
||||
}
|
||||
import { getStaticFrontendMiddleware, renderMap, type RenderMapParams } from "./frontend";
|
||||
|
||||
type PathParams = {
|
||||
padId: PadId
|
||||
|
@ -27,54 +17,34 @@ type PathParams = {
|
|||
export async function initWebserver(database: Database, port: number, host?: string): Promise<HttpServer> {
|
||||
const padMiddleware = async function(req: Request<PathParams>, res: Response<string>, next: NextFunction) {
|
||||
try {
|
||||
const [template, padData, manifest] = await Promise.all([
|
||||
readFile(paths.mapEjs).then((t) => t.toString()),
|
||||
(async () => {
|
||||
if(req.params && req.params.padId) {
|
||||
return database.pads.getPadDataByAnyId(req.params.padId).then((padData) => {
|
||||
if (!padData)
|
||||
throw new Error();
|
||||
return padData;
|
||||
}).catch(() => {
|
||||
// Error will be handled on the client side when it tries to fetch the pad data
|
||||
return {
|
||||
id: undefined,
|
||||
searchEngines: false,
|
||||
description: ""
|
||||
};
|
||||
});
|
||||
}
|
||||
})(),
|
||||
!isDevMode ? getManifest() : undefined
|
||||
]);
|
||||
|
||||
let scripts: string[], preloadScripts: string[], styles: string[];
|
||||
if (isDevMode) {
|
||||
scripts = ["@vite/client", paths.mapEntry];
|
||||
preloadScripts = [];
|
||||
styles = [];
|
||||
let params: RenderMapParams;
|
||||
if(req.params?.padId) {
|
||||
const padData = await database.pads.getPadDataByAnyId(req.params.padId);
|
||||
if (padData) {
|
||||
params = {
|
||||
padData,
|
||||
isReadOnly: padData.id === req.params.padId
|
||||
};
|
||||
} else {
|
||||
res.status(404);
|
||||
params = {
|
||||
padData: {
|
||||
searchEngines: false,
|
||||
name: "",
|
||||
description: ""
|
||||
},
|
||||
isReadOnly: true
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const mainChunk = manifest![paths.mapEntry];
|
||||
scripts = [mainChunk.file];
|
||||
preloadScripts = [...mainChunk.imports ?? [], ...mainChunk.dynamicImports ?? []].map((key) => manifest![key].file);
|
||||
styles = mainChunk.css ?? [];
|
||||
params = {
|
||||
padData: undefined,
|
||||
isReadOnly: true
|
||||
};
|
||||
}
|
||||
|
||||
res.type("html");
|
||||
|
||||
if (padData && padData.id == null) {
|
||||
res.status(404);
|
||||
}
|
||||
|
||||
res.send(ejs.render(template, {
|
||||
padData: padData,
|
||||
isReadOnly: padData?.id == req.params.padId,
|
||||
config: {},
|
||||
scripts,
|
||||
preloadScripts,
|
||||
styles,
|
||||
paths
|
||||
}));
|
||||
res.send(await renderMap(params));
|
||||
} catch (err: any) {
|
||||
next(err);
|
||||
}
|
||||
|
@ -84,33 +54,9 @@ export async function initWebserver(database: Database, port: number, host?: str
|
|||
app.use(domainMiddleware);
|
||||
app.use(compression());
|
||||
|
||||
/* app.get("/frontend-:hash.js", function(req, res, next) {
|
||||
res.setHeader('Cache-Control', 'public, max-age=31557600'); // one year
|
||||
|
||||
next();
|
||||
}); */
|
||||
|
||||
app.get("/", padMiddleware);
|
||||
//app.get("/map.ejs", padMiddleware);
|
||||
//app.get("/table.ejs", padMiddleware);
|
||||
|
||||
if (isDevMode) {
|
||||
const devServer = await serve({
|
||||
server: {
|
||||
middlewareMode: true,
|
||||
/* hmr: {
|
||||
protocol: 'ws',
|
||||
host: '127.0.0.1'
|
||||
} */
|
||||
//origin: "http://localhost:40829"
|
||||
},
|
||||
appType: "custom"
|
||||
});
|
||||
|
||||
app.use(devServer.middlewares);
|
||||
} else {
|
||||
app.use(paths.base, expressStatic(paths.dist));
|
||||
}
|
||||
app.use(await getStaticFrontendMiddleware());
|
||||
|
||||
// If no file with this name has been found, we render a pad
|
||||
app.get("/:padId", padMiddleware);
|
||||
|
@ -132,10 +78,13 @@ export async function initWebserver(database: Database, port: number, host?: str
|
|||
|
||||
app.get("/:padId/table", async function(req: Request<PathParams>, res: Response<string>, next) {
|
||||
try {
|
||||
const renderedTable = await createTable(database, req.params.padId, req.query.filter as string | undefined, req.query.hide ? (req.query.hide as string).split(',') : []);
|
||||
|
||||
res.type("html");
|
||||
res.send(renderedTable);
|
||||
res.send(await createTable(
|
||||
database,
|
||||
req.params.padId,
|
||||
req.query.filter as string | undefined,
|
||||
req.query.hide ? (req.query.hide as string).split(',') : []
|
||||
));
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { defineConfig } from "vite";
|
||||
import dtsPlugin from "vite-plugin-dts";
|
||||
import autoExternalPlugin from "rollup-plugin-auto-external";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
dtsPlugin({ rollupTypes: true }),
|
||||
autoExternalPlugin()
|
||||
dtsPlugin({ rollupTypes: true })
|
||||
],
|
||||
build: {
|
||||
sourcemap: false,
|
||||
|
@ -16,6 +14,9 @@ export default defineConfig({
|
|||
name: 'facilmap-server',
|
||||
fileName: () => 'facilmap-server.mjs',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => !id.startsWith("./") && !id.startsWith("../") && /* resolved internal modules */ !id.startsWith("/")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-dts": "^3.6.3"
|
||||
|
|
|
@ -28,7 +28,13 @@ export type Width = z.infer<typeof widthValidator>;
|
|||
export const idValidator = z.number();
|
||||
export type ID = z.infer<typeof idValidator>;
|
||||
|
||||
export const padIdValidator = z.string();
|
||||
export const forbiddenPadIds = [
|
||||
"_app" // Static frontend resources are hosted under https://facilmap.org/_app/, so a pad with such an ID would not be accessible
|
||||
];
|
||||
export const padIdValidator = z.string()
|
||||
.min(1)
|
||||
.refine((val) => !val.includes("/"), { message: "May not contain a slash." })
|
||||
.refine((val) => !forbiddenPadIds.includes(val), { message: `The following IDs are not allowed: ${forbiddenPadIds.join(", ")}.` });
|
||||
export type PadId = z.infer<typeof padIdValidator>;
|
||||
|
||||
export const routeModeValidator = z.string();
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { defineConfig } from "vite";
|
||||
import dtsPlugin from "vite-plugin-dts";
|
||||
import autoExternalPlugin from "rollup-plugin-auto-external";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
dtsPlugin({ rollupTypes: true }),
|
||||
autoExternalPlugin()
|
||||
dtsPlugin({ rollupTypes: true })
|
||||
],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
|
@ -16,6 +14,9 @@ export default defineConfig({
|
|||
name: 'facilmap-types',
|
||||
fileName: () => 'facilmap-types.mjs',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => !id.startsWith("./") && !id.startsWith("../") && /* resolved internal modules */ !id.startsWith("/")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
"@types/jsdom": "^21.1.5",
|
||||
"@types/linkifyjs": "^2.1.7",
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup-plugin-auto-external": "^2.0.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-dts": "^3.6.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { compileExpression as filtrexCompileExpression } from "filtrex";
|
||||
import { flattenObject, getProperty, quoteRegExp } from "./utils.js";
|
||||
import { type ID, type Marker, type Line, type Type, type Field, CRU } from "facilmap-types";
|
||||
import type { ID, Marker, Line, Type, Field, CRU } from "facilmap-types";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
export type FilterFunc = (obj: Marker<CRU> | Line<CRU>, type: Type) => boolean;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { defineConfig } from "vite";
|
||||
import dtsPlugin from "vite-plugin-dts";
|
||||
import autoExternalPlugin from "rollup-plugin-auto-external";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
dtsPlugin({ rollupTypes: true }),
|
||||
autoExternalPlugin()
|
||||
dtsPlugin({ rollupTypes: true })
|
||||
],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
|
@ -16,6 +14,9 @@ export default defineConfig({
|
|||
name: 'facilmap-utils',
|
||||
fileName: () => 'facilmap-utils.mjs',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => !id.startsWith("./") && !id.startsWith("../") && /* resolved internal modules */ !id.startsWith("/")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
197
yarn.lock
197
yarn.lock
|
@ -1148,15 +1148,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/rollup-plugin-auto-external@npm:^2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "@types/rollup-plugin-auto-external@npm:2.0.5"
|
||||
dependencies:
|
||||
rollup: ">=2.0.0"
|
||||
checksum: a24aa6bb2de86e766208756c921096bf56f45aafcc8d11bb314407328e4511bd5b9678d6815e77ff9b14a4e38b704998aaf83bfe2e761055e731dcc99ba4e445
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/scheduler@npm:*":
|
||||
version: 0.16.6
|
||||
resolution: "@types/scheduler@npm:0.16.6"
|
||||
|
@ -2059,15 +2050,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"builtins@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "builtins@npm:2.0.1"
|
||||
dependencies:
|
||||
semver: ^6.0.0
|
||||
checksum: 5b90d6df8b60cac1589435f23674d731a212a7b6ae385a7c73cf11d7584f0538cfa5ac1f9f6154fa84eac46ac246da0da7e9ef83cb334cbbf350bd1fddff74fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bytes@npm:3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "bytes@npm:3.0.0"
|
||||
|
@ -2968,15 +2950,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"error-ex@npm:^1.3.1":
|
||||
version: 1.3.2
|
||||
resolution: "error-ex@npm:1.3.2"
|
||||
dependencies:
|
||||
is-arrayish: ^0.2.1
|
||||
checksum: c1c2b8b65f9c91b0f9d75f0debaa7ec5b35c266c2cac5de412c1a6de86d4cbae04ae44e510378cb14d032d0645a36925d0186f8bb7367bcc629db256b743a001
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-abstract@npm:^1.22.1":
|
||||
version: 1.22.3
|
||||
resolution: "es-abstract@npm:1.22.3"
|
||||
|
@ -3502,10 +3475,8 @@ __metadata:
|
|||
resolution: "facilmap-client@workspace:client"
|
||||
dependencies:
|
||||
"@types/geojson": ^7946.0.13
|
||||
"@types/rollup-plugin-auto-external": ^2.0.5
|
||||
facilmap-types: "workspace:^"
|
||||
rimraf: ^5.0.5
|
||||
rollup-plugin-auto-external: ^2.0.0
|
||||
socket.io-client: ^4.7.2
|
||||
typescript: ^5.2.2
|
||||
vite: ^4.5.0
|
||||
|
@ -3556,7 +3527,6 @@ __metadata:
|
|||
pluralize: ^8.0.0
|
||||
popper-max-size-modifier: ^0.2.0
|
||||
rimraf: ^5.0.5
|
||||
rollup-plugin-auto-external: ^2.0.0
|
||||
sass: ^1.69.5
|
||||
svgo: ^3.0.3
|
||||
tablesorter: ^2.31.3
|
||||
|
@ -3584,7 +3554,6 @@ __metadata:
|
|||
"@types/leaflet.markercluster": ^1.5.4
|
||||
"@types/lodash-es": ^4.17.11
|
||||
"@types/node-fetch": ^2.6.9
|
||||
"@types/rollup-plugin-auto-external": ^2.0.5
|
||||
"@types/yauzl": ^2.10.3
|
||||
cheerio: ^1.0.0-rc.12
|
||||
facilmap-client: "workspace:^"
|
||||
|
@ -3603,7 +3572,6 @@ __metadata:
|
|||
node-fetch: ^3.3.2
|
||||
rimraf: ^5.0.5
|
||||
rollup: 3
|
||||
rollup-plugin-auto-external: ^2.0.0
|
||||
svgo: ^3.0.3
|
||||
ts-node: ^10.9.1
|
||||
typescript: ^5.2.2
|
||||
|
@ -3667,7 +3635,6 @@ __metadata:
|
|||
node-cron: ^3.0.3
|
||||
p-throttle: ^5.1.0
|
||||
rimraf: ^5.0.5
|
||||
rollup-plugin-auto-external: ^2.0.0
|
||||
sequelize: ^6.34.0
|
||||
socket.io: ^4.7.2
|
||||
string-similarity: ^4.0.4
|
||||
|
@ -3687,7 +3654,6 @@ __metadata:
|
|||
dependencies:
|
||||
"@types/geojson": ^7946.0.13
|
||||
rimraf: ^5.0.5
|
||||
rollup-plugin-auto-external: ^2.0.0
|
||||
typescript: ^5.2.2
|
||||
vite: ^4.5.0
|
||||
vite-plugin-dts: ^3.6.3
|
||||
|
@ -3715,7 +3681,6 @@ __metadata:
|
|||
lodash-es: ^4.17.21
|
||||
marked: ^9.1.6
|
||||
rimraf: ^5.0.5
|
||||
rollup-plugin-auto-external: ^2.0.0
|
||||
typescript: ^5.2.2
|
||||
vite: ^4.5.0
|
||||
vite-plugin-dts: ^3.6.3
|
||||
|
@ -4317,13 +4282,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hosted-git-info@npm:^2.1.4":
|
||||
version: 2.8.9
|
||||
resolution: "hosted-git-info@npm:2.8.9"
|
||||
checksum: c955394bdab888a1e9bb10eb33029e0f7ce5a2ac7b3f158099dc8c486c99e73809dca609f5694b223920ca2174db33d32b12f9a2a47141dc59607c29da5a62dd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"html-encoding-sniffer@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "html-encoding-sniffer@npm:3.0.0"
|
||||
|
@ -4555,13 +4513,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-arrayish@npm:^0.2.1":
|
||||
version: 0.2.1
|
||||
resolution: "is-arrayish@npm:0.2.1"
|
||||
checksum: eef4417e3c10e60e2c810b6084942b3ead455af16c4509959a27e490e7aee87cfb3f38e01bbde92220b528a0ee1a18d52b787e1458ee86174d8c7f0e58cd488f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-bigint@npm:^1.0.1":
|
||||
version: 1.0.4
|
||||
resolution: "is-bigint@npm:1.0.4"
|
||||
|
@ -4881,13 +4832,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"json-parse-better-errors@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "json-parse-better-errors@npm:1.0.2"
|
||||
checksum: ff2b5ba2a70e88fd97a3cb28c1840144c5ce8fae9cbeeddba15afa333a5c407cf0e42300cd0a2885dbb055227fe68d405070faad941beeffbfde9cf3b2c78c5d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"json-schema-traverse@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "json-schema-traverse@npm:0.4.1"
|
||||
|
@ -5088,18 +5032,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"load-json-file@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "load-json-file@npm:4.0.0"
|
||||
dependencies:
|
||||
graceful-fs: ^4.1.2
|
||||
parse-json: ^4.0.0
|
||||
pify: ^3.0.0
|
||||
strip-bom: ^3.0.0
|
||||
checksum: 8f5d6d93ba64a9620445ee9bde4d98b1eac32cf6c8c2d20d44abfa41a6945e7969456ab5f1ca2fb06ee32e206c9769a20eec7002fe290de462e8c884b6b8b356
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"local-pkg@npm:^0.4.3":
|
||||
version: 0.4.3
|
||||
resolution: "local-pkg@npm:0.4.3"
|
||||
|
@ -5705,18 +5637,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"normalize-package-data@npm:^2.3.2":
|
||||
version: 2.5.0
|
||||
resolution: "normalize-package-data@npm:2.5.0"
|
||||
dependencies:
|
||||
hosted-git-info: ^2.1.4
|
||||
resolve: ^1.10.0
|
||||
semver: 2 || 3 || 4 || 5
|
||||
validate-npm-package-license: ^3.0.1
|
||||
checksum: 7999112efc35a6259bc22db460540cae06564aa65d0271e3bdfa86876d08b0e578b7b5b0028ee61b23f1cae9fc0e7847e4edc0948d3068a39a2a82853efc8499
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "normalize-path@npm:3.0.0"
|
||||
|
@ -5994,16 +5914,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse-json@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "parse-json@npm:4.0.0"
|
||||
dependencies:
|
||||
error-ex: ^1.3.1
|
||||
json-parse-better-errors: ^1.0.1
|
||||
checksum: 0fe227d410a61090c247e34fa210552b834613c006c2c64d9a05cfe9e89cf8b4246d1246b1a99524b53b313e9ac024438d0680f67e33eaed7e6f38db64cfe7b5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse5-htmlparser2-tree-adapter@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0"
|
||||
|
@ -6089,15 +5999,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-type@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "path-type@npm:3.0.0"
|
||||
dependencies:
|
||||
pify: ^3.0.0
|
||||
checksum: 735b35e256bad181f38fa021033b1c33cfbe62ead42bb2222b56c210e42938eecb272ae1949f3b6db4ac39597a61b44edd8384623ec4d79bfdc9a9c0f12537a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-type@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "path-type@npm:4.0.0"
|
||||
|
@ -6159,13 +6060,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pify@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "pify@npm:3.0.0"
|
||||
checksum: 6cdcbc3567d5c412450c53261a3f10991665d660961e06605decf4544a61a97a54fefe70a68d5c37080ff9d6f4cf51444c90198d1ba9f9309a6c0d6e9f5c4fde
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pkg-dir@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "pkg-dir@npm:7.0.0"
|
||||
|
@ -6345,17 +6239,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"read-pkg@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "read-pkg@npm:3.0.0"
|
||||
dependencies:
|
||||
load-json-file: ^4.0.0
|
||||
normalize-package-data: ^2.3.2
|
||||
path-type: ^3.0.0
|
||||
checksum: 398903ebae6c7e9965419a1062924436cc0b6f516c42c4679a90290d2f87448ed8f977e7aa2dbba4aa1ac09248628c43e493ac25b2bc76640e946035200e34c6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"readable-stream@npm:1.1":
|
||||
version: 1.1.14
|
||||
resolution: "readable-stream@npm:1.1.14"
|
||||
|
@ -6444,7 +6327,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve@npm:^1.10.0, resolve@npm:^1.22.4, resolve@npm:~1.22.1":
|
||||
"resolve@npm:^1.22.4, resolve@npm:~1.22.1":
|
||||
version: 1.22.8
|
||||
resolution: "resolve@npm:1.22.8"
|
||||
dependencies:
|
||||
|
@ -6467,7 +6350,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve@patch:resolve@^1.10.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.4#~builtin<compat/resolve>, resolve@patch:resolve@~1.22.1#~builtin<compat/resolve>":
|
||||
"resolve@patch:resolve@^1.22.4#~builtin<compat/resolve>, resolve@patch:resolve@~1.22.1#~builtin<compat/resolve>":
|
||||
version: 1.22.8
|
||||
resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin<compat/resolve>::version=1.22.8&hash=c3c19d"
|
||||
dependencies:
|
||||
|
@ -6544,20 +6427,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rollup-plugin-auto-external@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "rollup-plugin-auto-external@npm:2.0.0"
|
||||
dependencies:
|
||||
builtins: ^2.0.0
|
||||
read-pkg: ^3.0.0
|
||||
safe-resolve: ^1.0.0
|
||||
semver: ^5.5.0
|
||||
peerDependencies:
|
||||
rollup: ">=0.45.2"
|
||||
checksum: 316751e04888eaceac67a43ed94c2c81dedcfe2d8b1095e129911361b6efd535eddb6452bc0ef0b87cdc2206eea8e554705432fbeae063c42be6353390518ce3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rollup@npm:3.29.4":
|
||||
version: 3.29.4
|
||||
resolution: "rollup@npm:3.29.4"
|
||||
|
@ -6625,13 +6494,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-resolve@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "safe-resolve@npm:1.0.0"
|
||||
checksum: 3a1ea6bc72965061d52799da473dd52920dab55f16532bf295c19920cb745661461e4756c35a299c96a6bdc436ab6abd9d217dd48d6cce776657c6ecd8d5add0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0":
|
||||
version: 2.1.2
|
||||
resolution: "safer-buffer@npm:2.1.2"
|
||||
|
@ -6661,16 +6523,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.5.0":
|
||||
version: 5.7.2
|
||||
resolution: "semver@npm:5.7.2"
|
||||
bin:
|
||||
semver: bin/semver
|
||||
checksum: fb4ab5e0dd1c22ce0c937ea390b4a822147a9c53dbd2a9a0132f12fe382902beef4fbf12cf51bb955248d8d15874ce8cd89532569756384f994309825f10b686
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^6.0.0, semver@npm:^6.3.1":
|
||||
"semver@npm:^6.3.1":
|
||||
version: 6.3.1
|
||||
resolution: "semver@npm:6.3.1"
|
||||
bin:
|
||||
|
@ -6967,40 +6820,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"spdx-correct@npm:^3.0.0":
|
||||
version: 3.2.0
|
||||
resolution: "spdx-correct@npm:3.2.0"
|
||||
dependencies:
|
||||
spdx-expression-parse: ^3.0.0
|
||||
spdx-license-ids: ^3.0.0
|
||||
checksum: e9ae98d22f69c88e7aff5b8778dc01c361ef635580e82d29e5c60a6533cc8f4d820803e67d7432581af0cc4fb49973125076ee3b90df191d153e223c004193b2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"spdx-exceptions@npm:^2.1.0":
|
||||
version: 2.3.0
|
||||
resolution: "spdx-exceptions@npm:2.3.0"
|
||||
checksum: cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"spdx-expression-parse@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "spdx-expression-parse@npm:3.0.1"
|
||||
dependencies:
|
||||
spdx-exceptions: ^2.1.0
|
||||
spdx-license-ids: ^3.0.0
|
||||
checksum: a1c6e104a2cbada7a593eaa9f430bd5e148ef5290d4c0409899855ce8b1c39652bcc88a725259491a82601159d6dc790bedefc9016c7472f7de8de7361f8ccde
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"spdx-license-ids@npm:^3.0.0":
|
||||
version: 3.0.16
|
||||
resolution: "spdx-license-ids@npm:3.0.16"
|
||||
checksum: 5cdaa85aaa24bd02f9353a2e357b4df0a4f205cb35655f3fd0a5674a4fb77081f28ffd425379214bc3be2c2b7593ce1215df6bcc75884aeee0a9811207feabe2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sprintf-js@npm:~1.0.2":
|
||||
version: 1.0.3
|
||||
resolution: "sprintf-js@npm:1.0.3"
|
||||
|
@ -7708,16 +7527,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"validate-npm-package-license@npm:^3.0.1":
|
||||
version: 3.0.4
|
||||
resolution: "validate-npm-package-license@npm:3.0.4"
|
||||
dependencies:
|
||||
spdx-correct: ^3.0.0
|
||||
spdx-expression-parse: ^3.0.0
|
||||
checksum: 35703ac889d419cf2aceef63daeadbe4e77227c39ab6287eeb6c1b36a746b364f50ba22e88591f5d017bc54685d8137bc2d328d0a896e4d3fd22093c0f32a9ad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"validator@npm:^13.7.0, validator@npm:^13.9.0":
|
||||
version: 13.11.0
|
||||
resolution: "validator@npm:13.11.0"
|
||||
|
|
Ładowanie…
Reference in New Issue