From edabd9aba97da4f72d948ca5f6d7889701d79311 Mon Sep 17 00:00:00 2001 From: Owen Ou Date: Tue, 14 Jan 2025 14:16:54 -0800 Subject: [PATCH] Improvement on local dev exp (#22) --- Dockerfile.dev | 23 +++++++ LICENSE | 19 ++++++ README.md | 68 +++++++++++++------ compose.yaml | 38 +++++++++++ {app => src/app}/api/health/route.ts | 0 {app => src/app}/api/jq/route.ts | 0 {app => src/app}/api/snippets/[slug]/route.ts | 0 {app => src/app}/api/snippets/route.ts | 0 {app => src/app}/global-error.tsx | 0 {app => src/app}/globals.css | 0 {app => src/app}/layout.tsx | 0 {app => src/app}/page.tsx | 0 {app => src/app}/s/[slug]/page.tsx | 0 .../components}/CheatSheetDialog.tsx | 0 {components => src/components}/Editor.tsx | 0 .../components}/EditorWrapper.tsx | 0 {components => src/components}/HTTP.tsx | 0 {components => src/components}/Header.tsx | 0 .../components}/InitMonacoEditor.tsx | 0 {components => src/components}/JSONEditor.tsx | 1 - {components => src/components}/Logo.tsx | 0 .../components}/Notification.tsx | 0 .../components}/OptionsSelector.tsx | 0 .../components}/OutputEditor.tsx | 0 {components => src/components}/Playground.tsx | 0 .../components}/QueryEditor.tsx | 0 .../components}/SectionTitle.tsx | 3 +- {components => src/components}/TabList.tsx | 0 .../components}/ThemeProvider.tsx | 2 +- {lib => src/lib}/prisma.ts | 0 {lib => src/lib}/utils.ts | 0 {workers => src/workers}/index.ts | 3 +- {workers => src/workers}/model.ts | 0 {workers => src/workers}/process.ts | 0 {workers => src/workers}/worker.ts | 0 tailwind.config.ts | 6 +- tsconfig.json | 2 +- 37 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 Dockerfile.dev create mode 100644 LICENSE create mode 100644 compose.yaml rename {app => src/app}/api/health/route.ts (100%) rename {app => src/app}/api/jq/route.ts (100%) rename {app => src/app}/api/snippets/[slug]/route.ts (100%) rename {app => src/app}/api/snippets/route.ts (100%) rename {app => src/app}/global-error.tsx (100%) rename {app => src/app}/globals.css (100%) rename {app => src/app}/layout.tsx (100%) rename {app => src/app}/page.tsx (100%) rename {app => src/app}/s/[slug]/page.tsx (100%) rename {components => src/components}/CheatSheetDialog.tsx (100%) rename {components => src/components}/Editor.tsx (100%) rename {components => src/components}/EditorWrapper.tsx (100%) rename {components => src/components}/HTTP.tsx (100%) rename {components => src/components}/Header.tsx (100%) rename {components => src/components}/InitMonacoEditor.tsx (100%) rename {components => src/components}/JSONEditor.tsx (96%) rename {components => src/components}/Logo.tsx (100%) rename {components => src/components}/Notification.tsx (100%) rename {components => src/components}/OptionsSelector.tsx (100%) rename {components => src/components}/OutputEditor.tsx (100%) rename {components => src/components}/Playground.tsx (100%) rename {components => src/components}/QueryEditor.tsx (100%) rename {components => src/components}/SectionTitle.tsx (86%) rename {components => src/components}/TabList.tsx (100%) rename {components => src/components}/ThemeProvider.tsx (96%) rename {lib => src/lib}/prisma.ts (100%) rename {lib => src/lib}/utils.ts (100%) rename {workers => src/workers}/index.ts (94%) rename {workers => src/workers}/model.ts (100%) rename {workers => src/workers}/process.ts (100%) rename {workers => src/workers}/worker.ts (100%) diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 00000000..ddcf8dc7 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,23 @@ +# syntax = docker/dockerfile:1 + +# Use a specific Node.js version suitable for your project +FROM node:22 + +# Set the working directory +WORKDIR /app + +# Copy only package.json and package-lock.json first for dependency installation +COPY package*.json ./ +COPY prisma/ ./ + +# Install dependencies +RUN npm install + +# Copy the rest of the project files +COPY . . + +# Expose the development server port +EXPOSE 3000 + +# Set default command +CMD ["npm", "run", "dev"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..1d2d2ce3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024-2025 Owen Ou + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c4033664..d7b59772 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,62 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +# jqplay + +A [jq](https://jqlang.github.io/jq) playground built with [Next.js](https://nextjs.org). +Test your jq queries against JSON directly in your browser. All jq queries and HTTP requests to fetch JSON are processed **locally** in your browser. Snippets are sent to the server **only** if you choose to share them. + +✨ **Try it out at [jqplay.org](https://jqplay.org)!** + +## How It Works + +- **WebAssembly-Powered**: jqplay integrates the [jq-wasm](https://github.com/owenthereal/jq-wasm) package, a WebAssembly-based jq JSON processor for Node.js and browsers, with no native dependencies. This ensures that all jq queries run directly in your browser. +- **Local Data Processing**: Your JSON input is processed locally in your browser, ensuring your data stays private and secure. +- **Shareable Snippets**: If you share your jq query, a unique URL is generated on the server. Others can open the shared snippet, but the query will still run locally in their browser. ## Getting Started -First, run the development server: +Prerequisites + +- Node.js (>= 14.x recommended) +- npm or yarn package manager +- PostgreSQL (for storing shared snippets) + +## Running the App + +### 1. Clone the repository -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +```console +git clone https://github.com/owenthereal/jqplay +cd jqplay ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +### 2. Start in Development Mode -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +To start the app in development mode with hot reload enabled and a local PostgreSQL database: -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. +```console +docker compose up +``` + +Open your browser to to explore jqplay. + +### 3. Run a Production Build + +For a production-ready build, use: + +```console +npm run build +npm run start +``` -## Learn More +Open your browser to to use jqplay locally in production mode. -To learn more about Next.js, take a look at the following resources: +## Contributing -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +Contributions are welcome! πŸŽ‰ Whether you’re fixing bugs, adding features, or improving documentation, your help is appreciated. -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +## License -## Deploy on Vercel +πŸ“œ jqplay is licensed under the [MIT License](LICENSE). -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +--- -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +Happy querying! πŸš€ diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 00000000..e9101970 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,38 @@ +version: '3.8' +services: + jqplay: + build: + context: . + dockerfile: Dockerfile.dev + container_name: jqplay + environment: + NODE_ENV: development + DATABASE_URL: postgres://postgres:postgres@pg:5432/jqplay + command: > + sh -c "npx prisma migrate dev && npm run dev" + + ports: + - "3000:3000" + depends_on: + pg: + condition: service_healthy + volumes: + - .:/app + - /app/node_modules + - /app/.next + restart: unless-stopped + pg: + image: postgres:16 + container_name: pg + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: jqplay + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 diff --git a/app/api/health/route.ts b/src/app/api/health/route.ts similarity index 100% rename from app/api/health/route.ts rename to src/app/api/health/route.ts diff --git a/app/api/jq/route.ts b/src/app/api/jq/route.ts similarity index 100% rename from app/api/jq/route.ts rename to src/app/api/jq/route.ts diff --git a/app/api/snippets/[slug]/route.ts b/src/app/api/snippets/[slug]/route.ts similarity index 100% rename from app/api/snippets/[slug]/route.ts rename to src/app/api/snippets/[slug]/route.ts diff --git a/app/api/snippets/route.ts b/src/app/api/snippets/route.ts similarity index 100% rename from app/api/snippets/route.ts rename to src/app/api/snippets/route.ts diff --git a/app/global-error.tsx b/src/app/global-error.tsx similarity index 100% rename from app/global-error.tsx rename to src/app/global-error.tsx diff --git a/app/globals.css b/src/app/globals.css similarity index 100% rename from app/globals.css rename to src/app/globals.css diff --git a/app/layout.tsx b/src/app/layout.tsx similarity index 100% rename from app/layout.tsx rename to src/app/layout.tsx diff --git a/app/page.tsx b/src/app/page.tsx similarity index 100% rename from app/page.tsx rename to src/app/page.tsx diff --git a/app/s/[slug]/page.tsx b/src/app/s/[slug]/page.tsx similarity index 100% rename from app/s/[slug]/page.tsx rename to src/app/s/[slug]/page.tsx diff --git a/components/CheatSheetDialog.tsx b/src/components/CheatSheetDialog.tsx similarity index 100% rename from components/CheatSheetDialog.tsx rename to src/components/CheatSheetDialog.tsx diff --git a/components/Editor.tsx b/src/components/Editor.tsx similarity index 100% rename from components/Editor.tsx rename to src/components/Editor.tsx diff --git a/components/EditorWrapper.tsx b/src/components/EditorWrapper.tsx similarity index 100% rename from components/EditorWrapper.tsx rename to src/components/EditorWrapper.tsx diff --git a/components/HTTP.tsx b/src/components/HTTP.tsx similarity index 100% rename from components/HTTP.tsx rename to src/components/HTTP.tsx diff --git a/components/Header.tsx b/src/components/Header.tsx similarity index 100% rename from components/Header.tsx rename to src/components/Header.tsx diff --git a/components/InitMonacoEditor.tsx b/src/components/InitMonacoEditor.tsx similarity index 100% rename from components/InitMonacoEditor.tsx rename to src/components/InitMonacoEditor.tsx diff --git a/components/JSONEditor.tsx b/src/components/JSONEditor.tsx similarity index 96% rename from components/JSONEditor.tsx rename to src/components/JSONEditor.tsx index 862b112b..d4f1dd87 100644 --- a/components/JSONEditor.tsx +++ b/src/components/JSONEditor.tsx @@ -2,7 +2,6 @@ import { HttpMethodType, HttpType } from '@/workers/model'; import Editor from './Editor'; import HTTP from './HTTP'; import TabList from './TabList'; -import { useState } from 'react'; interface JSONEditorProps { json?: string; diff --git a/components/Logo.tsx b/src/components/Logo.tsx similarity index 100% rename from components/Logo.tsx rename to src/components/Logo.tsx diff --git a/components/Notification.tsx b/src/components/Notification.tsx similarity index 100% rename from components/Notification.tsx rename to src/components/Notification.tsx diff --git a/components/OptionsSelector.tsx b/src/components/OptionsSelector.tsx similarity index 100% rename from components/OptionsSelector.tsx rename to src/components/OptionsSelector.tsx diff --git a/components/OutputEditor.tsx b/src/components/OutputEditor.tsx similarity index 100% rename from components/OutputEditor.tsx rename to src/components/OutputEditor.tsx diff --git a/components/Playground.tsx b/src/components/Playground.tsx similarity index 100% rename from components/Playground.tsx rename to src/components/Playground.tsx diff --git a/components/QueryEditor.tsx b/src/components/QueryEditor.tsx similarity index 100% rename from components/QueryEditor.tsx rename to src/components/QueryEditor.tsx diff --git a/components/SectionTitle.tsx b/src/components/SectionTitle.tsx similarity index 86% rename from components/SectionTitle.tsx rename to src/components/SectionTitle.tsx index febc4f3c..ea5abb17 100644 --- a/components/SectionTitle.tsx +++ b/src/components/SectionTitle.tsx @@ -1,5 +1,4 @@ -import { Box, Typography, useTheme, IconButton } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; +import { Box, Typography, useTheme } from '@mui/material'; import { useDarkMode } from './ThemeProvider'; interface SectionTitleProps { diff --git a/components/TabList.tsx b/src/components/TabList.tsx similarity index 100% rename from components/TabList.tsx rename to src/components/TabList.tsx diff --git a/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx similarity index 96% rename from components/ThemeProvider.tsx rename to src/components/ThemeProvider.tsx index 881032db..f4ce6812 100644 --- a/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -1,5 +1,5 @@ import { createContext, useContext, useState, ReactNode, useEffect, FC } from 'react'; -import { ThemeProvider as MuiThemeProvider, createTheme, Theme } from '@mui/material/styles'; +import { ThemeProvider as MuiThemeProvider, createTheme } from '@mui/material/styles'; interface ThemeContextType { darkMode: boolean; diff --git a/lib/prisma.ts b/src/lib/prisma.ts similarity index 100% rename from lib/prisma.ts rename to src/lib/prisma.ts diff --git a/lib/utils.ts b/src/lib/utils.ts similarity index 100% rename from lib/utils.ts rename to src/lib/utils.ts diff --git a/workers/index.ts b/src/workers/index.ts similarity index 94% rename from workers/index.ts rename to src/workers/index.ts index 9c815f9c..dd18341d 100644 --- a/workers/index.ts +++ b/src/workers/index.ts @@ -1,7 +1,6 @@ import * as Comlink from "comlink"; import type { WorkerInterface } from "./process"; import { worker } from "./worker"; -import { z } from 'zod'; import { Snippet, SnippetType } from "./model"; export class JQWorker { @@ -22,7 +21,7 @@ export class JQWorker { private initializeWorker(): void { try { - this.#webWorker = new Worker(new URL("/workers/process.ts", import.meta.url), { type: "module" }); + this.#webWorker = new Worker(new URL("/src/workers/process.ts", import.meta.url), { type: "module" }); this.#worker = Comlink.wrap(this.#webWorker); } catch (error) { console.error("Failed to initialize Web Worker:", error); diff --git a/workers/model.ts b/src/workers/model.ts similarity index 100% rename from workers/model.ts rename to src/workers/model.ts diff --git a/workers/process.ts b/src/workers/process.ts similarity index 100% rename from workers/process.ts rename to src/workers/process.ts diff --git a/workers/worker.ts b/src/workers/worker.ts similarity index 100% rename from workers/worker.ts rename to src/workers/worker.ts diff --git a/tailwind.config.ts b/tailwind.config.ts index 86bead02..ea4f5638 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -3,9 +3,9 @@ import type { Config } from "tailwindcss"; const config: Config = { darkMode: 'class', content: [ - "./pages/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "./app/**/*.{js,ts,jsx,tsx,mdx}", + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", ], plugins: [], }; diff --git a/tsconfig.json b/tsconfig.json index d9ce21b7..93ac33e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,7 +24,7 @@ ], "paths": { "@/*": [ - "./*" + "./src/*" ] } },