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,188 @@
|
|
|
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
|
+
const sharedModules = require('../utils/modules');
|
|
9
|
+
const globalConfig = require('../utils/globalConfig');
|
|
10
|
+
|
|
11
|
+
const devCommand = new Command('dev')
|
|
12
|
+
.description('Start development server with hot reload')
|
|
13
|
+
.option('-p, --port <port>', 'Port number', '3000')
|
|
14
|
+
.option('-h, --host <host>', 'Host address', 'localhost')
|
|
15
|
+
.option('--open', 'Open browser', true)
|
|
16
|
+
.option('--no-shared', 'Disable shared modules for this run')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
logger.header('⚡ Starting Development Server');
|
|
19
|
+
|
|
20
|
+
const detector = new ProjectDetector();
|
|
21
|
+
const config = new ConfigLoader();
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const projectInfo = await detector.detect();
|
|
25
|
+
const configData = await config.load();
|
|
26
|
+
|
|
27
|
+
logger.info(`Framework: ${projectInfo.framework?.name || 'Unknown'}`);
|
|
28
|
+
logger.info(`Type: ${projectInfo.type || 'Unknown'}`);
|
|
29
|
+
logger.info(`Port: ${options.port}`);
|
|
30
|
+
|
|
31
|
+
// Use shared modules by default (unless --no-shared is passed)
|
|
32
|
+
if (options.shared !== false) {
|
|
33
|
+
await sharedModules.init();
|
|
34
|
+
const configData = await globalConfig.load();
|
|
35
|
+
const useShared = configData.sharedModules?.enabled !== false;
|
|
36
|
+
|
|
37
|
+
if (useShared) {
|
|
38
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
39
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
40
|
+
logger.dim('Using shared modules...');
|
|
41
|
+
await sharedModules.installFromPackageJson(packageJsonPath);
|
|
42
|
+
await setupNodePath();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Set environment
|
|
48
|
+
process.env.NODE_ENV = 'development';
|
|
49
|
+
process.env.PORT = options.port;
|
|
50
|
+
process.env.HOST = options.host;
|
|
51
|
+
|
|
52
|
+
await startDevServer(projectInfo, configData, options);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
logger.error(`Failed to start dev server: ${error.message}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
async function startDevServer(projectInfo, config, options) {
|
|
60
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
61
|
+
const scripts = pkg.scripts || {};
|
|
62
|
+
|
|
63
|
+
let command = null;
|
|
64
|
+
let args = [];
|
|
65
|
+
|
|
66
|
+
if (projectInfo.framework) {
|
|
67
|
+
const devCommands = {
|
|
68
|
+
// Backend
|
|
69
|
+
express: { command: 'nodemon', args: ['src/index.js'] },
|
|
70
|
+
fastify: { command: 'nodemon', args: ['src/index.js'] },
|
|
71
|
+
koa: { command: 'nodemon', args: ['src/index.js'] },
|
|
72
|
+
hapi: { command: 'nodemon', args: ['src/index.js'] },
|
|
73
|
+
nestjs: { command: 'npm', args: ['run', 'start:dev'] },
|
|
74
|
+
adonis: { command: 'npm', args: ['run', 'dev'] },
|
|
75
|
+
strapi: { command: 'npm', args: ['run', 'develop'] },
|
|
76
|
+
hono: { command: 'npm', args: ['run', 'dev'] },
|
|
77
|
+
trpc: { command: 'npm', args: ['run', 'dev'] },
|
|
78
|
+
graphql: { command: 'npm', args: ['run', 'dev'] },
|
|
79
|
+
socketio: { command: 'npm', args: ['run', 'dev'] },
|
|
80
|
+
|
|
81
|
+
// Frontend
|
|
82
|
+
react: { command: 'npm', args: ['start'] },
|
|
83
|
+
vue: { command: 'npm', args: ['run', 'serve'] },
|
|
84
|
+
angular: { command: 'ng', args: ['serve'] },
|
|
85
|
+
svelte: { command: 'npm', args: ['run', 'dev'] },
|
|
86
|
+
solid: { command: 'npm', args: ['run', 'dev'] },
|
|
87
|
+
preact: { command: 'npm', args: ['run', 'dev'] },
|
|
88
|
+
lit: { command: 'npm', args: ['run', 'dev'] },
|
|
89
|
+
alpine: { command: 'npm', args: ['run', 'dev'] },
|
|
90
|
+
htmx: { command: 'npm', args: ['run', 'dev'] },
|
|
91
|
+
|
|
92
|
+
// Full-stack
|
|
93
|
+
nextjs: { command: 'npm', args: ['run', 'dev'] },
|
|
94
|
+
nuxt: { command: 'npm', args: ['run', 'dev'] },
|
|
95
|
+
remix: { command: 'npm', args: ['run', 'dev'] },
|
|
96
|
+
gatsby: { command: 'npm', args: ['run', 'develop'] },
|
|
97
|
+
astro: { command: 'npm', args: ['run', 'dev'] },
|
|
98
|
+
sveltekit: { command: 'npm', args: ['run', 'dev'] },
|
|
99
|
+
solidstart: { command: 'npm', args: ['run', 'dev'] },
|
|
100
|
+
qwik: { command: 'npm', args: ['run', 'dev'] },
|
|
101
|
+
analog: { command: 'npm', args: ['run', 'dev'] },
|
|
102
|
+
fresh: { command: 'deno', args: ['task', 'dev'] },
|
|
103
|
+
tanstack: { command: 'npm', args: ['run', 'dev'] },
|
|
104
|
+
waku: { command: 'npm', args: ['run', 'dev'] },
|
|
105
|
+
|
|
106
|
+
// Desktop
|
|
107
|
+
electron: { command: 'npm', args: ['run', 'dev'] },
|
|
108
|
+
tauri: { command: 'npm', args: ['run', 'dev'] },
|
|
109
|
+
nwjs: { command: 'npm', args: ['run', 'dev'] },
|
|
110
|
+
|
|
111
|
+
// Mobile
|
|
112
|
+
reactnative: { command: 'npm', args: ['start'] },
|
|
113
|
+
expo: { command: 'npm', args: ['start'] },
|
|
114
|
+
ionic: { command: 'npm', args: ['start'] },
|
|
115
|
+
tamagui: { command: 'npm', args: ['start'] },
|
|
116
|
+
nativebase: { command: 'npm', args: ['start'] },
|
|
117
|
+
|
|
118
|
+
// Database
|
|
119
|
+
drizzle: { command: 'npm', args: ['run', 'dev'] },
|
|
120
|
+
prisma: { command: 'npm', args: ['run', 'dev'] },
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const devConfig = devCommands[projectInfo.framework.name];
|
|
124
|
+
if (devConfig) {
|
|
125
|
+
command = devConfig.command;
|
|
126
|
+
args = devConfig.args;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Fallback to package.json scripts
|
|
131
|
+
if (!command) {
|
|
132
|
+
if (scripts.dev) {
|
|
133
|
+
command = 'npm';
|
|
134
|
+
args = ['run', 'dev'];
|
|
135
|
+
} else if (scripts['start:dev']) {
|
|
136
|
+
command = 'npm';
|
|
137
|
+
args = ['run', 'start:dev'];
|
|
138
|
+
} else if (scripts.serve) {
|
|
139
|
+
command = 'npm';
|
|
140
|
+
args = ['run', 'serve'];
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error('No dev script found. Add a "dev" script to package.json');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
logger.info(`Starting: ${command} ${args.join(' ')}`);
|
|
147
|
+
logger.dim('Press Ctrl+C to stop');
|
|
148
|
+
logger.info('');
|
|
149
|
+
|
|
150
|
+
const child = spawn(command, args, {
|
|
151
|
+
stdio: 'inherit',
|
|
152
|
+
shell: true,
|
|
153
|
+
env: {
|
|
154
|
+
...process.env,
|
|
155
|
+
NODE_ENV: 'development',
|
|
156
|
+
PORT: options.port,
|
|
157
|
+
HOST: options.host,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
child.on('error', (error) => {
|
|
162
|
+
logger.error(`Process failed: ${error.message}`);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
child.on('close', (code) => {
|
|
167
|
+
if (code !== 0) {
|
|
168
|
+
logger.error(`Process exited with code ${code}`);
|
|
169
|
+
process.exit(code);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function setupNodePath() {
|
|
175
|
+
const sharedPath = sharedModules.getSharedModulesPath();
|
|
176
|
+
const currentPath = process.env.NODE_PATH || '';
|
|
177
|
+
|
|
178
|
+
if (!currentPath.includes(sharedPath)) {
|
|
179
|
+
process.env.NODE_PATH = currentPath
|
|
180
|
+
? `${sharedPath}${path.delimiter}${currentPath}`
|
|
181
|
+
: sharedPath;
|
|
182
|
+
|
|
183
|
+
// Rehash require paths
|
|
184
|
+
require('module')._initPaths();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = devCommand;
|
|
@@ -0,0 +1,264 @@
|
|
|
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 dockerCommand = new Command('docker')
|
|
10
|
+
.description('Docker operations')
|
|
11
|
+
.option('-a, --action <action>', 'Action (build, run, stop, logs, push)', 'build')
|
|
12
|
+
.option('-t, --tag <tag>', 'Docker image tag', 'latest')
|
|
13
|
+
.option('-p, --port <port>', 'Port mapping', '3000')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
logger.header('🐳 Docker Operations');
|
|
16
|
+
|
|
17
|
+
const detector = new ProjectDetector();
|
|
18
|
+
const config = new ConfigLoader();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const projectInfo = await detector.detect();
|
|
22
|
+
const configData = await config.load();
|
|
23
|
+
|
|
24
|
+
logger.info(`Action: ${options.action}`);
|
|
25
|
+
logger.info(`Tag: ${options.tag}`);
|
|
26
|
+
|
|
27
|
+
await dockerOperation(projectInfo, configData, options);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(`Docker operation failed: ${error.message}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
async function dockerOperation(projectInfo, config, options) {
|
|
35
|
+
// Ensure Dockerfile exists
|
|
36
|
+
const dockerfilePath = path.join(process.cwd(), 'Dockerfile');
|
|
37
|
+
if (!(await fs.pathExists(dockerfilePath))) {
|
|
38
|
+
logger.warning('No Dockerfile found. Generating one...');
|
|
39
|
+
await generateDockerfile(projectInfo, config);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const actions = {
|
|
43
|
+
build: dockerBuild,
|
|
44
|
+
run: dockerRun,
|
|
45
|
+
stop: dockerStop,
|
|
46
|
+
logs: dockerLogs,
|
|
47
|
+
push: dockerPush,
|
|
48
|
+
compose: dockerCompose,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const action = actions[options.action];
|
|
52
|
+
if (!action) {
|
|
53
|
+
throw new Error(`Unknown action: ${options.action}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await action(projectInfo, config, options);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function generateDockerfile(projectInfo, config) {
|
|
60
|
+
const dockerConfig = config.docker || {};
|
|
61
|
+
const baseImage = dockerConfig.baseImage || 'node:20-alpine';
|
|
62
|
+
const workdir = dockerConfig.workdir || '/app';
|
|
63
|
+
const port = dockerConfig.port || 3000;
|
|
64
|
+
|
|
65
|
+
const dockerfile = `# Use Node.js alpine image
|
|
66
|
+
FROM ${baseImage} AS builder
|
|
67
|
+
|
|
68
|
+
# Set working directory
|
|
69
|
+
WORKDIR ${workdir}
|
|
70
|
+
|
|
71
|
+
# Copy package files
|
|
72
|
+
COPY package*.json ./
|
|
73
|
+
|
|
74
|
+
# Install dependencies
|
|
75
|
+
RUN npm ci
|
|
76
|
+
|
|
77
|
+
# Copy source code
|
|
78
|
+
COPY . .
|
|
79
|
+
|
|
80
|
+
# Build application (if needed)
|
|
81
|
+
RUN npm run build 2>/dev/null || true
|
|
82
|
+
|
|
83
|
+
# Production stage
|
|
84
|
+
FROM ${baseImage}
|
|
85
|
+
|
|
86
|
+
# Set working directory
|
|
87
|
+
WORKDIR ${workdir}
|
|
88
|
+
|
|
89
|
+
# Copy package files
|
|
90
|
+
COPY package*.json ./
|
|
91
|
+
|
|
92
|
+
# Install production dependencies only
|
|
93
|
+
RUN npm ci --only=production
|
|
94
|
+
|
|
95
|
+
# Copy built application
|
|
96
|
+
COPY --from=builder ${workdir}/dist ./dist
|
|
97
|
+
COPY --from=builder ${workdir}/src ./src
|
|
98
|
+
|
|
99
|
+
# Expose port
|
|
100
|
+
EXPOSE ${port}
|
|
101
|
+
|
|
102
|
+
# Start application
|
|
103
|
+
CMD ["node", "src/index.js"]
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
await fs.writeFile(dockerfilePath, dockerfile);
|
|
107
|
+
logger.success('Dockerfile generated');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function dockerBuild(projectInfo, config, options) {
|
|
111
|
+
logger.info('Building Docker image...');
|
|
112
|
+
|
|
113
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
114
|
+
const imageName = pkg.name || 'app';
|
|
115
|
+
|
|
116
|
+
const child = spawn('docker', ['build', '-t', `${imageName}:${options.tag}`, '.'], {
|
|
117
|
+
stdio: 'inherit',
|
|
118
|
+
shell: true,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
child.on('close', (code) => {
|
|
123
|
+
if (code === 0) {
|
|
124
|
+
logger.success(`Docker image built: ${imageName}:${options.tag}`);
|
|
125
|
+
resolve();
|
|
126
|
+
} else {
|
|
127
|
+
reject(new Error('Docker build failed'));
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function dockerRun(projectInfo, config, options) {
|
|
134
|
+
logger.info('Running Docker container...');
|
|
135
|
+
|
|
136
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
137
|
+
const imageName = pkg.name || 'app';
|
|
138
|
+
const containerName = `${imageName}-container`;
|
|
139
|
+
|
|
140
|
+
// Stop existing container if running
|
|
141
|
+
try {
|
|
142
|
+
await new Promise((resolve) => {
|
|
143
|
+
const stop = spawn('docker', ['stop', containerName], {
|
|
144
|
+
shell: true,
|
|
145
|
+
});
|
|
146
|
+
stop.on('close', () => resolve());
|
|
147
|
+
});
|
|
148
|
+
} catch (e) {
|
|
149
|
+
// Ignore if container doesn't exist
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const child = spawn('docker', [
|
|
153
|
+
'run', '-d',
|
|
154
|
+
'--name', containerName,
|
|
155
|
+
'-p', `${options.port}:3000`,
|
|
156
|
+
`${imageName}:${options.tag}`,
|
|
157
|
+
], {
|
|
158
|
+
stdio: 'inherit',
|
|
159
|
+
shell: true,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
child.on('close', (code) => {
|
|
164
|
+
if (code === 0) {
|
|
165
|
+
logger.success(`Container running on port ${options.port}`);
|
|
166
|
+
resolve();
|
|
167
|
+
} else {
|
|
168
|
+
reject(new Error('Docker run failed'));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function dockerStop(projectInfo, config, options) {
|
|
175
|
+
logger.info('Stopping Docker container...');
|
|
176
|
+
|
|
177
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
178
|
+
const imageName = pkg.name || 'app';
|
|
179
|
+
const containerName = `${imageName}-container`;
|
|
180
|
+
|
|
181
|
+
const child = spawn('docker', ['stop', containerName], {
|
|
182
|
+
stdio: 'inherit',
|
|
183
|
+
shell: true,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
child.on('close', (code) => {
|
|
188
|
+
if (code === 0) {
|
|
189
|
+
logger.success('Container stopped');
|
|
190
|
+
resolve();
|
|
191
|
+
} else {
|
|
192
|
+
reject(new Error('Docker stop failed'));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function dockerLogs(projectInfo, config, options) {
|
|
199
|
+
logger.info('Showing Docker logs...');
|
|
200
|
+
|
|
201
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
202
|
+
const imageName = pkg.name || 'app';
|
|
203
|
+
const containerName = `${imageName}-container`;
|
|
204
|
+
|
|
205
|
+
const child = spawn('docker', ['logs', '-f', containerName], {
|
|
206
|
+
stdio: 'inherit',
|
|
207
|
+
shell: true,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return new Promise((resolve, reject) => {
|
|
211
|
+
child.on('close', (code) => {
|
|
212
|
+
if (code === 0) {
|
|
213
|
+
resolve();
|
|
214
|
+
} else {
|
|
215
|
+
reject(new Error('Docker logs failed'));
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function dockerPush(projectInfo, config, options) {
|
|
222
|
+
logger.info('Pushing Docker image...');
|
|
223
|
+
|
|
224
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
225
|
+
const imageName = pkg.name || 'app';
|
|
226
|
+
|
|
227
|
+
const child = spawn('docker', ['push', `${imageName}:${options.tag}`], {
|
|
228
|
+
stdio: 'inherit',
|
|
229
|
+
shell: true,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
child.on('close', (code) => {
|
|
234
|
+
if (code === 0) {
|
|
235
|
+
logger.success('Image pushed successfully');
|
|
236
|
+
resolve();
|
|
237
|
+
} else {
|
|
238
|
+
reject(new Error('Docker push failed'));
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function dockerCompose(projectInfo, config, options) {
|
|
245
|
+
logger.info('Running Docker Compose...');
|
|
246
|
+
|
|
247
|
+
const child = spawn('docker-compose', ['up', '-d'], {
|
|
248
|
+
stdio: 'inherit',
|
|
249
|
+
shell: true,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
child.on('close', (code) => {
|
|
254
|
+
if (code === 0) {
|
|
255
|
+
logger.success('Docker Compose started');
|
|
256
|
+
resolve();
|
|
257
|
+
} else {
|
|
258
|
+
reject(new Error('Docker Compose failed'));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = dockerCommand;
|
|
@@ -0,0 +1,210 @@
|
|
|
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 electronCommand = new Command('electron')
|
|
10
|
+
.description('Electron operations')
|
|
11
|
+
.option('-a, --action <action>', 'Action (dev, build, pack)', 'dev')
|
|
12
|
+
.option('-p, --platform <platform>', 'Target platform (win, mac, linux, all)', 'current')
|
|
13
|
+
.option('--arch <arch>', 'Target architecture (x64, arm64, all)', 'current')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
logger.header('⚡ Electron Operations');
|
|
16
|
+
|
|
17
|
+
const detector = new ProjectDetector();
|
|
18
|
+
const config = new ConfigLoader();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const projectInfo = await detector.detect();
|
|
22
|
+
const configData = await config.load();
|
|
23
|
+
|
|
24
|
+
logger.info(`Action: ${options.action}`);
|
|
25
|
+
logger.info(`Platform: ${options.platform}`);
|
|
26
|
+
|
|
27
|
+
await electronOperation(projectInfo, configData, options);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(`Electron operation failed: ${error.message}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
async function electronOperation(projectInfo, config, options) {
|
|
35
|
+
// Ensure electron config exists
|
|
36
|
+
const electronConfig = config.electron || {};
|
|
37
|
+
|
|
38
|
+
// Check if electron-builder config exists
|
|
39
|
+
const builderConfigPath = path.join(process.cwd(), 'electron-builder.yml');
|
|
40
|
+
const builderJsonPath = path.join(process.cwd(), 'electron-builder.json');
|
|
41
|
+
|
|
42
|
+
if (!(await fs.pathExists(builderConfigPath)) && !(await fs.pathExists(builderJsonPath))) {
|
|
43
|
+
logger.warning('No electron-builder config found. Generating one...');
|
|
44
|
+
await generateElectronConfig(projectInfo, config);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const actions = {
|
|
48
|
+
dev: electronDev,
|
|
49
|
+
build: electronBuild,
|
|
50
|
+
pack: electronPack,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const action = actions[options.action];
|
|
54
|
+
if (!action) {
|
|
55
|
+
throw new Error(`Unknown action: ${options.action}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await action(projectInfo, config, options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function generateElectronConfig(projectInfo, config) {
|
|
62
|
+
const electronConfig = config.electron || {};
|
|
63
|
+
|
|
64
|
+
const builderConfig = {
|
|
65
|
+
appId: `com.${projectInfo.packageJson?.name || 'app'}.electron`,
|
|
66
|
+
productName: projectInfo.packageJson?.name || 'Electron App',
|
|
67
|
+
directories: {
|
|
68
|
+
output: electronConfig.output || 'release',
|
|
69
|
+
},
|
|
70
|
+
files: [
|
|
71
|
+
'electron/**/*',
|
|
72
|
+
'dist/**/*',
|
|
73
|
+
'package.json',
|
|
74
|
+
],
|
|
75
|
+
mac: {
|
|
76
|
+
category: 'public.app-category.developer-tools',
|
|
77
|
+
icon: electronConfig.icon || null,
|
|
78
|
+
target: [
|
|
79
|
+
{ target: 'dmg', arch: ['x64', 'arm64'] },
|
|
80
|
+
{ target: 'zip', arch: ['x64', 'arm64'] },
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
win: {
|
|
84
|
+
icon: electronConfig.icon || null,
|
|
85
|
+
target: [
|
|
86
|
+
{ target: 'nsis', arch: ['x64'] },
|
|
87
|
+
{ target: 'portable', arch: ['x64'] },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
linux: {
|
|
91
|
+
icon: electronConfig.icon || null,
|
|
92
|
+
target: [
|
|
93
|
+
{ target: 'AppImage', arch: ['x64'] },
|
|
94
|
+
{ target: 'deb', arch: ['x64'] },
|
|
95
|
+
{ target: 'rpm', arch: ['x64'] },
|
|
96
|
+
],
|
|
97
|
+
category: 'Development',
|
|
98
|
+
},
|
|
99
|
+
nsis: {
|
|
100
|
+
oneClick: false,
|
|
101
|
+
allowToChangeInstallationDirectory: true,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
await fs.writeFile(
|
|
106
|
+
path.join(process.cwd(), 'electron-builder.yml'),
|
|
107
|
+
require('js-yaml').dump(builderConfig)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
logger.success('electron-builder.yml generated');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function electronDev(projectInfo, config, options) {
|
|
114
|
+
logger.info('Starting Electron in development mode...');
|
|
115
|
+
|
|
116
|
+
// Check if electron is installed
|
|
117
|
+
const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
118
|
+
if (!pkg.devDependencies?.electron && !pkg.dependencies?.electron) {
|
|
119
|
+
logger.info('Installing Electron...');
|
|
120
|
+
await new Promise((resolve, reject) => {
|
|
121
|
+
const install = spawn('npm', ['install', 'electron', '--save-dev'], {
|
|
122
|
+
stdio: 'inherit',
|
|
123
|
+
shell: true,
|
|
124
|
+
});
|
|
125
|
+
install.on('close', (code) => {
|
|
126
|
+
if (code === 0) resolve();
|
|
127
|
+
else reject(new Error('Failed to install Electron'));
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const child = spawn('electron', ['.'], {
|
|
133
|
+
stdio: 'inherit',
|
|
134
|
+
shell: true,
|
|
135
|
+
env: {
|
|
136
|
+
...process.env,
|
|
137
|
+
NODE_ENV: 'development',
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
child.on('close', (code) => {
|
|
143
|
+
if (code === 0) {
|
|
144
|
+
resolve();
|
|
145
|
+
} else {
|
|
146
|
+
reject(new Error('Electron dev failed'));
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function electronBuild(projectInfo, config, options) {
|
|
153
|
+
logger.info('Building Electron application...');
|
|
154
|
+
|
|
155
|
+
const args = ['electron-builder'];
|
|
156
|
+
|
|
157
|
+
// Platform
|
|
158
|
+
if (options.platform === 'all') {
|
|
159
|
+
args.push('-mwl');
|
|
160
|
+
} else if (options.platform === 'win') {
|
|
161
|
+
args.push('--win');
|
|
162
|
+
} else if (options.platform === 'mac') {
|
|
163
|
+
args.push('--mac');
|
|
164
|
+
} else if (options.platform === 'linux') {
|
|
165
|
+
args.push('--linux');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Architecture
|
|
169
|
+
if (options.arch && options.arch !== 'current') {
|
|
170
|
+
args.push(`--${options.arch}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const child = spawn('npx', args, {
|
|
174
|
+
stdio: 'inherit',
|
|
175
|
+
shell: true,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return new Promise((resolve, reject) => {
|
|
179
|
+
child.on('close', (code) => {
|
|
180
|
+
if (code === 0) {
|
|
181
|
+
logger.success('Electron build completed!');
|
|
182
|
+
resolve();
|
|
183
|
+
} else {
|
|
184
|
+
reject(new Error('Electron build failed'));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function electronPack(projectInfo, config, options) {
|
|
191
|
+
logger.info('Packaging Electron application...');
|
|
192
|
+
|
|
193
|
+
const child = spawn('npx', ['electron-builder', '--dir'], {
|
|
194
|
+
stdio: 'inherit',
|
|
195
|
+
shell: true,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return new Promise((resolve, reject) => {
|
|
199
|
+
child.on('close', (code) => {
|
|
200
|
+
if (code === 0) {
|
|
201
|
+
logger.success('Electron packaging completed!');
|
|
202
|
+
resolve();
|
|
203
|
+
} else {
|
|
204
|
+
reject(new Error('Electron packaging failed'));
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = electronCommand;
|