Skip to content

Commit

Permalink
Merge branch 'electric-sql:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
pmp-p committed Jun 17, 2024
2 parents d3b64d3 + a1d41a8 commit 10b7df2
Show file tree
Hide file tree
Showing 22 changed files with 1,477 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/repl/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
25 changes: 25 additions & 0 deletions packages/repl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
dist-webcomponent
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
91 changes: 91 additions & 0 deletions packages/repl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# PGlite REPL React Component

A REPL, or terminal, for use in the browser with PGlite, allowing you to have an interactive session with your WASM Postgres in the page.

<img width="918" src="https://github.com/electric-sql/pglite/assets/31130/f7c9c2dd-4de8-4033-9905-9637ae998034">

## Features:

- Available as both a React.js component and a Web Components
- [CodeMirror](https://codemirror.net) for input editing
- Auto complete, including table and column names from the database
- Input history (up and down keys)
- `\d` PSQL commands (via [psql-describe](https://www.npmjs.com/package/psql-describe))

## How to use with React

```
npm install @electric-sql/pglite-repl
```

then to include in a page:

```tsx
import { PGlite } from "@electric-sql/pglite";
import { Repl } from "@electric-sql/pglite-repl";

function MyComponent() {
const pg = new PGlite();

return <>
<Repl pg={pg} />
</>
}
```

The props for the `<Repl>` component are described by this interface:

```ts
// The theme to use, auto is auto switching based on the system
type ReplTheme = "light" | "dark" | "auto";

interface ReplProps {
pg: PGlite; // PGlite db instance
border?: boolean; // Outer border on the component, defaults to false
lightTheme?: Extension;
darkTheme?: Extension;
theme?: ReplTheme; // Defaults to "auto"
}
```

The `lightTheme` and `darkTheme` should be instances of a [React CodeMirror](https://uiwjs.github.io/react-codemirror/) theme.

## How to use as a Web Component

Although the PGlite REPL is built with React, its also available as a web component for easy inclusion in any page or other framework.

```html
<script src="https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist-webcomponent/Repl.js" type="module"></script>

<!-- Include the Repl web component in your page -->
<pglite-repl id="repl"></pglite-repl>

<script type="module">
import { PGlite } from "https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js";
// Create a PGlite instance
const pg = new PGlite();
// Retrieve the Repl element
const repl = document.getElementById('repl');
// REPL to your PGlite instance
repl.pg = pg;
</script>
```

## Development

Checkout this repo and from package dir:

```sh
# Install deps
pnpm install

# Run dev server
pnpm dev
# then open a browser to the url shown

# Build the lib
pnpm build
```
13 changes: 13 additions & 0 deletions packages/repl/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PGlite REPL</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
69 changes: 69 additions & 0 deletions packages/repl/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "@electric-sql/pglite-repl",
"version": "0.0.0",
"author": "Electric DB Limited",
"license": "Apache-2.0",
"private": false,
"publishConfig": {
"access": "public"
},
"type": "module",
"files": [
"dist",
"dist-webcomponent"
],
"module": "dist/Repl.js",
"exports": {
".": {
"import": "./dist/Repl.js"
},
"./webcomponent": {
"import": "./dist-webcomponent/Repl.js"
}
},
"scripts": {
"dev": "vite",
"build:react": "tsc && vite build",
"build:webcomp": "vite build --config vite.webcomp.config.ts",
"build": "npm run build:react && npm run build:webcomp",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"format": "prettier --write ./src && prettier --write ./src-webcomponent"
},
"dependencies": {
"@codemirror/autocomplete": "^6.16.2",
"@codemirror/commands": "^6.6.0",
"@codemirror/lang-sql": "^6.6.4",
"@codemirror/language": "^6.10.2",
"@codemirror/view": "^6.28.1",
"@uiw/codemirror-theme-xcode": "^4.22.2",
"@uiw/react-codemirror": "^4.22.2",
"psql-describe": "^0.1.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"peerDependencies": {
"@electric-sql/pglite": "workspace:^"
},
"peerDependenciesMeta": {
"@electric-sql/pglite": {
"optional": true
}
},
"devDependencies": {
"@electric-sql/pglite": "workspace:^",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"terser": "^5.31.1",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"vite-plugin-dts": "^3.9.1",
"vite-plugin-libcss": "^1.1.1"
}
}
101 changes: 101 additions & 0 deletions packages/repl/src-webcomponent/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { createRoot } from "react-dom/client";
import { Repl } from "../src/Repl";
import type { ReplProps, ReplTheme } from "../src/Repl";
import type { PGlite } from "@electric-sql/pglite";
import type { Extension } from "@uiw/react-codemirror";

// @ts-ignore
import css from "../src/Repl.css?raw";

export type { ReplProps, ReplTheme };

export class PGliteREPL extends HTMLElement {
#mountPoint: HTMLDivElement;
#root: ReturnType<typeof createRoot>;
#pg?: PGlite;
#lightTheme?: Extension;
#darkTheme?: Extension;

constructor() {
super();
this.#mountPoint = document.createElement("div");
const shadowRoot = this.attachShadow({ mode: "open" });
const style = document.createElement("style");
style.textContent = css;
shadowRoot.appendChild(style);
shadowRoot.appendChild(this.#mountPoint);
this.#root = createRoot(this.#mountPoint);
}

static get observedAttributes() {
return ["border", "theme"];
}

connectedCallback() {
this.render();
}

attributeChangedCallback(_name: string, oldValue: any, newValue: any) {
if (oldValue !== newValue) {
this.render();
}
}

disconnectedCallback() {
this.#root?.unmount();
}

get pg() {
return this.#pg;
}

set pg(pg: PGlite | undefined) {
this.#pg = pg;
this.render();
}

get lightTheme() {
return this.#lightTheme;
}

set lightTheme(lightTheme: Extension | undefined) {
this.#lightTheme = lightTheme;
this.render();
}

get darkTheme() {
return this.#darkTheme;
}

set darkTheme(darkTheme: Extension | undefined) {
this.#darkTheme = darkTheme;
this.render();
}

render() {
const border = this.hasAttribute("border")
? this.getAttribute("border") === "true"
: undefined;
const theme = this.getAttribute("theme");

const props: ReplProps = {
pg: this.#pg!,
border,
lightTheme: this.#lightTheme,
darkTheme: this.#darkTheme,
theme: (theme as ReplTheme | null) || "auto",
};

this.#root.render(
<>
{this.#pg ? (
<Repl {...props} />
) : (
<div>PGlite instance not provided</div>
)}
</>,
);
}
}

customElements.define("pglite-repl", PGliteREPL);
12 changes: 12 additions & 0 deletions packages/repl/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#root {
padding: 1rem;
height: calc(100vh - 2rem);
display: flex;
flex-direction: column;
}

.App {
display: flex;
flex-direction: column;
height: 100%;
}
15 changes: 15 additions & 0 deletions packages/repl/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PGlite } from "@electric-sql/pglite";
import "./App.css";
import { Repl } from "./Repl";

const pg = new PGlite();

function App() {
return (
<div className="App">
<Repl pg={pg} border />
</div>
);
}

export default App;
Loading

0 comments on commit 10b7df2

Please sign in to comment.