chore(docs): Thorough tsdoc generation
Bump node v in docs workflow fix(docs): treat warnings as errors false fix(docs)Correct docs pattern in gitignore Add target branch to docs build fix(docs)Correct docs pattern in gitignore Add target branch to docs build Add target branch to docs build
This commit is contained in:
parent
71fac7ffa2
commit
57599b4542
40
.github/workflows/docs.yml
vendored
40
.github/workflows/docs.yml
vendored
@ -1,34 +1,40 @@
|
||||
name: Generate and Deploy Documentation
|
||||
name: Generate and Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master, develop, develop-qwrk ] # Trigger on push to these branches
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for proper versioning
|
||||
|
||||
fetch-depth: 0 # Fetch all history for proper branch detection
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: '22'
|
||||
cache: 'yarn'
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
|
||||
- name: Update TypeDoc config for current branch
|
||||
run: |
|
||||
CURRENT_BRANCH=${GITHUB_REF#refs/heads/}
|
||||
echo "Current branch: $CURRENT_BRANCH"
|
||||
# Update gitRevision in typedoc.json to use the current branch
|
||||
sed -i "s|\"gitRevision\": \"qrigin/[^\"]*\"|\"gitRevision\": \"qrigin/$CURRENT_BRANCH\"|" typedoc.mjs
|
||||
|
||||
- name: Generate documentation
|
||||
run: node scripts/generate-docs.js
|
||||
|
||||
run: yarn typedoc
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
branch: gh-pages # The branch the action should deploy to
|
||||
folder: docs # The folder the action should deploy
|
||||
clean: true # Automatically remove deleted files from the deploy branch
|
||||
folder: docs
|
||||
clean: true
|
||||
token: ${{ secrets.ACTIONS_ONLY }} # Use the ACTION_ONLY token
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,4 +14,4 @@ packages/frontend/dist/
|
||||
.vscode
|
||||
|
||||
# TypeDoc generated documentation
|
||||
/docs
|
||||
docs/
|
||||
|
12
package.json
12
package.json
@ -5,15 +5,18 @@
|
||||
"packages/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@microsoft/tsdoc": "^0.15.1",
|
||||
"chalk": "^4.1.2",
|
||||
"concurrently": "^8.2.0",
|
||||
"depcheck": "^1.4.2",
|
||||
"husky": "^8.0.3",
|
||||
"lerna": "^8.0.0",
|
||||
"lerna": "^8.2.0",
|
||||
"patch-package": "^8.0.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"tsdoc": "^0.0.4",
|
||||
"typedoc": "^0.27.9",
|
||||
"typedoc-plugin-markdown": "^4.4.2"
|
||||
"typedoc-plugin-markdown": "^4.4.2",
|
||||
"typedoc-unhoax-theme": "^0.4.6"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "husky install",
|
||||
@ -24,9 +27,8 @@
|
||||
"start:backend": "yarn workspace backend start",
|
||||
"start:frontend": "yarn workspace frontend dev",
|
||||
"kill:ports": "node scripts/kill-ports.js",
|
||||
"docs": "node scripts/generate-docs.js",
|
||||
"docs:clean": "node scripts/generate-docs.js --clean",
|
||||
"docs:watch": "typedoc --watch"
|
||||
"docs": "yarn typedoc",
|
||||
"docs:watch": "yarn typedoc --watch"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
# Backend server URL
|
||||
VITE_SERVER_URL='http://localhost:8000'
|
||||
|
||||
# Frontend app URL for OAuth callbacks
|
||||
NEXT_PUBLIC_APP_URL='http://localhost:3000'
|
||||
|
||||
# Session configuration
|
||||
SESSION_SECRET="complex_secret_at_least_32_chars_long"
|
||||
NEXT_PUBLIC_SESSION_SECRET="complex_secret_at_least_32_chars_long"
|
||||
|
||||
# Enable auto-authentication in development (bypasses wallet)
|
||||
NEXT_PUBLIC_DEV_AUTO_AUTH='true'
|
||||
|
||||
# Enable UI-only mode (bypasses all authentication)
|
||||
NEXT_PUBLIC_UI_ONLY_MODE='true'
|
||||
|
||||
# Same as GitHub client id set in backend config local.toml
|
||||
NEXT_PUBLIC_GITHUB_CLIENT_ID="Ov23li29Afs0s2Hw2VV1"
|
||||
NEXT_PUBLIC_GITHUB_CLIENT_SECRET="c16bd30c29169bbada5c4ded16d6639a79cf9789"
|
||||
|
||||
# GitHub repos in the format <org>/<repo-name>
|
||||
# Templates from https://github.com/orgs/deploy-laconic-templates/repositories
|
||||
NEXT_PUBLIC_GITHUB_PWA_TEMPLATE_REPO="deploy-laconic-templates/test-progressive-web-app"
|
||||
NEXT_PUBLIC_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO="deploy-laconic-templates/image-upload-pwa-example"
|
||||
NEXT_PUBLIC_GITHUB_NEXT_APP_TEMPLATE_REPO="deploy-laconic-templates/starter.nextjs-react-tailwind"
|
||||
|
||||
NEXT_PUBLIC_WALLET_CONNECT_ID="getaidw"
|
||||
|
||||
# Chain ID set in backend config
|
||||
NEXT_PUBLIC_LACONICD_CHAIN_ID="laconic_9000-1"
|
||||
|
||||
# Set to URL of locally running wallet https://git.vdb.to/cerc-io/laconic-wallet-web
|
||||
# Example: http://localhost:3001
|
||||
NEXT_PUBLIC_WALLET_IFRAME_URL="http://localhost:3001"
|
||||
|
||||
|
||||
# NOTES FOR WALLET CONNECT
|
||||
# In laconic-wallet-web (https://git.vdb.to/cerc-io/laconic-wallet-web)
|
||||
# Create a WalletConnect project in https://cloud.reown.com/app (site renamed to reown) and set ID from the project dashboard
|
||||
REACT_APP_WALLET_CONNECT_PROJECT_ID=
|
||||
|
||||
# Set to URL of locally running deploy frontend app
|
||||
# Example: http://localhost:3000
|
||||
REACT_APP_DEPLOY_APP_URL=
|
||||
|
||||
|
||||
# The following env vars are not required and corresponding functionality has been commented in code
|
||||
NEXT_PUBLIC_LIT_RELAY_API_KEY=
|
||||
NEXT_PUBLIC_BUGSNAG_API_KEY=
|
||||
NEXT_PUBLIC_PASSKEY_WALLET_RPID=
|
||||
NEXT_PUBLIC_TURNKEY_API_BASE_URL=
|
2
packages/frontend/.gitignore
vendored
2
packages/frontend/.gitignore
vendored
@ -16,8 +16,10 @@
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local.example
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
@ -11,7 +11,7 @@ const THEMES = { light: "", dark: ".dark" } as const
|
||||
/**
|
||||
* Configuration type for chart colors and themes.
|
||||
*
|
||||
* @typedef {Object} ChartConfig
|
||||
* @type {Object} Configuration object for chart elements
|
||||
* @property {Object} [key: string] - Configuration for each chart element
|
||||
* @property {React.ReactNode} [key.label] - Label for the element
|
||||
* @property {React.ComponentType} [key.icon] - Icon component for the element
|
||||
|
@ -4,12 +4,12 @@ import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import * as React from "react"
|
||||
import {
|
||||
Controller,
|
||||
ControllerProps,
|
||||
FieldPath,
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
Controller,
|
||||
ControllerProps,
|
||||
FieldPath,
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
} from "react-hook-form"
|
||||
|
||||
import { Label } from "@/components/ui/label"
|
||||
@ -69,8 +69,9 @@ const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
* @template TFieldValues - Type of form values
|
||||
* @template TName - Name of the field within the form values
|
||||
*
|
||||
* @param control - Form control from useForm()
|
||||
* @param render - Render prop for field implementation
|
||||
* @param {Object} props - The component props
|
||||
* @param {Object} props.control - Form control from useForm()
|
||||
* @param {Function} props.render - Render prop for field implementation
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
@ -227,8 +228,8 @@ const FormMessage = React.forwardRef<
|
||||
FormMessage.displayName = "FormMessage"
|
||||
|
||||
export {
|
||||
Form, FormControl,
|
||||
FormDescription, FormField, FormItem,
|
||||
FormLabel, FormMessage, useFormField
|
||||
Form, FormControl,
|
||||
FormDescription, FormField, FormItem,
|
||||
FormLabel, FormMessage, useFormField
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ const SelectGroup = SelectPrimitive.Group
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param placeholder - Text to show when no value is selected
|
||||
* @param {Object} props - The component props
|
||||
*/
|
||||
const SelectValue = SelectPrimitive.Value
|
||||
|
||||
@ -100,9 +100,8 @@ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param className - Additional CSS class for styling the scroll up button
|
||||
* @param props - Additional props are spread to the underlying Radix UI ScrollUpButton component
|
||||
* @param ref - Ref to attach to the scroll up button element for direct DOM access
|
||||
* @param {Object} props - The component props
|
||||
* @param {string} [props.className] - Additional CSS class for styling the scroll up button
|
||||
*/
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
@ -127,9 +126,8 @@ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param className - Additional CSS class for styling the scroll down button
|
||||
* @param props - Additional props are spread to the underlying Radix UI ScrollDownButton component
|
||||
* @param ref - Ref to attach to the scroll down button element for direct DOM access
|
||||
* @param {Object} props - The component props
|
||||
* @param {string} [props.className] - Additional CSS class for styling the scroll down button
|
||||
*/
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
@ -155,11 +153,10 @@ SelectScrollDownButton.displayName =
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param className - Additional CSS class for styling the content container
|
||||
* @param children - Elements to display within the content container
|
||||
* @param position - Positioning strategy for the dropdown content
|
||||
* @param props - Additional props are spread to the underlying Radix UI Content component
|
||||
* @param ref - Ref to attach to the content element for direct DOM access
|
||||
* @param {Object} props - The component props
|
||||
* @param {string} [props.className] - Additional CSS class for styling the content container
|
||||
* @param {React.ReactNode} props.children - Elements to display within the content container
|
||||
* @param {string} [props.position="popper"] - Positioning strategy for the dropdown content
|
||||
*/
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
@ -199,9 +196,8 @@ SelectContent.displayName = "SelectContent"
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param className - Additional CSS class for styling the label
|
||||
* @param props - Additional props are spread to the underlying Radix UI Label component
|
||||
* @param ref - Ref to attach to the label element for direct DOM access
|
||||
* @param {Object} props - The component props
|
||||
* @param {string} [props.className] - Additional CSS class for styling the label
|
||||
*/
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
@ -221,10 +217,9 @@ SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param className - Additional CSS class for styling the item
|
||||
* @param children - Elements to display within the item
|
||||
* @param props - Additional props are spread to the underlying Radix UI Item component
|
||||
* @param ref - Ref to attach to the item element for direct DOM access
|
||||
* @param {Object} props - The component props
|
||||
* @param {string} [props.className] - Additional CSS class for styling the item
|
||||
* @param {React.ReactNode} props.children - Elements to display within the item
|
||||
*/
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
@ -249,14 +244,13 @@ const SelectItem = React.forwardRef<
|
||||
SelectItem.displayName = "SelectItem"
|
||||
|
||||
/**
|
||||
* Separator for dividing Select items, providing visual separation.
|
||||
* Visual separator for grouping Select items within the dropdown.
|
||||
*
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param className - Additional CSS class for styling the separator
|
||||
* @param props - Additional props are spread to the underlying Radix UI Separator component
|
||||
* @param ref - Ref to attach to the separator element for direct DOM access
|
||||
* @param {Object} props - The component props
|
||||
* @param {string} [props.className] - Additional CSS class for styling the separator
|
||||
*/
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
@ -271,15 +265,15 @@ const SelectSeparator = React.forwardRef<
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,9 @@ TabsTrigger.displayName = "TabsTrigger"
|
||||
* @component
|
||||
* @public
|
||||
*
|
||||
* @param value - Must match corresponding trigger's value
|
||||
* @param {Object} props - Component props
|
||||
* @param {string} props.value - Must match corresponding trigger's value
|
||||
* @param {string} [props.className] - Additional custom classes
|
||||
*
|
||||
* @see {@link https://ui.shadcn.com/docs/components/tabs | Shadcn TabsContent Documentation}
|
||||
* @see {@link https://www.radix-ui.com/primitives/docs/components/tabs#content | Radix TabsContent Primitive}
|
||||
|
@ -10,8 +10,9 @@ const GQLClientContext = createContext({} as GQLClient);
|
||||
/**
|
||||
* @component GQLClientProvider
|
||||
* @description Provides the GQLClientContext to its children.
|
||||
* @param {ReactNode} children - The children to render.
|
||||
* @param {GQLClient} client - The GQLClient instance.
|
||||
* @param {Object} props - The component props
|
||||
* @param {ReactNode} props.children - The children to render.
|
||||
* @param {GQLClient} props.client - The GQLClient instance.
|
||||
*/
|
||||
export const GQLClientProvider = ({
|
||||
client,
|
||||
|
@ -42,8 +42,9 @@ const OctokitContext = createContext<ContextValue>({
|
||||
/**
|
||||
* @component OctokitProvider
|
||||
* @description Provides the OctokitContext to its children.
|
||||
* @param {ReactNode} children - The children to render.
|
||||
* @param {Function} [navigate] - Optional navigation function. If not provided, useNavigate will be used.
|
||||
* @param {Object} props - Component props
|
||||
* @param {ReactNode} props.children - The children to render.
|
||||
* @param {Function} [props.navigate] - Optional navigation function. If not provided, useNavigate will be used.
|
||||
*/
|
||||
export const OctokitProvider = ({
|
||||
children,
|
||||
|
@ -34,7 +34,8 @@ const WalletContext = createContext<WalletContextType | undefined>(undefined);
|
||||
/**
|
||||
* @component WalletProvider
|
||||
* @description Provides the WalletContext to its children.
|
||||
* @param {ReactNode} children - The children to render.
|
||||
* @param {Object} props - Component props
|
||||
* @param {ReactNode} props.children - The children to render.
|
||||
*/
|
||||
export const WalletProvider: React.FC<{ children: ReactNode }> = ({
|
||||
children,
|
||||
|
@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script to generate TypeDoc documentation for the monorepo
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
// Ensure docs directory exists
|
||||
const docsDir = path.join(__dirname, '..', 'docs');
|
||||
if (!fs.existsSync(docsDir)) {
|
||||
fs.mkdirSync(docsDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Run TypeDoc
|
||||
console.log('Generating documentation...');
|
||||
try {
|
||||
// Use npx to run TypeDoc
|
||||
execSync('npx typedoc', { stdio: 'inherit' });
|
||||
console.log('Documentation generated successfully in the docs/ directory');
|
||||
|
||||
console.log('\nCI/CD Approach for Documentation:');
|
||||
console.log('1. Add docs/ to your .gitignore file to prevent committing generated docs');
|
||||
console.log('2. Set up a GitHub Action workflow that:');
|
||||
console.log(' - Runs on push to main/develop/develop-qwrk branches');
|
||||
console.log(' - Generates documentation using this script');
|
||||
console.log(' - Deploys the generated docs to GitHub Pages');
|
||||
|
||||
console.log('\nExample GitHub Action workflow (.github/workflows/docs.yml):');
|
||||
console.log(`
|
||||
name: Generate and Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master, develop, develop-qwrk ] # Trigger on these branches
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Generate documentation
|
||||
run: node scripts/generate-docs.js
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: docs
|
||||
clean: true
|
||||
`);
|
||||
|
||||
console.log('\nThis way, your documentation will be:');
|
||||
console.log('- Generated automatically when you push to main, develop, or develop-qwrk');
|
||||
console.log('- Deployed to GitHub Pages without cluttering your main branch');
|
||||
console.log('- Available at https://[username].github.io/[repository]/');
|
||||
|
||||
// Clean up docs directory if the --clean flag is provided
|
||||
if (process.argv.includes('--clean')) {
|
||||
console.log('\nCleaning up docs directory...');
|
||||
rimraf.sync(docsDir);
|
||||
console.log('Docs directory removed. Documentation will only be available through GitHub Pages.');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating documentation:', error.message);
|
||||
process.exit(1);
|
||||
}
|
@ -1,303 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn, exec } = require('child_process');
|
||||
const chalk = require('chalk');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { promisify } = require('util');
|
||||
const os = require('os');
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
// Configuration
|
||||
const config = {
|
||||
rootDir: process.cwd(),
|
||||
backendDir: path.join(process.cwd(), 'packages/backend'),
|
||||
frontendDir: path.join(process.cwd(), 'packages/frontend'),
|
||||
ports: {
|
||||
backend: 8000,
|
||||
frontend: 3000,
|
||||
alternativeFrontend: 3001
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to check if a port is in use and kill the process if needed
|
||||
async function ensurePortAvailable(port) {
|
||||
try {
|
||||
console.log(chalk.blue(`🔍 Checking if port ${port} is in use...`));
|
||||
|
||||
// Different commands for different operating systems
|
||||
let command;
|
||||
if (process.platform === 'win32') {
|
||||
command = `netstat -ano | findstr :${port}`;
|
||||
} else {
|
||||
command = `lsof -i :${port} -t`;
|
||||
}
|
||||
|
||||
const { stdout } = await execAsync(command);
|
||||
|
||||
if (stdout.trim()) {
|
||||
console.log(chalk.yellow(`⚠️ Port ${port} is in use. Attempting to terminate processes...`));
|
||||
|
||||
let pids;
|
||||
if (process.platform === 'win32') {
|
||||
// Extract PIDs from Windows netstat output
|
||||
pids = stdout.split('\n')
|
||||
.filter(line => line.includes(`:${port}`))
|
||||
.map(line => line.trim().split(/\s+/).pop() || '')
|
||||
.filter(Boolean);
|
||||
} else {
|
||||
// Unix lsof already returns PIDs directly
|
||||
pids = stdout.trim().split('\n').filter(Boolean);
|
||||
}
|
||||
|
||||
// Kill each process
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
const killCommand = process.platform === 'win32'
|
||||
? `taskkill /F /PID ${pid}`
|
||||
: `kill -9 ${pid}`;
|
||||
|
||||
await execAsync(killCommand);
|
||||
console.log(chalk.green(`✅ Terminated process ${pid} using port ${port}`));
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Failed to kill process ${pid}: ${error}`));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(chalk.green(`✅ Port ${port} is available`));
|
||||
}
|
||||
} catch (error) {
|
||||
// If the command fails (e.g., lsof not found), assume port is available
|
||||
console.log(chalk.yellow(`⚠️ Could not check port ${port}: ${error}`));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to run commands with proper output handling
|
||||
function runCommand(command, args, cwd, label) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(chalk.blue(`🚀 Starting: ${label}...`));
|
||||
|
||||
const childProcess = spawn(command, args, {
|
||||
cwd,
|
||||
stdio: 'pipe',
|
||||
shell: true
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
childProcess.stdout && childProcess.stdout.on('data', (data) => {
|
||||
const output = data.toString();
|
||||
stdout += output;
|
||||
process.stdout.write(chalk.gray(`[${label}] `) + output);
|
||||
});
|
||||
|
||||
childProcess.stderr && childProcess.stderr.on('data', (data) => {
|
||||
const output = data.toString();
|
||||
stderr += output;
|
||||
process.stderr.write(chalk.yellow(`[${label}] `) + output);
|
||||
});
|
||||
|
||||
childProcess.on('error', (error) => {
|
||||
console.error(chalk.red(`❌ Error in ${label}: ${error.message}`));
|
||||
reject(error);
|
||||
});
|
||||
|
||||
childProcess.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(chalk.green(`✅ Completed: ${label}`));
|
||||
resolve();
|
||||
} else {
|
||||
console.error(chalk.red(`❌ Failed: ${label} (exit code: ${code})`));
|
||||
reject(new Error(`Command failed with exit code ${code}: ${stderr}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Check if iTerm2 is available (macOS only)
|
||||
async function isITerm2Available() {
|
||||
if (process.platform !== 'darwin') return false;
|
||||
|
||||
try {
|
||||
const { stdout } = await execAsync('osascript -e "exists application \\"iTerm2\\""');
|
||||
return stdout.trim() === 'true';
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open split panes in iTerm2
|
||||
async function openITerm2SplitPanes(backendCommand, frontendCommand) {
|
||||
const script = `
|
||||
tell application "iTerm2"
|
||||
create window with default profile
|
||||
tell current window
|
||||
tell current session
|
||||
set backendSession to (split horizontally with default profile)
|
||||
set frontendSession to (split vertically with default profile)
|
||||
|
||||
select
|
||||
write text "cd ${config.backendDir} && clear && echo '🚀 BACKEND SERVER' && ${backendCommand}"
|
||||
set name to "Backend Server"
|
||||
|
||||
tell backendSession
|
||||
select
|
||||
write text "cd ${config.frontendDir} && clear && echo '🚀 FRONTEND DEV SERVER' && ${frontendCommand}"
|
||||
set name to "Frontend Dev Server"
|
||||
end tell
|
||||
|
||||
tell frontendSession
|
||||
select
|
||||
write text "cd ${config.rootDir} && clear && echo '📊 MONOREPO ROOT'"
|
||||
set name to "Monorepo Root"
|
||||
end tell
|
||||
end tell
|
||||
end tell
|
||||
end tell
|
||||
`;
|
||||
|
||||
try {
|
||||
await execAsync(`osascript -e '${script}'`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(chalk.yellow(`⚠️ Failed to open iTerm2 with split panes: ${error.message}`));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open a new terminal based on platform
|
||||
async function openTerminals(backendCommand, frontendCommand) {
|
||||
// First check if we can use iTerm2 on macOS
|
||||
if (process.platform === 'darwin') {
|
||||
const iTerm2Available = await isITerm2Available();
|
||||
if (iTerm2Available) {
|
||||
console.log(chalk.blue('🖥️ Opening iTerm2 with split panes...'));
|
||||
const success = await openITerm2SplitPanes(backendCommand, frontendCommand);
|
||||
if (success) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to separate terminal windows
|
||||
const escapedBackendCommand = backendCommand.replace(/"/g, '\\"');
|
||||
const escapedFrontendCommand = frontendCommand.replace(/"/g, '\\"');
|
||||
const escapedBackendDir = config.backendDir.replace(/"/g, '\\"');
|
||||
const escapedFrontendDir = config.frontendDir.replace(/"/g, '\\"');
|
||||
|
||||
// Platform-specific terminal opening commands
|
||||
if (process.platform === 'darwin') {
|
||||
// macOS - Terminal.app
|
||||
const backendScript = `
|
||||
tell application "Terminal"
|
||||
do script "cd \\"${escapedBackendDir}\\" && clear && echo '🚀 BACKEND SERVER' && ${escapedBackendCommand}"
|
||||
set custom title of front window to "Backend Server"
|
||||
end tell
|
||||
`;
|
||||
|
||||
const frontendScript = `
|
||||
tell application "Terminal"
|
||||
do script "cd \\"${escapedFrontendDir}\\" && clear && echo '🚀 FRONTEND DEV SERVER' && ${escapedFrontendCommand}"
|
||||
set custom title of front window to "Frontend Dev Server"
|
||||
end tell
|
||||
`;
|
||||
|
||||
await execAsync(`osascript -e '${backendScript}'`);
|
||||
await execAsync(`osascript -e '${frontendScript}'`);
|
||||
} else if (process.platform === 'win32') {
|
||||
// Windows - try Windows Terminal first, fall back to cmd
|
||||
try {
|
||||
// Windows Terminal (supports multiple tabs)
|
||||
await execAsync(`wt -w 0 -d "${escapedBackendDir}" cmd /k "title Backend Server && ${escapedBackendCommand}" ; split-pane -d "${escapedFrontendDir}" cmd /k "title Frontend Dev Server && ${escapedFrontendCommand}"`);
|
||||
} catch (error) {
|
||||
// Fallback to regular cmd windows
|
||||
spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/K',
|
||||
`cd /d "${escapedBackendDir}" && title Backend Server && ${escapedBackendCommand}`]);
|
||||
spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/K',
|
||||
`cd /d "${escapedFrontendDir}" && title Frontend Dev Server && ${escapedFrontendCommand}`]);
|
||||
}
|
||||
} else {
|
||||
// Linux terminals with split support
|
||||
try {
|
||||
// Try Tilix (supports split screen)
|
||||
await execAsync(`tilix --window-style=disable-csd-hide-toolbar --maximize --session-file=<(echo '[{"command":"cd ${escapedBackendDir} && ${escapedBackendCommand}","title":"Backend Server"},{"command":"cd ${escapedFrontendDir} && ${escapedFrontendCommand}","title":"Frontend Dev Server"}]')`);
|
||||
} catch (error) {
|
||||
try {
|
||||
// Try Terminator (supports split screen)
|
||||
await execAsync(`terminator --maximize -e "bash -c 'cd ${escapedBackendDir} && ${escapedBackendCommand}'" -e "bash -c 'cd ${escapedFrontendDir} && ${escapedFrontendCommand}'"`);
|
||||
} catch (error) {
|
||||
// Fallback to separate gnome-terminal windows
|
||||
try {
|
||||
spawn('gnome-terminal', ['--', 'bash', '-c',
|
||||
`cd "${escapedBackendDir}" && echo -e "\\033]0;Backend Server\\007" && ${escapedBackendCommand}; exec bash`]);
|
||||
spawn('gnome-terminal', ['--', 'bash', '-c',
|
||||
`cd "${escapedFrontendDir}" && echo -e "\\033]0;Frontend Dev Server\\007" && ${escapedFrontendCommand}; exec bash`]);
|
||||
} catch (error) {
|
||||
// Last resort: try xterm
|
||||
spawn('xterm', ['-T', 'Backend Server', '-e',
|
||||
`cd "${escapedBackendDir}" && ${escapedBackendCommand}; exec bash`]);
|
||||
spawn('xterm', ['-T', 'Frontend Dev Server', '-e',
|
||||
`cd "${escapedFrontendDir}" && ${escapedFrontendCommand}; exec bash`]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if directory exists
|
||||
function checkDirectoryExists(dir, name) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.error(chalk.red(`❌ Error: ${name} directory not found at ${dir}`));
|
||||
throw new Error(`Directory not found: ${dir}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Main execution function
|
||||
async function runMonorepoWorkflow() {
|
||||
console.log(chalk.cyan.bold('📦 Monorepo Workflow Script'));
|
||||
console.log(chalk.cyan('---------------------------'));
|
||||
|
||||
try {
|
||||
// Validate directories
|
||||
checkDirectoryExists(config.rootDir, 'Root');
|
||||
checkDirectoryExists(config.backendDir, 'Backend');
|
||||
checkDirectoryExists(config.frontendDir, 'Frontend');
|
||||
|
||||
// Ensure all required ports are available
|
||||
await ensurePortAvailable(config.ports.backend);
|
||||
await ensurePortAvailable(config.ports.frontend);
|
||||
await ensurePortAvailable(config.ports.alternativeFrontend);
|
||||
|
||||
// Step 1: Install dependencies at root
|
||||
await runCommand('yarn', [], config.rootDir, 'Root dependency installation');
|
||||
|
||||
// Step 2: Build packages (ignoring frontend)
|
||||
await runCommand('yarn', ['build', '--ignore', 'frontend'], config.rootDir, 'Building packages');
|
||||
|
||||
// Step 3: Start services in split terminal
|
||||
console.log(chalk.blue('🚀 Opening terminal with services...'));
|
||||
await openTerminals('yarn start', 'yarn dev');
|
||||
|
||||
console.log(chalk.green.bold('✅ Development environment started!'));
|
||||
console.log(chalk.cyan('Backend running at:') + chalk.yellow(` http://localhost:${config.ports.backend}`));
|
||||
console.log(chalk.cyan('Frontend running at:') + chalk.yellow(` http://localhost:${config.ports.frontend}`));
|
||||
console.log(chalk.gray('Check the opened terminal windows for detailed logs.'));
|
||||
|
||||
// Exit successfully
|
||||
process.exit(0);
|
||||
|
||||
} catch (error) {
|
||||
console.error(chalk.red.bold('❌ Workflow failed:'));
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the workflow
|
||||
runMonorepoWorkflow().catch(error => {
|
||||
console.error(chalk.red.bold('❌ Unhandled error:'));
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const chalk = require('chalk');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const rootDir = process.cwd();
|
||||
const backendDir = path.join(rootDir, 'packages/backend');
|
||||
const frontendDir = path.join(rootDir, 'packages/frontend');
|
||||
|
||||
// Create shell scripts for each service
|
||||
const backendScript = path.join(rootDir, 'scripts', 'start-backend.sh');
|
||||
const frontendScript = path.join(rootDir, 'scripts', 'start-frontend.sh');
|
||||
|
||||
// Write shell scripts
|
||||
fs.writeFileSync(backendScript, `#!/bin/bash
|
||||
cd "${backendDir}"
|
||||
echo "🚀 Starting Backend Server..."
|
||||
yarn start
|
||||
`, { mode: 0o755 });
|
||||
|
||||
fs.writeFileSync(frontendScript, `#!/bin/bash
|
||||
cd "${frontendDir}"
|
||||
echo "🚀 Starting Frontend Dev Server..."
|
||||
yarn dev
|
||||
`, { mode: 0o755 });
|
||||
|
||||
console.log(chalk.cyan.bold('📦 Starting development environment'));
|
||||
|
||||
try {
|
||||
// Clean up ports first
|
||||
console.log(chalk.blue('🧹 Cleaning up ports...'));
|
||||
execSync('yarn kill:ports', { stdio: 'inherit' });
|
||||
|
||||
// Build packages
|
||||
console.log(chalk.blue('🔨 Building packages...'));
|
||||
execSync('yarn build --ignore frontend', { stdio: 'inherit' });
|
||||
|
||||
console.log(chalk.green('✅ Build complete'));
|
||||
console.log(chalk.blue('🚀 Starting services in separate terminals...'));
|
||||
|
||||
// Open new terminals with the shell scripts
|
||||
execSync(`open -a Terminal ${backendScript}`);
|
||||
execSync(`open -a Terminal ${frontendScript}`);
|
||||
|
||||
console.log(chalk.green('✅ Services started in separate terminal windows'));
|
||||
console.log(chalk.cyan('Backend running at:') + chalk.yellow(' http://localhost:8000'));
|
||||
console.log(chalk.cyan('Frontend running at:') + chalk.yellow(' http://localhost:3000'));
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`❌ Error: ${error.message}`));
|
||||
process.exit(1);
|
||||
}
|
49
start-app.js
49
start-app.js
@ -1,49 +0,0 @@
|
||||
const { exec } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
// Function to execute shell commands
|
||||
const runCommand = (command, cwd) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Running command: ${command} in ${cwd}`);
|
||||
const process = exec(command, { cwd }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`Command failed: ${command}`);
|
||||
console.error(stderr);
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
console.log(stdout);
|
||||
resolve(stdout);
|
||||
});
|
||||
|
||||
process.stdout.pipe(process.stdout);
|
||||
process.stderr.pipe(process.stderr);
|
||||
});
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const rootDir = __dirname;
|
||||
const backendDir = path.join(rootDir, 'packages', 'backend');
|
||||
const frontendDir = path.join(rootDir, 'packages', 'frontend');
|
||||
|
||||
console.log('Starting application setup...');
|
||||
|
||||
console.log('Running yarn install...');
|
||||
await runCommand('yarn', rootDir);
|
||||
|
||||
console.log('Running yarn build...');
|
||||
await runCommand('yarn build', rootDir);
|
||||
|
||||
console.log('Starting backend...');
|
||||
await runCommand('yarn start', backendDir);
|
||||
|
||||
console.log('Starting frontend in new terminal...');
|
||||
exec(`osascript -e 'tell application "Terminal" to do script "cd ${frontendDir} && yarn dev"'`);
|
||||
|
||||
console.log('Application started successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error during setup:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
@ -1,4 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"./packages/frontend/tsconfig.json",
|
||||
"./packages/backend/tsconfig.json",
|
||||
"./packages/gql-client/tsconfig.json"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
@ -9,15 +14,11 @@
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"packages/*/src/**/*.ts",
|
||||
"packages/*/src/**/*.tsx"
|
||||
],
|
||||
"include": ["packages/*/src/**/*.ts", "packages/*/src/**/*.tsx"],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"packages/*/node_modules",
|
||||
"packages/*/dist",
|
||||
"packages/frontend/src/components",
|
||||
"packages/frontend/src/stories"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
18
tsdoc.json
Normal file
18
tsdoc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/en-us/json-schemas/tsdoc/v0/tsdoc.schema.json",
|
||||
"extends": ["typedoc/tsdoc.json"],
|
||||
"tagDefinitions": [
|
||||
{
|
||||
"tagName": "@description",
|
||||
"syntaxKind": "block"
|
||||
},
|
||||
{
|
||||
"tagName": "@component",
|
||||
"syntaxKind": "block"
|
||||
},
|
||||
{
|
||||
"tagName": "@function",
|
||||
"syntaxKind": "block"
|
||||
}
|
||||
]
|
||||
}
|
25
typedoc.json
25
typedoc.json
@ -1,25 +0,0 @@
|
||||
{
|
||||
"$schema": "https://typedoc.org/schema.json",
|
||||
"entryPoints": ["packages/*/src"],
|
||||
"entryPointStrategy": "expand",
|
||||
"out": "docs",
|
||||
"plugin": ["typedoc-plugin-markdown"],
|
||||
"readme": "docs-readme.md",
|
||||
"name": "Snowball Tools API Documentation",
|
||||
"includeVersion": true,
|
||||
"excludePrivate": true,
|
||||
"excludeProtected": false,
|
||||
"excludeExternals": true,
|
||||
"excludeInternal": true,
|
||||
"hideGenerator": true,
|
||||
"disableSources": false,
|
||||
"categorizeByGroup": true,
|
||||
"categoryOrder": ["Core", "API", "Utilities", "*"],
|
||||
"sort": ["source-order"],
|
||||
"validation": {
|
||||
"invalidLink": true,
|
||||
"notDocumented": true
|
||||
},
|
||||
"skipErrorChecking": true,
|
||||
"tsconfig": "./tsconfig.doc.json"
|
||||
}
|
44
typedoc.mjs
Normal file
44
typedoc.mjs
Normal file
@ -0,0 +1,44 @@
|
||||
import { OptionDefaults } from 'typedoc';
|
||||
|
||||
const config = {
|
||||
$schema: 'https://typedoc.org/schema.json',
|
||||
entryPoints: [
|
||||
'./packages/**/src/**/*.{ts,tsx}',
|
||||
// './packages/frontend/src/**/*.{ts,tsx}',
|
||||
// './packages/backend/src/**/*.{ts,tsx}',
|
||||
// './packages/gql-client/src/**/*.{ts,tsx}',
|
||||
],
|
||||
entryPointStrategy: 'expand',
|
||||
out: './docs',
|
||||
plugin: ['typedoc-plugin-markdown'],
|
||||
// plugin: ['typedoc-unhoax-theme'],
|
||||
readme: './README.md',
|
||||
mergeReadme: true,
|
||||
name: 'Laconic Deploy FE API Documentation',
|
||||
includeVersion: true,
|
||||
githubPages: true,
|
||||
searchInComments: true,
|
||||
enumMembersFormat: 'table',
|
||||
parametersFormat: 'table',
|
||||
propertiesFormat: 'table',
|
||||
typeDeclarationFormat: 'table',
|
||||
indexFormat: 'table',
|
||||
useCodeBlocks: true,
|
||||
expandObjects: true,
|
||||
textContentMappings: {
|
||||
'breadcrumbs.home': 'Home',
|
||||
},
|
||||
validation: {
|
||||
notDocumented: false,
|
||||
invalidLink: false,
|
||||
notExported: true,
|
||||
},
|
||||
skipErrorChecking: true,
|
||||
tsconfig: './tsconfig.doc.json',
|
||||
gitRevision: 'qrigin/develop-qwrk',
|
||||
gitRemote: 'qrigin',
|
||||
|
||||
blockTags: [...OptionDefaults.blockTags, '@component', '@description'],
|
||||
};
|
||||
|
||||
export default config;
|
Loading…
Reference in New Issue
Block a user