Skip to content

Commit

Permalink
Merge pull request #79 from liam-hq/setup-erd-generator-cli
Browse files Browse the repository at this point in the history
new cli
  • Loading branch information
hoshinotsuyoshi authored Nov 13, 2024
2 parents 9e0ac89 + d10fbcc commit e41f2a1
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 36 deletions.
6 changes: 5 additions & 1 deletion frontend/packages/cli/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ dist-ssr
*.sw?

# typed-css-modules
*.css.d.ts
*.css.d.ts


dist-cli
public/schema.json
61 changes: 61 additions & 0 deletions frontend/packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# @liam/cli

Command-line tool designed to generate a web application that displays ER diagrams.

```bash
$ liam erd build --input {your .sql}
# Outputs the web application to the ./public and ./dist directories

$ liam erd preview
# Launches the web application for preview
```

## Building and Installing the Standalone CLI for Development

To build the CLI for development purposes, run:

```bash
pnpm run build:cli
# The executable will be output to dist-cli/bin/cli.js.
```

After building, you can invoke it locally with:

```bash
node ./dist-cli/bin/cli.js erd build --input ./fixtures/input.sql
```

To make it globally accessible as `liam`, use:

```bash
pnpm link --global
```

## Explanation of npm Scripts for Development

### Commands

1. **Build**
```bash
pnpm run build
```
- Internally, `./fixtures/input.sql` is passed as the `build --input` argument.
- Runs Vite's build process.

2. **Dev**
```bash
pnpm run dev
```
- Internally, `./fixtures/input.sql` is passed as the `dev --input` argument.
- Starts the Vite development server.

3. **Preview**
```bash
pnpm run preview
```
- Previews the production build using Vite's built-in preview functionality.

## File Structure

- **bin/cli.ts**: The main CLI script.
- **fixtures/input.sql**: A sample `.sql` file for testing purposes.
- **src/**: The web application that displays ER diagrams.
107 changes: 107 additions & 0 deletions frontend/packages/cli/bin/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { Command } from 'commander'
import { build, createServer, preview } from 'vite'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const root = path.resolve(__dirname, '../..')
const publicDir = path.join(process.cwd(), 'public')
const outDir = path.join(process.cwd(), 'dist')

function runPreprocess(inputPath: string | null) {
if (inputPath && !fs.existsSync(inputPath)) {
throw new Error('Invalid input path. Please provide a valid .sql file.')
}

const sqlContent = inputPath ? fs.readFileSync(inputPath, 'utf8') : '{}'
const filePath = path.join(publicDir, 'schema.json')

if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true })
}

try {
const jsonContent = JSON.stringify({ sql: sqlContent }, null, 2)
fs.writeFileSync(filePath, jsonContent, 'utf8')
return filePath
} catch (error) {
console.error(
`Error during preprocessing: ${error instanceof Error ? error.message : 'Unknown error'}`,
)
return null
}
}

const program = new Command()

program.name('liam').description('CLI tool for Liam').version('0.0.0')

const erdCommand = new Command('erd').description('ERD commands')

program.addCommand(erdCommand)

erdCommand
.command('build')
.description('Run Vite build')
.option('--input <path>', 'Path to the .sql file')
.action(async (options) => {
try {
const inputPath = options.input
runPreprocess(inputPath)
await build({
publicDir,
root,
build: {
outDir,
emptyOutDir: false,
},
})
} catch (error) {
console.error('Build failed:', error)
process.exit(1)
}
})

erdCommand
.command('dev')
.description('Run Vite dev server')
.option('--input <path>', 'Path to the .sql file')
.action(async (options) => {
try {
const inputPath = options.input
runPreprocess(inputPath)
const server = await createServer({ publicDir, root })
const address = server.httpServer?.address()
const port = typeof address === 'object' && address ? address.port : 5173
console.info(`Dev server is running at http://localhost:${port}`)
await server.listen()
} catch (error) {
console.error('Failed to start dev server:', error)
process.exit(1)
}
})

erdCommand
.command('preview')
.description('Preview the production build')
.action(async () => {
try {
const previewServer = await preview({
publicDir,
root,
build: {
outDir,
emptyOutDir: false,
},
})
const address = previewServer.httpServer?.address()
const port = typeof address === 'object' && address ? address.port : 4173
console.info(`Preview server is running at http://localhost:${port}`)
} catch (error) {
console.error('Failed to start preview server:', error)
process.exit(1)
}
})
program.parse(process.argv)
5 changes: 5 additions & 0 deletions frontend/packages/cli/fixtures/input.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE products (
brand VARCHAR(255),
model VARCHAR(255),
year INT
);
18 changes: 12 additions & 6 deletions frontend/packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
"private": true,
"version": "0.0.0",
"type": "module",
"bin": {
"liam": "./dist-cli/bin/cli.js"
},
"scripts": {
"dev": "conc -c auto pnpm:dev:*",
"dev": "pnpm run build:cli && node ./dist-cli/bin/cli.js erd dev --input ./fixtures/input.sql",
"build": "pnpm run build:cli && node ./dist-cli/bin/cli.js erd build --input ./fixtures/input.sql",
"preview": "pnpm run build:cli && node ./dist-cli/bin/cli.js erd preview",
"build:cli": "tsc -p tsconfig.node.json",
"dev:app": "vite",
"dev:css": "tcm src --watch",
"gen": "conc -c auto pnpm:gen:*",
"gen:css": "tcm src",
"build": "tsc -b && vite build",
"preview": "vite preview",
"lint": "conc -c auto pnpm:lint:*",
"lint:biome": "biome check .",
"lint:tsc": "tsc --noEmit",
Expand All @@ -19,17 +23,19 @@
},
"dependencies": {
"@liam/erd-core": "workspace:*",
"commander": "^12.1.0",
"destyle.css": "^4.0.1",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"vite": "^5.4.10"
},
"devDependencies": {
"@biomejs/biome": "1.9.3",
"@types/node": "^22.9.0",
"@types/react": "^18",
"@types/react-dom": "^18",
"@vitejs/plugin-react": "^4.3.3",
"typed-css-modules": "^0.9.1",
"typescript": "^5",
"vite": "^5.4.10"
"typescript": "^5"
}
}
Empty file.
32 changes: 31 additions & 1 deletion frontend/packages/cli/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
import { ERDRenderer } from '@liam/erd-core'
import { useEffect, useState } from 'react'

function App() {
return <ERDRenderer />
const [fileContent, setFileContent] = useState<string | null>(null)

useEffect(() => {
async function loadSchemaContent() {
const response = await fetch('/schema.json')
const data = (await response.json()) || 'No file content available'
setFileContent(JSON.stringify(data, null, 2))
}

loadSchemaContent()
}, [])

return (
<>
<ERDRenderer />

{/* TODO: Remove this */}
{/* Display the file content in a pre tag for demo purposes */}
<pre
style={{
backgroundColor: '#f5f5f5',
padding: '10px',
borderRadius: '4px',
whiteSpace: 'pre-wrap',
}}
>
{fileContent || 'Loading...'}
</pre>
</>
)
}

export default App
2 changes: 2 additions & 0 deletions frontend/packages/cli/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"incremental": true,
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
Expand All @@ -13,6 +14,7 @@
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"outDir": "dist",
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
Expand Down
7 changes: 4 additions & 3 deletions frontend/packages/cli/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"incremental": true,
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"noEmit": false,
"strict": true,
"outDir": "dist-cli",
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
"include": ["vite.config.ts", "bin"]
}
Loading

0 comments on commit e41f2a1

Please sign in to comment.