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,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stone Package Lock File Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles reading and writing package.lock.son files.
|
|
5
|
+
* Ensures reproducible builds with locked versions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { parseSON, stringifySON } from '../frontend/parsing/sonParser.js';
|
|
9
|
+
import { parseVersion, valid as validVersion } from './semver.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Lock file version - increment when format changes
|
|
13
|
+
*/
|
|
14
|
+
const LOCK_VERSION = 1;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Lock file error
|
|
18
|
+
*/
|
|
19
|
+
export class LockFileError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'LockFileError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Locked package entry
|
|
28
|
+
*/
|
|
29
|
+
export class LockedPackage {
|
|
30
|
+
constructor(data = {}) {
|
|
31
|
+
this.name = data.name || '';
|
|
32
|
+
this.version = data.version || '';
|
|
33
|
+
this.source = data.source || null;
|
|
34
|
+
this.integrity = data.integrity || null;
|
|
35
|
+
this.dependencies = data.dependencies || {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
toObject() {
|
|
39
|
+
const obj = {
|
|
40
|
+
version: this.version,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (this.source) {
|
|
44
|
+
obj.source = this.source;
|
|
45
|
+
}
|
|
46
|
+
if (this.integrity) {
|
|
47
|
+
obj.integrity = this.integrity;
|
|
48
|
+
}
|
|
49
|
+
if (Object.keys(this.dependencies).length > 0) {
|
|
50
|
+
obj.dependencies = this.dependencies;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return obj;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Lock file representation
|
|
59
|
+
*/
|
|
60
|
+
export class LockFile {
|
|
61
|
+
constructor() {
|
|
62
|
+
this.lockVersion = LOCK_VERSION;
|
|
63
|
+
this.packages = new Map(); // name -> LockedPackage
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Add or update a locked package
|
|
68
|
+
*/
|
|
69
|
+
setPackage(name, version, source = null, integrity = null, dependencies = {}) {
|
|
70
|
+
this.packages.set(name, new LockedPackage({
|
|
71
|
+
name,
|
|
72
|
+
version,
|
|
73
|
+
source,
|
|
74
|
+
integrity,
|
|
75
|
+
dependencies,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get a locked package
|
|
81
|
+
*/
|
|
82
|
+
getPackage(name) {
|
|
83
|
+
return this.packages.get(name) || null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if a package is locked
|
|
88
|
+
*/
|
|
89
|
+
hasPackage(name) {
|
|
90
|
+
return this.packages.has(name);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the locked version of a package
|
|
95
|
+
*/
|
|
96
|
+
getVersion(name) {
|
|
97
|
+
const pkg = this.packages.get(name);
|
|
98
|
+
return pkg ? pkg.version : null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Remove a locked package
|
|
103
|
+
*/
|
|
104
|
+
removePackage(name) {
|
|
105
|
+
this.packages.delete(name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Clear all locked packages
|
|
110
|
+
*/
|
|
111
|
+
clear() {
|
|
112
|
+
this.packages.clear();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get all package names
|
|
117
|
+
*/
|
|
118
|
+
getPackageNames() {
|
|
119
|
+
return Array.from(this.packages.keys());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Validate the lock file
|
|
124
|
+
*/
|
|
125
|
+
validate() {
|
|
126
|
+
for (const [name, pkg] of this.packages) {
|
|
127
|
+
if (!pkg.version) {
|
|
128
|
+
throw new LockFileError(`Missing version for package: ${name}`);
|
|
129
|
+
}
|
|
130
|
+
if (!validVersion(pkg.version)) {
|
|
131
|
+
throw new LockFileError(`Invalid version for ${name}: ${pkg.version}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Convert to plain object for serialization
|
|
139
|
+
*/
|
|
140
|
+
toObject() {
|
|
141
|
+
const packages = {};
|
|
142
|
+
|
|
143
|
+
for (const [name, pkg] of this.packages) {
|
|
144
|
+
packages[name] = pkg.toObject();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
lockVersion: this.lockVersion,
|
|
149
|
+
packages,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Convert to .son string
|
|
155
|
+
*/
|
|
156
|
+
toString() {
|
|
157
|
+
return stringifySON(this.toObject());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Create from resolved dependencies
|
|
162
|
+
* @param {Map|object} resolved - Map of name to { version, source?, integrity? }
|
|
163
|
+
*/
|
|
164
|
+
static fromResolved(resolved) {
|
|
165
|
+
const lockFile = new LockFile();
|
|
166
|
+
|
|
167
|
+
const entries = resolved instanceof Map
|
|
168
|
+
? Array.from(resolved.entries())
|
|
169
|
+
: Object.entries(resolved);
|
|
170
|
+
|
|
171
|
+
for (const [name, info] of entries) {
|
|
172
|
+
lockFile.setPackage(
|
|
173
|
+
name,
|
|
174
|
+
info.version,
|
|
175
|
+
info.source || null,
|
|
176
|
+
info.integrity || null,
|
|
177
|
+
info.dependencies || {}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return lockFile;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Parse a package.lock.son file content
|
|
187
|
+
* @param {string} content - The file content
|
|
188
|
+
* @param {string} filename - Optional filename for error messages
|
|
189
|
+
* @returns {LockFile} Parsed lock file
|
|
190
|
+
*/
|
|
191
|
+
export function parseLockFile(content, filename = 'package.lock.son') {
|
|
192
|
+
let data;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
data = parseSON(content, filename);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
throw new LockFileError(`Failed to parse lock file: ${e.message}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check version compatibility
|
|
201
|
+
if (data.lockVersion && data.lockVersion > LOCK_VERSION) {
|
|
202
|
+
throw new LockFileError(
|
|
203
|
+
`Lock file version ${data.lockVersion} is newer than supported version ${LOCK_VERSION}. ` +
|
|
204
|
+
'Please update your Stone installation.'
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const lockFile = new LockFile();
|
|
209
|
+
lockFile.lockVersion = data.lockVersion || 1;
|
|
210
|
+
|
|
211
|
+
if (data.packages && typeof data.packages === 'object') {
|
|
212
|
+
for (const [name, pkgData] of Object.entries(data.packages)) {
|
|
213
|
+
lockFile.setPackage(
|
|
214
|
+
name,
|
|
215
|
+
pkgData.version,
|
|
216
|
+
pkgData.source,
|
|
217
|
+
pkgData.integrity,
|
|
218
|
+
pkgData.dependencies
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
lockFile.validate();
|
|
224
|
+
return lockFile;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Stringify a lock file to .son format
|
|
229
|
+
*/
|
|
230
|
+
export function stringifyLockFile(lockFile) {
|
|
231
|
+
if (lockFile instanceof LockFile) {
|
|
232
|
+
return lockFile.toString();
|
|
233
|
+
}
|
|
234
|
+
return stringifySON(lockFile);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Check if a lock file is up to date with a manifest
|
|
239
|
+
* @param {LockFile} lockFile - The lock file
|
|
240
|
+
* @param {object} manifest - The package manifest
|
|
241
|
+
* @returns {{upToDate: boolean, missing: string[], extra: string[], outdated: string[]}}
|
|
242
|
+
*/
|
|
243
|
+
export function checkLockFileStatus(lockFile, manifest) {
|
|
244
|
+
const deps = manifest.dependencies || {};
|
|
245
|
+
const locked = lockFile.getPackageNames();
|
|
246
|
+
const depNames = Object.keys(deps);
|
|
247
|
+
|
|
248
|
+
const missing = depNames.filter(name => !lockFile.hasPackage(name));
|
|
249
|
+
const extra = locked.filter(name => !deps[name]);
|
|
250
|
+
|
|
251
|
+
// Check if any locked versions are outside their range
|
|
252
|
+
// (This would require re-resolving, so we just flag any missing)
|
|
253
|
+
const outdated = []; // Would need to compare with constraints
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
upToDate: missing.length === 0 && extra.length === 0,
|
|
257
|
+
missing,
|
|
258
|
+
extra,
|
|
259
|
+
outdated,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export default {
|
|
264
|
+
LockFile,
|
|
265
|
+
LockedPackage,
|
|
266
|
+
LockFileError,
|
|
267
|
+
parseLockFile,
|
|
268
|
+
stringifyLockFile,
|
|
269
|
+
checkLockFileStatus,
|
|
270
|
+
LOCK_VERSION,
|
|
271
|
+
};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stone Package Manifest Handler
|
|
3
|
+
*
|
|
4
|
+
* Reads, writes, and validates package.son files.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { parseSON, stringifySON, SONParseError } from '../frontend/parsing/sonParser.js';
|
|
8
|
+
import { parseVersion, parseRange, valid as validVersion } from './semver.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Manifest validation error
|
|
12
|
+
*/
|
|
13
|
+
export class ManifestError extends Error {
|
|
14
|
+
constructor(message, field = null) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'ManifestError';
|
|
17
|
+
this.field = field;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parsed package manifest
|
|
23
|
+
*/
|
|
24
|
+
export class PackageManifest {
|
|
25
|
+
constructor(data = {}) {
|
|
26
|
+
// Required fields
|
|
27
|
+
this.name = data.name || '';
|
|
28
|
+
this.version = data.version || '';
|
|
29
|
+
|
|
30
|
+
// Optional fields
|
|
31
|
+
this.entry = data.entry || 'main.stn';
|
|
32
|
+
this.description = data.description || '';
|
|
33
|
+
this.author = data.author || '';
|
|
34
|
+
this.license = data.license || '';
|
|
35
|
+
this.keywords = data.keywords || [];
|
|
36
|
+
this.repository = data.repository || '';
|
|
37
|
+
|
|
38
|
+
// Dependencies: { name: { version, source } }
|
|
39
|
+
this.dependencies = data.dependencies || {};
|
|
40
|
+
|
|
41
|
+
// Dev dependencies (not needed for runtime)
|
|
42
|
+
this.devDependencies = data.devDependencies || {};
|
|
43
|
+
|
|
44
|
+
// Raw data for any additional fields
|
|
45
|
+
this._raw = data;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate the manifest
|
|
50
|
+
* @throws {ManifestError} If validation fails
|
|
51
|
+
*/
|
|
52
|
+
validate() {
|
|
53
|
+
// Required: name
|
|
54
|
+
if (!this.name) {
|
|
55
|
+
throw new ManifestError('Missing required field: name', 'name');
|
|
56
|
+
}
|
|
57
|
+
if (typeof this.name !== 'string') {
|
|
58
|
+
throw new ManifestError('name must be a string', 'name');
|
|
59
|
+
}
|
|
60
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(this.name)) {
|
|
61
|
+
throw new ManifestError(
|
|
62
|
+
'name must start with a letter and contain only letters, numbers, underscores, and hyphens',
|
|
63
|
+
'name'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Required: version
|
|
68
|
+
if (!this.version) {
|
|
69
|
+
throw new ManifestError('Missing required field: version', 'version');
|
|
70
|
+
}
|
|
71
|
+
if (!validVersion(this.version)) {
|
|
72
|
+
throw new ManifestError(`Invalid version: ${this.version}`, 'version');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Optional: entry
|
|
76
|
+
if (this.entry && typeof this.entry !== 'string') {
|
|
77
|
+
throw new ManifestError('entry must be a string', 'entry');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Optional: keywords
|
|
81
|
+
if (this.keywords && !Array.isArray(this.keywords)) {
|
|
82
|
+
throw new ManifestError('keywords must be an array', 'keywords');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Validate dependencies
|
|
86
|
+
this.validateDependencies(this.dependencies, 'dependencies');
|
|
87
|
+
this.validateDependencies(this.devDependencies, 'devDependencies');
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validate a dependencies object
|
|
94
|
+
*/
|
|
95
|
+
validateDependencies(deps, field) {
|
|
96
|
+
if (!deps || typeof deps !== 'object') return;
|
|
97
|
+
|
|
98
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
99
|
+
// Validate dependency name
|
|
100
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
|
|
101
|
+
throw new ManifestError(
|
|
102
|
+
`Invalid dependency name: ${name}`,
|
|
103
|
+
`${field}.${name}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Spec can be a string (version only) or object (version + source)
|
|
108
|
+
if (typeof spec === 'string') {
|
|
109
|
+
// Validate version range
|
|
110
|
+
try {
|
|
111
|
+
parseRange(spec);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
throw new ManifestError(
|
|
114
|
+
`Invalid version range for ${name}: ${spec}`,
|
|
115
|
+
`${field}.${name}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
} else if (typeof spec === 'object' && spec !== null) {
|
|
119
|
+
// Must have version
|
|
120
|
+
if (!spec.version) {
|
|
121
|
+
throw new ManifestError(
|
|
122
|
+
`Missing version for dependency: ${name}`,
|
|
123
|
+
`${field}.${name}.version`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
parseRange(spec.version);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
throw new ManifestError(
|
|
130
|
+
`Invalid version range for ${name}: ${spec.version}`,
|
|
131
|
+
`${field}.${name}.version`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Optional source
|
|
136
|
+
if (spec.source && typeof spec.source !== 'string') {
|
|
137
|
+
throw new ManifestError(
|
|
138
|
+
`source must be a string for dependency: ${name}`,
|
|
139
|
+
`${field}.${name}.source`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
throw new ManifestError(
|
|
144
|
+
`Invalid dependency specification for: ${name}`,
|
|
145
|
+
`${field}.${name}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get normalized dependencies (always { version, source? })
|
|
153
|
+
*/
|
|
154
|
+
getNormalizedDependencies() {
|
|
155
|
+
const result = {};
|
|
156
|
+
|
|
157
|
+
for (const [name, spec] of Object.entries(this.dependencies)) {
|
|
158
|
+
if (typeof spec === 'string') {
|
|
159
|
+
result[name] = { version: spec };
|
|
160
|
+
} else {
|
|
161
|
+
result[name] = { ...spec };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Add a dependency
|
|
170
|
+
*/
|
|
171
|
+
addDependency(name, version, source = null) {
|
|
172
|
+
if (source) {
|
|
173
|
+
this.dependencies[name] = { version, source };
|
|
174
|
+
} else {
|
|
175
|
+
this.dependencies[name] = version;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Remove a dependency
|
|
181
|
+
*/
|
|
182
|
+
removeDependency(name) {
|
|
183
|
+
delete this.dependencies[name];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if a dependency exists
|
|
188
|
+
*/
|
|
189
|
+
hasDependency(name) {
|
|
190
|
+
return name in this.dependencies;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Convert to plain object for serialization
|
|
195
|
+
*/
|
|
196
|
+
toObject() {
|
|
197
|
+
const obj = {
|
|
198
|
+
name: this.name,
|
|
199
|
+
version: this.version,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (this.entry && this.entry !== 'main.stn') {
|
|
203
|
+
obj.entry = this.entry;
|
|
204
|
+
}
|
|
205
|
+
if (this.description) {
|
|
206
|
+
obj.description = this.description;
|
|
207
|
+
}
|
|
208
|
+
if (this.author) {
|
|
209
|
+
obj.author = this.author;
|
|
210
|
+
}
|
|
211
|
+
if (this.license) {
|
|
212
|
+
obj.license = this.license;
|
|
213
|
+
}
|
|
214
|
+
if (this.keywords && this.keywords.length > 0) {
|
|
215
|
+
obj.keywords = this.keywords;
|
|
216
|
+
}
|
|
217
|
+
if (this.repository) {
|
|
218
|
+
obj.repository = this.repository;
|
|
219
|
+
}
|
|
220
|
+
if (Object.keys(this.dependencies).length > 0) {
|
|
221
|
+
obj.dependencies = this.dependencies;
|
|
222
|
+
}
|
|
223
|
+
if (Object.keys(this.devDependencies).length > 0) {
|
|
224
|
+
obj.devDependencies = this.devDependencies;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return obj;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Convert to .son string
|
|
232
|
+
*/
|
|
233
|
+
toString() {
|
|
234
|
+
return stringifySON(this.toObject());
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Parse a package.son file content
|
|
240
|
+
* @param {string} content - The file content
|
|
241
|
+
* @param {string} filename - Optional filename for error messages
|
|
242
|
+
* @returns {PackageManifest} Parsed manifest
|
|
243
|
+
*/
|
|
244
|
+
export function parseManifest(content, filename = 'package.son') {
|
|
245
|
+
let data;
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
data = parseSON(content, filename);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
if (e instanceof SONParseError) {
|
|
251
|
+
throw new ManifestError(`Failed to parse ${filename}: ${e.message}`);
|
|
252
|
+
}
|
|
253
|
+
throw e;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const manifest = new PackageManifest(data);
|
|
257
|
+
manifest.validate();
|
|
258
|
+
|
|
259
|
+
return manifest;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Create a new manifest with default values
|
|
264
|
+
*/
|
|
265
|
+
export function createManifest(name, version = '1.0.0') {
|
|
266
|
+
const manifest = new PackageManifest({
|
|
267
|
+
name,
|
|
268
|
+
version,
|
|
269
|
+
entry: 'main.stn',
|
|
270
|
+
});
|
|
271
|
+
manifest.validate();
|
|
272
|
+
return manifest;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Stringify a manifest to .son format
|
|
277
|
+
*/
|
|
278
|
+
export function stringifyManifest(manifest) {
|
|
279
|
+
if (manifest instanceof PackageManifest) {
|
|
280
|
+
return manifest.toString();
|
|
281
|
+
}
|
|
282
|
+
return stringifySON(manifest);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export default {
|
|
286
|
+
PackageManifest,
|
|
287
|
+
ManifestError,
|
|
288
|
+
parseManifest,
|
|
289
|
+
createManifest,
|
|
290
|
+
stringifyManifest,
|
|
291
|
+
};
|