@shapeshift-labs/frontier-lang-cli 0.3.33 → 0.3.35

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 CHANGED
@@ -14,10 +14,13 @@ frontier-lang native-coverage src/todo.ts
14
14
  frontier-lang native-capabilities src/todo.ts --target rust
15
15
  frontier-lang slice src/parser.ts --symbol parseExpression --focused-command "npm test -- parser-expression" --out parser.slice.json
16
16
  frontier-lang test-slice parser.slice.json --source src/parser.ts
17
+ frontier-lang corpus-roundtrip ./src --target rust --out corpus-evidence.json
17
18
  ```
18
19
 
19
20
  `slice` extracts a `frontier.lang.semanticSlice` for a symbol, ownership region, native node, or source path. The slice includes source-map links, source hashes, conflict keys, focused commands, fixture hints, and merge-admission metadata so Frontier Swarm workers can operate on surgical context instead of full repo copies. `test-slice` validates that the slice is non-empty, refs resolved, conflict keys exist, source maps are present unless disabled, and supplied current source files still match the recorded hashes.
20
21
 
22
+ `corpus-roundtrip` scans a file, manifest, or directory and records native-source import, universal AST JSON, source preservation, projection review, semantic sidecar quality, and merge-signal summaries for mixed-language conversion evidence.
23
+
21
24
  ## Related Packages
22
25
 
23
26
  The published Frontier package family is generated from one shared package catalog so READMEs stay in sync across packages:
@@ -66,7 +69,7 @@ The published Frontier package family is generated from one shared package catal
66
69
  - [`@shapeshift-labs/frontier-lang-go`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang-go): Go source-language importer package for Frontier Lang semantic documents, including package-level metadata, Go AST adapter helpers, native import results, and semantic sidecar generation for go/ast File or Package trees.
67
70
  - [`@shapeshift-labs/frontier-lang-csharp`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang-csharp): C# Roslyn source-language importer package for Frontier Lang semantic documents, including package-level metadata, Roslyn adapter helpers, native import results, and semantic sidecar generation for SyntaxTree/SyntaxNode-shaped ASTs.
68
71
  - [`@shapeshift-labs/frontier-lang-clang`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang-clang): Clang AST source-language importer package for Frontier Lang semantic documents, including package-level metadata, Clang AST JSON adapter helpers, native import results, and semantic sidecar generation for C/C++ translation units.
69
- - [`@shapeshift-labs/frontier-lang`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang): Umbrella package for Frontier Lang kernel, parser, checker, compiler facade, universal AST helpers, and projection adapters.
72
+ - [`@shapeshift-labs/frontier-lang`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang): Umbrella package for Frontier Lang kernel, parser, checker, compiler facade, universal AST helpers, projection adapters, and source-language importer adapters.
70
73
  - [`@shapeshift-labs/frontier-kv`](https://www.npmjs.com/package/@shapeshift-labs/frontier-kv): Serializable in-memory key/value state for Frontier apps, including TTL, versioned compare-and-set, batched patch mutations, scans, watchers, snapshots, JSONL event evidence, and replay verification.
71
74
  - [`@shapeshift-labs/frontier-kv-locks`](https://www.npmjs.com/package/@shapeshift-labs/frontier-kv-locks): Lease-style lock records on top of Frontier KV, including acquire, renew, release, fencing tokens, expiration, owner evidence, and replayable lock events.
72
75
  - [`@shapeshift-labs/frontier-kv-rate-limit`](https://www.npmjs.com/package/@shapeshift-labs/frontier-kv-rate-limit): Patch-native rate limit buckets for Frontier KV, including fixed windows, sliding windows, token buckets, deterministic refill, consume evidence, and reset records.
@@ -0,0 +1,12 @@
1
+ import { writeFileSync } from 'node:fs';
2
+
3
+ export function output(io, value) {
4
+ io.log(JSON.stringify(value, null, 2));
5
+ }
6
+
7
+ export function outputMaybeFile(io, args, value) {
8
+ const json = JSON.stringify(value, null, 2);
9
+ const outIndex = args.indexOf('--out');
10
+ if (outIndex >= 0 && args[outIndex + 1]) writeFileSync(args[outIndex + 1], json + '\n');
11
+ else io.log(json);
12
+ }
@@ -0,0 +1,155 @@
1
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { parseFrontierFile } from '@shapeshift-labs/frontier-lang-parser';
4
+ import {
5
+ compileFrontierDocument,
6
+ createSemanticImportSidecar,
7
+ createUniversalAstFromDocument,
8
+ importNativeSource,
9
+ projectNativeImportToSource,
10
+ readUniversalAstJson,
11
+ writeUniversalAstJson
12
+ } from '@shapeshift-labs/frontier-lang-compiler';
13
+ import { idFragment, inferLanguage, readOption } from './options.js';
14
+
15
+ export function runCorpusRoundtrip(inputPath, args) {
16
+ const target = readOption(args, '--target') ?? 'typescript';
17
+ const entries = collectCorpusEntries(inputPath);
18
+ const files = entries.map((entry) => corpusRoundtripFile(entry, { target, parser: readOption(args, '--parser') }));
19
+ const failed = files.filter((fileResult) => !fileResult.ok);
20
+ return {
21
+ kind: 'frontier.lang.corpusRoundtrip',
22
+ version: 1,
23
+ inputPath,
24
+ target,
25
+ total: files.length,
26
+ passed: files.length - failed.length,
27
+ failed: failed.length,
28
+ sourceMapCount: files.reduce((sum, fileResult) => sum + (fileResult.sourceMapCount ?? 0), 0),
29
+ lossCount: files.reduce((sum, fileResult) => sum + (fileResult.lossCount ?? 0), 0),
30
+ sourcePreservationCount: files.filter((fileResult) => fileResult.sourcePreservationId).length,
31
+ semanticImpactRecords: files.reduce((sum, fileResult) => sum + (fileResult.semanticImpactRecords ?? 0), 0),
32
+ weakMergeSignals: files.reduce((sum, fileResult) => sum + (fileResult.weakMergeSignals ?? 0), 0),
33
+ reviewRequiredMergeSignals: files.reduce((sum, fileResult) => sum + (fileResult.reviewRequiredMergeSignals ?? 0), 0),
34
+ projectionReviewRequired: files.filter((fileResult) => fileResult.projectionReviewRequired).length,
35
+ projectionModes: files.reduce((counts, fileResult) => incrementCount(counts, fileResult.projectionMode), {}),
36
+ projectionReviewStatuses: files.reduce((counts, fileResult) => incrementCount(counts, fileResult.projectionReviewStatus), {}),
37
+ readiness: files.reduce((counts, fileResult) => {
38
+ for (const readiness of fileResult.mergeReadiness ?? []) counts[readiness] = (counts[readiness] ?? 0) + 1;
39
+ return counts;
40
+ }, {}),
41
+ files
42
+ };
43
+ }
44
+
45
+ function corpusRoundtripFile(entry, options) {
46
+ const path = entry.path;
47
+ try {
48
+ const source = readFileSync(path, 'utf8');
49
+ const language = entry.language ?? inferLanguage(path);
50
+ if (/\.frontier$/i.test(path)) return corpusFrontierFile(path, source, options);
51
+ const imported = importNativeSource({ language, parser: entry.parser ?? options.parser, sourcePath: path, sourceText: source });
52
+ const projection = projectNativeImportToSource(imported);
53
+ const sidecar = createSemanticImportSidecar(imported);
54
+ const projectionReview = projection.metadata?.projectionReview;
55
+ const encoded = writeUniversalAstJson(imported.universalAst);
56
+ const decoded = readUniversalAstJson(encoded);
57
+ return {
58
+ path,
59
+ language,
60
+ kind: 'nativeSource',
61
+ ok: decoded.document.id === imported.document.id,
62
+ sourceMapCount: imported.sourceMaps?.length ?? 0,
63
+ sourceMapMappingCount: (imported.sourceMaps ?? []).reduce((sum, sourceMap) => sum + (sourceMap.mappings?.length ?? 0), 0),
64
+ lossCount: imported.losses?.length ?? 0,
65
+ evidenceCount: imported.evidence?.length ?? 0,
66
+ symbolCount: imported.semanticIndex?.symbols?.length ?? 0,
67
+ occurrenceCount: imported.semanticIndex?.occurrences?.length ?? 0,
68
+ sourcePreservationId: imported.metadata?.sourcePreservationId,
69
+ sourcePreservationExact: imported.metadata?.sourcePreservation?.summary?.exactSourceAvailable === true,
70
+ projectionMode: projection.mode,
71
+ projectionReadiness: projection.readiness?.readiness,
72
+ projectionReviewStatus: projectionReview?.status,
73
+ projectionReviewRequired: projectionReview?.reviewRequired === true,
74
+ projectionReviewFallbackReasons: projectionReview?.fallbackReasons ?? [],
75
+ projectionLossCount: projection.losses?.length ?? 0,
76
+ semanticImpactRecords: sidecar.semanticImpact?.summary?.total ?? 0,
77
+ weakMergeSignals: sidecar.semanticImpact?.summary?.weakMergeSignals ?? 0,
78
+ reviewRequiredMergeSignals: sidecar.semanticImpact?.summary?.reviewRequiredMergeSignals ?? 0,
79
+ mergeSignalQueryKeys: sidecar.semanticImpact?.summary?.mergeSignalQueryKeys ?? [],
80
+ sidecarQuality: {
81
+ imported: sidecar.quality?.imported === true,
82
+ eligible: sidecar.quality?.eligible === true,
83
+ warnings: sidecar.quality?.warnings?.length ?? 0
84
+ },
85
+ mergeReadiness: (imported.mergeCandidates ?? []).map((candidate) => candidate.readiness),
86
+ jsonBytes: encoded.length
87
+ };
88
+ } catch (error) {
89
+ return { path, language: entry.language ?? inferLanguage(path), kind: 'error', ok: false, error: error instanceof Error ? error.message : String(error) };
90
+ }
91
+ }
92
+
93
+ function corpusFrontierFile(path, source, options) {
94
+ const document = parseFrontierFile(path, source);
95
+ const envelope = createUniversalAstFromDocument(document, {
96
+ evidence: [{ id: `frontier_lang_cli_corpus_${idFragment(path)}`, kind: 'test', status: 'passed', path, summary: 'Corpus Frontier source parsed into universal AST.' }]
97
+ });
98
+ const encoded = writeUniversalAstJson(envelope);
99
+ const decoded = readUniversalAstJson(encoded);
100
+ const result = compileFrontierDocument(decoded.document, { target: options.target });
101
+ return {
102
+ path,
103
+ language: 'frontier',
104
+ kind: 'frontierSource',
105
+ ok: result.ok && decoded.document.id === document.id,
106
+ hash: result.hash,
107
+ sourceMapCount: envelope.sourceMaps?.length ?? 0,
108
+ lossCount: envelope.losses?.length ?? 0,
109
+ evidenceCount: envelope.evidence?.length ?? 0,
110
+ diagnostics: result.diagnostics,
111
+ outputBytes: result.output.length,
112
+ jsonBytes: encoded.length
113
+ };
114
+ }
115
+
116
+ function collectCorpusEntries(inputPath) {
117
+ const absolute = resolve(inputPath);
118
+ const stat = statSync(absolute);
119
+ if (stat.isDirectory()) return collectCorpusDirectory(absolute).map((path) => ({ path }));
120
+ if (/\.json$/i.test(absolute)) return readCorpusManifest(absolute);
121
+ return [{ path: absolute }];
122
+ }
123
+
124
+ function collectCorpusDirectory(root) {
125
+ const files = [];
126
+ for (const item of readdirSync(root, { withFileTypes: true })) {
127
+ const path = join(root, item.name);
128
+ if (item.isDirectory()) {
129
+ if (!isIgnoredCorpusDirectory(item.name)) files.push(...collectCorpusDirectory(path));
130
+ continue;
131
+ }
132
+ if (item.isFile() && isCorpusSourceFile(path)) files.push(path);
133
+ }
134
+ return files.sort();
135
+ }
136
+
137
+ function readCorpusManifest(path) {
138
+ const manifest = JSON.parse(readFileSync(path, 'utf8'));
139
+ const base = dirname(path);
140
+ const rawEntries = Array.isArray(manifest) ? manifest : manifest.files ?? manifest.entries ?? [];
141
+ return rawEntries.map((entry) => typeof entry === 'string' ? { path: resolve(base, entry) } : { ...entry, path: resolve(base, entry.path ?? entry.file) });
142
+ }
143
+
144
+ function isIgnoredCorpusDirectory(name) {
145
+ return name === 'node_modules' || name === '.git' || name === 'dist' || name === 'coverage' || name === '.next';
146
+ }
147
+
148
+ function isCorpusSourceFile(path) {
149
+ return /\.(frontier|[cm]?tsx?|m?jsx?|rs|py|c|h|hpp|cpp|cc|cxx|go|java|kt|cs|swift|php|rb|rake)$/i.test(path);
150
+ }
151
+
152
+ function incrementCount(counts, key) {
153
+ if (key) counts[key] = (counts[key] ?? 0) + 1;
154
+ return counts;
155
+ }
package/dist/help.js ADDED
@@ -0,0 +1,3 @@
1
+ export function help(io) {
2
+ io.log('frontier-lang <parse|check|hash|ast|capabilities|to-json|from-json|import|project-native|native-compile|native-coverage|native-capabilities|native-diff|slice|test-slice|roundtrip|corpus-roundtrip|emit|emit-ts|emit-js|emit-rust|emit-python|emit-c> <file> [--after file] [--target target] [--language language] [--parser parser] [--platform platform] [--symbol name] [--region key] [--ref ref] [--source file] [--focused-command command] [--fixture-hint hint] [--ast] [--sidecar] [--sidecar-only] [--source-only] [--stubs] [--emit-on-blocked] [--all-languages] [--out file] [--strict-effects]');
3
+ }
package/dist/index.js CHANGED
@@ -1,15 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
3
- import { dirname, join, resolve } from 'node:path';
2
+ import { readFileSync, realpathSync, writeFileSync } from 'node:fs';
4
3
  import { fileURLToPath } from 'node:url';
5
4
  import { parseFrontierFile, parseFrontierSource } from '@shapeshift-labs/frontier-lang-parser';
6
5
  import { checkDocument } from '@shapeshift-labs/frontier-lang-checker';
7
6
  import { hashDocumentBase } from '@shapeshift-labs/frontier-lang-kernel';
8
7
  import {
9
- NativeImportLanguageProfiles,
10
8
  compileNativeSource,
11
9
  compileFrontierDocument,
12
- createNativeSourcePreservation,
13
10
  createNativeImportCoverageMatrix,
14
11
  createSemanticImportSidecar,
15
12
  createSemanticSlice,
@@ -26,6 +23,24 @@ import {
26
23
  writeSemanticSliceJson,
27
24
  writeUniversalAstJson
28
25
  } from '@shapeshift-labs/frontier-lang-compiler';
26
+ import { output, outputMaybeFile } from './cli-output.js';
27
+ import { runCorpusRoundtrip } from './corpus-roundtrip.js';
28
+ import { help } from './help.js';
29
+ import {
30
+ importNativeFile,
31
+ nativeCapabilityLanguages,
32
+ readCurrentSources,
33
+ readNativeImportForProjection,
34
+ sliceEntryRefs
35
+ } from './native-helpers.js';
36
+ import {
37
+ idFragment,
38
+ inferLanguage,
39
+ readIntegerOption,
40
+ readOption,
41
+ readOptions,
42
+ tryParseJson
43
+ } from './options.js';
29
44
 
30
45
  export async function runCli(argv = process.argv.slice(2), io = console) {
31
46
  const [command, file, ...rest] = argv;
@@ -200,291 +215,6 @@ export async function runCli(argv = process.argv.slice(2), io = console) {
200
215
  }
201
216
  throw new Error(`Unknown command: ${command}`);
202
217
  }
203
- function output(io, value) { io.log(JSON.stringify(value, null, 2)); }
204
- function outputMaybeFile(io, args, value) {
205
- const json = JSON.stringify(value, null, 2);
206
- const outIndex = args.indexOf('--out');
207
- if (outIndex >= 0 && args[outIndex + 1]) writeFileSync(args[outIndex + 1], json + '\n'); else io.log(json);
208
- }
209
- function runCorpusRoundtrip(inputPath, args) {
210
- const target = readOption(args, '--target') ?? 'typescript';
211
- const entries = collectCorpusEntries(inputPath);
212
- const files = entries.map((entry) => corpusRoundtripFile(entry, { target, parser: readOption(args, '--parser') }));
213
- const failed = files.filter((fileResult) => !fileResult.ok);
214
- return {
215
- kind: 'frontier.lang.corpusRoundtrip',
216
- version: 1,
217
- inputPath,
218
- target,
219
- total: files.length,
220
- passed: files.length - failed.length,
221
- failed: failed.length,
222
- sourceMapCount: files.reduce((sum, fileResult) => sum + (fileResult.sourceMapCount ?? 0), 0),
223
- lossCount: files.reduce((sum, fileResult) => sum + (fileResult.lossCount ?? 0), 0),
224
- sourcePreservationCount: files.filter((fileResult) => fileResult.sourcePreservationId).length,
225
- projectionModes: files.reduce((counts, fileResult) => {
226
- if (fileResult.projectionMode) counts[fileResult.projectionMode] = (counts[fileResult.projectionMode] ?? 0) + 1;
227
- return counts;
228
- }, {}),
229
- readiness: files.reduce((counts, fileResult) => {
230
- for (const readiness of fileResult.mergeReadiness ?? []) counts[readiness] = (counts[readiness] ?? 0) + 1;
231
- return counts;
232
- }, {}),
233
- files
234
- };
235
- }
236
-
237
- function corpusRoundtripFile(entry, options) {
238
- const path = entry.path;
239
- try {
240
- const source = readFileSync(path, 'utf8');
241
- const language = entry.language ?? inferLanguage(path);
242
- if (/\.frontier$/i.test(path)) {
243
- const document = parseFrontierFile(path, source);
244
- const envelope = createUniversalAstFromDocument(document, {
245
- evidence: [{ id: `frontier_lang_cli_corpus_${idFragment(path)}`, kind: 'test', status: 'passed', path, summary: 'Corpus Frontier source parsed into universal AST.' }]
246
- });
247
- const encoded = writeUniversalAstJson(envelope);
248
- const decoded = readUniversalAstJson(encoded);
249
- const result = compileFrontierDocument(decoded.document, { target: options.target });
250
- return {
251
- path,
252
- language: 'frontier',
253
- kind: 'frontierSource',
254
- ok: result.ok && decoded.document.id === document.id,
255
- hash: result.hash,
256
- sourceMapCount: envelope.sourceMaps?.length ?? 0,
257
- lossCount: envelope.losses?.length ?? 0,
258
- evidenceCount: envelope.evidence?.length ?? 0,
259
- diagnostics: result.diagnostics,
260
- outputBytes: result.output.length,
261
- jsonBytes: encoded.length
262
- };
263
- }
264
- const imported = importNativeSource({
265
- language,
266
- parser: entry.parser ?? options.parser,
267
- sourcePath: path,
268
- sourceText: source
269
- });
270
- const projection = projectNativeImportToSource(imported);
271
- const encoded = writeUniversalAstJson(imported.universalAst);
272
- const decoded = readUniversalAstJson(encoded);
273
- return {
274
- path,
275
- language,
276
- kind: 'nativeSource',
277
- ok: decoded.document.id === imported.document.id,
278
- sourceMapCount: imported.sourceMaps?.length ?? 0,
279
- sourceMapMappingCount: (imported.sourceMaps ?? []).reduce((sum, sourceMap) => sum + (sourceMap.mappings?.length ?? 0), 0),
280
- lossCount: imported.losses?.length ?? 0,
281
- evidenceCount: imported.evidence?.length ?? 0,
282
- symbolCount: imported.semanticIndex?.symbols?.length ?? 0,
283
- occurrenceCount: imported.semanticIndex?.occurrences?.length ?? 0,
284
- sourcePreservationId: imported.metadata?.sourcePreservationId,
285
- sourcePreservationExact: imported.metadata?.sourcePreservation?.summary?.exactSourceAvailable === true,
286
- projectionMode: projection.mode,
287
- projectionReadiness: projection.readiness?.readiness,
288
- projectionLossCount: projection.losses?.length ?? 0,
289
- mergeReadiness: (imported.mergeCandidates ?? []).map((candidate) => candidate.readiness),
290
- jsonBytes: encoded.length
291
- };
292
- } catch (error) {
293
- return {
294
- path,
295
- language: entry.language ?? inferLanguage(path),
296
- kind: 'error',
297
- ok: false,
298
- error: error instanceof Error ? error.message : String(error)
299
- };
300
- }
301
- }
302
-
303
- function collectCorpusEntries(inputPath) {
304
- const absolute = resolve(inputPath);
305
- const stat = statSync(absolute);
306
- if (stat.isDirectory()) {
307
- return collectCorpusDirectory(absolute).map((path) => ({ path }));
308
- }
309
- if (/\.json$/i.test(absolute)) {
310
- return readCorpusManifest(absolute);
311
- }
312
- return [{ path: absolute }];
313
- }
314
-
315
- function collectCorpusDirectory(root) {
316
- const files = [];
317
- for (const item of readdirSync(root, { withFileTypes: true })) {
318
- const path = join(root, item.name);
319
- if (item.isDirectory()) {
320
- if (!isIgnoredCorpusDirectory(item.name)) files.push(...collectCorpusDirectory(path));
321
- continue;
322
- }
323
- if (item.isFile() && isCorpusSourceFile(path)) files.push(path);
324
- }
325
- return files.sort();
326
- }
327
-
328
- function readCorpusManifest(path) {
329
- const manifest = JSON.parse(readFileSync(path, 'utf8'));
330
- const base = dirname(path);
331
- const rawEntries = Array.isArray(manifest) ? manifest : manifest.files ?? manifest.entries ?? [];
332
- return rawEntries.map((entry) => {
333
- if (typeof entry === 'string') return { path: resolve(base, entry) };
334
- return {
335
- ...entry,
336
- path: resolve(base, entry.path ?? entry.file)
337
- };
338
- });
339
- }
340
-
341
- function importNativeFile(file, source, args, defaults = {}) {
342
- const language = defaults.language ?? readOption(args, '--language') ?? inferLanguage(file);
343
- const sourceHash = readOption(args, '--source-hash');
344
- const sourcePreservation = sourcePreservationOptionsRequested(args)
345
- ? createNativeSourcePreservation({
346
- language,
347
- sourcePath: file,
348
- sourceHash,
349
- sourceText: source,
350
- includeSourceText: !args.includes('--omit-source-text'),
351
- includeTokens: !args.includes('--no-tokens'),
352
- includeTrivia: !args.includes('--no-trivia'),
353
- includeDirectives: !args.includes('--no-directives'),
354
- maxTokens: readIntegerOption(args, '--max-tokens'),
355
- maxTrivia: readIntegerOption(args, '--max-trivia'),
356
- maxDirectives: readIntegerOption(args, '--max-directives'),
357
- metadata: { cli: true }
358
- })
359
- : undefined;
360
- return importNativeSource({
361
- language,
362
- parser: readOption(args, '--parser'),
363
- sourcePath: file,
364
- sourceHash,
365
- sourceText: source,
366
- sourcePreservation,
367
- nativeAstMetadata: { sourceBytes: source.length }
368
- });
369
- }
370
-
371
- function readNativeImportForProjection(file, source, args) {
372
- const parsed = tryParseJson(source);
373
- if (!parsed) return importNativeFile(file, source, args);
374
- if (parsed.kind === 'frontier.lang.universalAst') {
375
- const nativeSource = parsed.nativeSources?.[0];
376
- return {
377
- id: parsed.metadata?.nativeImportId ?? `import_${idFragment(parsed.id)}`,
378
- language: parsed.metadata?.sourceLanguage ?? nativeSource?.language ?? readOption(args, '--language') ?? inferLanguage(file),
379
- sourcePath: parsed.metadata?.sourcePath ?? nativeSource?.sourcePath,
380
- universalAst: parsed,
381
- nativeSource,
382
- nativeAst: nativeSource?.ast,
383
- semanticIndex: parsed.semanticIndex,
384
- sourceMaps: parsed.sourceMaps,
385
- losses: parsed.losses,
386
- evidence: parsed.evidence,
387
- metadata: parsed.metadata ?? {}
388
- };
389
- }
390
- return parsed;
391
- }
392
-
393
- function nativeCapabilityLanguages(imported, args) {
394
- if (args.includes('--all-languages')) return undefined;
395
- const language = imported?.language;
396
- if (!language) return undefined;
397
- const normalized = String(language).toLowerCase();
398
- const matches = NativeImportLanguageProfiles.filter((profile) => (
399
- profile.language === normalized || profile.aliases?.includes(normalized)
400
- ));
401
- return matches.length ? matches : undefined;
402
- }
403
-
404
- function sliceEntryRefs(args) {
405
- return [
406
- ...readOptions(args, '--ref'),
407
- ...readOptions(args, '--semantic-ref'),
408
- ...readOptions(args, '--symbol').map((value) => `symbol:${value}`),
409
- ...readOptions(args, '--region').map((value) => `region:${value}`),
410
- ...readOptions(args, '--native-node').map((value) => `native:${value}`),
411
- ...readOptions(args, '--path').map((value) => `path:${value}`)
412
- ];
413
- }
414
-
415
- function readCurrentSources(args) {
416
- const paths = readOptions(args, '--source');
417
- if (!paths.length) return undefined;
418
- const currentSources = {};
419
- for (const sourcePath of paths) {
420
- currentSources[sourcePath] = readFileSync(sourcePath, 'utf8');
421
- }
422
- return currentSources;
423
- }
424
-
425
- function tryParseJson(source) {
426
- const trimmed = source.trim();
427
- if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return undefined;
428
- try {
429
- return JSON.parse(trimmed);
430
- } catch {
431
- return undefined;
432
- }
433
- }
434
-
435
- function isIgnoredCorpusDirectory(name) {
436
- return name === 'node_modules' || name === '.git' || name === 'dist' || name === 'coverage' || name === '.next';
437
- }
438
-
439
- function isCorpusSourceFile(path) {
440
- return /\.(frontier|[cm]?tsx?|m?jsx?|rs|py|c|h|hpp|cpp|cc|cxx|go|java|kt|cs|swift|php|rb|rake)$/i.test(path);
441
- }
442
-
443
- function idFragment(value) {
444
- return String(value ?? 'unknown').replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '').toLowerCase() || 'unknown';
445
- }
446
-
447
- function help(io) { io.log('frontier-lang <parse|check|hash|ast|capabilities|to-json|from-json|import|project-native|native-compile|native-coverage|native-capabilities|native-diff|slice|test-slice|roundtrip|corpus-roundtrip|emit|emit-ts|emit-js|emit-rust|emit-python|emit-c> <file> [--after file] [--target target] [--language language] [--parser parser] [--platform platform] [--symbol name] [--region key] [--ref ref] [--source file] [--focused-command command] [--fixture-hint hint] [--ast] [--sidecar] [--sidecar-only] [--source-only] [--stubs] [--emit-on-blocked] [--all-languages] [--out file] [--strict-effects]'); }
448
- function readOption(args, flag) { const index = args.indexOf(flag); return index >= 0 ? args[index + 1] : undefined; }
449
- function readOptions(args, flag) {
450
- const values = [];
451
- for (let index = 0; index < args.length; index += 1) {
452
- if (args[index] === flag && args[index + 1]) values.push(args[index + 1]);
453
- }
454
- return values;
455
- }
456
- function readIntegerOption(args, flag) {
457
- const value = readOption(args, flag);
458
- if (value === undefined) return undefined;
459
- const parsed = Number.parseInt(value, 10);
460
- return Number.isFinite(parsed) ? parsed : undefined;
461
- }
462
- function sourcePreservationOptionsRequested(args) {
463
- return args.includes('--omit-source-text')
464
- || args.includes('--no-tokens')
465
- || args.includes('--no-trivia')
466
- || args.includes('--no-directives')
467
- || args.includes('--max-tokens')
468
- || args.includes('--max-trivia')
469
- || args.includes('--max-directives');
470
- }
471
- function inferLanguage(file) {
472
- if (!file) return 'unknown';
473
- if (/\.[cm]?tsx?$/.test(file)) return 'typescript';
474
- if (/\.m?jsx?$/.test(file)) return 'javascript';
475
- if (/\.rs$/.test(file)) return 'rust';
476
- if (/\.py$/.test(file)) return 'python';
477
- if (/\.c$/.test(file)) return 'c';
478
- if (/\.cpp$|\.cc$|\.cxx$|\.hpp$|\.h$/.test(file)) return 'cpp';
479
- if (/\.go$/.test(file)) return 'go';
480
- if (/\.java$/.test(file)) return 'java';
481
- if (/\.kt$/.test(file)) return 'kotlin';
482
- if (/\.cs$/.test(file)) return 'csharp';
483
- if (/\.swift$/.test(file)) return 'swift';
484
- if (/\.php$/.test(file)) return 'php';
485
- if (/\.rb$|\.rake$/.test(file)) return 'ruby';
486
- return 'unknown';
487
- }
488
218
 
489
219
  function isDirectInvocation() {
490
220
  if (!process.argv[1]) return false;
@@ -0,0 +1,97 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import {
3
+ NativeImportLanguageProfiles,
4
+ createNativeSourcePreservation,
5
+ importNativeSource
6
+ } from '@shapeshift-labs/frontier-lang-compiler';
7
+ import {
8
+ idFragment,
9
+ inferLanguage,
10
+ readIntegerOption,
11
+ readOption,
12
+ readOptions,
13
+ sourcePreservationOptionsRequested,
14
+ tryParseJson
15
+ } from './options.js';
16
+
17
+ export function importNativeFile(file, source, args, defaults = {}) {
18
+ const language = defaults.language ?? readOption(args, '--language') ?? inferLanguage(file);
19
+ const sourceHash = readOption(args, '--source-hash');
20
+ const sourcePreservation = sourcePreservationOptionsRequested(args)
21
+ ? createNativeSourcePreservation({
22
+ language,
23
+ sourcePath: file,
24
+ sourceHash,
25
+ sourceText: source,
26
+ includeSourceText: !args.includes('--omit-source-text'),
27
+ includeTokens: !args.includes('--no-tokens'),
28
+ includeTrivia: !args.includes('--no-trivia'),
29
+ includeDirectives: !args.includes('--no-directives'),
30
+ maxTokens: readIntegerOption(args, '--max-tokens'),
31
+ maxTrivia: readIntegerOption(args, '--max-trivia'),
32
+ maxDirectives: readIntegerOption(args, '--max-directives'),
33
+ metadata: { cli: true }
34
+ })
35
+ : undefined;
36
+ return importNativeSource({
37
+ language,
38
+ parser: readOption(args, '--parser'),
39
+ sourcePath: file,
40
+ sourceHash,
41
+ sourceText: source,
42
+ sourcePreservation,
43
+ nativeAstMetadata: { sourceBytes: source.length }
44
+ });
45
+ }
46
+
47
+ export function readNativeImportForProjection(file, source, args) {
48
+ const parsed = tryParseJson(source);
49
+ if (!parsed) return importNativeFile(file, source, args);
50
+ if (parsed.kind === 'frontier.lang.universalAst') {
51
+ const nativeSource = parsed.nativeSources?.[0];
52
+ return {
53
+ id: parsed.metadata?.nativeImportId ?? `import_${idFragment(parsed.id)}`,
54
+ language: parsed.metadata?.sourceLanguage ?? nativeSource?.language ?? readOption(args, '--language') ?? inferLanguage(file),
55
+ sourcePath: parsed.metadata?.sourcePath ?? nativeSource?.sourcePath,
56
+ universalAst: parsed,
57
+ nativeSource,
58
+ nativeAst: nativeSource?.ast,
59
+ semanticIndex: parsed.semanticIndex,
60
+ sourceMaps: parsed.sourceMaps,
61
+ losses: parsed.losses,
62
+ evidence: parsed.evidence,
63
+ metadata: parsed.metadata ?? {}
64
+ };
65
+ }
66
+ return parsed;
67
+ }
68
+
69
+ export function nativeCapabilityLanguages(imported, args) {
70
+ if (args.includes('--all-languages')) return undefined;
71
+ const language = imported?.language;
72
+ if (!language) return undefined;
73
+ const normalized = String(language).toLowerCase();
74
+ const matches = NativeImportLanguageProfiles.filter((profile) => (
75
+ profile.language === normalized || profile.aliases?.includes(normalized)
76
+ ));
77
+ return matches.length ? matches : undefined;
78
+ }
79
+
80
+ export function sliceEntryRefs(args) {
81
+ return [
82
+ ...readOptions(args, '--ref'),
83
+ ...readOptions(args, '--semantic-ref'),
84
+ ...readOptions(args, '--symbol').map((value) => `symbol:${value}`),
85
+ ...readOptions(args, '--region').map((value) => `region:${value}`),
86
+ ...readOptions(args, '--native-node').map((value) => `native:${value}`),
87
+ ...readOptions(args, '--path').map((value) => `path:${value}`)
88
+ ];
89
+ }
90
+
91
+ export function readCurrentSources(args) {
92
+ const paths = readOptions(args, '--source');
93
+ if (!paths.length) return undefined;
94
+ const currentSources = {};
95
+ for (const sourcePath of paths) currentSources[sourcePath] = readFileSync(sourcePath, 'utf8');
96
+ return currentSources;
97
+ }
@@ -0,0 +1,61 @@
1
+ export function readOption(args, flag) {
2
+ const index = args.indexOf(flag);
3
+ return index >= 0 ? args[index + 1] : undefined;
4
+ }
5
+
6
+ export function readOptions(args, flag) {
7
+ const values = [];
8
+ for (let index = 0; index < args.length; index += 1) {
9
+ if (args[index] === flag && args[index + 1]) values.push(args[index + 1]);
10
+ }
11
+ return values;
12
+ }
13
+
14
+ export function readIntegerOption(args, flag) {
15
+ const value = readOption(args, flag);
16
+ if (value === undefined) return undefined;
17
+ const parsed = Number.parseInt(value, 10);
18
+ return Number.isFinite(parsed) ? parsed : undefined;
19
+ }
20
+
21
+ export function sourcePreservationOptionsRequested(args) {
22
+ return args.includes('--omit-source-text')
23
+ || args.includes('--no-tokens')
24
+ || args.includes('--no-trivia')
25
+ || args.includes('--no-directives')
26
+ || args.includes('--max-tokens')
27
+ || args.includes('--max-trivia')
28
+ || args.includes('--max-directives');
29
+ }
30
+
31
+ export function tryParseJson(source) {
32
+ const trimmed = source.trim();
33
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return undefined;
34
+ try {
35
+ return JSON.parse(trimmed);
36
+ } catch {
37
+ return undefined;
38
+ }
39
+ }
40
+
41
+ export function idFragment(value) {
42
+ return String(value ?? 'unknown').replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '').toLowerCase() || 'unknown';
43
+ }
44
+
45
+ export function inferLanguage(file) {
46
+ if (!file) return 'unknown';
47
+ if (/\.[cm]?tsx?$/.test(file)) return 'typescript';
48
+ if (/\.m?jsx?$/.test(file)) return 'javascript';
49
+ if (/\.rs$/.test(file)) return 'rust';
50
+ if (/\.py$/.test(file)) return 'python';
51
+ if (/\.c$/.test(file)) return 'c';
52
+ if (/\.cpp$|\.cc$|\.cxx$|\.hpp$|\.h$/.test(file)) return 'cpp';
53
+ if (/\.go$/.test(file)) return 'go';
54
+ if (/\.java$/.test(file)) return 'java';
55
+ if (/\.kt$/.test(file)) return 'kotlin';
56
+ if (/\.cs$/.test(file)) return 'csharp';
57
+ if (/\.swift$/.test(file)) return 'swift';
58
+ if (/\.php$/.test(file)) return 'php';
59
+ if (/\.rb$|\.rake$/.test(file)) return 'ruby';
60
+ return 'unknown';
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-cli",
3
- "version": "0.3.33",
3
+ "version": "0.3.35",
4
4
  "description": "Command line interface for parsing, checking, hashing, and emitting Frontier Lang projects.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,10 +54,10 @@
54
54
  "access": "public"
55
55
  },
56
56
  "dependencies": {
57
- "@shapeshift-labs/frontier-lang-checker": "0.3.5",
58
- "@shapeshift-labs/frontier-lang-compiler": "0.2.41",
59
- "@shapeshift-labs/frontier-lang-kernel": "0.3.9",
60
- "@shapeshift-labs/frontier-lang-parser": "0.3.5"
57
+ "@shapeshift-labs/frontier-lang-checker": "0.3.7",
58
+ "@shapeshift-labs/frontier-lang-compiler": "0.2.71",
59
+ "@shapeshift-labs/frontier-lang-kernel": "0.3.12",
60
+ "@shapeshift-labs/frontier-lang-parser": "0.3.7"
61
61
  },
62
62
  "bin": {
63
63
  "frontier-lang": "dist/index.js"