You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
frontend: start using CSS modules for components with design tokens
This commit is contained in:
21
frontend/src/components/Block.module.css
Normal file
21
frontend/src/components/Block.module.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.block {
|
||||||
|
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
padding: var(--cpd-space-4x);
|
||||||
|
border-radius: var(--cpd-space-2x);
|
||||||
|
}
|
@ -12,15 +12,18 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import styles from "./Block.module.css";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode;
|
|
||||||
highlight?: boolean;
|
highlight?: boolean;
|
||||||
className?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Block: React.FC<Props> = ({ children, highlight, className }) => {
|
const Block: React.FC<React.PropsWithChildren<Props>> = ({
|
||||||
|
children,
|
||||||
|
highlight,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={className} data-active={highlight}>
|
<div className={styles.block} data-active={highlight}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
21
frontend/src/components/BlockList.module.css
Normal file
21
frontend/src/components/BlockList.module.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.block-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--cpd-space-4x);
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
@ -12,14 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
type Props = {
|
import styles from "./BlockList.module.css";
|
||||||
children: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const BlockList: React.FC<Props> = ({ children }) => {
|
const BlockList: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
|
||||||
return (
|
return <div className={styles.blockList}>{children}</div>;
|
||||||
<div className="grid grid-cols-1 gap-4 group content-start">{children}</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlockList;
|
export default BlockList;
|
||||||
|
24
frontend/src/components/BrowserSession.module.css
Normal file
24
frontend/src/components/BrowserSession.module.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.session-icon {
|
||||||
|
color: var(--cpd-color-icon-secondary);
|
||||||
|
background: var(--cpd-color-bg-subtle-secondary);
|
||||||
|
height: var(--cpd-space-10x);
|
||||||
|
width: var(--cpd-space-10x);
|
||||||
|
padding: var(--cpd-space-2x);
|
||||||
|
border-radius: var(--cpd-space-1x);
|
||||||
|
margin-right: var(--cpd-space-2x);
|
||||||
|
}
|
@ -22,7 +22,7 @@ import { useTransition } from "react";
|
|||||||
import { currentBrowserSessionIdAtom, currentUserIdAtom } from "../atoms";
|
import { currentBrowserSessionIdAtom, currentUserIdAtom } from "../atoms";
|
||||||
import { FragmentType, graphql, useFragment } from "../gql";
|
import { FragmentType, graphql, useFragment } from "../gql";
|
||||||
|
|
||||||
import Block from "./Block";
|
import styles from "./BrowserSession.module.css";
|
||||||
import DateTime from "./DateTime";
|
import DateTime from "./DateTime";
|
||||||
|
|
||||||
const FRAGMENT = graphql(/* GraphQL */ `
|
const FRAGMENT = graphql(/* GraphQL */ `
|
||||||
@ -92,8 +92,8 @@ const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Block className="flex items-center">
|
<div className="flex items-center">
|
||||||
<IconWebBrowser className="mr-4 session-icon" />
|
<IconWebBrowser className={styles.sessionIcon} />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Body size="md" weight="medium">
|
<Body size="md" weight="medium">
|
||||||
{isCurrent ? (
|
{isCurrent ? (
|
||||||
@ -118,7 +118,7 @@ const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
|
|||||||
>
|
>
|
||||||
Sign out
|
Sign out
|
||||||
</Button>
|
</Button>
|
||||||
</Block>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ const CompatSession: React.FC<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Block className="p-4 bg-grey-25 dark:bg-grey-450 rounded-lg">
|
<Block>
|
||||||
<Body>
|
<Body>
|
||||||
Started: <DateTime datetime={data.createdAt} />
|
Started: <DateTime datetime={data.createdAt} />
|
||||||
</Body>
|
</Body>
|
||||||
|
31
frontend/src/components/Layout.module.css
Normal file
31
frontend/src/components/Layout.module.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 378px;
|
||||||
|
margin: var(--cpd-space-10x) auto var(--cpd-space-6x) auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
border: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--cpd-color-border-interactive-secondary);
|
||||||
|
margin: var(--cpd-space-4x) 0;
|
||||||
|
}
|
||||||
|
|
@ -12,24 +12,27 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import styles from "./Layout.module.css";
|
||||||
import NavBar from "./NavBar";
|
import NavBar from "./NavBar";
|
||||||
import NavItem from "./NavItem";
|
import NavItem, { ExternalLink } from "./NavItem";
|
||||||
|
|
||||||
|
const Separator: React.FC = () => <hr className={styles.separator} />;
|
||||||
|
|
||||||
const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={styles.container}>
|
||||||
<NavBar className="nav-bar container">
|
<NavBar>
|
||||||
<NavItem route={{ type: "home" }}>Sessions</NavItem>
|
<NavItem route={{ type: "home" }}>Sessions</NavItem>
|
||||||
<NavItem route={{ type: "account" }}>Emails</NavItem>
|
<NavItem route={{ type: "account" }}>Emails</NavItem>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
|
|
||||||
<hr className="my-2" />
|
<Separator />
|
||||||
|
|
||||||
<main className="container">{children}</main>
|
<main>{children}</main>
|
||||||
|
|
||||||
<hr className="my-2" />
|
<Separator />
|
||||||
|
|
||||||
<footer className="text-center">
|
<footer className={styles.footer}>
|
||||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
|
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
|
||||||
<img
|
<img
|
||||||
className="inline my-2"
|
className="inline my-2"
|
||||||
@ -40,19 +43,19 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<NavBar className="nav-bar container">
|
<NavBar>
|
||||||
<NavItem href="https://matrix.org/legal/copyright-notice">
|
<ExternalLink href="https://matrix.org/legal/copyright-notice">
|
||||||
Info
|
Info
|
||||||
</NavItem>
|
</ExternalLink>
|
||||||
<NavItem href="https://matrix.org/legal/privacy-notice">
|
<ExternalLink href="https://matrix.org/legal/privacy-notice">
|
||||||
Privacy
|
Privacy
|
||||||
</NavItem>
|
</ExternalLink>
|
||||||
<NavItem href="https://matrix.org/legal/terms-and-conditions">
|
<ExternalLink href="https://matrix.org/legal/terms-and-conditions">
|
||||||
Terms & Conditions
|
Terms & Conditions
|
||||||
</NavItem>
|
</ExternalLink>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
</footer>
|
</footer>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
21
frontend/src/components/NavBar.module.css
Normal file
21
frontend/src/components/NavBar.module.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.nav-bar-items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--cpd-space-4x);
|
||||||
|
}
|
@ -12,12 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
const NavBar: React.FC<{
|
import styles from "./NavBar.module.css";
|
||||||
className: string;
|
|
||||||
children: React.ReactNode;
|
const NavBar: React.FC<React.PropsWithChildren<{}>> = ({ children }) => (
|
||||||
}> = ({ className, children }) => (
|
<nav>
|
||||||
<nav className={className}>
|
<ul className={styles.navBarItems}>{children}</ul>
|
||||||
<ul className="flex flex-row gap-4 justify-center">{children}</ul>
|
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
30
frontend/src/components/NavItem.module.css
Normal file
30
frontend/src/components/NavItem.module.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
padding: var(--cpd-space-1x) var(--cpd-space-2x);
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
|
line-height: var(--cpd-space-6x);
|
||||||
|
border-radius: var(--cpd-radius-pill-effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover {
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item[aria-current="page"] {
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
}
|
@ -12,41 +12,37 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Link as CpdLink } from "@vector-im/compound-web";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
|
|
||||||
import { Link, Route, routeAtom } from "../Router";
|
import { Link, Route, routeAtom } from "../Router";
|
||||||
|
|
||||||
type NavItemProps = {
|
import styles from "./NavItem.module.css";
|
||||||
children: React.ReactNode;
|
|
||||||
} & ({ route: Route; href?: never } | { route?: never; href: string });
|
|
||||||
|
|
||||||
function isRoute(route: Route | undefined): route is Route {
|
const NavItem: React.FC<React.PropsWithChildren<{ route: Route }>> = ({
|
||||||
return !!route?.type;
|
route,
|
||||||
}
|
children,
|
||||||
|
}) => {
|
||||||
function isHref(href: string | undefined): href is string {
|
|
||||||
return typeof href === "string";
|
|
||||||
}
|
|
||||||
|
|
||||||
const NavItem: React.FC<NavItemProps> = ({ route, href, children }) => {
|
|
||||||
const currentRoute = useAtomValue(routeAtom);
|
const currentRoute = useAtomValue(routeAtom);
|
||||||
return (
|
return (
|
||||||
<li className="m-1 mr-0">
|
<li>
|
||||||
{isRoute(route) && (
|
<Link
|
||||||
<Link
|
className={styles.navItem}
|
||||||
route={route}
|
route={route}
|
||||||
className={currentRoute.type === route.type ? "active" : ""}
|
aria-current={currentRoute.type === route.type ? "page" : undefined}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
|
||||||
{isHref(href) && (
|
|
||||||
<a href={href} target="_blank" rel="noopenner noreferrer">
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ExternalLink: React.FC<
|
||||||
|
React.PropsWithChildren<{ href: string }>
|
||||||
|
> = ({ href, children }) => (
|
||||||
|
<li>
|
||||||
|
<CpdLink href={href}>{children}</CpdLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
export default NavItem;
|
export default NavItem;
|
||||||
|
@ -99,13 +99,7 @@ const OAuth2Session: React.FC<Props> = ({ session }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Block
|
<Block>
|
||||||
className={`p-4 bg-grey-25 dark:bg-grey-450 rounded-lg ${
|
|
||||||
data.finishedAt
|
|
||||||
? "opacity-50 group-hover:opacity-100 transition-opacity"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Typography variant="body" bold>
|
<Typography variant="body" bold>
|
||||||
<Link
|
<Link
|
||||||
route={{ type: "client", id: data.client.id }}
|
route={{ type: "client", id: data.client.id }}
|
||||||
|
@ -25,37 +25,10 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
/* XXX: I'm unsure why this is not part of the tokens */
|
||||||
|
--cpd-radius-pill-effect: 9999px;
|
||||||
|
|
||||||
font: var(--cpd-font-body-md-regular);
|
font: var(--cpd-font-body-md-regular);
|
||||||
background: var(--cpd-color-bg-canvas-default);
|
background: var(--cpd-color-bg-canvas-default);
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
width: 378px;
|
}
|
||||||
margin: var(--cpd-space-10x) auto var(--cpd-space-6x) auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-bar a {
|
|
||||||
color: var(--cpd-color-text-action-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-bar .active {
|
|
||||||
font-weight: var(--cpd-font-weight-semibold);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-bar li:not(:first-child) {
|
|
||||||
list-style-type: disc;
|
|
||||||
color: var(--cpd-color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
height: 1px;
|
|
||||||
background: var(--cpd-color-gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-icon {
|
|
||||||
color: var(--cpd-color-icon-secondary);
|
|
||||||
background: var(--cpd-color-bg-subtle-secondary);
|
|
||||||
height: var(--cpd-space-10x);
|
|
||||||
width: var(--cpd-space-10x);
|
|
||||||
padding: var(--cpd-space-2x);
|
|
||||||
border-radius: var(--cpd-space-1x);
|
|
||||||
}
|
|
@ -42,6 +42,11 @@ module.exports = {
|
|||||||
"black-950": "#21262C",
|
"black-950": "#21262C",
|
||||||
ice: "#F4F9FD",
|
ice: "#F4F9FD",
|
||||||
},
|
},
|
||||||
|
fontWeight: {
|
||||||
|
semibold: "var(--cpd-font-weight-semibold)",
|
||||||
|
medium: "var(--cpd-font-weight-medium)",
|
||||||
|
regular: "var(--cpd-font-weight-regular)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
extend: {},
|
extend: {},
|
||||||
|
@ -23,6 +23,13 @@ import { defineConfig } from "vitest/config";
|
|||||||
|
|
||||||
export default defineConfig((env) => ({
|
export default defineConfig((env) => ({
|
||||||
base: "./",
|
base: "./",
|
||||||
|
|
||||||
|
css: {
|
||||||
|
modules: {
|
||||||
|
localsConvention: "camelCaseOnly",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
build: {
|
build: {
|
||||||
manifest: true,
|
manifest: true,
|
||||||
assetsDir: "",
|
assetsDir: "",
|
||||||
@ -37,6 +44,7 @@ export default defineConfig((env) => ({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
codegen(),
|
codegen(),
|
||||||
|
|
||||||
@ -104,6 +112,7 @@ export default defineConfig((env) => ({
|
|||||||
ext: ".zz",
|
ext: ".zz",
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
server: {
|
server: {
|
||||||
base: "/account/",
|
base: "/account/",
|
||||||
proxy: {
|
proxy: {
|
||||||
@ -112,6 +121,7 @@ export default defineConfig((env) => ({
|
|||||||
"http://127.0.0.1:8080",
|
"http://127.0.0.1:8080",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
test: {
|
test: {
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: "v8",
|
provider: "v8",
|
||||||
|
Reference in New Issue
Block a user