mirror of
https://github.com/sqlchat/sqlchat.git
synced 2025-07-28 17:53:21 +08:00
feat: support execute sql
This commit is contained in:
@ -1,45 +1,80 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||||
|
import { useConnectionStore } from "@/store";
|
||||||
|
import Icon from "./Icon";
|
||||||
|
import ExecuteStatementModal from "./ExecuteStatementModal";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
language: string;
|
language: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkStatementIsSelect = (statement: string) => {
|
||||||
|
return statement.toUpperCase().trim().startsWith("SELECT");
|
||||||
|
};
|
||||||
|
|
||||||
export const CodeBlock = (props: Props) => {
|
export const CodeBlock = (props: Props) => {
|
||||||
const { language, value } = props;
|
const { language, value } = props;
|
||||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
const connectionStore = useConnectionStore();
|
||||||
|
const [showExecuteQueryModal, setShowExecuteQueryModal] = useState(false);
|
||||||
|
const currentConnectionCtx = connectionStore.currentConnectionCtx;
|
||||||
|
// Only show execute button in the following situations:
|
||||||
|
// * SQL code, and it is a SELECT statement;
|
||||||
|
// * Connection setup;
|
||||||
|
const showExecuteButton =
|
||||||
|
language.toUpperCase() === "SQL" && checkStatementIsSelect(value) && currentConnectionCtx?.connection && currentConnectionCtx?.database;
|
||||||
|
|
||||||
const copyToClipboard = () => {
|
const copyToClipboard = () => {
|
||||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
||||||
|
toast.error("Failed to copy to clipboard");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.clipboard.writeText(value).then(() => {
|
navigator.clipboard.writeText(value).then(() => {
|
||||||
setIsCopied(true);
|
toast.success("Copied to clipboard");
|
||||||
setTimeout(() => {
|
|
||||||
setIsCopied(false);
|
|
||||||
}, 2000);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full relative font-sans text-[16px]">
|
<>
|
||||||
<div className="flex items-center justify-between py-1.5 px-4">
|
<div className="w-full max-w-full relative font-sans text-[16px]">
|
||||||
<span className="text-xs text-white font-mono">{language}</span>
|
<div className="flex items-center justify-between py-2 px-4">
|
||||||
<div className="flex items-center">
|
<span className="text-xs text-white font-mono">{language}</span>
|
||||||
<button
|
<div className="flex items-center space-x-2">
|
||||||
className="flex items-center rounded bg-none py-0.5 px-2 text-xs text-white bg-gray-600 hover:opacity-80"
|
<button
|
||||||
onClick={copyToClipboard}
|
className="flex justify-center items-center rounded bg-none w-6 h-6 p-1 text-xs text-white bg-gray-500 opacity-70 hover:opacity-100"
|
||||||
>
|
onClick={copyToClipboard}
|
||||||
{isCopied ? "Copied!" : "Copy"}
|
>
|
||||||
</button>
|
<Icon.BiClipboard className="w-full h-auto" />
|
||||||
|
</button>
|
||||||
|
{showExecuteButton && (
|
||||||
|
<button
|
||||||
|
className="flex justify-center items-center rounded bg-none w-6 h-6 p-1 text-xs text-white bg-gray-500 opacity-70 hover:opacity-100"
|
||||||
|
onClick={() => setShowExecuteQueryModal(true)}
|
||||||
|
>
|
||||||
|
<Icon.IoPlay className="w-full h-auto" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<SyntaxHighlighter language={language.toLowerCase()} style={oneDark} customStyle={{ margin: 0 }}>
|
||||||
|
{value}
|
||||||
|
</SyntaxHighlighter>
|
||||||
</div>
|
</div>
|
||||||
<SyntaxHighlighter language={language.toLowerCase()} style={oneDark} customStyle={{ margin: 0 }}>
|
|
||||||
{value}
|
{showExecuteQueryModal &&
|
||||||
</SyntaxHighlighter>
|
showExecuteButton &&
|
||||||
</div>
|
createPortal(
|
||||||
|
<ExecuteStatementModal
|
||||||
|
connection={currentConnectionCtx.connection}
|
||||||
|
databaseName={currentConnectionCtx.database?.name || ""}
|
||||||
|
statement={value}
|
||||||
|
close={() => setShowExecuteQueryModal(false)}
|
||||||
|
/>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
81
components/ExecuteStatementModal.tsx
Normal file
81
components/ExecuteStatementModal.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { head } from "lodash-es";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import DataTable from "react-data-table-component";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
|
import { Connection } from "@/types";
|
||||||
|
import Icon from "./Icon";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
connection: Connection;
|
||||||
|
databaseName: string;
|
||||||
|
statement: string;
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawQueryResult = {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ExecuteStatementModal = (props: Props) => {
|
||||||
|
const { close, connection, databaseName, statement } = props;
|
||||||
|
const [rawResults, setRawResults] = useState<RawQueryResult[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const columns = Object.keys(head(rawResults) || {}).map((key) => {
|
||||||
|
return {
|
||||||
|
name: key,
|
||||||
|
selector: (row: RawQueryResult) => row[key],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const executeStatement = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/connection/execute", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
connection,
|
||||||
|
db: databaseName,
|
||||||
|
statement,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
setIsLoading(false);
|
||||||
|
setRawResults(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Failed to execute statement");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
executeStatement();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal modal-middle modal-open">
|
||||||
|
<div className="modal-box w-full">
|
||||||
|
<h3 className="font-bold text-lg">Execute query</h3>
|
||||||
|
<button className="btn btn-sm btn-circle absolute right-4 top-4" onClick={close}>
|
||||||
|
<Icon.IoMdClose className="w-5 h-auto" />
|
||||||
|
</button>
|
||||||
|
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="w-full flex justify-center py-6 pt-8">
|
||||||
|
<Icon.BiLoaderAlt className="w-6 h-auto opacity-70 animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : rawResults.length === 0 ? (
|
||||||
|
<div className="w-full flex justify-center py-6 pt-8">No data return.</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-full">
|
||||||
|
<DataTable className="w-full" columns={columns} data={rawResults} pagination responsive />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExecuteStatementModal;
|
@ -4,6 +4,7 @@ import postgres from "./postgres";
|
|||||||
|
|
||||||
export interface Connector {
|
export interface Connector {
|
||||||
testConnection: () => Promise<boolean>;
|
testConnection: () => Promise<boolean>;
|
||||||
|
execute: (databaseName: string, statement: string) => Promise<any>;
|
||||||
getDatabases: () => Promise<string[]>;
|
getDatabases: () => Promise<string[]>;
|
||||||
getTables: (databaseName: string) => Promise<string[]>;
|
getTables: (databaseName: string) => Promise<string[]>;
|
||||||
getTableStructure: (databaseName: string, tableName: string) => Promise<string>;
|
getTableStructure: (databaseName: string, tableName: string) => Promise<string>;
|
||||||
|
@ -20,6 +20,15 @@ const testConnection = async (connection: Connection): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const execute = async (connection: Connection, databaseName: string, statement: string): Promise<any> => {
|
||||||
|
connection.database = databaseName;
|
||||||
|
const connectionUrl = convertToConnectionUrl(connection);
|
||||||
|
const conn = await mysql.createConnection(connectionUrl);
|
||||||
|
const [rows] = await conn.query<RowDataPacket[]>(statement);
|
||||||
|
conn.destroy();
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
const getDatabases = async (connection: Connection): Promise<string[]> => {
|
const getDatabases = async (connection: Connection): Promise<string[]> => {
|
||||||
const connectionUrl = convertToConnectionUrl(connection);
|
const connectionUrl = convertToConnectionUrl(connection);
|
||||||
const conn = await mysql.createConnection(connectionUrl);
|
const conn = await mysql.createConnection(connectionUrl);
|
||||||
@ -68,6 +77,7 @@ const getTableStructure = async (connection: Connection, databaseName: string, t
|
|||||||
const newConnector = (connection: Connection): Connector => {
|
const newConnector = (connection: Connection): Connector => {
|
||||||
return {
|
return {
|
||||||
testConnection: () => testConnection(connection),
|
testConnection: () => testConnection(connection),
|
||||||
|
execute: (databaseName: string, statement: string) => execute(connection, databaseName, statement),
|
||||||
getDatabases: () => getDatabases(connection),
|
getDatabases: () => getDatabases(connection),
|
||||||
getTables: (databaseName: string) => getTables(connection, databaseName),
|
getTables: (databaseName: string) => getTables(connection, databaseName),
|
||||||
getTableStructure: (databaseName: string, tableName: string) => getTableStructure(connection, databaseName, tableName),
|
getTableStructure: (databaseName: string, tableName: string) => getTableStructure(connection, databaseName, tableName),
|
||||||
|
@ -27,6 +27,14 @@ const testConnection = async (connection: Connection): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const execute = async (connection: Connection, _: string, statement: string): Promise<any> => {
|
||||||
|
const client = newPostgresClient(connection);
|
||||||
|
await client.connect();
|
||||||
|
const { rows } = await client.query(statement);
|
||||||
|
await client.end();
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
const getDatabases = async (connection: Connection): Promise<string[]> => {
|
const getDatabases = async (connection: Connection): Promise<string[]> => {
|
||||||
const client = newPostgresClient(connection);
|
const client = newPostgresClient(connection);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
@ -75,6 +83,7 @@ const getTableStructure = async (connection: Connection, _: string, tableName: s
|
|||||||
const newConnector = (connection: Connection): Connector => {
|
const newConnector = (connection: Connection): Connector => {
|
||||||
return {
|
return {
|
||||||
testConnection: () => testConnection(connection),
|
testConnection: () => testConnection(connection),
|
||||||
|
execute: (databaseName: string, statement: string) => execute(connection, databaseName, statement),
|
||||||
getDatabases: () => getDatabases(connection),
|
getDatabases: () => getDatabases(connection),
|
||||||
getTables: (databaseName: string) => getTables(connection, databaseName),
|
getTables: (databaseName: string) => getTables(connection, databaseName),
|
||||||
getTableStructure: (databaseName: string, tableName: string) => getTableStructure(connection, databaseName, tableName),
|
getTableStructure: (databaseName: string, tableName: string) => getTableStructure(connection, databaseName, tableName),
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"next": "^13.2.4",
|
"next": "^13.2.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-data-table-component": "^7.5.3",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.8.0",
|
||||||
@ -24,6 +25,7 @@
|
|||||||
"react-markdown": "^8.0.6",
|
"react-markdown": "^8.0.6",
|
||||||
"react-textarea-autosize": "^8.4.0",
|
"react-textarea-autosize": "^8.4.0",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
|
"styled-components": "^5.3.9",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"zustand": "^4.3.6"
|
"zustand": "^4.3.6"
|
||||||
},
|
},
|
||||||
|
24
pages/api/connection/execute.ts
Normal file
24
pages/api/connection/execute.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { newConnector } from "@/lib/connectors";
|
||||||
|
import { Connection } from "@/types";
|
||||||
|
|
||||||
|
// POST /api/connection/execute
|
||||||
|
// req body: { connection: Connection, db: string, statement: string }
|
||||||
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
return res.status(405).json(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const connection = req.body.connection as Connection;
|
||||||
|
const db = req.body.db as string;
|
||||||
|
const statement = req.body.statement as string;
|
||||||
|
try {
|
||||||
|
const connector = newConnector(connection);
|
||||||
|
const result = await connector.execute(db, statement);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handler;
|
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@ -25,6 +25,7 @@ specifiers:
|
|||||||
pg: ^8.10.0
|
pg: ^8.10.0
|
||||||
postcss: ^8.4.20
|
postcss: ^8.4.20
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
|
react-data-table-component: ^7.5.3
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
react-hot-toast: ^2.4.0
|
react-hot-toast: ^2.4.0
|
||||||
react-icons: ^4.8.0
|
react-icons: ^4.8.0
|
||||||
@ -33,6 +34,7 @@ specifiers:
|
|||||||
react-syntax-highlighter: ^15.5.0
|
react-syntax-highlighter: ^15.5.0
|
||||||
react-textarea-autosize: ^8.4.0
|
react-textarea-autosize: ^8.4.0
|
||||||
remark-gfm: ^3.0.1
|
remark-gfm: ^3.0.1
|
||||||
|
styled-components: ^5.3.9
|
||||||
tailwindcss: ^3.2.4
|
tailwindcss: ^3.2.4
|
||||||
typescript: ^4.9.4
|
typescript: ^4.9.4
|
||||||
uuid: ^9.0.0
|
uuid: ^9.0.0
|
||||||
@ -49,6 +51,7 @@ dependencies:
|
|||||||
lodash-es: 4.17.21
|
lodash-es: 4.17.21
|
||||||
next: 13.2.4_biqbaboplfbrettd7655fr4n2y
|
next: 13.2.4_biqbaboplfbrettd7655fr4n2y
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
react-data-table-component: 7.5.3_hsvf4mq4wiyrkiyc3wzg6vdhxa
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
react-hot-toast: 2.4.0_owo25xnefcwdq3zjgtohz6dbju
|
react-hot-toast: 2.4.0_owo25xnefcwdq3zjgtohz6dbju
|
||||||
react-icons: 4.8.0_react@18.2.0
|
react-icons: 4.8.0_react@18.2.0
|
||||||
@ -56,6 +59,7 @@ dependencies:
|
|||||||
react-markdown: 8.0.6_pmekkgnqduwlme35zpnqhenc34
|
react-markdown: 8.0.6_pmekkgnqduwlme35zpnqhenc34
|
||||||
react-textarea-autosize: 8.4.0_pmekkgnqduwlme35zpnqhenc34
|
react-textarea-autosize: 8.4.0_pmekkgnqduwlme35zpnqhenc34
|
||||||
remark-gfm: 3.0.1
|
remark-gfm: 3.0.1
|
||||||
|
styled-components: 5.3.9_biqbaboplfbrettd7655fr4n2y
|
||||||
uuid: 9.0.0
|
uuid: 9.0.0
|
||||||
zustand: 4.3.6_react@18.2.0
|
zustand: 4.3.6_react@18.2.0
|
||||||
|
|
||||||
@ -793,7 +797,7 @@ packages:
|
|||||||
babel-plugin-syntax-jsx: 6.18.0
|
babel-plugin-syntax-jsx: 6.18.0
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
styled-components: 5.3.9_7i5myeigehqah43i5u7wbekgba
|
styled-components: 5.3.9_biqbaboplfbrettd7655fr4n2y
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/babel-plugin-syntax-jsx/6.18.0:
|
/babel-plugin-syntax-jsx/6.18.0:
|
||||||
@ -1099,6 +1103,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/deepmerge/4.3.1:
|
||||||
|
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/define-properties/1.2.0:
|
/define-properties/1.2.0:
|
||||||
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
|
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -3078,6 +3087,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
/react-data-table-component/7.5.3_hsvf4mq4wiyrkiyc3wzg6vdhxa:
|
||||||
|
resolution: {integrity: sha512-JhatRTgThAAa1HobPaPmkNPsjLT6+fnMIdtcXRCy+0bSYN7XJnTgob9Qyi4bjHh/8tMPTHtxZCV/TKiPwRvFMA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>= 16.8.3'
|
||||||
|
styled-components: '>= 4'
|
||||||
|
dependencies:
|
||||||
|
deepmerge: 4.3.1
|
||||||
|
react: 18.2.0
|
||||||
|
styled-components: 5.3.9_biqbaboplfbrettd7655fr4n2y
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-dom/18.2.0_react@18.2.0:
|
/react-dom/18.2.0_react@18.2.0:
|
||||||
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
|
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3484,6 +3504,28 @@ packages:
|
|||||||
supports-color: 5.5.0
|
supports-color: 5.5.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/styled-components/5.3.9_biqbaboplfbrettd7655fr4n2y:
|
||||||
|
resolution: {integrity: sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>= 16.8.0'
|
||||||
|
react-dom: '>= 16.8.0'
|
||||||
|
react-is: '>= 16.8.0'
|
||||||
|
dependencies:
|
||||||
|
'@babel/helper-module-imports': 7.18.6
|
||||||
|
'@babel/traverse': 7.21.3_supports-color@5.5.0
|
||||||
|
'@emotion/is-prop-valid': 1.2.0
|
||||||
|
'@emotion/stylis': 0.8.5
|
||||||
|
'@emotion/unitless': 0.7.5
|
||||||
|
babel-plugin-styled-components: 2.0.7_styled-components@5.3.9
|
||||||
|
css-to-react-native: 3.2.0
|
||||||
|
hoist-non-react-statics: 3.3.2
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0_react@18.2.0
|
||||||
|
shallowequal: 1.1.0
|
||||||
|
supports-color: 5.5.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/styled-jsx/5.1.1_react@18.2.0:
|
/styled-jsx/5.1.1_react@18.2.0:
|
||||||
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
|
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
|
Reference in New Issue
Block a user