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,203 @@
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 analyzeCommand = new Command('analyze')
9
+ .description('Analyze bundle size and dependencies')
10
+ .option('-d, --dir <dir>', 'Directory to analyze', 'dist')
11
+ .option('-f, --format <format>', 'Output format (text, json)', 'text')
12
+ .action(async (options) => {
13
+ logger.header('📊 Analyzing Project');
14
+
15
+ const detector = new ProjectDetector();
16
+
17
+ try {
18
+ const projectInfo = await detector.detect();
19
+
20
+ await analyzeProject(projectInfo, options);
21
+ } catch (error) {
22
+ logger.error(`Analysis failed: ${error.message}`);
23
+ process.exit(1);
24
+ }
25
+ });
26
+
27
+ async function analyzeProject(projectInfo, options) {
28
+ const pkg = await fs.readJson(path.join(process.cwd(), 'package.json'));
29
+
30
+ // Analyze package.json dependencies
31
+ logger.section('Dependencies Analysis');
32
+
33
+ const deps = pkg.dependencies || {};
34
+ const devDeps = pkg.devDependencies || {};
35
+
36
+ logger.info(`Production dependencies: ${Object.keys(deps).length}`);
37
+ logger.info(`Development dependencies: ${Object.keys(devDeps).length}`);
38
+
39
+ // Analyze dependencies
40
+ const allDeps = { ...deps, ...devDeps };
41
+ const categories = categorizeDependencies(allDeps);
42
+
43
+ logger.info('');
44
+ logger.section('Dependency Categories');
45
+
46
+ for (const [category, depList] of Object.entries(categories)) {
47
+ if (depList.length > 0) {
48
+ logger.info(`${category}: ${depList.length}`);
49
+ depList.forEach(dep => logger.dim(` - ${dep}`));
50
+ }
51
+ }
52
+
53
+ // Check for outdated dependencies
54
+ logger.info('');
55
+ logger.section('Outdated Dependencies Check');
56
+
57
+ const child = spawn('npm', ['outdated', '--json'], {
58
+ stdio: 'pipe',
59
+ shell: true,
60
+ });
61
+
62
+ let output = '';
63
+ child.stdout.on('data', (data) => {
64
+ output += data.toString();
65
+ });
66
+
67
+ child.on('close', (code) => {
68
+ if (output) {
69
+ try {
70
+ const outdated = JSON.parse(output);
71
+ const outdatedCount = Object.keys(outdated).length;
72
+
73
+ if (outdatedCount > 0) {
74
+ logger.warning(`Found ${outdatedCount} outdated dependencies:`);
75
+ for (const [name, info] of Object.entries(outdated)) {
76
+ logger.dim(` ${name}: ${info.current} -> ${info.latest}`);
77
+ }
78
+ } else {
79
+ logger.success('All dependencies are up to date!');
80
+ }
81
+ } catch (e) {
82
+ logger.dim('Could not parse outdated dependencies');
83
+ }
84
+ } else {
85
+ logger.dim('No outdated dependencies found');
86
+ }
87
+
88
+ // Analyze dist folder if it exists
89
+ analyzeDistFolder(options.dir);
90
+ });
91
+ }
92
+
93
+ function categorizeDependencies(deps) {
94
+ const categories = {
95
+ 'Backend': [],
96
+ 'Frontend': [],
97
+ 'Database': [],
98
+ 'Testing': [],
99
+ 'Build Tools': [],
100
+ 'Linting': [],
101
+ 'TypeScript': [],
102
+ 'Utilities': [],
103
+ 'Other': [],
104
+ };
105
+
106
+ const patterns = {
107
+ 'Backend': ['express', 'fastify', 'koa', 'hapi', 'nest', 'adonis', 'strapi'],
108
+ 'Frontend': ['react', 'vue', 'angular', 'svelte', 'preact', 'lit'],
109
+ 'Database': ['mongoose', 'sequelize', 'typeorm', 'prisma', 'knex', 'pg', 'mysql', 'mongodb'],
110
+ 'Testing': ['jest', 'mocha', 'chai', 'vitest', 'cypress', 'playwright'],
111
+ 'Build Tools': ['webpack', 'esbuild', 'vite', 'rollup', 'parcel', 'turbo', 'babel'],
112
+ 'Linting': ['eslint', 'prettier', 'stylelint'],
113
+ 'TypeScript': ['typescript', 'ts-node', 'tsx'],
114
+ 'Utilities': ['lodash', 'axios', 'dotenv', 'chalk', 'commander', 'inquirer'],
115
+ };
116
+
117
+ for (const [dep] of Object.entries(deps)) {
118
+ let categorized = false;
119
+
120
+ for (const [category, keywords] of Object.entries(patterns)) {
121
+ if (keywords.some(keyword => dep.toLowerCase().includes(keyword))) {
122
+ categories[category].push(dep);
123
+ categorized = true;
124
+ break;
125
+ }
126
+ }
127
+
128
+ if (!categorized) {
129
+ categories['Other'].push(dep);
130
+ }
131
+ }
132
+
133
+ return categories;
134
+ }
135
+
136
+ async function analyzeDistFolder(dir) {
137
+ const distPath = path.join(process.cwd(), dir);
138
+
139
+ if (!(await fs.pathExists(distPath))) {
140
+ logger.info('');
141
+ logger.dim(`No ${dir} folder found. Run "easy build" first to analyze build output.`);
142
+ return;
143
+ }
144
+
145
+ logger.info('');
146
+ logger.section('Build Output Analysis');
147
+
148
+ const files = await getAllFiles(distPath);
149
+ let totalSize = 0;
150
+
151
+ for (const file of files) {
152
+ const stat = await fs.stat(file);
153
+ totalSize += stat.size;
154
+ }
155
+
156
+ logger.info(`Total files: ${files.length}`);
157
+ logger.info(`Total size: ${formatSize(totalSize)}`);
158
+
159
+ // Find largest files
160
+ const fileSizes = [];
161
+ for (const file of files) {
162
+ const stat = await fs.stat(file);
163
+ fileSizes.push({ file: path.relative(distPath, file), size: stat.size });
164
+ }
165
+
166
+ fileSizes.sort((a, b) => b.size - a.size);
167
+
168
+ if (fileSizes.length > 0) {
169
+ logger.info('');
170
+ logger.info('Largest files:');
171
+ fileSizes.slice(0, 10).forEach(({ file, size }) => {
172
+ logger.dim(` ${file}: ${formatSize(size)}`);
173
+ });
174
+ }
175
+ }
176
+
177
+ async function getAllFiles(dir) {
178
+ const files = [];
179
+ const items = await fs.readdir(dir);
180
+
181
+ for (const item of items) {
182
+ const fullPath = path.join(dir, item);
183
+ const stat = await fs.stat(fullPath);
184
+
185
+ if (stat.isDirectory()) {
186
+ files.push(...await getAllFiles(fullPath));
187
+ } else {
188
+ files.push(fullPath);
189
+ }
190
+ }
191
+
192
+ return files;
193
+ }
194
+
195
+ function formatSize(bytes) {
196
+ if (bytes === 0) return '0 Bytes';
197
+ const k = 1024;
198
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
199
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
200
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
201
+ }
202
+
203
+ module.exports = analyzeCommand;
@@ -0,0 +1,80 @@
1
+ const { Command } = require('commander');
2
+ const path = require('path');
3
+ const fs = require('fs-extra');
4
+ const logger = require('../utils/logger');
5
+ const ProjectDetector = require('../utils/detector');
6
+ const ConfigLoader = require('../utils/config');
7
+ const NodeBuilder = require('../targets/node');
8
+ const FrontendBuilder = require('../targets/frontend');
9
+ const ElectronBuilder = require('../targets/electron');
10
+ const DockerBuilder = require('../targets/docker');
11
+ const LibraryBuilder = require('../targets/library');
12
+
13
+ const buildCommand = new Command('build')
14
+ .description('Build your project')
15
+ .option('-t, --target <target>', 'Build target (node, frontend, electron, docker, library)', 'auto')
16
+ .option('-p, --platform <platform>', 'Target platform (win, linux, mac, all)', 'current')
17
+ .option('--minify', 'Minify output', true)
18
+ .option('--sourcemap', 'Generate source maps', false)
19
+ .option('--analyze', 'Analyze bundle size', false)
20
+ .option('-o, --outDir <dir>', 'Output directory', 'dist')
21
+ .action(async (options) => {
22
+ logger.header('🔨 Building Project');
23
+
24
+ const detector = new ProjectDetector();
25
+ const config = new ConfigLoader();
26
+
27
+ try {
28
+ const projectInfo = await detector.detect();
29
+ const configData = await config.load();
30
+
31
+ logger.info(`Framework: ${projectInfo.framework?.name || 'Unknown'}`);
32
+ logger.info(`Type: ${projectInfo.type || 'Unknown'}`);
33
+ logger.info(`Bundler: ${projectInfo.bundler || 'Unknown'}`);
34
+ logger.info(`Target: ${options.target}`);
35
+
36
+ // Clean output directory if configured
37
+ if (configData.build.clean) {
38
+ await fs.remove(path.join(process.cwd(), options.outDir));
39
+ }
40
+
41
+ // Build based on target
42
+ let builder;
43
+ const target = options.target === 'auto' ? determineTarget(projectInfo) : options.target;
44
+
45
+ switch (target) {
46
+ case 'electron':
47
+ builder = new ElectronBuilder(projectInfo, configData);
48
+ break;
49
+ case 'docker':
50
+ builder = new DockerBuilder(projectInfo, configData);
51
+ break;
52
+ case 'library':
53
+ builder = new LibraryBuilder(projectInfo, configData);
54
+ break;
55
+ case 'frontend':
56
+ builder = new FrontendBuilder(projectInfo, configData);
57
+ break;
58
+ case 'node':
59
+ default:
60
+ builder = new NodeBuilder(projectInfo, configData);
61
+ }
62
+
63
+ await builder.build(options);
64
+
65
+ logger.success('Build completed successfully!');
66
+ } catch (error) {
67
+ logger.error(`Build failed: ${error.message}`);
68
+ process.exit(1);
69
+ }
70
+ });
71
+
72
+ function determineTarget(projectInfo) {
73
+ if (projectInfo.type === 'desktop') return 'electron';
74
+ if (projectInfo.type === 'frontend') return 'frontend';
75
+ if (projectInfo.type === 'library') return 'library';
76
+ if (projectInfo.type === 'mobile') return 'frontend';
77
+ return 'node';
78
+ }
79
+
80
+ module.exports = buildCommand;
@@ -0,0 +1,109 @@
1
+ const { Command } = require('commander');
2
+ const path = require('path');
3
+ const fs = require('fs-extra');
4
+ const logger = require('../utils/logger');
5
+
6
+ const cleanCommand = new Command('clean')
7
+ .description('Clean build artifacts and caches')
8
+ .option('-d, --dist', 'Clean dist folder', false)
9
+ .option('-n, --node-modules', 'Clean node_modules', false)
10
+ .option('-c, --cache', 'Clean cache', false)
11
+ .option('-a, --all', 'Clean everything', false)
12
+ .action(async (options) => {
13
+ logger.header('🧹 Cleaning Project');
14
+
15
+ try {
16
+ if (options.all || (!options.dist && !options.nodeModules && !options.cache)) {
17
+ // Clean everything by default
18
+ await cleanDist();
19
+ await cleanCache();
20
+ logger.success('Project cleaned!');
21
+ } else {
22
+ if (options.dist) await cleanDist();
23
+ if (options.nodeModules) await cleanNodeModules();
24
+ if (options.cache) await cleanCache();
25
+ logger.success('Selected items cleaned!');
26
+ }
27
+ } catch (error) {
28
+ logger.error(`Clean failed: ${error.message}`);
29
+ process.exit(1);
30
+ }
31
+ });
32
+
33
+ async function cleanDist() {
34
+ const distPath = path.join(process.cwd(), 'dist');
35
+ const buildPath = path.join(process.cwd(), 'build');
36
+ const outPath = path.join(process.cwd(), 'out');
37
+ const releasePath = path.join(process.cwd(), 'release');
38
+
39
+ const paths = [distPath, buildPath, outPath, releasePath];
40
+
41
+ for (const p of paths) {
42
+ if (await fs.pathExists(p)) {
43
+ await fs.remove(p);
44
+ logger.info(`Removed: ${path.relative(process.cwd(), p)}`);
45
+ }
46
+ }
47
+ }
48
+
49
+ async function cleanNodeModules() {
50
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules');
51
+ const packageLockPath = path.join(process.cwd(), 'package-lock.json');
52
+ const yarnLockPath = path.join(process.cwd(), 'yarn.lock');
53
+ const pnpmLockPath = path.join(process.cwd(), 'pnpm-lock.yaml');
54
+
55
+ const paths = [nodeModulesPath, packageLockPath, yarnLockPath, pnpmLockPath];
56
+
57
+ for (const p of paths) {
58
+ if (await fs.pathExists(p)) {
59
+ await fs.remove(p);
60
+ logger.info(`Removed: ${path.relative(process.cwd(), p)}`);
61
+ }
62
+ }
63
+ }
64
+
65
+ async function cleanCache() {
66
+ // npm cache
67
+ const npmCachePath = path.join(process.cwd(), '.npm');
68
+ if (await fs.pathExists(npmCachePath)) {
69
+ await fs.remove(npmCachePath);
70
+ logger.info('Removed: .npm cache');
71
+ }
72
+
73
+ // ESLint cache
74
+ const eslintCachePath = path.join(process.cwd(), '.eslintcache');
75
+ if (await fs.pathExists(eslintCachePath)) {
76
+ await fs.remove(eslintCachePath);
77
+ logger.info('Removed: .eslintcache');
78
+ }
79
+
80
+ // TypeScript cache
81
+ const tsCachePath = path.join(process.cwd(), 'tsconfig.tsbuildinfo');
82
+ if (await fs.pathExists(tsCachePath)) {
83
+ await fs.remove(tsCachePath);
84
+ logger.info('Removed: tsconfig.tsbuildinfo');
85
+ }
86
+
87
+ // Vite cache
88
+ const viteCachePath = path.join(process.cwd(), 'node_modules/.vite');
89
+ if (await fs.pathExists(viteCachePath)) {
90
+ await fs.remove(viteCachePath);
91
+ logger.info('Removed: Vite cache');
92
+ }
93
+
94
+ // Next.js cache
95
+ const nextCachePath = path.join(process.cwd(), '.next');
96
+ if (await fs.pathExists(nextCachePath)) {
97
+ await fs.remove(nextCachePath);
98
+ logger.info('Removed: .next cache');
99
+ }
100
+
101
+ // Nuxt cache
102
+ const nuxtCachePath = path.join(process.cwd(), '.nuxt');
103
+ if (await fs.pathExists(nuxtCachePath)) {
104
+ await fs.remove(nuxtCachePath);
105
+ logger.info('Removed: .nuxt cache');
106
+ }
107
+ }
108
+
109
+ module.exports = cleanCommand;
@@ -0,0 +1,104 @@
1
+ const { Command } = require('commander');
2
+ const logger = require('../utils/logger');
3
+ const globalConfig = require('../utils/globalConfig');
4
+
5
+ const configCommand = new Command('config')
6
+ .description('Manage global easy-build configuration')
7
+ .option('-g, --get <key>', 'Get config value')
8
+ .option('--enable-shared', 'Enable shared modules globally')
9
+ .option('--disable-shared', 'Disable shared modules globally')
10
+ .option('--show', 'Show current configuration')
11
+ .action(async (options) => {
12
+ logger.header('⚙️ Global Configuration');
13
+
14
+ try {
15
+ await globalConfig.load();
16
+
17
+ if (options.show) {
18
+ await showConfig();
19
+ } else if (options.enableShared) {
20
+ await globalConfig.enableSharedModules();
21
+ } else if (options.disableShared) {
22
+ await globalConfig.disableSharedModules();
23
+ } else if (options.get) {
24
+ const value = globalConfig.get(options.get);
25
+ if (value !== undefined) {
26
+ console.log(JSON.stringify(value, null, 2));
27
+ } else {
28
+ logger.warning(`Key "${options.get}" not found`);
29
+ }
30
+ } else {
31
+ await showConfig();
32
+ }
33
+ } catch (error) {
34
+ logger.error(`Config operation failed: ${error.message}`);
35
+ process.exit(1);
36
+ }
37
+ });
38
+
39
+ configCommand
40
+ .command('set <key> <value>')
41
+ .description('Set a config value')
42
+ .action(async (key, value) => {
43
+ logger.header('⚙️ Global Configuration');
44
+
45
+ try {
46
+ await globalConfig.load();
47
+ globalConfig.set(key, parseValue(value));
48
+ await globalConfig.save();
49
+ logger.success(`Set ${key} = ${value}`);
50
+ } catch (error) {
51
+ logger.error(`Config operation failed: ${error.message}`);
52
+ process.exit(1);
53
+ }
54
+ });
55
+
56
+ async function showConfig() {
57
+ const config = await globalConfig.load();
58
+
59
+ logger.section('Current Configuration');
60
+ logger.info(`Config file: ${globalConfig.getConfigPath()}`);
61
+ logger.info('');
62
+
63
+ // Shared Modules
64
+ logger.bold('Shared Modules:');
65
+ logger.dim(` enabled: ${config.sharedModules?.enabled || false}`);
66
+ logger.dim(` autoSync: ${config.sharedModules?.autoSync || false}`);
67
+ logger.dim(` autoInstall: ${config.sharedModules?.autoInstall || true}`);
68
+ logger.info('');
69
+
70
+ // Build
71
+ logger.bold('Build:');
72
+ logger.dim(` minify: ${config.build?.minify || true}`);
73
+ logger.dim(` sourcemap: ${config.build?.sourcemap || false}`);
74
+ logger.dim(` outDir: ${config.build?.outDir || 'dist'}`);
75
+ logger.info('');
76
+
77
+ // Dev
78
+ logger.bold('Dev Server:');
79
+ logger.dim(` port: ${config.dev?.port || 3000}`);
80
+ logger.dim(` host: ${config.dev?.host || 'localhost'}`);
81
+ logger.dim(` open: ${config.dev?.open || true}`);
82
+ logger.info('');
83
+
84
+ // Tips
85
+ logger.section('Quick Commands');
86
+ logger.dim(' easy config --enable-shared Enable shared modules');
87
+ logger.dim(' easy config --disable-shared Disable shared modules');
88
+ logger.dim(' easy config -s build.minify false Disable minification');
89
+ }
90
+
91
+ function parseValue(value) {
92
+ // Parse boolean
93
+ if (value === 'true') return true;
94
+ if (value === 'false') return false;
95
+
96
+ // Parse number
97
+ const num = Number(value);
98
+ if (!isNaN(num)) return num;
99
+
100
+ // Return string
101
+ return value;
102
+ }
103
+
104
+ module.exports = configCommand;