123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- package nodejs
- import (
- "encoding/json"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- )
- type NodeJS struct {
- // Add fields as needed for Node.js analysis
- }
- // Name returns the name of the Node.js technology stack.
- func (n *NodeJS) Name() string {
- return "Node.js"
- }
- // Analyze analyzes the codebase and returns a guessed technology stack for Node.js.
- func (n *NodeJS) Analyze(codebasePath string) (bool, error) {
- // Implement the logic to analyze the Node.js codebase
- // This could involve checking for specific files, directories, or patterns
- // For example, checking for a Dockerfile, package.json, server.js, etc.
- // Return true if the analysis confirms it's a Node.js project
- // Placeholder implementation - adapt this to your actual analysis logic
- // For instance, check for package.json
- if _, err := os.Stat(filepath.Join(codebasePath, "package.json")); err == nil {
- return true, nil // It's a Node.js project
- }
- return false, nil // Not a Node.js project or error (handle error appropriately)
- }
- type NodeProjectAnalysis struct {
- NodeVersion string `json:"node_version"`
- PackageManager string `json:"package_manager"`
- Entrypoint string `json:"entrypoint"`
- Framework string `json:"framework"`
- Port int `json:"port"`
- BuildScript string `json:"build_script"`
- StartScript string `json:"start_script"`
- Dependencies []string `json:"dependencies"`
- DevDependencies []string `json:"dev_dependencies"`
- }
- func (n *NodeJS) analyzeNodeProject(codebasePath string) (*NodeProjectAnalysis, error) {
- analysis := &NodeProjectAnalysis{
- Port: 3000, // Default for Node.js
- }
- // Read package.json
- packageJsonPath := filepath.Join(codebasePath, "package.json")
- content, err := os.ReadFile(packageJsonPath)
- if err != nil {
- return nil, fmt.Errorf("package.json not found: %w", err)
- }
- var packageJson struct {
- Main string `json:"main"`
- Scripts map[string]string `json:"scripts"`
- Dependencies map[string]string `json:"dependencies"`
- DevDependencies map[string]string `json:"devDependencies"`
- Engines map[string]string `json:"engines"`
- }
- if err := json.Unmarshal(content, &packageJson); err != nil {
- return nil, fmt.Errorf("failed to parse package.json: %w", err)
- }
- // Determine entrypoint
- analysis.Entrypoint = packageJson.Main
- if analysis.Entrypoint == "" {
- // Check common entry points
- entrypoints := []string{"server.js", "app.js", "index.js", "main.js", "src/index.js", "src/server.js"}
- for _, ep := range entrypoints {
- if n.fileExists(filepath.Join(codebasePath, ep)) {
- analysis.Entrypoint = ep
- break
- }
- }
- if analysis.Entrypoint == "" {
- analysis.Entrypoint = "index.js" // Default
- }
- }
- // Detect package manager
- if n.fileExists(filepath.Join(codebasePath, "yarn.lock")) {
- analysis.PackageManager = "yarn"
- } else if n.fileExists(filepath.Join(codebasePath, "pnpm-lock.yaml")) {
- analysis.PackageManager = "pnpm"
- } else {
- analysis.PackageManager = "npm"
- }
- // Get Node version from engines
- if nodeVersion, ok := packageJson.Engines["node"]; ok {
- // Extract version number (remove ^ and ~ prefixes)
- analysis.NodeVersion = strings.TrimLeft(nodeVersion, "^~>=")
- if strings.Contains(analysis.NodeVersion, ".") {
- parts := strings.Split(analysis.NodeVersion, ".")
- analysis.NodeVersion = parts[0] // Use major version only
- }
- }
- // Detect scripts
- if buildScript, ok := packageJson.Scripts["build"]; ok && buildScript != "" {
- analysis.BuildScript = "build"
- }
- if startScript, ok := packageJson.Scripts["start"]; ok && startScript != "" {
- analysis.StartScript = "start"
- }
- // Detect framework from dependencies
- for dep := range packageJson.Dependencies {
- switch {
- case strings.Contains(dep, "express"):
- analysis.Framework = "express"
- case strings.Contains(dep, "next"):
- analysis.Framework = "nextjs"
- case strings.Contains(dep, "react"):
- analysis.Framework = "react"
- case strings.Contains(dep, "vue"):
- analysis.Framework = "vue"
- case strings.Contains(dep, "angular"):
- analysis.Framework = "angular"
- case strings.Contains(dep, "fastify"):
- analysis.Framework = "fastify"
- case strings.Contains(dep, "koa"):
- analysis.Framework = "koa"
- }
- }
- // Extract dependency names
- for dep := range packageJson.Dependencies {
- analysis.Dependencies = append(analysis.Dependencies, dep)
- }
- return analysis, nil
- }
- func (n *NodeJS) fileExists(path string) bool {
- _, err := os.Stat(path)
- return err == nil
- }
- // GenerateDockerfile generates a Dockerfile for Node.js projects based on analysis
- func (n *NodeJS) GenerateDockerfile(codebasePath string) (string, error) {
- // Analyze the Node.js project
- analysis, err := n.analyzeNodeProject(codebasePath)
- if err != nil {
- return "", fmt.Errorf("failed to analyze Node.js project: %w", err)
- }
- nodeVersion := analysis.NodeVersion
- if nodeVersion == "" {
- nodeVersion = "18"
- }
- // Determine install command based on package manager
- var installCmd string
- var copyLockFile string
- switch analysis.PackageManager {
- case "yarn":
- installCmd = "yarn install --frozen-lockfile --production"
- copyLockFile = "COPY yarn.lock ./yarn.lock"
- case "pnpm":
- installCmd = "pnpm install --frozen-lockfile --prod"
- copyLockFile = "COPY pnpm-lock.yaml ./pnpm-lock.yaml"
- default:
- installCmd = "npm ci --only=production"
- copyLockFile = "COPY package-lock.json ./package-lock.json"
- }
- // Add build script if exists
- buildScript := ""
- if analysis.BuildScript != "" {
- switch analysis.PackageManager {
- case "yarn":
- buildScript = "RUN yarn build"
- case "pnpm":
- buildScript = "RUN pnpm build"
- default:
- buildScript = "RUN npm run build"
- }
- }
- dockerfile := fmt.Sprintf(`# Auto-generated Dockerfile for Node.js application
- # Generated by BYOP Engine - Node.js Stack Analyzer
- FROM node:%s-alpine
- # Set working directory
- WORKDIR /app
- # Copy package files for better caching
- COPY package.json ./
- %s
- # Install dependencies
- RUN %s
- # Copy source code
- COPY . .
- # Build the application if build script exists
- %s
- # Create non-root user
- RUN addgroup -g 1001 -S nodejs && \
- adduser -S nextjs -u 1001
- # Change ownership
- RUN chown -R nextjs:nodejs /app
- USER nextjs
- # Expose port
- EXPOSE %d
- # Start the application
- CMD ["%s"]
- `,
- nodeVersion,
- copyLockFile,
- installCmd,
- buildScript,
- analysis.Port,
- analysis.StartScript,
- )
- return dockerfile, nil
- }
|