stone-lang 0.1.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/README.md +52 -0
- package/StoneEngine.js +879 -0
- package/StoneEngineService.js +1727 -0
- package/adapters/FileSystemAdapter.js +230 -0
- package/adapters/OutputAdapter.js +208 -0
- package/adapters/index.js +6 -0
- package/cli/CLIOutputAdapter.js +196 -0
- package/cli/DaemonClient.js +349 -0
- package/cli/JSONOutputAdapter.js +135 -0
- package/cli/ReplSession.js +567 -0
- package/cli/ViewerServer.js +590 -0
- package/cli/commands/check.js +84 -0
- package/cli/commands/daemon.js +189 -0
- package/cli/commands/kill.js +66 -0
- package/cli/commands/package.js +713 -0
- package/cli/commands/ps.js +65 -0
- package/cli/commands/run.js +537 -0
- package/cli/entry.js +169 -0
- package/cli/index.js +14 -0
- package/cli/stonec.js +358 -0
- package/cli/test-compiler.js +181 -0
- package/cli/viewer/index.html +495 -0
- package/daemon/IPCServer.js +455 -0
- package/daemon/ProcessManager.js +327 -0
- package/daemon/ProcessRunner.js +307 -0
- package/daemon/daemon.js +398 -0
- package/daemon/index.js +16 -0
- package/frontend/analysis/index.js +5 -0
- package/frontend/analysis/livenessAnalyzer.js +568 -0
- package/frontend/analysis/treeShaker.js +265 -0
- package/frontend/index.js +20 -0
- package/frontend/parsing/astBuilder.js +2196 -0
- package/frontend/parsing/index.js +7 -0
- package/frontend/parsing/sonParser.js +592 -0
- package/frontend/parsing/stoneAstTypes.js +703 -0
- package/frontend/parsing/terminal-registry.js +435 -0
- package/frontend/parsing/tokenizer.js +692 -0
- package/frontend/type-checker/OverloadedFunctionType.js +43 -0
- package/frontend/type-checker/TypeEnvironment.js +165 -0
- package/frontend/type-checker/bidirectionalInference.js +149 -0
- package/frontend/type-checker/index.js +10 -0
- package/frontend/type-checker/moduleAnalysis.js +248 -0
- package/frontend/type-checker/operatorMappings.js +35 -0
- package/frontend/type-checker/overloadResolution.js +605 -0
- package/frontend/type-checker/typeChecker.js +452 -0
- package/frontend/type-checker/typeCompatibility.js +389 -0
- package/frontend/type-checker/visitors/controlFlow.js +483 -0
- package/frontend/type-checker/visitors/functions.js +604 -0
- package/frontend/type-checker/visitors/index.js +38 -0
- package/frontend/type-checker/visitors/literals.js +341 -0
- package/frontend/type-checker/visitors/modules.js +159 -0
- package/frontend/type-checker/visitors/operators.js +109 -0
- package/frontend/type-checker/visitors/statements.js +768 -0
- package/frontend/types/index.js +5 -0
- package/frontend/types/operatorMap.js +134 -0
- package/frontend/types/types.js +2046 -0
- package/frontend/utils/errorCollector.js +244 -0
- package/frontend/utils/index.js +5 -0
- package/frontend/utils/moduleResolver.js +479 -0
- package/package.json +50 -0
- package/packages/browserCache.js +359 -0
- package/packages/fetcher.js +236 -0
- package/packages/index.js +130 -0
- package/packages/lockfile.js +271 -0
- package/packages/manifest.js +291 -0
- package/packages/packageResolver.js +356 -0
- package/packages/resolver.js +310 -0
- package/packages/semver.js +635 -0
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package commands - Manage Stone packages
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* stone install Install dependencies from package.son
|
|
6
|
+
* stone install <pkg> --source <url> Add and install from URL
|
|
7
|
+
* stone install <pkg> --source <path> Add and install from local directory
|
|
8
|
+
* stone install -g <pkg> Install package globally
|
|
9
|
+
* stone remove <pkg> Remove a dependency
|
|
10
|
+
* stone update Update all dependencies
|
|
11
|
+
* stone update <pkg> Update specific package
|
|
12
|
+
* stone list List installed packages
|
|
13
|
+
* stone init Create new package.son
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { parseManifest, createManifest, stringifyManifest } from '../../packages/manifest.js';
|
|
19
|
+
import { parseLockFile, LockFile, stringifyLockFile } from '../../packages/lockfile.js';
|
|
20
|
+
import { DependencyResolver } from '../../packages/resolver.js';
|
|
21
|
+
import { createFetcher } from '../../packages/fetcher.js';
|
|
22
|
+
import { parseRange, maxSatisfying } from '../../packages/semver.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if a source is a URL or a local path
|
|
26
|
+
*/
|
|
27
|
+
function isUrl(source) {
|
|
28
|
+
return source.startsWith('http://') || source.startsWith('https://');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a source is a local path
|
|
33
|
+
*/
|
|
34
|
+
function isLocalPath(source) {
|
|
35
|
+
// Absolute paths or relative paths starting with . or /
|
|
36
|
+
return !isUrl(source) && (
|
|
37
|
+
path.isAbsolute(source) ||
|
|
38
|
+
source.startsWith('./') ||
|
|
39
|
+
source.startsWith('../') ||
|
|
40
|
+
source.startsWith('.\\') ||
|
|
41
|
+
source.startsWith('..\\') ||
|
|
42
|
+
// Windows drive letters
|
|
43
|
+
/^[a-zA-Z]:/.test(source)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Find project root by walking up to find package.son
|
|
49
|
+
*/
|
|
50
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
51
|
+
let currentDir = startDir;
|
|
52
|
+
|
|
53
|
+
while (currentDir) {
|
|
54
|
+
const manifestPath = path.join(currentDir, 'package.son');
|
|
55
|
+
if (fs.existsSync(manifestPath)) {
|
|
56
|
+
return currentDir;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const parent = path.dirname(currentDir);
|
|
60
|
+
if (parent === currentDir) break; // Hit root
|
|
61
|
+
currentDir = parent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get Stone home directory
|
|
69
|
+
*/
|
|
70
|
+
function getStoneHome() {
|
|
71
|
+
if (process.env.STONE_HOME) {
|
|
72
|
+
return process.env.STONE_HOME;
|
|
73
|
+
}
|
|
74
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
75
|
+
if (home) {
|
|
76
|
+
return path.join(home, '.stone');
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initialize a new package.son
|
|
83
|
+
*/
|
|
84
|
+
export async function initCommand(options = {}) {
|
|
85
|
+
const { json = false, force = false } = options;
|
|
86
|
+
const cwd = process.cwd();
|
|
87
|
+
const manifestPath = path.join(cwd, 'package.son');
|
|
88
|
+
|
|
89
|
+
if (fs.existsSync(manifestPath) && !force) {
|
|
90
|
+
if (json) {
|
|
91
|
+
console.log(JSON.stringify({ error: 'package.son already exists. Use --force to overwrite.' }));
|
|
92
|
+
} else {
|
|
93
|
+
console.error('package.son already exists. Use --force to overwrite.');
|
|
94
|
+
}
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Get project name from directory
|
|
99
|
+
const projectName = path.basename(cwd).toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
100
|
+
|
|
101
|
+
const manifest = createManifest(projectName, '1.0.0');
|
|
102
|
+
const content = stringifyManifest(manifest);
|
|
103
|
+
|
|
104
|
+
fs.writeFileSync(manifestPath, content);
|
|
105
|
+
|
|
106
|
+
if (json) {
|
|
107
|
+
console.log(JSON.stringify({ success: true, file: manifestPath }));
|
|
108
|
+
} else {
|
|
109
|
+
console.log(`Created package.son`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Install dependencies
|
|
115
|
+
*/
|
|
116
|
+
export async function installCommand(pkg = null, options = {}) {
|
|
117
|
+
const { json = false, global: isGlobal = false, source = null, save = true } = options;
|
|
118
|
+
|
|
119
|
+
// Determine target directory
|
|
120
|
+
let targetDir;
|
|
121
|
+
let manifestPath;
|
|
122
|
+
|
|
123
|
+
if (isGlobal) {
|
|
124
|
+
targetDir = getStoneHome();
|
|
125
|
+
if (!targetDir) {
|
|
126
|
+
const msg = 'Could not determine Stone home directory';
|
|
127
|
+
if (json) {
|
|
128
|
+
console.log(JSON.stringify({ error: msg }));
|
|
129
|
+
} else {
|
|
130
|
+
console.error(msg);
|
|
131
|
+
}
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
manifestPath = path.join(targetDir, 'package.son');
|
|
135
|
+
|
|
136
|
+
// Create global dirs if needed
|
|
137
|
+
if (!fs.existsSync(targetDir)) {
|
|
138
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
const globalModules = path.join(targetDir, 'stone_modules');
|
|
141
|
+
if (!fs.existsSync(globalModules)) {
|
|
142
|
+
fs.mkdirSync(globalModules, { recursive: true });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Create global package.son if needed
|
|
146
|
+
if (!fs.existsSync(manifestPath)) {
|
|
147
|
+
const manifest = createManifest('global', '0.0.0');
|
|
148
|
+
fs.writeFileSync(manifestPath, stringifyManifest(manifest));
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
targetDir = findProjectRoot();
|
|
152
|
+
if (!targetDir) {
|
|
153
|
+
const msg = 'No package.son found. Run `stone init` first.';
|
|
154
|
+
if (json) {
|
|
155
|
+
console.log(JSON.stringify({ error: msg }));
|
|
156
|
+
} else {
|
|
157
|
+
console.error(msg);
|
|
158
|
+
}
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
manifestPath = path.join(targetDir, 'package.son');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Read manifest
|
|
165
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
|
|
166
|
+
const manifest = parseManifest(manifestContent, manifestPath);
|
|
167
|
+
|
|
168
|
+
if (pkg) {
|
|
169
|
+
// Install specific package
|
|
170
|
+
await installPackage(pkg, source, manifest, manifestPath, targetDir, options);
|
|
171
|
+
} else {
|
|
172
|
+
// Install all dependencies from manifest
|
|
173
|
+
await installAllDependencies(manifest, targetDir, options);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Install a package from a local directory
|
|
179
|
+
*/
|
|
180
|
+
async function installFromLocalPath(pkg, sourcePath, manifest, manifestPath, targetDir, options) {
|
|
181
|
+
const { json = false, save = true } = options;
|
|
182
|
+
|
|
183
|
+
// Resolve the source path
|
|
184
|
+
const resolvedSource = path.resolve(sourcePath);
|
|
185
|
+
|
|
186
|
+
if (!fs.existsSync(resolvedSource)) {
|
|
187
|
+
throw new Error(`Source directory not found: ${resolvedSource}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const stat = fs.statSync(resolvedSource);
|
|
191
|
+
if (!stat.isDirectory()) {
|
|
192
|
+
throw new Error(`Source is not a directory: ${resolvedSource}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Read the source package.son if it exists
|
|
196
|
+
const sourceManifestPath = path.join(resolvedSource, 'package.son');
|
|
197
|
+
let pkgManifest = null;
|
|
198
|
+
let version = '*';
|
|
199
|
+
|
|
200
|
+
if (fs.existsSync(sourceManifestPath)) {
|
|
201
|
+
try {
|
|
202
|
+
const content = fs.readFileSync(sourceManifestPath, 'utf8');
|
|
203
|
+
pkgManifest = parseManifest(content, sourceManifestPath);
|
|
204
|
+
version = pkgManifest.version || '*';
|
|
205
|
+
} catch (e) {
|
|
206
|
+
// Ignore manifest parse errors, proceed without version info
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Install to stone_modules
|
|
211
|
+
const modulesDir = path.join(targetDir, 'stone_modules', pkg);
|
|
212
|
+
|
|
213
|
+
// Remove existing if present
|
|
214
|
+
if (fs.existsSync(modulesDir)) {
|
|
215
|
+
fs.rmSync(modulesDir, { recursive: true, force: true });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Copy all files from source to target
|
|
219
|
+
copyDirectorySync(resolvedSource, modulesDir);
|
|
220
|
+
|
|
221
|
+
// Update project manifest
|
|
222
|
+
if (save) {
|
|
223
|
+
// Store the absolute path as source for local packages
|
|
224
|
+
manifest.addDependency(pkg, version === '*' ? '*' : `^${version}`, resolvedSource);
|
|
225
|
+
fs.writeFileSync(manifestPath, stringifyManifest(manifest));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { version };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Recursively copy a directory
|
|
233
|
+
*/
|
|
234
|
+
function copyDirectorySync(src, dest) {
|
|
235
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
236
|
+
|
|
237
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
238
|
+
|
|
239
|
+
for (const entry of entries) {
|
|
240
|
+
const srcPath = path.join(src, entry.name);
|
|
241
|
+
const destPath = path.join(dest, entry.name);
|
|
242
|
+
|
|
243
|
+
if (entry.isDirectory()) {
|
|
244
|
+
copyDirectorySync(srcPath, destPath);
|
|
245
|
+
} else {
|
|
246
|
+
fs.copyFileSync(srcPath, destPath);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Install a specific package
|
|
253
|
+
*/
|
|
254
|
+
async function installPackage(pkg, source, manifest, manifestPath, targetDir, options) {
|
|
255
|
+
const { json = false, save = true } = options;
|
|
256
|
+
|
|
257
|
+
if (!source) {
|
|
258
|
+
const msg = `Source required. Use: stone install ${pkg} --source <url-or-path>`;
|
|
259
|
+
if (json) {
|
|
260
|
+
console.log(JSON.stringify({ error: msg }));
|
|
261
|
+
} else {
|
|
262
|
+
console.error(msg);
|
|
263
|
+
}
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check if source is a local path or URL
|
|
268
|
+
if (isLocalPath(source)) {
|
|
269
|
+
// Install from local directory
|
|
270
|
+
if (!json) {
|
|
271
|
+
console.log(`Installing ${pkg} from local path ${source}...`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const result = await installFromLocalPath(pkg, source, manifest, manifestPath, targetDir, options);
|
|
276
|
+
|
|
277
|
+
if (json) {
|
|
278
|
+
console.log(JSON.stringify({ success: true, package: pkg, version: result.version, source: 'local' }));
|
|
279
|
+
} else {
|
|
280
|
+
console.log(` Installed ${pkg}@${result.version} (from local)`);
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
if (json) {
|
|
284
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
285
|
+
} else {
|
|
286
|
+
console.error(`Failed to install ${pkg}: ${err.message}`);
|
|
287
|
+
}
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Install from URL
|
|
294
|
+
if (!json) {
|
|
295
|
+
console.log(`Installing ${pkg} from ${source}...`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const fetcher = createFetcher();
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
// Fetch available versions
|
|
302
|
+
const versionInfo = await fetcher.fetchVersions(pkg, source);
|
|
303
|
+
|
|
304
|
+
if (!versionInfo.versions || versionInfo.versions.length === 0) {
|
|
305
|
+
// No versions.son - try fetching latest directly
|
|
306
|
+
if (!json) {
|
|
307
|
+
console.log(` Fetching package...`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const pkgData = await fetcher.fetchPackage(pkg, 'latest', source);
|
|
311
|
+
|
|
312
|
+
// Install to stone_modules
|
|
313
|
+
const modulesDir = path.join(targetDir, 'stone_modules', pkg);
|
|
314
|
+
fs.mkdirSync(modulesDir, { recursive: true });
|
|
315
|
+
|
|
316
|
+
// Write files
|
|
317
|
+
for (const [filename, content] of pkgData.files) {
|
|
318
|
+
const filePath = path.join(modulesDir, filename);
|
|
319
|
+
const fileDir = path.dirname(filePath);
|
|
320
|
+
if (!fs.existsSync(fileDir)) {
|
|
321
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
322
|
+
}
|
|
323
|
+
fs.writeFileSync(filePath, content);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Write package.son if available
|
|
327
|
+
if (pkgData.manifest) {
|
|
328
|
+
fs.writeFileSync(
|
|
329
|
+
path.join(modulesDir, 'package.son'),
|
|
330
|
+
JSON.stringify(pkgData.manifest, null, 2) // TODO: use stringifySON
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Update project manifest
|
|
335
|
+
if (save) {
|
|
336
|
+
manifest.addDependency(pkg, pkgData.manifest?.version || '*', source);
|
|
337
|
+
fs.writeFileSync(manifestPath, stringifyManifest(manifest));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (json) {
|
|
341
|
+
console.log(JSON.stringify({ success: true, package: pkg, version: pkgData.manifest?.version }));
|
|
342
|
+
} else {
|
|
343
|
+
console.log(` Installed ${pkg}@${pkgData.manifest?.version || 'latest'}`);
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
// Pick highest version
|
|
347
|
+
const version = versionInfo.versions[versionInfo.versions.length - 1];
|
|
348
|
+
|
|
349
|
+
if (!json) {
|
|
350
|
+
console.log(` Found version ${version}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const pkgData = await fetcher.fetchPackage(pkg, version, source);
|
|
354
|
+
|
|
355
|
+
// Install to stone_modules
|
|
356
|
+
const modulesDir = path.join(targetDir, 'stone_modules', pkg);
|
|
357
|
+
fs.mkdirSync(modulesDir, { recursive: true });
|
|
358
|
+
|
|
359
|
+
for (const [filename, content] of pkgData.files) {
|
|
360
|
+
const filePath = path.join(modulesDir, filename);
|
|
361
|
+
const fileDir = path.dirname(filePath);
|
|
362
|
+
if (!fs.existsSync(fileDir)) {
|
|
363
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
364
|
+
}
|
|
365
|
+
fs.writeFileSync(filePath, content);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Update project manifest
|
|
369
|
+
if (save) {
|
|
370
|
+
manifest.addDependency(pkg, `^${version}`, source);
|
|
371
|
+
fs.writeFileSync(manifestPath, stringifyManifest(manifest));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (json) {
|
|
375
|
+
console.log(JSON.stringify({ success: true, package: pkg, version }));
|
|
376
|
+
} else {
|
|
377
|
+
console.log(` Installed ${pkg}@${version}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch (err) {
|
|
381
|
+
if (json) {
|
|
382
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
383
|
+
} else {
|
|
384
|
+
console.error(`Failed to install ${pkg}: ${err.message}`);
|
|
385
|
+
}
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Install all dependencies from manifest
|
|
392
|
+
*/
|
|
393
|
+
async function installAllDependencies(manifest, targetDir, options) {
|
|
394
|
+
const { json = false } = options;
|
|
395
|
+
|
|
396
|
+
const deps = manifest.getNormalizedDependencies();
|
|
397
|
+
const depNames = Object.keys(deps);
|
|
398
|
+
|
|
399
|
+
if (depNames.length === 0) {
|
|
400
|
+
if (json) {
|
|
401
|
+
console.log(JSON.stringify({ success: true, installed: 0 }));
|
|
402
|
+
} else {
|
|
403
|
+
console.log('No dependencies to install.');
|
|
404
|
+
}
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!json) {
|
|
409
|
+
console.log(`Installing ${depNames.length} dependencies...`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const fetcher = createFetcher();
|
|
413
|
+
const installed = [];
|
|
414
|
+
const errors = [];
|
|
415
|
+
|
|
416
|
+
for (const name of depNames) {
|
|
417
|
+
const spec = deps[name];
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
if (!json) {
|
|
421
|
+
console.log(` ${name}...`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Check if source is a local path
|
|
425
|
+
if (spec.source && isLocalPath(spec.source)) {
|
|
426
|
+
// Install from local directory
|
|
427
|
+
const resolvedSource = path.resolve(spec.source);
|
|
428
|
+
|
|
429
|
+
if (!fs.existsSync(resolvedSource)) {
|
|
430
|
+
throw new Error(`Source directory not found: ${resolvedSource}`);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Read the source package.son if it exists
|
|
434
|
+
const sourceManifestPath = path.join(resolvedSource, 'package.son');
|
|
435
|
+
let version = spec.version || '*';
|
|
436
|
+
|
|
437
|
+
if (fs.existsSync(sourceManifestPath)) {
|
|
438
|
+
try {
|
|
439
|
+
const content = fs.readFileSync(sourceManifestPath, 'utf8');
|
|
440
|
+
const pkgManifest = parseManifest(content, sourceManifestPath);
|
|
441
|
+
version = pkgManifest.version || version;
|
|
442
|
+
} catch (e) {
|
|
443
|
+
// Ignore manifest parse errors
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Install to stone_modules
|
|
448
|
+
const modulesDir = path.join(targetDir, 'stone_modules', name);
|
|
449
|
+
|
|
450
|
+
// Remove existing if present
|
|
451
|
+
if (fs.existsSync(modulesDir)) {
|
|
452
|
+
fs.rmSync(modulesDir, { recursive: true, force: true });
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Copy all files
|
|
456
|
+
copyDirectorySync(resolvedSource, modulesDir);
|
|
457
|
+
|
|
458
|
+
installed.push({ name, version });
|
|
459
|
+
|
|
460
|
+
if (!json) {
|
|
461
|
+
console.log(` ✓ ${name}@${version} (local)`);
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
// Install from URL
|
|
465
|
+
const versionInfo = await fetcher.fetchVersions(name, spec.source);
|
|
466
|
+
|
|
467
|
+
let version;
|
|
468
|
+
if (versionInfo.versions && versionInfo.versions.length > 0) {
|
|
469
|
+
version = maxSatisfying(versionInfo.versions, spec.version);
|
|
470
|
+
if (!version) {
|
|
471
|
+
throw new Error(`No version satisfies ${spec.version}`);
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
version = 'latest';
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const pkgData = await fetcher.fetchPackage(name, version, spec.source);
|
|
478
|
+
|
|
479
|
+
// Install to stone_modules
|
|
480
|
+
const modulesDir = path.join(targetDir, 'stone_modules', name);
|
|
481
|
+
fs.mkdirSync(modulesDir, { recursive: true });
|
|
482
|
+
|
|
483
|
+
for (const [filename, content] of pkgData.files) {
|
|
484
|
+
const filePath = path.join(modulesDir, filename);
|
|
485
|
+
const fileDir = path.dirname(filePath);
|
|
486
|
+
if (!fs.existsSync(fileDir)) {
|
|
487
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
488
|
+
}
|
|
489
|
+
fs.writeFileSync(filePath, content);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
installed.push({ name, version: pkgData.manifest?.version || version });
|
|
493
|
+
|
|
494
|
+
if (!json) {
|
|
495
|
+
console.log(` ✓ ${name}@${pkgData.manifest?.version || version}`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} catch (err) {
|
|
499
|
+
errors.push({ name, error: err.message });
|
|
500
|
+
if (!json) {
|
|
501
|
+
console.error(` ✗ ${name}: ${err.message}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Write lock file
|
|
507
|
+
const lockFile = new LockFile();
|
|
508
|
+
for (const { name, version } of installed) {
|
|
509
|
+
const spec = deps[name];
|
|
510
|
+
lockFile.setPackage(name, version, spec.source);
|
|
511
|
+
}
|
|
512
|
+
const lockPath = path.join(targetDir, 'package.lock.son');
|
|
513
|
+
fs.writeFileSync(lockPath, stringifyLockFile(lockFile));
|
|
514
|
+
|
|
515
|
+
if (json) {
|
|
516
|
+
console.log(JSON.stringify({
|
|
517
|
+
success: errors.length === 0,
|
|
518
|
+
installed: installed.length,
|
|
519
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
520
|
+
}));
|
|
521
|
+
} else {
|
|
522
|
+
console.log(`\nInstalled ${installed.length}/${depNames.length} packages.`);
|
|
523
|
+
if (errors.length > 0) {
|
|
524
|
+
console.log(`${errors.length} errors occurred.`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (errors.length > 0) {
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Remove a package
|
|
535
|
+
*/
|
|
536
|
+
export async function removeCommand(pkg, options = {}) {
|
|
537
|
+
const { json = false, global: isGlobal = false } = options;
|
|
538
|
+
|
|
539
|
+
let targetDir;
|
|
540
|
+
let manifestPath;
|
|
541
|
+
|
|
542
|
+
if (isGlobal) {
|
|
543
|
+
targetDir = getStoneHome();
|
|
544
|
+
if (!targetDir) {
|
|
545
|
+
const msg = 'Could not determine Stone home directory';
|
|
546
|
+
if (json) console.log(JSON.stringify({ error: msg }));
|
|
547
|
+
else console.error(msg);
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
manifestPath = path.join(targetDir, 'package.son');
|
|
551
|
+
} else {
|
|
552
|
+
targetDir = findProjectRoot();
|
|
553
|
+
if (!targetDir) {
|
|
554
|
+
const msg = 'No package.son found.';
|
|
555
|
+
if (json) console.log(JSON.stringify({ error: msg }));
|
|
556
|
+
else console.error(msg);
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
manifestPath = path.join(targetDir, 'package.son');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Read manifest
|
|
563
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
|
|
564
|
+
const manifest = parseManifest(manifestContent, manifestPath);
|
|
565
|
+
|
|
566
|
+
if (!manifest.hasDependency(pkg)) {
|
|
567
|
+
const msg = `Package ${pkg} is not a dependency`;
|
|
568
|
+
if (json) console.log(JSON.stringify({ error: msg }));
|
|
569
|
+
else console.error(msg);
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Remove from manifest
|
|
574
|
+
manifest.removeDependency(pkg);
|
|
575
|
+
fs.writeFileSync(manifestPath, stringifyManifest(manifest));
|
|
576
|
+
|
|
577
|
+
// Remove from stone_modules
|
|
578
|
+
const moduleDir = path.join(targetDir, 'stone_modules', pkg);
|
|
579
|
+
if (fs.existsSync(moduleDir)) {
|
|
580
|
+
fs.rmSync(moduleDir, { recursive: true, force: true });
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (json) {
|
|
584
|
+
console.log(JSON.stringify({ success: true, removed: pkg }));
|
|
585
|
+
} else {
|
|
586
|
+
console.log(`Removed ${pkg}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Update dependencies
|
|
592
|
+
*/
|
|
593
|
+
export async function updateCommand(pkg = null, options = {}) {
|
|
594
|
+
const { json = false } = options;
|
|
595
|
+
|
|
596
|
+
const targetDir = findProjectRoot();
|
|
597
|
+
if (!targetDir) {
|
|
598
|
+
const msg = 'No package.son found.';
|
|
599
|
+
if (json) console.log(JSON.stringify({ error: msg }));
|
|
600
|
+
else console.error(msg);
|
|
601
|
+
process.exit(1);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// For now, just re-install
|
|
605
|
+
// TODO: Implement proper update logic with version bumping
|
|
606
|
+
if (!json) {
|
|
607
|
+
console.log('Updating dependencies...');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const manifestPath = path.join(targetDir, 'package.son');
|
|
611
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
|
|
612
|
+
const manifest = parseManifest(manifestContent, manifestPath);
|
|
613
|
+
|
|
614
|
+
await installAllDependencies(manifest, targetDir, options);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* List installed packages
|
|
619
|
+
*/
|
|
620
|
+
export async function listCommand(options = {}) {
|
|
621
|
+
const { json = false, global: isGlobal = false } = options;
|
|
622
|
+
|
|
623
|
+
let targetDir;
|
|
624
|
+
let manifestPath;
|
|
625
|
+
|
|
626
|
+
if (isGlobal) {
|
|
627
|
+
targetDir = getStoneHome();
|
|
628
|
+
if (!targetDir) {
|
|
629
|
+
const msg = 'Could not determine Stone home directory';
|
|
630
|
+
if (json) console.log(JSON.stringify({ error: msg }));
|
|
631
|
+
else console.error(msg);
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
manifestPath = path.join(targetDir, 'package.son');
|
|
635
|
+
} else {
|
|
636
|
+
targetDir = findProjectRoot();
|
|
637
|
+
if (!targetDir) {
|
|
638
|
+
const msg = 'No package.son found.';
|
|
639
|
+
if (json) console.log(JSON.stringify({ error: msg }));
|
|
640
|
+
else console.error(msg);
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
manifestPath = path.join(targetDir, 'package.son');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (!fs.existsSync(manifestPath)) {
|
|
647
|
+
if (json) {
|
|
648
|
+
console.log(JSON.stringify({ packages: [] }));
|
|
649
|
+
} else {
|
|
650
|
+
console.log('No packages installed.');
|
|
651
|
+
}
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
|
|
656
|
+
const manifest = parseManifest(manifestContent, manifestPath);
|
|
657
|
+
|
|
658
|
+
const deps = manifest.getNormalizedDependencies();
|
|
659
|
+
const packages = [];
|
|
660
|
+
|
|
661
|
+
// Check what's actually installed
|
|
662
|
+
const modulesDir = path.join(targetDir, 'stone_modules');
|
|
663
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
664
|
+
const pkgDir = path.join(modulesDir, name);
|
|
665
|
+
const installed = fs.existsSync(pkgDir);
|
|
666
|
+
|
|
667
|
+
let installedVersion = null;
|
|
668
|
+
if (installed) {
|
|
669
|
+
const pkgManifest = path.join(pkgDir, 'package.son');
|
|
670
|
+
if (fs.existsSync(pkgManifest)) {
|
|
671
|
+
try {
|
|
672
|
+
const content = fs.readFileSync(pkgManifest, 'utf8');
|
|
673
|
+
const pkg = parseManifest(content, pkgManifest);
|
|
674
|
+
installedVersion = pkg.version;
|
|
675
|
+
} catch (e) {
|
|
676
|
+
// Ignore parse errors
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
packages.push({
|
|
682
|
+
name,
|
|
683
|
+
wanted: spec.version,
|
|
684
|
+
installed: installedVersion,
|
|
685
|
+
source: spec.source,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (json) {
|
|
690
|
+
console.log(JSON.stringify({ packages }));
|
|
691
|
+
} else {
|
|
692
|
+
if (packages.length === 0) {
|
|
693
|
+
console.log('No packages.');
|
|
694
|
+
} else {
|
|
695
|
+
console.log(`${isGlobal ? 'Global' : 'Project'} packages:\n`);
|
|
696
|
+
for (const pkg of packages) {
|
|
697
|
+
const status = pkg.installed ? `@${pkg.installed}` : '(not installed)';
|
|
698
|
+
console.log(` ${pkg.name}${status}`);
|
|
699
|
+
if (pkg.source) {
|
|
700
|
+
console.log(` source: ${pkg.source}`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
export default {
|
|
708
|
+
initCommand,
|
|
709
|
+
installCommand,
|
|
710
|
+
removeCommand,
|
|
711
|
+
updateCommand,
|
|
712
|
+
listCommand,
|
|
713
|
+
};
|