easybuild-nox 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +405 -0
- package/bin/easy.js +71 -0
- package/easy.config.js +58 -0
- package/package.json +69 -0
- package/src/commands/analyze.js +203 -0
- package/src/commands/build.js +80 -0
- package/src/commands/clean.js +109 -0
- package/src/commands/config.js +104 -0
- package/src/commands/db.js +423 -0
- package/src/commands/deploy.js +203 -0
- package/src/commands/dev.js +188 -0
- package/src/commands/docker.js +264 -0
- package/src/commands/electron.js +210 -0
- package/src/commands/env.js +302 -0
- package/src/commands/generate.js +556 -0
- package/src/commands/health.js +261 -0
- package/src/commands/info.js +139 -0
- package/src/commands/init.js +794 -0
- package/src/commands/lint.js +111 -0
- package/src/commands/modules.js +333 -0
- package/src/commands/package.js +265 -0
- package/src/commands/proxy.js +73 -0
- package/src/commands/run.js +199 -0
- package/src/commands/test.js +104 -0
- package/src/index.js +30 -0
- package/src/targets/docker.js +201 -0
- package/src/targets/electron.js +157 -0
- package/src/targets/frontend.js +391 -0
- package/src/targets/library.js +164 -0
- package/src/targets/node.js +146 -0
- package/src/utils/config.js +191 -0
- package/src/utils/detector.js +407 -0
- package/src/utils/globalConfig.js +106 -0
- package/src/utils/logger.js +98 -0
- package/src/utils/modules.js +571 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const logger = require('../utils/logger');
|
|
6
|
+
const ProjectDetector = require('../utils/detector');
|
|
7
|
+
|
|
8
|
+
const lintCommand = new Command('lint')
|
|
9
|
+
.description('Run linting and formatting')
|
|
10
|
+
.option('-f, --fix', 'Auto-fix issues', false)
|
|
11
|
+
.option('-p, --prettier', 'Run Prettier', false)
|
|
12
|
+
.option('-e, --eslint', 'Run ESLint', false)
|
|
13
|
+
.option('-a, --all', 'Run all linters', false)
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
logger.header('🔍 Linting Project');
|
|
16
|
+
|
|
17
|
+
const detector = new ProjectDetector();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const projectInfo = await detector.detect();
|
|
21
|
+
|
|
22
|
+
if (options.all || (!options.prettier && !options.eslint)) {
|
|
23
|
+
// Run all linters
|
|
24
|
+
await runESLint(projectInfo, options);
|
|
25
|
+
await runPrettier(projectInfo, options);
|
|
26
|
+
} else {
|
|
27
|
+
if (options.eslint) await runESLint(projectInfo, options);
|
|
28
|
+
if (options.prettier) await runPrettier(projectInfo, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
logger.success('Linting completed!');
|
|
32
|
+
} catch (error) {
|
|
33
|
+
logger.error(`Linting failed: ${error.message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
async function runESLint(projectInfo, options) {
|
|
39
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
40
|
+
const deps = {
|
|
41
|
+
...pkg.dependencies,
|
|
42
|
+
...pkg.devDependencies,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (!deps.eslint) {
|
|
46
|
+
logger.dim('ESLint not installed, skipping...');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.info('Running ESLint...');
|
|
51
|
+
|
|
52
|
+
const args = ['eslint', '.'];
|
|
53
|
+
if (options.fix) args.push('--fix');
|
|
54
|
+
|
|
55
|
+
const child = spawn('npx', args, {
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
shell: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
child.on('close', (code) => {
|
|
62
|
+
if (code === 0) {
|
|
63
|
+
logger.success('ESLint passed!');
|
|
64
|
+
resolve();
|
|
65
|
+
} else if (code === 1) {
|
|
66
|
+
logger.warning('ESLint found issues');
|
|
67
|
+
resolve(); // Don't fail, just warn
|
|
68
|
+
} else {
|
|
69
|
+
reject(new Error('ESLint failed'));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function runPrettier(projectInfo, options) {
|
|
76
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
77
|
+
const deps = {
|
|
78
|
+
...pkg.dependencies,
|
|
79
|
+
...pkg.devDependencies,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (!deps.prettier) {
|
|
83
|
+
logger.dim('Prettier not installed, skipping...');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.info('Running Prettier...');
|
|
88
|
+
|
|
89
|
+
const args = ['prettier', '--write', '.'];
|
|
90
|
+
if (!options.fix) {
|
|
91
|
+
args[2] = '--check';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const child = spawn('npx', args, {
|
|
95
|
+
stdio: 'inherit',
|
|
96
|
+
shell: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
child.on('close', (code) => {
|
|
101
|
+
if (code === 0) {
|
|
102
|
+
logger.success('Prettier passed!');
|
|
103
|
+
resolve();
|
|
104
|
+
} else {
|
|
105
|
+
reject(new Error('Prettier failed'));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = lintCommand;
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { spawn } = require('child_process');
|
|
5
|
+
const logger = require('../utils/logger');
|
|
6
|
+
const sharedModules = require('../utils/modules');
|
|
7
|
+
|
|
8
|
+
const modulesCommand = new Command('modules')
|
|
9
|
+
.description('Manage shared modules (saves disk space!)')
|
|
10
|
+
.option('-a, --action <action>', 'Action (install, update, list, clean, sync, stats)', 'list')
|
|
11
|
+
.option('-n, --name <name>', 'Module name for specific operations')
|
|
12
|
+
.option('-f, --force', 'Force reinstall/update', false)
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
logger.header('📦 Shared Modules Manager');
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await sharedModules.init();
|
|
18
|
+
|
|
19
|
+
// Check if using sub-commands (handled by commander)
|
|
20
|
+
if (options.args && options.args.length > 0) {
|
|
21
|
+
return; // Let commander handle sub-commands
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const actions = {
|
|
25
|
+
install: modulesInstall,
|
|
26
|
+
update: modulesUpdate,
|
|
27
|
+
list: modulesList,
|
|
28
|
+
clean: modulesClean,
|
|
29
|
+
sync: modulesSync,
|
|
30
|
+
stats: modulesStats,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const action = actions[options.action];
|
|
34
|
+
if (!action) {
|
|
35
|
+
logger.error(`Unknown action: ${options.action}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await action(options);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error(`Modules operation failed: ${error.message}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
async function modulesInstall(options) {
|
|
47
|
+
logger.section('Installing Modules');
|
|
48
|
+
|
|
49
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
50
|
+
|
|
51
|
+
if (!(await fs.pathExists(packageJsonPath))) {
|
|
52
|
+
logger.error('No package.json found in current directory');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (options.name) {
|
|
57
|
+
// Install specific module
|
|
58
|
+
logger.info(`Installing ${options.name}...`);
|
|
59
|
+
const result = await sharedModules.installModule(options.name, '*', { force: options.force });
|
|
60
|
+
|
|
61
|
+
if (result.installed) {
|
|
62
|
+
logger.success(`${options.name} installed to shared modules`);
|
|
63
|
+
} else if (result.cached) {
|
|
64
|
+
logger.info(`${options.name} already exists in shared modules`);
|
|
65
|
+
} else {
|
|
66
|
+
logger.error(`Failed to install ${options.name}`);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
// Install all from package.json
|
|
70
|
+
const results = await sharedModules.installFromPackageJson(packageJsonPath, { force: options.force });
|
|
71
|
+
|
|
72
|
+
logger.info('');
|
|
73
|
+
logger.section('Installation Summary');
|
|
74
|
+
logger.success(`Newly installed: ${results.installed}`);
|
|
75
|
+
logger.info(`Already cached: ${results.cached}`);
|
|
76
|
+
if (results.failed > 0) {
|
|
77
|
+
logger.error(`Failed: ${results.failed}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Create symlink in project
|
|
81
|
+
await createModuleSymlink();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function modulesUpdate(options) {
|
|
86
|
+
logger.section('Updating Modules');
|
|
87
|
+
|
|
88
|
+
if (options.name) {
|
|
89
|
+
// Update specific module
|
|
90
|
+
logger.info(`Updating ${options.name}...`);
|
|
91
|
+
const result = await sharedModules.updateModule(options.name, { force: options.force });
|
|
92
|
+
|
|
93
|
+
if (result.updated) {
|
|
94
|
+
logger.success(`${options.name} updated successfully`);
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
// Update all modules
|
|
98
|
+
const results = await sharedModules.updateAllModules({ force: options.force });
|
|
99
|
+
|
|
100
|
+
logger.info('');
|
|
101
|
+
logger.section('Update Summary');
|
|
102
|
+
logger.success(`Updated: ${results.updated}`);
|
|
103
|
+
if (results.failed > 0) {
|
|
104
|
+
logger.error(`Failed: ${results.failed}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function modulesList(options) {
|
|
110
|
+
const modules = await sharedModules.listModules();
|
|
111
|
+
const moduleList = Object.values(modules);
|
|
112
|
+
|
|
113
|
+
if (moduleList.length === 0) {
|
|
114
|
+
logger.info('No shared modules installed yet');
|
|
115
|
+
logger.info('Run: easy modules install');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
logger.section('Shared Modules');
|
|
120
|
+
|
|
121
|
+
// Group by first letter
|
|
122
|
+
const grouped = {};
|
|
123
|
+
for (const mod of moduleList) {
|
|
124
|
+
const firstLetter = mod.name[0].toUpperCase();
|
|
125
|
+
if (!grouped[firstLetter]) grouped[firstLetter] = [];
|
|
126
|
+
grouped[firstLetter].push(mod);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Display grouped
|
|
130
|
+
for (const [letter, mods] of Object.entries(grouped).sort()) {
|
|
131
|
+
logger.bold(`\n${letter}:`);
|
|
132
|
+
for (const mod of mods) {
|
|
133
|
+
logger.dim(` ${mod.name}@${mod.version}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
logger.info('');
|
|
138
|
+
logger.dim(`Total: ${moduleList.length} modules`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function modulesClean(options) {
|
|
142
|
+
logger.section('Cleaning Unused Modules');
|
|
143
|
+
|
|
144
|
+
const results = await sharedModules.cleanUnused();
|
|
145
|
+
|
|
146
|
+
logger.info('');
|
|
147
|
+
logger.section('Clean Summary');
|
|
148
|
+
logger.success(`Removed: ${results.removed} modules`);
|
|
149
|
+
logger.info(`Kept: ${results.kept} modules`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function modulesSync(options) {
|
|
153
|
+
logger.section('Syncing Project Dependencies');
|
|
154
|
+
|
|
155
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
156
|
+
|
|
157
|
+
if (!(await fs.pathExists(packageJsonPath))) {
|
|
158
|
+
logger.error('No package.json found in current directory');
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Install all dependencies from package.json
|
|
163
|
+
const results = await sharedModules.installFromPackageJson(packageJsonPath, { force: options.force });
|
|
164
|
+
|
|
165
|
+
// Create symlink
|
|
166
|
+
await createModuleSymlink();
|
|
167
|
+
|
|
168
|
+
logger.info('');
|
|
169
|
+
logger.section('Sync Summary');
|
|
170
|
+
logger.success(`Synced ${results.installed + results.cached} modules`);
|
|
171
|
+
logger.info('Project now uses shared modules');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function modulesStats(options) {
|
|
175
|
+
const stats = await sharedModules.getStats();
|
|
176
|
+
|
|
177
|
+
logger.section('Shared Modules Statistics');
|
|
178
|
+
|
|
179
|
+
logger.info(`Modules installed: ${stats.moduleCount}`);
|
|
180
|
+
logger.info(`Total disk usage: ${formatSize(stats.totalSize)}`);
|
|
181
|
+
logger.info(`Shared location: ${stats.sharedPath}`);
|
|
182
|
+
|
|
183
|
+
if (stats.lastUpdate) {
|
|
184
|
+
logger.info(`Last updated: ${new Date(stats.lastUpdate).toLocaleString()}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Calculate savings
|
|
188
|
+
const estimatedPerProject = stats.moduleCount * 2 * 1024 * 1024; // ~2MB per module average
|
|
189
|
+
const potentialSavings = estimatedPerProject - stats.totalSize;
|
|
190
|
+
|
|
191
|
+
if (potentialSavings > 0) {
|
|
192
|
+
logger.info('');
|
|
193
|
+
logger.success(`Estimated disk savings: ${formatSize(potentialSavings)}`);
|
|
194
|
+
logger.dim('(vs having modules in each project)');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function modulesLink(options) {
|
|
199
|
+
logger.section('Linking Local Modules');
|
|
200
|
+
|
|
201
|
+
const results = await sharedModules.linkFromLocal();
|
|
202
|
+
|
|
203
|
+
logger.info('');
|
|
204
|
+
logger.section('Link Summary');
|
|
205
|
+
logger.success(`Linked: ${results.linked} modules`);
|
|
206
|
+
logger.info(`Skipped (already exists): ${results.skipped} modules`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function modulesOpen(options) {
|
|
210
|
+
const modulesPath = sharedModules.getSharedModulesPath();
|
|
211
|
+
|
|
212
|
+
logger.info(`Opening: ${modulesPath}`);
|
|
213
|
+
|
|
214
|
+
// Open in file manager based on platform
|
|
215
|
+
const platform = process.platform;
|
|
216
|
+
let command;
|
|
217
|
+
|
|
218
|
+
if (platform === 'darwin') {
|
|
219
|
+
command = 'open';
|
|
220
|
+
} else if (platform === 'win32') {
|
|
221
|
+
command = 'explorer';
|
|
222
|
+
} else {
|
|
223
|
+
command = 'xdg-open';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
spawn(command, [modulesPath], {
|
|
228
|
+
stdio: 'ignore',
|
|
229
|
+
detached: true
|
|
230
|
+
}).unref();
|
|
231
|
+
logger.success('Opened modules folder!');
|
|
232
|
+
} catch (error) {
|
|
233
|
+
logger.dim(`Could not open folder automatically`);
|
|
234
|
+
logger.info(`Manually navigate to: ${modulesPath}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function createModuleSymlink() {
|
|
239
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules');
|
|
240
|
+
const sharedPath = sharedModules.getSharedModulesPath();
|
|
241
|
+
|
|
242
|
+
// Check if node_modules already exists
|
|
243
|
+
if (await fs.pathExists(nodeModulesPath)) {
|
|
244
|
+
const stat = await fs.lstat(nodeModulesPath);
|
|
245
|
+
if (stat.isSymbolicLink()) {
|
|
246
|
+
// Already a symlink, update it
|
|
247
|
+
await fs.remove(nodeModulesPath);
|
|
248
|
+
} else {
|
|
249
|
+
// It's a real directory, don't overwrite
|
|
250
|
+
logger.dim('node_modules directory exists, skipping symlink creation');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Create symlink
|
|
256
|
+
try {
|
|
257
|
+
await fs.symlink(sharedPath, nodeModulesPath, 'junction');
|
|
258
|
+
logger.dim('Created symlink: node_modules -> shared modules');
|
|
259
|
+
} catch (error) {
|
|
260
|
+
// Fallback: copy package.json references
|
|
261
|
+
logger.dim('Could not create symlink, using NODE_PATH instead');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function formatSize(bytes) {
|
|
266
|
+
if (bytes === 0) return '0 Bytes';
|
|
267
|
+
const k = 1024;
|
|
268
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
269
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
270
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Add sub-commands
|
|
274
|
+
modulesCommand
|
|
275
|
+
.command('install [name]')
|
|
276
|
+
.description('Install modules to shared folder')
|
|
277
|
+
.option('-f, --force', 'Force reinstall')
|
|
278
|
+
.action(async (name, options) => {
|
|
279
|
+
await modulesInstall({ name, force: options.force });
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
modulesCommand
|
|
283
|
+
.command('update [name]')
|
|
284
|
+
.description('Update modules')
|
|
285
|
+
.option('-f, --force', 'Force update')
|
|
286
|
+
.action(async (name, options) => {
|
|
287
|
+
await modulesUpdate({ name, force: options.force });
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
modulesCommand
|
|
291
|
+
.command('list')
|
|
292
|
+
.description('List installed modules')
|
|
293
|
+
.action(async () => {
|
|
294
|
+
await modulesList({});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
modulesCommand
|
|
298
|
+
.command('clean')
|
|
299
|
+
.description('Remove unused modules')
|
|
300
|
+
.action(async () => {
|
|
301
|
+
await modulesClean({});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
modulesCommand
|
|
305
|
+
.command('sync')
|
|
306
|
+
.description('Sync project dependencies to shared modules')
|
|
307
|
+
.option('-f, --force', 'Force sync')
|
|
308
|
+
.action(async (options) => {
|
|
309
|
+
await modulesSync({ force: options.force });
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
modulesCommand
|
|
313
|
+
.command('stats')
|
|
314
|
+
.description('Show disk usage statistics')
|
|
315
|
+
.action(async () => {
|
|
316
|
+
await modulesStats({});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
modulesCommand
|
|
320
|
+
.command('link')
|
|
321
|
+
.description('Link local node_modules to shared folder')
|
|
322
|
+
.action(async () => {
|
|
323
|
+
await modulesLink({});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
modulesCommand
|
|
327
|
+
.command('open')
|
|
328
|
+
.description('Open shared modules folder')
|
|
329
|
+
.action(async () => {
|
|
330
|
+
await modulesOpen({});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
module.exports = modulesCommand;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const logger = require('../utils/logger');
|
|
6
|
+
const ProjectDetector = require('../utils/detector');
|
|
7
|
+
const ConfigLoader = require('../utils/config');
|
|
8
|
+
|
|
9
|
+
const packageCommand = new Command('package')
|
|
10
|
+
.description('Package your application for distribution')
|
|
11
|
+
.option('-t, --type <type>', 'Package type (bin, app, docker, npm)', 'bin')
|
|
12
|
+
.option('-p, --platform <platform>', 'Target platform (win, linux, mac, current)', 'current')
|
|
13
|
+
.option('--arch <arch>', 'Target architecture (x64, arm64, all)', 'x64')
|
|
14
|
+
.option('-o, --output <dir>', 'Output directory', 'dist')
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
logger.header('📦 Packaging Application');
|
|
17
|
+
|
|
18
|
+
const detector = new ProjectDetector();
|
|
19
|
+
const config = new ConfigLoader();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const projectInfo = await detector.detect();
|
|
23
|
+
const configData = await config.load();
|
|
24
|
+
|
|
25
|
+
logger.info(`Type: ${options.type}`);
|
|
26
|
+
logger.info(`Platform: ${options.platform}`);
|
|
27
|
+
logger.info(`Architecture: ${options.arch}`);
|
|
28
|
+
|
|
29
|
+
await packageApp(projectInfo, configData, options);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logger.error(`Packaging failed: ${error.message}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
async function packageApp(projectInfo, config, options) {
|
|
37
|
+
const types = {
|
|
38
|
+
bin: packageAsBinary,
|
|
39
|
+
app: packageAsApp,
|
|
40
|
+
docker: packageAsDocker,
|
|
41
|
+
npm: packageAsNpm,
|
|
42
|
+
single: packageAsSingleFile,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const packager = types[options.type];
|
|
46
|
+
if (!packager) {
|
|
47
|
+
throw new Error(`Unknown package type: ${options.type}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await packager(projectInfo, config, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function packageAsBinary(projectInfo, config, options) {
|
|
54
|
+
logger.info('Packaging as standalone binary...');
|
|
55
|
+
|
|
56
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
57
|
+
const entryPoint = pkg.main || 'src/index.js';
|
|
58
|
+
|
|
59
|
+
// Ensure pkg is available
|
|
60
|
+
try {
|
|
61
|
+
require.resolve('pkg');
|
|
62
|
+
} catch {
|
|
63
|
+
logger.info('Installing pkg...');
|
|
64
|
+
await new Promise((resolve, reject) => {
|
|
65
|
+
const install = spawn('npm', ['install', 'pkg', '--save-dev'], {
|
|
66
|
+
stdio: 'inherit',
|
|
67
|
+
shell: true,
|
|
68
|
+
});
|
|
69
|
+
install.on('close', (code) => {
|
|
70
|
+
if (code === 0) resolve();
|
|
71
|
+
else reject(new Error('Failed to install pkg'));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const platforms = {
|
|
77
|
+
win: 'win',
|
|
78
|
+
linux: 'linux',
|
|
79
|
+
mac: 'macos',
|
|
80
|
+
current: process.platform === 'win32' ? 'win' : process.platform === 'darwin' ? 'macos' : 'linux',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const target = platforms[options.platform] || 'linux';
|
|
84
|
+
const arch = options.arch === 'all' ? 'x64' : options.arch;
|
|
85
|
+
|
|
86
|
+
const args = [
|
|
87
|
+
'pkg',
|
|
88
|
+
entryPoint,
|
|
89
|
+
'--targets',
|
|
90
|
+
`${target}-${arch}`,
|
|
91
|
+
'--output',
|
|
92
|
+
`${options.output}/${pkg.name}`,
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const child = spawn('npx', args, {
|
|
96
|
+
stdio: 'inherit',
|
|
97
|
+
shell: true,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
child.on('close', (code) => {
|
|
102
|
+
if (code === 0) {
|
|
103
|
+
logger.success(`Binary packaged: ${options.output}/${pkg.name}`);
|
|
104
|
+
resolve();
|
|
105
|
+
} else {
|
|
106
|
+
reject(new Error('Binary packaging failed'));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function packageAsApp(projectInfo, config, options) {
|
|
113
|
+
logger.info('Packaging as application...');
|
|
114
|
+
|
|
115
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
116
|
+
|
|
117
|
+
// Check if it's an Electron app
|
|
118
|
+
if (projectInfo.type === 'desktop' || pkg.dependencies?.electron || pkg.devDependencies?.electron) {
|
|
119
|
+
// Use electron-builder
|
|
120
|
+
const args = ['electron-builder'];
|
|
121
|
+
|
|
122
|
+
if (options.platform === 'all') {
|
|
123
|
+
args.push('-mwl');
|
|
124
|
+
} else if (options.platform === 'win') {
|
|
125
|
+
args.push('--win');
|
|
126
|
+
} else if (options.platform === 'mac') {
|
|
127
|
+
args.push('--mac');
|
|
128
|
+
} else if (options.platform === 'linux') {
|
|
129
|
+
args.push('--linux');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const child = spawn('npx', args, {
|
|
133
|
+
stdio: 'inherit',
|
|
134
|
+
shell: true,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
child.on('close', (code) => {
|
|
139
|
+
if (code === 0) {
|
|
140
|
+
logger.success('Application packaged!');
|
|
141
|
+
resolve();
|
|
142
|
+
} else {
|
|
143
|
+
reject(new Error('Application packaging failed'));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// For Node.js apps, use pkg
|
|
150
|
+
await packageAsBinary(projectInfo, config, options);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function packageAsDocker(projectInfo, config, options) {
|
|
154
|
+
logger.info('Packaging as Docker image...');
|
|
155
|
+
|
|
156
|
+
// Ensure Dockerfile exists
|
|
157
|
+
const dockerfilePath = path.join(process.cwd(), 'Dockerfile');
|
|
158
|
+
if (!(await fs.pathExists(dockerfilePath))) {
|
|
159
|
+
logger.warning('No Dockerfile found. Please run: easy docker build');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
164
|
+
const imageName = pkg.name || 'app';
|
|
165
|
+
|
|
166
|
+
const child = spawn('docker', ['build', '-t', `${imageName}:latest`, '.'], {
|
|
167
|
+
stdio: 'inherit',
|
|
168
|
+
shell: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
child.on('close', (code) => {
|
|
173
|
+
if (code === 0) {
|
|
174
|
+
logger.success(`Docker image packaged: ${imageName}:latest`);
|
|
175
|
+
resolve();
|
|
176
|
+
} else {
|
|
177
|
+
reject(new Error('Docker packaging failed'));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function packageAsNpm(projectInfo, config, options) {
|
|
184
|
+
logger.info('Packaging for npm...');
|
|
185
|
+
|
|
186
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
187
|
+
|
|
188
|
+
// Ensure package is ready for publishing
|
|
189
|
+
const requiredFields = ['name', 'version', 'main'];
|
|
190
|
+
const missing = requiredFields.filter(field => !pkg[field]);
|
|
191
|
+
|
|
192
|
+
if (missing.length > 0) {
|
|
193
|
+
logger.error(`Missing required fields in package.json: ${missing.join(', ')}`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Build if needed
|
|
198
|
+
if (pkg.scripts?.build) {
|
|
199
|
+
logger.info('Building package...');
|
|
200
|
+
await new Promise((resolve, reject) => {
|
|
201
|
+
const build = spawn('npm', ['run', 'build'], {
|
|
202
|
+
stdio: 'inherit',
|
|
203
|
+
shell: true,
|
|
204
|
+
});
|
|
205
|
+
build.on('close', (code) => {
|
|
206
|
+
if (code === 0) resolve();
|
|
207
|
+
else reject(new Error('Build failed'));
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Pack
|
|
213
|
+
const child = spawn('npm', ['pack'], {
|
|
214
|
+
stdio: 'inherit',
|
|
215
|
+
shell: true,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
child.on('close', (code) => {
|
|
220
|
+
if (code === 0) {
|
|
221
|
+
logger.success('Package created for npm!');
|
|
222
|
+
resolve();
|
|
223
|
+
} else {
|
|
224
|
+
reject(new Error('npm pack failed'));
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function packageAsSingleFile(projectInfo, config, options) {
|
|
231
|
+
logger.info('Packaging as single executable...');
|
|
232
|
+
|
|
233
|
+
// Use Node.js SEA (Single Executable Application) if available
|
|
234
|
+
logger.info('Note: Single Executable Application requires Node.js 20+');
|
|
235
|
+
logger.info('Building...');
|
|
236
|
+
|
|
237
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
238
|
+
const entryPoint = pkg.main || 'src/index.js';
|
|
239
|
+
|
|
240
|
+
// Bundle with esbuild first
|
|
241
|
+
const child = spawn('npx', [
|
|
242
|
+
'esbuild',
|
|
243
|
+
entryPoint,
|
|
244
|
+
'--bundle',
|
|
245
|
+
'--platform=node',
|
|
246
|
+
'--target=node20',
|
|
247
|
+
`--outfile=${options.output}/bundle.js`,
|
|
248
|
+
], {
|
|
249
|
+
stdio: 'inherit',
|
|
250
|
+
shell: true,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
child.on('close', (code) => {
|
|
255
|
+
if (code === 0) {
|
|
256
|
+
logger.success(`Single file packaged: ${options.output}/bundle.js`);
|
|
257
|
+
resolve();
|
|
258
|
+
} else {
|
|
259
|
+
reject(new Error('Single file packaging failed'));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = packageCommand;
|