๐Ÿ“ฆ langgenius / dify

๐Ÿ“„ optimize-standalone.js ยท 164 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164/**
 * Script to optimize Next.js standalone output for production
 * Removes unnecessary files like jest-worker that are bundled with Next.js
 */

import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

console.log('๐Ÿ”ง Optimizing standalone output...')

const standaloneDir = path.join(__dirname, '..', '.next', 'standalone')

// Check if standalone directory exists
if (!fs.existsSync(standaloneDir)) {
  console.error('โŒ Standalone directory not found. Please run "next build" first.')
  process.exit(1)
}

// List of paths to remove (relative to standalone directory)
const pathsToRemove = [
  // Remove jest-worker from Next.js compiled dependencies
  'node_modules/.pnpm/next@*/node_modules/next/dist/compiled/jest-worker',
  // Remove jest-worker symlinks from terser-webpack-plugin
  'node_modules/.pnpm/terser-webpack-plugin@*/node_modules/jest-worker',
  // Remove actual jest-worker packages (directories only, not symlinks)
  'node_modules/.pnpm/jest-worker@*',
]

// Function to safely remove a path
function removePath(basePath, relativePath) {
  const fullPath = path.join(basePath, relativePath)

  // Handle wildcard patterns
  if (relativePath.includes('*')) {
    const parts = relativePath.split('/')
    let currentPath = basePath

    for (let i = 0; i < parts.length; i++) {
      const part = parts[i]
      if (part.includes('*')) {
        // Find matching directories
        if (fs.existsSync(currentPath)) {
          const entries = fs.readdirSync(currentPath)

          // replace '*' with '.*'
          const regexPattern = part.replace(/\*/g, '.*')

          const regex = new RegExp(`^${regexPattern}$`)

          for (const entry of entries) {
            if (regex.test(entry)) {
              const remainingPath = parts.slice(i + 1).join('/')
              const matchedPath = path.join(currentPath, entry, remainingPath)

              try {
                // Use lstatSync to check if path exists (works for both files and symlinks)
                const stats = fs.lstatSync(matchedPath)

                if (stats.isSymbolicLink()) {
                  // Remove symlink
                  fs.unlinkSync(matchedPath)
                  console.log(`โœ… Removed symlink: ${path.relative(basePath, matchedPath)}`)
                }
                else {
                  // Remove directory/file
                  fs.rmSync(matchedPath, { recursive: true, force: true })
                  console.log(`โœ… Removed: ${path.relative(basePath, matchedPath)}`)
                }
              }
              catch (error) {
                // Silently ignore ENOENT (path not found) errors
                if (error.code !== 'ENOENT') {
                  console.error(`โŒ Failed to remove ${matchedPath}: ${error.message}`)
                }
              }
            }
          }
        }
        return
      }
      else {
        currentPath = path.join(currentPath, part)
      }
    }
  }
  else {
    // Direct path removal
    if (fs.existsSync(fullPath)) {
      try {
        fs.rmSync(fullPath, { recursive: true, force: true })
        console.log(`โœ… Removed: ${relativePath}`)
      }
      catch (error) {
        console.error(`โŒ Failed to remove ${fullPath}: ${error.message}`)
      }
    }
  }
}

// Remove unnecessary paths
console.log('๐Ÿ—‘๏ธ  Removing unnecessary files...')
for (const pathToRemove of pathsToRemove) {
  removePath(standaloneDir, pathToRemove)
}

// Calculate size reduction
console.log('\n๐Ÿ“Š Optimization complete!')

// Optional: Display the size of remaining jest-related files (if any)
const checkForJest = (dir) => {
  const jestFiles = []

  function walk(currentPath) {
    if (!fs.existsSync(currentPath))
      return

    try {
      const entries = fs.readdirSync(currentPath)
      for (const entry of entries) {
        const fullPath = path.join(currentPath, entry)

        try {
          const stat = fs.lstatSync(fullPath) // Use lstatSync to handle symlinks

          if (stat.isDirectory() && !stat.isSymbolicLink()) {
            // Skip node_modules subdirectories to avoid deep traversal
            if (entry === 'node_modules' && currentPath !== standaloneDir) {
              continue
            }
            walk(fullPath)
          }
          else if (stat.isFile() && entry.includes('jest')) {
            jestFiles.push(path.relative(standaloneDir, fullPath))
          }
        }
        catch (err) {
          // Skip files that can't be accessed
          continue
        }
      }
    }
    catch (err) {
      // Skip directories that can't be read

    }
  }

  walk(dir)
  return jestFiles
}

const remainingJestFiles = checkForJest(standaloneDir)
if (remainingJestFiles.length > 0) {
  console.log('\nโš ๏ธ  Warning: Some jest-related files still remain:')
  remainingJestFiles.forEach(file => console.log(`  - ${file}`))
}
else {
  console.log('\nโœจ No jest-related files found in standalone output!')
}