WIP finished: Migrated to Vue 3, Bootstrap 5, ESM, Vite (only minor issues remaining)

pull/256/head
Candid Dauth 2023-11-13 23:04:41 +01:00
rodzic 44c16a2f76
commit f4555ee628
66 zmienionych plików z 601 dodań i 694 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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"

Wyświetl plik

@ -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);

Wyświetl plik

@ -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("/")
}
}
});

Wyświetl plik

@ -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 Reacts `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.

Wyświetl plik

@ -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 FacilMaps 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

Wyświetl plik

@ -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

Wyświetl plik

@ -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/).

Wyświetl plik

@ -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",

Wyświetl plik

@ -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",

Wyświetl plik

@ -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>

Wyświetl plik

@ -25,6 +25,7 @@
width: 50%;
}
</style>
<script type="module" src="./example.ts"></script>
</head>
<body>
<div id="app"></div>

Wyświetl plik

@ -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);
}
});

Wyświetl plik

@ -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>

Wyświetl plik

@ -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;
};

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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.";
}
}

Wyświetl plik

@ -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">

Wyświetl plik

@ -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>

Wyświetl plik

@ -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;

Wyświetl plik

@ -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";

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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;
}
});

Wyświetl plik

@ -1,4 +1,4 @@
import { Tooltip } from "bootstrap";
import Tooltip from "bootstrap/js/dist/tooltip";
import type { Directive } from "vue";
declare global {

Wyświetl plik

@ -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>

Wyświetl plik

@ -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() {

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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>

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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("/")
}
}
}));

Wyświetl plik

@ -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

Wyświetl plik

@ -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$/
})
}
];
};

Wyświetl plik

@ -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",

Wyświetl plik

@ -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);

Wyświetl plik

@ -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";

Wyświetl plik

@ -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" {

Wyświetl plik

@ -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";

Wyświetl plik

@ -1,4 +1,4 @@
declare module "custom:icons" {
declare module "virtual:icons" {
const rawIcons: Record<string, Record<string, string>>;
export default rawIcons;
}

Wyświetl plik

@ -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.

Wyświetl plik

@ -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();

Wyświetl plik

@ -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: {

Wyświetl plik

@ -1 +0,0 @@
*

Wyświetl plik

@ -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",

Wyświetl plik

@ -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";

Wyświetl plik

@ -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> = {};

Wyświetl plik

@ -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>;

Wyświetl plik

@ -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
})
});

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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";

Wyświetl plik

@ -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";

Wyświetl plik

@ -1,4 +1,4 @@
import { access } from "node:fs/promises";
import { access } from "fs/promises";
const LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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("/")
}
}
});

Wyświetl plik

@ -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"

Wyświetl plik

@ -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();

Wyświetl plik

@ -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("/")
}
}
});

Wyświetl plik

@ -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",

Wyświetl plik

@ -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;

Wyświetl plik

@ -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
Wyświetl plik

@ -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"