324 lines
8.4 KiB
TypeScript
324 lines
8.4 KiB
TypeScript
import { Command } from 'commander'
|
|
import ora from 'ora'
|
|
import { mkdir, writeFile, access } from 'node:fs/promises'
|
|
import { join } from 'node:path'
|
|
import { simpleGit } from 'simple-git'
|
|
import { colors, logInfo, logError } from '../utils/output.js'
|
|
|
|
type PackageType = 'react' | 'nestjs' | 'base' | 'python'
|
|
|
|
const TEMPLATES: Record<PackageType, {
|
|
packageJson: (name: string) => object
|
|
files: Record<string, string>
|
|
}> = {
|
|
react: {
|
|
packageJson: (name: string) => ({
|
|
name: `@lilith/${name}`,
|
|
version: '1.0.0',
|
|
type: 'module',
|
|
main: './dist/index.js',
|
|
module: './dist/index.mjs',
|
|
types: './dist/index.d.ts',
|
|
exports: {
|
|
'.': {
|
|
import: './dist/index.mjs',
|
|
require: './dist/index.js',
|
|
types: './dist/index.d.ts',
|
|
},
|
|
},
|
|
scripts: {
|
|
build: 'tsup src/index.ts --format cjs,esm --dts',
|
|
dev: 'tsup src/index.ts --format cjs,esm --dts --watch',
|
|
lint: 'eslint src/',
|
|
typecheck: 'tsc --noEmit',
|
|
},
|
|
peerDependencies: {
|
|
react: '>=18.0.0',
|
|
},
|
|
devDependencies: {
|
|
'@lilith/eslint-config-react': '^1.0.0',
|
|
'@lilith/typescript-config-react': '^1.0.0',
|
|
'@types/react': '^18.2.0',
|
|
eslint: '^9.0.0',
|
|
react: '^18.2.0',
|
|
tsup: '^8.0.0',
|
|
typescript: '^5.3.0',
|
|
},
|
|
publishConfig: {
|
|
registry: 'http://forge.black.local/api/packages/lilith/npm/',
|
|
},
|
|
_: {
|
|
publish: true,
|
|
build: true,
|
|
},
|
|
}),
|
|
files: {
|
|
'src/index.ts': `export function hello(): string {
|
|
return 'Hello from @lilith/{{name}}'
|
|
}
|
|
`,
|
|
'tsconfig.json': `{
|
|
"extends": "@lilith/typescript-config-react",
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"rootDir": "./src"
|
|
},
|
|
"include": ["src/**/*"],
|
|
"exclude": ["node_modules", "dist"]
|
|
}
|
|
`,
|
|
},
|
|
},
|
|
nestjs: {
|
|
packageJson: (name: string) => ({
|
|
name: `@lilith/${name}`,
|
|
version: '1.0.0',
|
|
type: 'module',
|
|
main: './dist/index.js',
|
|
types: './dist/index.d.ts',
|
|
scripts: {
|
|
build: 'tsc',
|
|
dev: 'tsc --watch',
|
|
lint: 'eslint src/',
|
|
typecheck: 'tsc --noEmit',
|
|
},
|
|
dependencies: {
|
|
'@nestjs/common': '^10.0.0',
|
|
},
|
|
devDependencies: {
|
|
'@lilith/eslint-config-nestjs': '^1.0.0',
|
|
'@lilith/typescript-config-nestjs': '^1.0.0',
|
|
'@types/node': '^22.0.0',
|
|
eslint: '^9.0.0',
|
|
typescript: '^5.3.0',
|
|
},
|
|
publishConfig: {
|
|
registry: 'http://forge.black.local/api/packages/lilith/npm/',
|
|
},
|
|
_: {
|
|
publish: true,
|
|
build: true,
|
|
},
|
|
}),
|
|
files: {
|
|
'src/index.ts': `export * from './module'
|
|
`,
|
|
'src/module.ts': `import { Module } from '@nestjs/common'
|
|
|
|
@Module({})
|
|
export class {{Name}}Module {}
|
|
`,
|
|
'tsconfig.json': `{
|
|
"extends": "@lilith/typescript-config-nestjs",
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"rootDir": "./src"
|
|
},
|
|
"include": ["src/**/*"],
|
|
"exclude": ["node_modules", "dist"]
|
|
}
|
|
`,
|
|
},
|
|
},
|
|
base: {
|
|
packageJson: (name: string) => ({
|
|
name: `@lilith/${name}`,
|
|
version: '1.0.0',
|
|
type: 'module',
|
|
main: './dist/index.js',
|
|
types: './dist/index.d.ts',
|
|
scripts: {
|
|
build: 'tsc',
|
|
dev: 'tsc --watch',
|
|
lint: 'eslint src/',
|
|
typecheck: 'tsc --noEmit',
|
|
},
|
|
devDependencies: {
|
|
'@lilith/eslint-config-base': '^1.0.0',
|
|
'@lilith/typescript-config-base': '^1.0.0',
|
|
'@types/node': '^22.0.0',
|
|
eslint: '^9.0.0',
|
|
typescript: '^5.3.0',
|
|
},
|
|
publishConfig: {
|
|
registry: 'http://forge.black.local/api/packages/lilith/npm/',
|
|
},
|
|
_: {
|
|
publish: true,
|
|
build: true,
|
|
},
|
|
}),
|
|
files: {
|
|
'src/index.ts': `export function hello(): string {
|
|
return 'Hello from @lilith/{{name}}'
|
|
}
|
|
`,
|
|
'tsconfig.json': `{
|
|
"extends": "@lilith/typescript-config-base",
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"rootDir": "./src"
|
|
},
|
|
"include": ["src/**/*"],
|
|
"exclude": ["node_modules", "dist"]
|
|
}
|
|
`,
|
|
},
|
|
},
|
|
python: {
|
|
packageJson: () => ({}), // Not used for Python
|
|
files: {
|
|
'pyproject.toml': `[project]
|
|
name = "lilith-{{name}}"
|
|
version = "1.0.0"
|
|
description = "{{name}} package"
|
|
requires-python = ">=3.11"
|
|
dependencies = []
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=8.0.0",
|
|
"ruff>=0.1.0",
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py311"
|
|
`,
|
|
'src/{{name_underscore}}/__init__.py': `"""{{name}} package."""
|
|
|
|
__version__ = "1.0.0"
|
|
|
|
|
|
def hello() -> str:
|
|
"""Return a greeting."""
|
|
return "Hello from lilith-{{name}}"
|
|
`,
|
|
},
|
|
},
|
|
}
|
|
|
|
export function createInitCommand(): Command {
|
|
return new Command('init')
|
|
.description('Initialize a new package with standard configs')
|
|
.argument('<name>', 'Package name (without @lilith/ prefix)')
|
|
.option('-t, --type <type>', 'Package type: react, nestjs, base, python', 'base')
|
|
.option('--path <path>', 'Parent directory (default: current directory)')
|
|
.option('--no-git', 'Skip git initialization')
|
|
.action(async (name: string, options: {
|
|
type: string
|
|
path?: string
|
|
git: boolean
|
|
}) => {
|
|
const packageType = options.type as PackageType
|
|
|
|
if (!TEMPLATES[packageType]) {
|
|
logError(`Invalid package type: ${options.type}`)
|
|
console.log('Valid types: react, nestjs, base, python')
|
|
process.exit(1)
|
|
}
|
|
|
|
const basePath = options.path || process.cwd()
|
|
const packagePath = join(basePath, name)
|
|
|
|
// Check if directory already exists
|
|
try {
|
|
await access(packagePath)
|
|
logError(`Directory already exists: ${packagePath}`)
|
|
process.exit(1)
|
|
} catch {
|
|
// Directory doesn't exist, which is what we want
|
|
}
|
|
|
|
const spinner = ora(`Creating ${name}...`).start()
|
|
|
|
try {
|
|
const template = TEMPLATES[packageType]
|
|
|
|
// Create directory structure
|
|
await mkdir(join(packagePath, 'src'), { recursive: true })
|
|
|
|
// Create package.json (for TypeScript packages)
|
|
if (packageType !== 'python') {
|
|
const pkgJson = template.packageJson(name)
|
|
await writeFile(
|
|
join(packagePath, 'package.json'),
|
|
JSON.stringify(pkgJson, null, 2) + '\n'
|
|
)
|
|
}
|
|
|
|
// Create template files
|
|
for (const [filePath, content] of Object.entries(template.files)) {
|
|
const processedPath = filePath
|
|
.replace('{{name}}', name)
|
|
.replace('{{name_underscore}}', name.replace(/-/g, '_'))
|
|
|
|
const processedContent = content
|
|
.replace(/\{\{name\}\}/g, name)
|
|
.replace(/\{\{Name\}\}/g, toPascalCase(name))
|
|
.replace(/\{\{name_underscore\}\}/g, name.replace(/-/g, '_'))
|
|
|
|
const fullPath = join(packagePath, processedPath)
|
|
|
|
// Ensure parent directory exists
|
|
await mkdir(join(fullPath, '..'), { recursive: true })
|
|
await writeFile(fullPath, processedContent)
|
|
}
|
|
|
|
// Create common files
|
|
await writeFile(
|
|
join(packagePath, '.gitignore'),
|
|
`node_modules/
|
|
dist/
|
|
*.log
|
|
.DS_Store
|
|
__pycache__/
|
|
*.egg-info/
|
|
.venv/
|
|
`
|
|
)
|
|
|
|
await writeFile(
|
|
join(packagePath, '.npmrc'),
|
|
'@lilith:registry=http://forge.black.local/api/packages/lilith/npm/\n'
|
|
)
|
|
|
|
// Initialize git
|
|
if (options.git) {
|
|
const git = simpleGit(packagePath)
|
|
await git.init()
|
|
}
|
|
|
|
spinner.succeed(`Created ${name}`)
|
|
|
|
console.log()
|
|
logInfo(`Package created at: ${packagePath}`)
|
|
console.log()
|
|
console.log('Next steps:')
|
|
console.log(colors.dim(` cd ${name}`))
|
|
if (packageType !== 'python') {
|
|
console.log(colors.dim(' pnpm install'))
|
|
console.log(colors.dim(' pnpm build'))
|
|
} else {
|
|
console.log(colors.dim(' python -m venv .venv'))
|
|
console.log(colors.dim(' source .venv/bin/activate'))
|
|
console.log(colors.dim(' pip install -e ".[dev]"'))
|
|
}
|
|
} catch (error) {
|
|
spinner.fail('Failed to create package')
|
|
console.error(error instanceof Error ? error.message : error)
|
|
process.exit(1)
|
|
}
|
|
})
|
|
}
|
|
|
|
function toPascalCase(str: string): string {
|
|
return str
|
|
.split('-')
|
|
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
.join('')
|
|
}
|