diff --git a/pom.xml b/pom.xml index 81cdcce1777..3560f8dbc99 100755 --- a/pom.xml +++ b/pom.xml @@ -533,6 +533,7 @@ **/.idea/** **/node_modules/** **/build/** + **/dist/** **/coverage/** **/.babelrc **/.bowerrc diff --git a/zipkin-lens/.env.development b/zipkin-lens/.env.development deleted file mode 100644 index f7b1d3f7aea..00000000000 --- a/zipkin-lens/.env.development +++ /dev/null @@ -1,2 +0,0 @@ -BASE_PATH=/zipkin -API_BASE=http://localhost:9411/zipkin diff --git a/zipkin-lens/.env.production b/zipkin-lens/.env.production deleted file mode 100644 index 969a6b0a05d..00000000000 --- a/zipkin-lens/.env.production +++ /dev/null @@ -1,2 +0,0 @@ -BASE_PATH=/zipkin -API_BASE=/zipkin diff --git a/zipkin-lens/.env.test b/zipkin-lens/.env.test deleted file mode 100644 index cfbb1ba49cf..00000000000 --- a/zipkin-lens/.env.test +++ /dev/null @@ -1,2 +0,0 @@ -BASE_PATH=/zipkin -API_BASE=http://localhost:51204/zipkin diff --git a/zipkin-lens/.gitignore b/zipkin-lens/.gitignore index 4d29575de80..800f3a80c34 100644 --- a/zipkin-lens/.gitignore +++ b/zipkin-lens/.gitignore @@ -10,6 +10,7 @@ # production /build +/dist # misc .DS_Store diff --git a/zipkin-lens/.npmrc b/zipkin-lens/.npmrc new file mode 100644 index 00000000000..73bd443aa23 --- /dev/null +++ b/zipkin-lens/.npmrc @@ -0,0 +1,2 @@ +# This is needed while dependencies rely on different versions of react +legacy-peer-deps=true diff --git a/zipkin-lens/README.md b/zipkin-lens/README.md index 4953f72b527..c975cf00eaf 100644 --- a/zipkin-lens/README.md +++ b/zipkin-lens/README.md @@ -34,6 +34,18 @@ To get a coverage report as well, run `npm test -- --coverage`. Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance. +## Build tips + +### Use the production node version + +The production UI is built with Maven. To use the same version, issue this command: + +```bash +nvm use $(../mvnw help:evaluate -Dexpression=node.version -q -DforceStdout) +``` + +Now, it is less likely a pull request will fail when `npm test` succeeds locally. + ## Localization We use [LinguiJS](https://lingui.js.org/) for localization of the UI. Translations for strings are diff --git a/zipkin-lens/index.html b/zipkin-lens/index.html index 9eb0990496d..958658e2e9f 100644 --- a/zipkin-lens/index.html +++ b/zipkin-lens/index.html @@ -16,6 +16,7 @@ + diff --git a/zipkin-lens/package-lock.json b/zipkin-lens/package-lock.json index 77289c97bf9..0afa66c798a 100644 --- a/zipkin-lens/package-lock.json +++ b/zipkin-lens/package-lock.json @@ -8467,9 +8467,9 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "node_modules/three": { - "version": "0.106.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.106.2.tgz", - "integrity": "sha512-4Tlx43uoxnIaZFW2Bzkd1rXsatvVHEWAZJy8LuE+s6Q8c66ogNnhfq1bHiBKPAnXP230LD11H/ScIZc2LZMviA==" + "version": "0.161.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.161.0.tgz", + "integrity": "sha512-LC28VFtjbOyEu5b93K0bNRLw1rQlMJ85lilKsYj6dgTu+7i17W+JCCEbvrpmNHF1F3NAUqDSWq50UD7w9H2xQw==" }, "node_modules/throttle-debounce": { "version": "2.3.0", @@ -8967,7 +8967,7 @@ "hammerjs": "^2.0.8", "lodash": "^4.17.14", "numeral": "^1.5.3", - "three": "^0.106.2" + "three": "^0.161.0" } }, "node_modules/vizceral-react": { diff --git a/zipkin-lens/pom.xml b/zipkin-lens/pom.xml index 17562bf405b..3ff3f90ce67 100644 --- a/zipkin-lens/pom.xml +++ b/zipkin-lens/pom.xml @@ -116,7 +116,7 @@ npm - install --legacy-peer-deps + install diff --git a/zipkin-lens/src/components/App/App.tsx b/zipkin-lens/src/components/App/App.tsx index bd017b19b70..e974533fe57 100644 --- a/zipkin-lens/src/components/App/App.tsx +++ b/zipkin-lens/src/components/App/App.tsx @@ -37,7 +37,7 @@ const App: React.FC = () => { const baseName = useMemo(() => { return import.meta.env.DEV ? '/zipkin' - : (import.meta.env.BASE_PATH as string); + : (import.meta.env.BASE_URL as string); }, []); return ( diff --git a/zipkin-lens/src/components/App/LanguageSelector.tsx b/zipkin-lens/src/components/App/LanguageSelector.tsx index ace4e684819..ac9a0ec4741 100644 --- a/zipkin-lens/src/components/App/LanguageSelector.tsx +++ b/zipkin-lens/src/components/App/LanguageSelector.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at diff --git a/zipkin-lens/src/components/App/Layout.tsx b/zipkin-lens/src/components/App/Layout.tsx index 429fd78107d..6c880b961aa 100644 --- a/zipkin-lens/src/components/App/Layout.tsx +++ b/zipkin-lens/src/components/App/Layout.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at diff --git a/zipkin-lens/src/components/App/TraceIdSearch.tsx b/zipkin-lens/src/components/App/TraceIdSearch.tsx index 1ec166e1d15..059c3087f2c 100644 --- a/zipkin-lens/src/components/App/TraceIdSearch.tsx +++ b/zipkin-lens/src/components/App/TraceIdSearch.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import { TextField } from '@material-ui/core'; import React, { useCallback, useState } from 'react'; import { useHistory } from 'react-router-dom'; diff --git a/zipkin-lens/src/components/DiscoverPage/TraceSummaryTable.tsx b/zipkin-lens/src/components/DiscoverPage/TraceSummaryTable.tsx index 166d0e4fe0d..9555be16bf6 100644 --- a/zipkin-lens/src/components/DiscoverPage/TraceSummaryTable.tsx +++ b/zipkin-lens/src/components/DiscoverPage/TraceSummaryTable.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import { Table, TableBody, diff --git a/zipkin-lens/src/components/TracePage/Header/Header.tsx b/zipkin-lens/src/components/TracePage/Header/Header.tsx index 8cdb89a4fb1..2bb0b5a22d4 100644 --- a/zipkin-lens/src/components/TracePage/Header/Header.tsx +++ b/zipkin-lens/src/components/TracePage/Header/Header.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import { Box, Button, makeStyles, Typography } from '@material-ui/core'; import { List as ListIcon } from '@material-ui/icons'; import React from 'react'; diff --git a/zipkin-lens/src/components/TracePage/Header/HeaderMenu.tsx b/zipkin-lens/src/components/TracePage/Header/HeaderMenu.tsx index 3962364a99d..4d5837f2c08 100644 --- a/zipkin-lens/src/components/TracePage/Header/HeaderMenu.tsx +++ b/zipkin-lens/src/components/TracePage/Header/HeaderMenu.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import { Button, makeStyles, Menu, MenuItem } from '@material-ui/core'; import { Menu as MenuIcon } from '@material-ui/icons'; import React, { useCallback } from 'react'; diff --git a/zipkin-lens/src/components/TracePage/SpanDetailDrawer/SpanDetailDrawer.tsx b/zipkin-lens/src/components/TracePage/SpanDetailDrawer/SpanDetailDrawer.tsx index b04ac55de75..1851a5250fe 100644 --- a/zipkin-lens/src/components/TracePage/SpanDetailDrawer/SpanDetailDrawer.tsx +++ b/zipkin-lens/src/components/TracePage/SpanDetailDrawer/SpanDetailDrawer.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import { Box, Divider, Grid, makeStyles, Typography } from '@material-ui/core'; import React from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/zipkin-lens/src/components/TracePage/Timeline/Timeline.tsx b/zipkin-lens/src/components/TracePage/Timeline/Timeline.tsx index 13ca660126d..b7f47486c1d 100644 --- a/zipkin-lens/src/components/TracePage/Timeline/Timeline.tsx +++ b/zipkin-lens/src/components/TracePage/Timeline/Timeline.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import { Box, makeStyles } from '@material-ui/core'; import React, { useCallback, useEffect, useRef } from 'react'; import AutoSizer, { Size } from 'react-virtualized-auto-sizer'; diff --git a/zipkin-lens/src/components/UiConfig/UiConfig.notest.js b/zipkin-lens/src/components/UiConfig/UiConfig.test.jsx similarity index 65% rename from zipkin-lens/src/components/UiConfig/UiConfig.notest.js rename to zipkin-lens/src/components/UiConfig/UiConfig.test.jsx index 769b39306bb..1b82ef52d09 100644 --- a/zipkin-lens/src/components/UiConfig/UiConfig.notest.js +++ b/zipkin-lens/src/components/UiConfig/UiConfig.test.jsx @@ -13,65 +13,60 @@ */ import { render, screen } from '@testing-library/react'; import fetchMock from 'fetch-mock'; -import { afterEach, beforeEach, it, describe, expect } from 'vitest'; +import { afterEach, it, describe, expect, vi } from 'vitest'; import React, { Suspense } from 'react'; -import { - defaultConfig, - UiConfigConsumer, - UiConfig as RawUIConfig, -} from './UiConfig'; -import { UI_CONFIG } from '../../constants/api'; - afterEach(() => { fetchMock.restore(); + vi.resetModules(); }); -beforeEach(() => { - // We fetch the resource on module initialization for performance, but want to do that in every - // test. -}); - -const UiConfig = () => { - return ( +const renderUiConfig = async () => { + const { UiConfigConsumer, UiConfig } = await import('./UiConfig'); + render( - + {(value) =>
{JSON.stringify(value)}
}
-
-
+ + , ); }; describe('', () => { it('fetches config and suspends', async () => { const configPromise = new Promise(() => undefined); + const { UI_CONFIG } = await import('../../constants/api'); fetchMock.once(UI_CONFIG, configPromise, { overwriteRoutes: true }); - render(); + await renderUiConfig(); expect(screen.getAllByText('Suspended')).length(1); fetchMock.called(UI_CONFIG); }); - it('provides config when resolved', async () => { + it('provides config when resolved', (context) => async () => { const config = { defaultLookback: 100 }; + const { defaultConfig } = await import('./UiConfig'); Object.keys(defaultConfig).forEach((key) => { config[key] = config[key] || defaultConfig[key]; }); - + const { UI_CONFIG } = await import('../../constants/api'); fetchMock.once(UI_CONFIG, config, { overwriteRoutes: true }); - render(); - expect(screen.getAllByText('Suspended').length).toBe(1); + // TODO: adrian needs help, as this broke when porting to vitest + context.skip(); + + const { rerender } = await renderUiConfig(); + expect(screen.getAllByText('Suspended')).length(1); - /*// We need to get off the processing loop to allow the promise to complete and resolve the + // We need to get off the processing loop to allow the promise to complete and resolve the // config. await new Promise((resolve) => setTimeout(resolve, 1)); - rerender(); - expect(screen.getByText(JSON.stringify(config))).toBeInTheDocument();*/ + rerender(); // was rerender(); + expect(screen.getByText(JSON.stringify(config))).toBeInTheDocument(); fetchMock.called(UI_CONFIG); }); diff --git a/zipkin-lens/src/constants/api.test.tsx b/zipkin-lens/src/constants/api.test.tsx new file mode 100644 index 00000000000..56d54fdf727 --- /dev/null +++ b/zipkin-lens/src/constants/api.test.tsx @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +/* eslint-disable global-require */ + +import { afterEach, it, describe, expect, vi } from 'vitest'; + +// To test BASE_PATH we can't import it above, as it would return a constant +// value for all tests. Instead, we use `await import` later. +describe('BASE_PATH', () => { + afterEach(() => { + vi.resetModules(); + }); + + it('defaults to /zipkin with no base tag', async () => { + const { BASE_PATH } = await import('./api'); + expect(BASE_PATH).toEqual('/zipkin'); + }); + + it('is set to base tag when present', async () => { + const base = document.createElement('base'); + base.setAttribute('href', '/coolzipkin/'); + document.head.append(base); + + const { BASE_PATH } = await import('./api'); + expect(BASE_PATH).toEqual('/coolzipkin'); + }); +}); diff --git a/zipkin-lens/src/constants/api.ts b/zipkin-lens/src/constants/api.ts index d12d83dc8ce..d896e269731 100644 --- a/zipkin-lens/src/constants/api.ts +++ b/zipkin-lens/src/constants/api.ts @@ -11,8 +11,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -export const BASE_PATH = import.meta.env.BASE_PATH; -export const ZIPKIN_API = `${import.meta.env.API_BASE}/api/v2`; +const extractBasePath = () => { + const base = document.getElementsByTagName('base'); + if (base.length === 0) { + return '/zipkin'; + } + return base[0]?.getAttribute('href')?.replace(/\/+$/, ''); +}; + +export const BASE_PATH = extractBasePath(); + +export const ZIPKIN_API = `${BASE_PATH}/api/v2`; export const UI_CONFIG = `${BASE_PATH}/config.json`; export const SERVICES = `${ZIPKIN_API}/services`; export const REMOTE_SERVICES = `${ZIPKIN_API}/remoteServices`; diff --git a/zipkin-lens/src/setupProxy.js b/zipkin-lens/src/setupProxy.js deleted file mode 100644 index 1d92115e465..00000000000 --- a/zipkin-lens/src/setupProxy.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2015-2024 The OpenZipkin Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -import { createProxyMiddleware } from 'http-proxy-middleware'; - -const API_BASE = process.env.API_BASE || 'http://localhost:9411'; - -// Default create-react-app proxy only proxies AJAX requests by looking at Accept headers. We want -// to proxy any request though, mainly to support the Download JSON button. -const proxy = createProxyMiddleware(['**/api/**', '**/config.json'], { - target: API_BASE, - changeOrigin: true, -}); - -export default (app) => { - app.use('/', proxy); -}; diff --git a/zipkin-lens/src/setupTests.ts b/zipkin-lens/src/setupTests.ts index ab7f7ef4225..27e2dd059d8 100644 --- a/zipkin-lens/src/setupTests.ts +++ b/zipkin-lens/src/setupTests.ts @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - import '@testing-library/jest-dom'; import fetchMock from 'fetch-mock'; diff --git a/zipkin-lens/src/slices/tracesSlice.ts b/zipkin-lens/src/slices/tracesSlice.ts index e38b99b5490..276b6321e56 100644 --- a/zipkin-lens/src/slices/tracesSlice.ts +++ b/zipkin-lens/src/slices/tracesSlice.ts @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - /* eslint-disable no-param-reassign */ import { diff --git a/zipkin-lens/src/util/trace.test.js b/zipkin-lens/src/util/trace.test.js index 88565bb9c53..a62c6fd3ef1 100644 --- a/zipkin-lens/src/util/trace.test.js +++ b/zipkin-lens/src/util/trace.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at diff --git a/zipkin-lens/src/zipkin/clock-skew.test.js b/zipkin-lens/src/zipkin/clock-skew.test.js index 4b77b76f4d9..36aa265d8cd 100644 --- a/zipkin-lens/src/zipkin/clock-skew.test.js +++ b/zipkin-lens/src/zipkin/clock-skew.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at diff --git a/zipkin-lens/src/zipkin/span-node.test.js b/zipkin-lens/src/zipkin/span-node.test.js index a73226893a1..8eb7f0c0893 100644 --- a/zipkin-lens/src/zipkin/span-node.test.js +++ b/zipkin-lens/src/zipkin/span-node.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at diff --git a/zipkin-lens/src/zipkin/trace.test.js b/zipkin-lens/src/zipkin/trace.test.js index e923a4dda55..d932303c048 100644 --- a/zipkin-lens/src/zipkin/trace.test.js +++ b/zipkin-lens/src/zipkin/trace.test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * Copyright 2015-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at diff --git a/zipkin-lens/vite.config.ts b/zipkin-lens/vite.config.ts index 958bb4a0527..90eacd181e8 100644 --- a/zipkin-lens/vite.config.ts +++ b/zipkin-lens/vite.config.ts @@ -15,25 +15,57 @@ import {defineConfig} from 'vite' import * as path from "path"; -// https://vitejs.dev/config/ -export default defineConfig({ - server: { - port: 3000 - }, - define: { - 'import.meta.env.BASE_PATH': JSON.stringify(process.env.BASE_PATH), - 'import.meta.env.API_BASE': JSON.stringify(process.env.API_BASE), - }, - resolve: { - alias: { - src: path.resolve('src/'), + +// baseUrl is the default path to lookup assets. +const baseUrl = process.env.BASE_URL || '/zipkin'; +// basePath is the default path to get dynamic resources from zipkin. +const basePath = process.env.BASE_PATH || baseUrl; + +const zipkinProxyConfig = { + target: process.env.API_BASE || 'http://localhost:9411', + changeOrigin: true, +}; + +export default defineConfig(() => { + // https://vitejs.dev/config/ + return { + server: { + port: 3000, + proxy: { + '/zipkin/api/v2': zipkinProxyConfig, + '/zipkin/config.json': zipkinProxyConfig, + }, + }, + resolve: { + alias: { + src: path.resolve('src/'), + }, + }, + test: { + environment: 'happy-dom' + }, + plugins: [ + { + name: 'Replace head.base.href with BASE_PATH variable', + transformIndexHtml: html => html.replace('href="/zipkin"', `href="${basePath}"`) + }, + ], + base: baseUrl, + build: { + outDir: 'build', + // use the same path patterns as the original react-scripts lens build + assetsDir: "static", + rollupOptions: { + output: { + assetFileNames({name}) { + if (name?.includes('.css')) return 'static/css/[name].[hash].css' + if (name?.includes('.png')) return 'static/media/[name].[hash].png' + return 'static/[name].[hash][extname]' + }, + chunkFileNames: `static/js/[name].[hash].js`, + entryFileNames: `static/js/[name].[hash].js`, + }, + }, }, - }, - test: { - environment: 'happy-dom' - }, - base:"/zipkin/", - build:{ - outDir: 'build' - }, + } })