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.
@@ -0,0 +1,73 @@
1
+ const { Command } = require('commander');
2
+ const http = require('http');
3
+ const https = require('https');
4
+ const url = require('url');
5
+ const logger = require('../utils/logger');
6
+
7
+ const proxyCommand = new Command('proxy')
8
+ .description('Start a proxy server for development')
9
+ .option('-p, --port <port>', 'Port number', '8080')
10
+ .option('-t, --target <target>', 'Target URL to proxy to', 'http://localhost:3000')
11
+ .option('-c, --cors', 'Enable CORS', true)
12
+ .option('-l, --log', 'Log requests', true)
13
+ .action(async (options) => {
14
+ logger.header('🔄 Proxy Server');
15
+ logger.info(`Proxying to: ${options.target}`);
16
+ logger.info(`Listening on: http://localhost:${options.port}`);
17
+ logger.dim('Press Ctrl+C to stop');
18
+ logger.info('');
19
+
20
+ const server = http.createServer((req, res) => {
21
+ if (options.log) {
22
+ logger.dim(`${req.method} ${req.url}`);
23
+ }
24
+
25
+ // Handle CORS
26
+ if (options.cors) {
27
+ res.setHeader('Access-Control-Allow-Origin', '*');
28
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
29
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
30
+
31
+ if (req.method === 'OPTIONS') {
32
+ res.writeHead(200);
33
+ res.end();
34
+ return;
35
+ }
36
+ }
37
+
38
+ // Parse target URL
39
+ const targetUrl = new URL(req.url, options.target);
40
+
41
+ // Choose http or https based on protocol
42
+ const client = targetUrl.protocol === 'https:' ? https : http;
43
+
44
+ const proxyReq = client.request(
45
+ targetUrl,
46
+ {
47
+ method: req.method,
48
+ headers: {
49
+ ...req.headers,
50
+ host: targetUrl.host,
51
+ },
52
+ },
53
+ (proxyRes) => {
54
+ res.writeHead(proxyRes.statusCode, proxyRes.headers);
55
+ proxyRes.pipe(res);
56
+ }
57
+ );
58
+
59
+ proxyReq.on('error', (error) => {
60
+ logger.error(`Proxy error: ${error.message}`);
61
+ res.writeHead(502);
62
+ res.end('Bad Gateway');
63
+ });
64
+
65
+ req.pipe(proxyReq);
66
+ });
67
+
68
+ server.listen(options.port, () => {
69
+ logger.success(`Proxy server running on http://localhost:${options.port}`);
70
+ });
71
+ });
72
+
73
+ module.exports = proxyCommand;
@@ -0,0 +1,199 @@
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 runCommand = new Command('run')
12
+ .description('Run your project')
13
+ .option('-p, --port <port>', 'Port number', '3000')
14
+ .option('-h, --host <host>', 'Host address', 'localhost')
15
+ .option('-e, --env <environment>', 'Environment', 'development')
16
+ .option('--no-shared', 'Disable shared modules for this run')
17
+ .action(async (options) => {
18
+ logger.header('🏃 Running Project');
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(`Environment: ${options.env}`);
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 variables
48
+ process.env.NODE_ENV = options.env;
49
+ process.env.PORT = options.port;
50
+ process.env.HOST = options.host;
51
+
52
+ await runProject(projectInfo, configData, options);
53
+ } catch (error) {
54
+ logger.error(`Failed to run project: ${error.message}`);
55
+ process.exit(1);
56
+ }
57
+ });
58
+
59
+ async function runProject(projectInfo, config, options) {
60
+ const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
61
+ const scripts = pkg.scripts || {};
62
+
63
+ // Determine the run command based on framework
64
+ let command = null;
65
+ let args = [];
66
+
67
+ if (projectInfo.framework) {
68
+ const runCommands = {
69
+ // Backend
70
+ express: 'node',
71
+ fastify: 'node',
72
+ koa: 'node',
73
+ hapi: 'node',
74
+ nestjs: { command: 'npm', args: ['run', 'start:dev'] },
75
+ adonis: { command: 'npm', args: ['run', 'dev'] },
76
+ strapi: { command: 'npm', args: ['run', 'develop'] },
77
+ hono: 'npm run dev',
78
+ trpc: 'npm run dev',
79
+ graphql: 'npm run dev',
80
+ socketio: 'npm run dev',
81
+
82
+ // Frontend
83
+ react: 'npm start',
84
+ vue: 'npm run serve',
85
+ angular: 'ng serve',
86
+ svelte: 'npm run dev',
87
+ solid: 'npm run dev',
88
+ preact: 'npm run dev',
89
+ lit: 'npm run dev',
90
+ alpine: 'npm run dev',
91
+ htmx: 'npm run dev',
92
+
93
+ // Full-stack
94
+ nextjs: 'npm run dev',
95
+ nuxt: 'npm run dev',
96
+ remix: 'npm run dev',
97
+ gatsby: 'npm run develop',
98
+ astro: 'npm run dev',
99
+ sveltekit: 'npm run dev',
100
+ solidstart: 'npm run dev',
101
+ qwik: 'npm run dev',
102
+ analog: 'npm run dev',
103
+ fresh: 'deno task dev',
104
+ tanstack: 'npm run dev',
105
+ waku: 'npm run dev',
106
+
107
+ // Desktop
108
+ electron: 'npm run dev',
109
+ tauri: 'npm run dev',
110
+ nwjs: 'npm run dev',
111
+
112
+ // Mobile
113
+ reactnative: 'npm start',
114
+ expo: 'npm start',
115
+ ionic: 'npm start',
116
+ tamagui: 'npm start',
117
+ nativebase: 'npm start',
118
+
119
+ // Database
120
+ drizzle: 'npm run dev',
121
+ prisma: 'npm run dev',
122
+ };
123
+
124
+ const runConfig = runCommands[projectInfo.framework.name];
125
+
126
+ if (typeof runConfig === 'object') {
127
+ command = runConfig.command;
128
+ args = runConfig.args;
129
+ } else if (runConfig) {
130
+ const parts = runConfig.split(' ');
131
+ command = parts[0];
132
+ args = parts.slice(1);
133
+ }
134
+ }
135
+
136
+ // Fallback to package.json scripts
137
+ if (!command) {
138
+ if (scripts.dev) {
139
+ command = 'npm';
140
+ args = ['run', 'dev'];
141
+ } else if (scripts.start) {
142
+ command = 'npm';
143
+ args = ['run', 'start'];
144
+ } else if (scripts.serve) {
145
+ command = 'npm';
146
+ args = ['run', 'serve'];
147
+ } else {
148
+ // Try to run the entry point directly
149
+ const entryPoint = config.entry || 'src/index.js';
150
+ if (await fs.pathExists(path.join(process.cwd(), entryPoint))) {
151
+ command = 'node';
152
+ args = [entryPoint];
153
+ } else {
154
+ throw new Error('No run command found. Add a "dev" or "start" script to package.json');
155
+ }
156
+ }
157
+ }
158
+
159
+ logger.info(`Running: ${command} ${args.join(' ')}`);
160
+
161
+ const child = spawn(command, args, {
162
+ stdio: 'inherit',
163
+ shell: true,
164
+ env: {
165
+ ...process.env,
166
+ NODE_ENV: options.env,
167
+ PORT: options.port,
168
+ HOST: options.host,
169
+ },
170
+ });
171
+
172
+ child.on('error', (error) => {
173
+ logger.error(`Process failed: ${error.message}`);
174
+ process.exit(1);
175
+ });
176
+
177
+ child.on('close', (code) => {
178
+ if (code !== 0) {
179
+ logger.error(`Process exited with code ${code}`);
180
+ process.exit(code);
181
+ }
182
+ });
183
+ }
184
+
185
+ async function setupNodePath() {
186
+ const sharedPath = sharedModules.getSharedModulesPath();
187
+ const currentPath = process.env.NODE_PATH || '';
188
+
189
+ if (!currentPath.includes(sharedPath)) {
190
+ process.env.NODE_PATH = currentPath
191
+ ? `${sharedPath}${path.delimiter}${currentPath}`
192
+ : sharedPath;
193
+
194
+ // Rehash require paths
195
+ require('module')._initPaths();
196
+ }
197
+ }
198
+
199
+ module.exports = runCommand;
@@ -0,0 +1,104 @@
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 testCommand = new Command('test')
9
+ .description('Run tests')
10
+ .option('-w, --watch', 'Watch mode', false)
11
+ .option('-c, --coverage', 'Generate coverage report', false)
12
+ .option('-f, --filter <pattern>', 'Filter tests by pattern')
13
+ .action(async (options) => {
14
+ logger.header('🧪 Running Tests');
15
+
16
+ const detector = new ProjectDetector();
17
+
18
+ try {
19
+ const projectInfo = await detector.detect();
20
+
21
+ logger.info(`Framework: ${projectInfo.framework?.name || 'Unknown'}`);
22
+
23
+ await runTests(projectInfo, options);
24
+ } catch (error) {
25
+ logger.error(`Failed to run tests: ${error.message}`);
26
+ process.exit(1);
27
+ }
28
+ });
29
+
30
+ async function runTests(projectInfo, options) {
31
+ const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
32
+ const scripts = pkg.scripts || {};
33
+ const deps = {
34
+ ...pkg.dependencies,
35
+ ...pkg.devDependencies,
36
+ };
37
+
38
+ let command = null;
39
+ let args = [];
40
+
41
+ // Detect test framework
42
+ if (deps.jest || scripts.test?.includes('jest')) {
43
+ command = 'npm';
44
+ args = ['test'];
45
+ if (options.watch) args.push('--watch');
46
+ if (options.coverage) args.push('--coverage');
47
+ if (options.filter) args.push('--testNamePattern', options.filter);
48
+ } else if (deps.vitest || scripts.test?.includes('vitest')) {
49
+ command = 'npm';
50
+ args = ['test'];
51
+ if (options.watch) args.push('--watch');
52
+ if (options.coverage) args.push('--coverage');
53
+ if (options.filter) args.push('-t', options.filter);
54
+ } else if (deps.mocha || scripts.test?.includes('mocha')) {
55
+ command = 'npm';
56
+ args = ['test'];
57
+ if (options.watch) args.push('--watch');
58
+ if (options.filter) args.push('--grep', options.filter);
59
+ } else if (deps['@playwright/test'] || scripts.test?.includes('playwright')) {
60
+ command = 'npx';
61
+ args = ['playwright', 'test'];
62
+ if (options.filter) args.push(options.filter);
63
+ } else if (deps.cypress || scripts.test?.includes('cypress')) {
64
+ command = 'npx';
65
+ args = ['cypress', 'run'];
66
+ if (options.filter) args.push('--spec', options.filter);
67
+ } else if (scripts.test) {
68
+ command = 'npm';
69
+ args = ['test'];
70
+ } else {
71
+ // Try to run node test files
72
+ command = 'node';
73
+ args = ['--test'];
74
+ if (options.watch) args.push('--watch');
75
+ }
76
+
77
+ logger.info(`Running: ${command} ${args.join(' ')}`);
78
+ logger.info('');
79
+
80
+ const child = spawn(command, args, {
81
+ stdio: 'inherit',
82
+ shell: true,
83
+ env: {
84
+ ...process.env,
85
+ NODE_ENV: 'test',
86
+ },
87
+ });
88
+
89
+ child.on('error', (error) => {
90
+ logger.error(`Test process failed: ${error.message}`);
91
+ process.exit(1);
92
+ });
93
+
94
+ child.on('close', (code) => {
95
+ if (code !== 0) {
96
+ logger.error(`Tests failed with code ${code}`);
97
+ process.exit(code);
98
+ } else {
99
+ logger.success('All tests passed!');
100
+ }
101
+ });
102
+ }
103
+
104
+ module.exports = testCommand;
package/src/index.js ADDED
@@ -0,0 +1,30 @@
1
+ const ProjectDetector = require('./utils/detector');
2
+ const ConfigLoader = require('./utils/config');
3
+ const logger = require('./utils/logger');
4
+
5
+ module.exports = {
6
+ ProjectDetector,
7
+ ConfigLoader,
8
+ logger,
9
+
10
+ // Commands
11
+ run: require('./commands/run'),
12
+ build: require('./commands/build'),
13
+ init: require('./commands/init'),
14
+ dev: require('./commands/dev'),
15
+ test: require('./commands/test'),
16
+ deploy: require('./commands/deploy'),
17
+ docker: require('./commands/docker'),
18
+ electron: require('./commands/electron'),
19
+ package: require('./commands/package'),
20
+ analyze: require('./commands/analyze'),
21
+ clean: require('./commands/clean'),
22
+ lint: require('./commands/lint'),
23
+
24
+ // Targets
25
+ NodeBuilder: require('./targets/node'),
26
+ FrontendBuilder: require('./targets/frontend'),
27
+ ElectronBuilder: require('./targets/electron'),
28
+ DockerBuilder: require('./targets/docker'),
29
+ LibraryBuilder: require('./targets/library'),
30
+ };
@@ -0,0 +1,201 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs-extra');
4
+ const logger = require('../utils/logger');
5
+
6
+ class DockerBuilder {
7
+ constructor(projectInfo, config) {
8
+ this.projectInfo = projectInfo;
9
+ this.config = config;
10
+ this.dockerConfig = config.docker || {};
11
+ }
12
+
13
+ async build(options) {
14
+ logger.section('Building Docker Image');
15
+
16
+ // Ensure Dockerfile exists
17
+ await this.ensureDockerfile();
18
+
19
+ // Build image
20
+ await this.buildImage(options);
21
+
22
+ // Optionally run
23
+ if (options.run) {
24
+ await this.runContainer(options);
25
+ }
26
+ }
27
+
28
+ async ensureDockerfile() {
29
+ const dockerfilePath = path.join(process.cwd(), 'Dockerfile');
30
+
31
+ if (!(await fs.pathExists(dockerfilePath))) {
32
+ logger.info('Generating Dockerfile...');
33
+
34
+ const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
35
+ const baseImage = this.dockerConfig.baseImage || 'node:20-alpine';
36
+ const workdir = this.dockerConfig.workdir || '/app';
37
+ const port = this.dockerConfig.port || 3000;
38
+
39
+ // Determine if it's a frontend or backend project
40
+ const isFrontend = this.projectInfo.type === 'frontend' || this.projectInfo.type === 'fullstack';
41
+
42
+ let dockerfile;
43
+
44
+ if (isFrontend) {
45
+ dockerfile = `# Build stage
46
+ FROM ${baseImage} AS builder
47
+
48
+ WORKDIR ${workdir}
49
+
50
+ COPY package*.json ./
51
+ RUN npm ci
52
+
53
+ COPY . .
54
+ RUN npm run build
55
+
56
+ # Production stage
57
+ FROM nginx:alpine
58
+
59
+ COPY --from=builder ${workdir}/dist /usr/share/nginx/html
60
+
61
+ EXPOSE 80
62
+
63
+ CMD ["nginx", "-g", "daemon off;"]
64
+ `;
65
+ } else {
66
+ dockerfile = `# Build stage
67
+ FROM ${baseImage} AS builder
68
+
69
+ WORKDIR ${workdir}
70
+
71
+ COPY package*.json ./
72
+ RUN npm ci
73
+
74
+ COPY . .
75
+
76
+ # Build if build script exists
77
+ RUN npm run build 2>/dev/null || true
78
+
79
+ # Production stage
80
+ FROM ${baseImage}
81
+
82
+ WORKDIR ${workdir}
83
+
84
+ COPY package*.json ./
85
+ RUN npm ci --only=production
86
+
87
+ COPY --from=builder ${workdir}/dist ./dist
88
+ COPY --from=builder ${workdir}/src ./src
89
+
90
+ EXPOSE ${port}
91
+
92
+ CMD ["node", "src/index.js"]
93
+ `;
94
+ }
95
+
96
+ await fs.writeFile(dockerfilePath, dockerfile);
97
+ logger.success('Dockerfile generated');
98
+ }
99
+
100
+ // Ensure .dockerignore exists
101
+ const dockerignorePath = path.join(process.cwd(), '.dockerignore');
102
+ if (!(await fs.pathExists(dockerignorePath))) {
103
+ const dockerignore = `node_modules
104
+ npm-debug.log
105
+ dist
106
+ build
107
+ .git
108
+ .env
109
+ .env.*
110
+ *.log
111
+ `;
112
+ await fs.writeFile(dockerignorePath, dockerignore);
113
+ logger.success('.dockerignore generated');
114
+ }
115
+ }
116
+
117
+ async buildImage(options) {
118
+ const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
119
+ const imageName = pkg.name || 'app';
120
+ const tag = options.tag || 'latest';
121
+
122
+ logger.info(`Building Docker image: ${imageName}:${tag}`);
123
+
124
+ const args = ['build', '-t', `${imageName}:${tag}`, '.'];
125
+
126
+ const child = spawn('docker', args, {
127
+ stdio: 'inherit',
128
+ shell: true,
129
+ });
130
+
131
+ return new Promise((resolve, reject) => {
132
+ child.on('close', (code) => {
133
+ if (code === 0) {
134
+ logger.success(`Docker image built: ${imageName}:${tag}`);
135
+ resolve();
136
+ } else {
137
+ reject(new Error('Docker build failed'));
138
+ }
139
+ });
140
+ });
141
+ }
142
+
143
+ async runContainer(options) {
144
+ const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
145
+ const imageName = pkg.name || 'app';
146
+ const tag = options.tag || 'latest';
147
+ const containerName = `${imageName}-container`;
148
+ const port = this.dockerConfig.port || 3000;
149
+
150
+ logger.info(`Running container: ${containerName}`);
151
+
152
+ // Stop existing container if running
153
+ try {
154
+ await new Promise((resolve) => {
155
+ const stop = spawn('docker', ['stop', containerName], {
156
+ shell: true,
157
+ });
158
+ stop.on('close', () => resolve());
159
+ });
160
+ } catch (e) {
161
+ // Ignore
162
+ }
163
+
164
+ // Remove existing container
165
+ try {
166
+ await new Promise((resolve) => {
167
+ const rm = spawn('docker', ['rm', containerName], {
168
+ shell: true,
169
+ });
170
+ rm.on('close', () => resolve());
171
+ });
172
+ } catch (e) {
173
+ // Ignore
174
+ }
175
+
176
+ const args = [
177
+ 'run', '-d',
178
+ '--name', containerName,
179
+ '-p', `${port}:${port}`,
180
+ `${imageName}:${tag}`,
181
+ ];
182
+
183
+ const child = spawn('docker', args, {
184
+ stdio: 'inherit',
185
+ shell: true,
186
+ });
187
+
188
+ return new Promise((resolve, reject) => {
189
+ child.on('close', (code) => {
190
+ if (code === 0) {
191
+ logger.success(`Container running on port ${port}`);
192
+ resolve();
193
+ } else {
194
+ reject(new Error('Docker run failed'));
195
+ }
196
+ });
197
+ });
198
+ }
199
+ }
200
+
201
+ module.exports = DockerBuilder;