create-dry-monorepo 0.0.0-stage → 0.1.3
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/bin/index.js +270 -0
- package/package.json +29 -3
- package/README.md +0 -3
package/bin/index.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const https = require('https');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const { spawnSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const platform = os.platform();
|
|
12
|
+
const arch = os.arch();
|
|
13
|
+
|
|
14
|
+
const REPO = 'momanamjad/iframe';
|
|
15
|
+
const VERSION = (() => {
|
|
16
|
+
try {
|
|
17
|
+
return require('../package.json').version || '0.1.0';
|
|
18
|
+
} catch {
|
|
19
|
+
return '0.1.0';
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
22
|
+
|
|
23
|
+
const getArtifactName = () => {
|
|
24
|
+
const map = {
|
|
25
|
+
'win32-x64': 'dry-monorepo-win32-x64.exe',
|
|
26
|
+
'darwin-arm64': 'dry-monorepo-darwin-arm64',
|
|
27
|
+
'darwin-x64': 'dry-monorepo-darwin-x64',
|
|
28
|
+
'linux-x64': 'dry-monorepo-linux-x64',
|
|
29
|
+
'linux-arm64': 'dry-monorepo-linux-arm64',
|
|
30
|
+
};
|
|
31
|
+
return map[`${platform}-${arch}`];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getPackageName = () => {
|
|
35
|
+
const map = {
|
|
36
|
+
'win32-x64': '@dry-monorepo/cli-win32-x64',
|
|
37
|
+
'win32-arm64': '@dry-monorepo/cli-win32-arm64',
|
|
38
|
+
'darwin-x64': '@dry-monorepo/cli-darwin-x64',
|
|
39
|
+
'darwin-arm64': '@dry-monorepo/cli-darwin-arm64',
|
|
40
|
+
'linux-x64': '@dry-monorepo/cli-linux-x64',
|
|
41
|
+
'linux-arm64': '@dry-monorepo/cli-linux-arm64',
|
|
42
|
+
};
|
|
43
|
+
return map[`${platform}-${arch}`];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const downloadToBuffer = (url) => {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const request = (currentUrl, redirects = 0) => {
|
|
49
|
+
if (redirects > 5) {
|
|
50
|
+
return reject(new Error('Too many HTTP redirects'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const client = currentUrl.startsWith('https') ? https : http;
|
|
54
|
+
client
|
|
55
|
+
.get(currentUrl, (response) => {
|
|
56
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
57
|
+
return request(response.headers.location, redirects + 1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (response.statusCode !== 200) {
|
|
61
|
+
return reject(new Error(`Download failed with status ${response.statusCode}`));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const chunks = [];
|
|
65
|
+
response.on('data', (chunk) => chunks.push(chunk));
|
|
66
|
+
response.on('end', () => resolve(Buffer.concat(chunks)));
|
|
67
|
+
})
|
|
68
|
+
.on('error', reject);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
request(url);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Download and parse the checksums file from GitHub Releases.
|
|
77
|
+
* Returns a Map of filename -> sha256 hash.
|
|
78
|
+
*/
|
|
79
|
+
const downloadChecksums = async () => {
|
|
80
|
+
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/checksums.sha256`;
|
|
81
|
+
try {
|
|
82
|
+
const buffer = await downloadToBuffer(url);
|
|
83
|
+
const text = buffer.toString('utf8');
|
|
84
|
+
const checksums = new Map();
|
|
85
|
+
for (const line of text.split('\n')) {
|
|
86
|
+
const trimmed = line.trim();
|
|
87
|
+
if (!trimmed) continue;
|
|
88
|
+
// Format: "<hash> <filename>" or "<hash> <filename>"
|
|
89
|
+
const parts = trimmed.split(/\s+/);
|
|
90
|
+
if (parts.length >= 2) {
|
|
91
|
+
checksums.set(parts[1], parts[0]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return checksums;
|
|
95
|
+
} catch {
|
|
96
|
+
return null; // Checksums file not available
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Verify SHA-256 checksum of a downloaded file.
|
|
102
|
+
*/
|
|
103
|
+
const verifyChecksum = (filePath, expectedHash) => {
|
|
104
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
105
|
+
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
|
106
|
+
return hash === expectedHash;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const findOrDownloadBinary = async () => {
|
|
110
|
+
// 1. Try resolving from optional dependencies
|
|
111
|
+
const packageName = getPackageName();
|
|
112
|
+
if (packageName) {
|
|
113
|
+
try {
|
|
114
|
+
return require.resolve(`${packageName}/bin/dry-monorepo`);
|
|
115
|
+
} catch {
|
|
116
|
+
// Optional package not installed
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 2. Check if installed in system PATH
|
|
121
|
+
const isWindows = platform === 'win32';
|
|
122
|
+
const binName = isWindows ? 'dry-monorepo.exe' : 'dry-monorepo';
|
|
123
|
+
const checkCmd = isWindows ? 'where' : 'which';
|
|
124
|
+
const pathCheck = spawnSync(checkCmd, [binName], { stdio: 'ignore' });
|
|
125
|
+
if (pathCheck.status === 0) {
|
|
126
|
+
return binName;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 2b. Check local development workspace target directories
|
|
130
|
+
const localCandidates = [
|
|
131
|
+
path.resolve(__dirname, '..', '..', 'target', 'release', binName),
|
|
132
|
+
path.resolve(__dirname, '..', '..', 'target', 'debug', binName),
|
|
133
|
+
path.resolve(process.cwd(), 'target', 'release', binName),
|
|
134
|
+
path.resolve(process.cwd(), 'target', 'debug', binName),
|
|
135
|
+
];
|
|
136
|
+
for (const localBin of localCandidates) {
|
|
137
|
+
if (fs.existsSync(localBin)) {
|
|
138
|
+
return localBin;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 3. Check user cache directory (~/.dry-monorepo/bin)
|
|
143
|
+
const artifactName = getArtifactName();
|
|
144
|
+
if (!artifactName) {
|
|
145
|
+
console.error(`\x1b[31mError: Unsupported platform or architecture: ${platform}-${arch}\x1b[0m`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const cacheDir = path.join(os.homedir(), '.dry-monorepo', 'bin');
|
|
150
|
+
const cachedBinPath = path.join(cacheDir, `${VERSION}-${artifactName}`);
|
|
151
|
+
|
|
152
|
+
if (fs.existsSync(cachedBinPath)) {
|
|
153
|
+
return cachedBinPath;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 4. Download from GitHub Releases with integrity verification
|
|
157
|
+
console.log(`\x1b[36m⚡ DRY Monorepo Generator: First-time setup...\x1b[0m`);
|
|
158
|
+
console.log(`\x1b[33m📦 Fetching native precompiled Rust binary for ${platform}-${arch}...\x1b[0m`);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
162
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/v${VERSION}/${artifactName}`;
|
|
163
|
+
|
|
164
|
+
// Download binary to a temp file first
|
|
165
|
+
const tempPath = cachedBinPath + '.tmp';
|
|
166
|
+
const binaryBuffer = await downloadToBuffer(downloadUrl);
|
|
167
|
+
fs.writeFileSync(tempPath, binaryBuffer);
|
|
168
|
+
|
|
169
|
+
// Verify SHA-256 checksum if checksums file is available
|
|
170
|
+
const checksums = await downloadChecksums();
|
|
171
|
+
if (checksums) {
|
|
172
|
+
const expectedHash = checksums.get(artifactName);
|
|
173
|
+
if (expectedHash) {
|
|
174
|
+
if (!verifyChecksum(tempPath, expectedHash)) {
|
|
175
|
+
fs.unlinkSync(tempPath);
|
|
176
|
+
throw new Error(`SHA-256 checksum mismatch for ${artifactName}. Binary may be corrupted or tampered with.`);
|
|
177
|
+
}
|
|
178
|
+
console.log(`\x1b[32m🔒 SHA-256 checksum verified.\x1b[0m`);
|
|
179
|
+
} else {
|
|
180
|
+
console.warn(`\x1b[33m⚠️ No checksum entry found for ${artifactName}, skipping verification.\x1b[0m`);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
console.warn(`\x1b[33m⚠️ Checksums file not available, skipping integrity verification.\x1b[0m`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Move temp file to final location
|
|
187
|
+
fs.renameSync(tempPath, cachedBinPath);
|
|
188
|
+
|
|
189
|
+
if (!isWindows) {
|
|
190
|
+
fs.chmodSync(cachedBinPath, 0o755);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log(`\x1b[32m✨ Setup complete! Launching DRY Monorepo Generator...\x1b[0m\n`);
|
|
194
|
+
return cachedBinPath;
|
|
195
|
+
} catch (err) {
|
|
196
|
+
// Clean up temp file on failure
|
|
197
|
+
const tempPath = cachedBinPath + '.tmp';
|
|
198
|
+
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
|
199
|
+
if (fs.existsSync(cachedBinPath)) fs.unlinkSync(cachedBinPath);
|
|
200
|
+
|
|
201
|
+
console.warn(`\x1b[33m⚠️ Could not auto-download prebuilt binary (${err.message}).\x1b[0m`);
|
|
202
|
+
console.warn(`Falling back to system PATH binary if available...\n`);
|
|
203
|
+
return binName;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const run = async () => {
|
|
208
|
+
const command = await findOrDownloadBinary();
|
|
209
|
+
const rawArgs = process.argv.slice(2);
|
|
210
|
+
const knownCommands = new Set([
|
|
211
|
+
'new', 'test', 'list', 'community', 'doctor', 'deploy', 'bench', 'help', '--help', '-h', '--version', '-V'
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
let isYes = false;
|
|
215
|
+
let forwardedArgs = [];
|
|
216
|
+
let selectedPm = null;
|
|
217
|
+
|
|
218
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
219
|
+
const arg = rawArgs[i];
|
|
220
|
+
if (arg === '--yes' || arg === '-y') {
|
|
221
|
+
isYes = true;
|
|
222
|
+
} else if (arg === '--pm' && i + 1 < rawArgs.length) {
|
|
223
|
+
selectedPm = rawArgs[++i];
|
|
224
|
+
} else if (arg.startsWith('--pm=')) {
|
|
225
|
+
selectedPm = arg.split('=')[1];
|
|
226
|
+
} else {
|
|
227
|
+
forwardedArgs.push(arg);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let finalArgs = [];
|
|
232
|
+
if (isYes) {
|
|
233
|
+
finalArgs.push('--yes');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (forwardedArgs.length > 0 && !knownCommands.has(forwardedArgs[0]) && !forwardedArgs[0].startsWith('-')) {
|
|
237
|
+
finalArgs.push('new', ...forwardedArgs);
|
|
238
|
+
} else if (forwardedArgs.length === 0 || (!knownCommands.has(forwardedArgs[0]) && forwardedArgs[0].startsWith('-'))) {
|
|
239
|
+
finalArgs.push('new', ...forwardedArgs);
|
|
240
|
+
} else {
|
|
241
|
+
finalArgs.push(...forwardedArgs);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const hasLocalTemplates = fs.existsSync(path.resolve(process.cwd(), 'templates')) ||
|
|
246
|
+
fs.existsSync(path.resolve(__dirname, '..', '..', 'templates'));
|
|
247
|
+
const env = { ...process.env };
|
|
248
|
+
if (hasLocalTemplates) {
|
|
249
|
+
env.DRY_DEV_TEMPLATES = '1';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const result = spawnSync(command, finalArgs, { stdio: 'inherit', env });
|
|
253
|
+
if (result.error) {
|
|
254
|
+
if (result.error.code === 'ENOENT') {
|
|
255
|
+
console.error(`\x1b[31mError: Could not find or launch the dry-monorepo executable.\x1b[0m`);
|
|
256
|
+
console.error(`Please build from source using 'cargo build --release -p dry-cli',`);
|
|
257
|
+
console.error(`or verify your internet connection to allow downloading the precompiled release binary.`);
|
|
258
|
+
} else {
|
|
259
|
+
console.error(result.error.message);
|
|
260
|
+
}
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
process.exit(result.status || 0);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.error('An unexpected error occurred:', err);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
run();
|
package/package.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-dry-monorepo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "🚀 One Command. Infinite Possibilities. Every Stack. Zero Limits.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-dry-monorepo": "./bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"monorepo",
|
|
13
|
+
"generator",
|
|
14
|
+
"scaffold",
|
|
15
|
+
"boilerplate",
|
|
16
|
+
"mern",
|
|
17
|
+
"pern",
|
|
18
|
+
"fastapi",
|
|
19
|
+
"django",
|
|
20
|
+
"go",
|
|
21
|
+
"flask",
|
|
22
|
+
"fullstack"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/momanamjad/iframe.git"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT"
|
|
6
32
|
}
|
package/README.md
DELETED