File size: 5,064 Bytes
1dbc34b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env node

/**
 * This script prepares the server for bundling with Electron.
 * It copies the server dist and installs production dependencies
 * in a way that works with npm workspaces.
 */

import { execSync } from 'child_process';
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, lstatSync } from 'fs';
import { join, dirname, resolve } from 'path';
import { fileURLToPath } from 'url';

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

const APP_DIR = join(__dirname, '..');
const SERVER_DIR = join(APP_DIR, '..', 'server');
const LIBS_DIR = join(APP_DIR, '..', '..', 'libs');
const BUNDLE_DIR = join(APP_DIR, 'server-bundle');

// Local workspace packages that need to be bundled
const LOCAL_PACKAGES = [
  '@automaker/types',
  '@automaker/utils',
  '@automaker/prompts',
  '@automaker/platform',
  '@automaker/model-resolver',
  '@automaker/dependency-resolver',
  '@automaker/git-utils',
];

console.log('πŸ”§ Preparing server for Electron bundling...\n');

// Step 1: Clean up previous bundle
if (existsSync(BUNDLE_DIR)) {
  console.log('πŸ—‘οΈ  Cleaning previous server-bundle...');
  rmSync(BUNDLE_DIR, { recursive: true });
}
mkdirSync(BUNDLE_DIR, { recursive: true });

// Step 2: Build the server TypeScript
console.log('πŸ“¦ Building server TypeScript...');
execSync('npm run build', { cwd: SERVER_DIR, stdio: 'inherit' });

// Step 3: Copy server dist
console.log('πŸ“‹ Copying server dist...');
cpSync(join(SERVER_DIR, 'dist'), join(BUNDLE_DIR, 'dist'), { recursive: true });

// Step 4: Copy local workspace packages
console.log('πŸ“¦ Copying local workspace packages...');
const bundleLibsDir = join(BUNDLE_DIR, 'libs');
mkdirSync(bundleLibsDir, { recursive: true });

for (const pkgName of LOCAL_PACKAGES) {
  const pkgDir = pkgName.replace('@automaker/', '');
  const srcDir = join(LIBS_DIR, pkgDir);
  const destDir = join(bundleLibsDir, pkgDir);

  if (!existsSync(srcDir)) {
    console.warn(`⚠️  Warning: Package ${pkgName} not found at ${srcDir}`);
    continue;
  }

  mkdirSync(destDir, { recursive: true });

  // Copy dist folder
  if (existsSync(join(srcDir, 'dist'))) {
    cpSync(join(srcDir, 'dist'), join(destDir, 'dist'), { recursive: true });
  }

  // Copy package.json
  if (existsSync(join(srcDir, 'package.json'))) {
    cpSync(join(srcDir, 'package.json'), join(destDir, 'package.json'));
  }

  console.log(`   βœ“ ${pkgName}`);
}

// Step 5: Create a minimal package.json for the server
console.log('πŸ“ Creating server package.json...');
const serverPkg = JSON.parse(readFileSync(join(SERVER_DIR, 'package.json'), 'utf-8'));

// Replace local package versions with file: references
const dependencies = { ...serverPkg.dependencies };
for (const pkgName of LOCAL_PACKAGES) {
  if (dependencies[pkgName]) {
    const pkgDir = pkgName.replace('@automaker/', '');
    dependencies[pkgName] = `file:libs/${pkgDir}`;
  }
}

const bundlePkg = {
  name: '@automaker/server-bundle',
  version: serverPkg.version,
  type: 'module',
  main: 'dist/index.js',
  dependencies,
};

writeFileSync(join(BUNDLE_DIR, 'package.json'), JSON.stringify(bundlePkg, null, 2));

// Step 6: Install production dependencies
console.log('πŸ“₯ Installing server production dependencies...');
execSync('npm install --omit=dev', {
  cwd: BUNDLE_DIR,
  stdio: 'inherit',
  env: {
    ...process.env,
    // Prevent npm from using workspace resolution
    npm_config_workspace: '',
  },
});

// Step 6b: Replace symlinks for local packages with real copies
// npm install creates symlinks for file: references, but these break when packaged by electron-builder
console.log('πŸ”— Replacing symlinks with real directory copies...');
const nodeModulesAutomaker = join(BUNDLE_DIR, 'node_modules', '@automaker');
for (const pkgName of LOCAL_PACKAGES) {
  const pkgDir = pkgName.replace('@automaker/', '');
  const nmPkgPath = join(nodeModulesAutomaker, pkgDir);
  try {
    // lstatSync does not follow symlinks, allowing us to check for broken ones
    if (lstatSync(nmPkgPath).isSymbolicLink()) {
      const realPath = resolve(BUNDLE_DIR, 'libs', pkgDir);
      rmSync(nmPkgPath);
      cpSync(realPath, nmPkgPath, { recursive: true });
      console.log(`   βœ“ Replaced symlink: ${pkgName}`);
    }
  } catch (error) {
    // If the path doesn't exist, lstatSync throws ENOENT. We can safely ignore this.
    if (error.code !== 'ENOENT') {
      throw error;
    }
  }
}

// Step 7: Rebuild native modules for current architecture
// This is critical for modules like node-pty that have native bindings
console.log('πŸ”¨ Rebuilding native modules for current architecture...');
try {
  execSync('npm rebuild', {
    cwd: BUNDLE_DIR,
    stdio: 'inherit',
  });
  console.log('βœ… Native modules rebuilt successfully');
} catch (error) {
  console.warn(
    '⚠️  Warning: Failed to rebuild native modules. Terminal functionality may not work.'
  );
  console.warn('   Error:', error.message);
}

console.log('\nβœ… Server prepared for bundling at:', BUNDLE_DIR);