@shapeshift-labs/frontier-lang-cli 0.3.8 → 0.3.9

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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/dist/index.js +114 -9
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -6,6 +6,9 @@ Command line interface for parsing, checking, hashing, and emitting Frontier Lan
6
6
  frontier-lang check app.frontier --strict-effects
7
7
  frontier-lang emit app.frontier --target rust --out generated.rs
8
8
  frontier-lang capabilities app.frontier --target typescript --platform node
9
+ frontier-lang import src/todo.ts --sidecar --out native-import.json
10
+ frontier-lang project-native native-import.json --source-only --out restored.ts
11
+ frontier-lang native-coverage src/todo.ts
9
12
  ```
10
13
 
11
14
  ## Related Packages
@@ -190,7 +193,7 @@ npm install -g @shapeshift-labs/frontier-lang-cli
190
193
  frontier-lang check examples/todo.frontier
191
194
  ```
192
195
 
193
- Commands: `parse`, `check`, `hash`, `ast`, `capabilities`, `to-json`, `from-json`, `import`, `roundtrip`, `corpus-roundtrip`, `emit`, `emit-ts`, `emit-js`, `emit-rust`, `emit-python`, and `emit-c`.
196
+ Commands: `parse`, `check`, `hash`, `ast`, `capabilities`, `to-json`, `from-json`, `import`, `project-native`, `native-coverage`, `roundtrip`, `corpus-roundtrip`, `emit`, `emit-ts`, `emit-js`, `emit-rust`, `emit-python`, and `emit-c`.
194
197
 
195
198
  ```sh
196
199
  frontier-lang emit app.frontier --target rust --out app.rs
package/dist/index.js CHANGED
@@ -7,8 +7,12 @@ import { checkDocument } from '@shapeshift-labs/frontier-lang-checker';
7
7
  import { hashDocumentBase } from '@shapeshift-labs/frontier-lang-kernel';
8
8
  import {
9
9
  compileFrontierDocument,
10
+ createNativeSourcePreservation,
11
+ createNativeImportCoverageMatrix,
12
+ createSemanticImportSidecar,
10
13
  createUniversalAstFromDocument,
11
14
  importNativeSource,
15
+ projectNativeImportToSource,
12
16
  projectFrontierAst,
13
17
  readUniversalAstJson,
14
18
  resolveCapabilityAdapters,
@@ -31,15 +35,28 @@ export async function runCli(argv = process.argv.slice(2), io = console) {
31
35
  return io.log(result.output);
32
36
  }
33
37
  if (command === 'import') {
38
+ const imported = importNativeFile(file, source, rest);
39
+ if (rest.includes('--sidecar-only')) return outputMaybeFile(io, rest, createSemanticImportSidecar(imported));
40
+ if (rest.includes('--sidecar')) {
41
+ return outputMaybeFile(io, rest, { import: imported, sidecar: createSemanticImportSidecar(imported) });
42
+ }
43
+ return outputMaybeFile(io, rest, imported);
44
+ }
45
+ if (command === 'project-native') {
46
+ const imported = readNativeImportForProjection(file, source, rest);
47
+ const projection = projectNativeImportToSource(imported, { preferPreservedSource: !rest.includes('--stubs') });
48
+ if (rest.includes('--source-only')) {
49
+ const outIndex = rest.indexOf('--out');
50
+ if (outIndex >= 0 && rest[outIndex + 1]) writeFileSync(rest[outIndex + 1], projection.sourceText);
51
+ else io.log(projection.sourceText);
52
+ return;
53
+ }
54
+ return outputMaybeFile(io, rest, projection);
55
+ }
56
+ if (command === 'native-coverage') {
34
57
  const language = readOption(rest, '--language') ?? inferLanguage(file);
35
- return outputMaybeFile(io, rest, importNativeSource({
36
- language,
37
- parser: readOption(rest, '--parser'),
38
- sourcePath: file,
39
- sourceHash: readOption(rest, '--source-hash'),
40
- sourceText: source,
41
- nativeAstMetadata: { sourceBytes: source.length }
42
- }));
58
+ const imported = importNativeFile(file, source, rest, { language });
59
+ return outputMaybeFile(io, rest, createNativeImportCoverageMatrix({ imports: [imported] }));
43
60
  }
44
61
  const document = file ? parseFrontierFile(file, source) : parseFrontierSource(source);
45
62
  if (command === 'to-json') {
@@ -111,6 +128,11 @@ function runCorpusRoundtrip(inputPath, args) {
111
128
  failed: failed.length,
112
129
  sourceMapCount: files.reduce((sum, fileResult) => sum + (fileResult.sourceMapCount ?? 0), 0),
113
130
  lossCount: files.reduce((sum, fileResult) => sum + (fileResult.lossCount ?? 0), 0),
131
+ sourcePreservationCount: files.filter((fileResult) => fileResult.sourcePreservationId).length,
132
+ projectionModes: files.reduce((counts, fileResult) => {
133
+ if (fileResult.projectionMode) counts[fileResult.projectionMode] = (counts[fileResult.projectionMode] ?? 0) + 1;
134
+ return counts;
135
+ }, {}),
114
136
  readiness: files.reduce((counts, fileResult) => {
115
137
  for (const readiness of fileResult.mergeReadiness ?? []) counts[readiness] = (counts[readiness] ?? 0) + 1;
116
138
  return counts;
@@ -152,6 +174,7 @@ function corpusRoundtripFile(entry, options) {
152
174
  sourcePath: path,
153
175
  sourceText: source
154
176
  });
177
+ const projection = projectNativeImportToSource(imported);
155
178
  const encoded = writeUniversalAstJson(imported.universalAst);
156
179
  const decoded = readUniversalAstJson(encoded);
157
180
  return {
@@ -165,6 +188,11 @@ function corpusRoundtripFile(entry, options) {
165
188
  evidenceCount: imported.evidence?.length ?? 0,
166
189
  symbolCount: imported.semanticIndex?.symbols?.length ?? 0,
167
190
  occurrenceCount: imported.semanticIndex?.occurrences?.length ?? 0,
191
+ sourcePreservationId: imported.metadata?.sourcePreservationId,
192
+ sourcePreservationExact: imported.metadata?.sourcePreservation?.summary?.exactSourceAvailable === true,
193
+ projectionMode: projection.mode,
194
+ projectionReadiness: projection.readiness?.readiness,
195
+ projectionLossCount: projection.losses?.length ?? 0,
168
196
  mergeReadiness: (imported.mergeCandidates ?? []).map((candidate) => candidate.readiness),
169
197
  jsonBytes: encoded.length
170
198
  };
@@ -217,6 +245,68 @@ function readCorpusManifest(path) {
217
245
  });
218
246
  }
219
247
 
248
+ function importNativeFile(file, source, args, defaults = {}) {
249
+ const language = defaults.language ?? readOption(args, '--language') ?? inferLanguage(file);
250
+ const sourceHash = readOption(args, '--source-hash');
251
+ const sourcePreservation = sourcePreservationOptionsRequested(args)
252
+ ? createNativeSourcePreservation({
253
+ language,
254
+ sourcePath: file,
255
+ sourceHash,
256
+ sourceText: source,
257
+ includeSourceText: !args.includes('--omit-source-text'),
258
+ includeTokens: !args.includes('--no-tokens'),
259
+ includeTrivia: !args.includes('--no-trivia'),
260
+ includeDirectives: !args.includes('--no-directives'),
261
+ maxTokens: readIntegerOption(args, '--max-tokens'),
262
+ maxTrivia: readIntegerOption(args, '--max-trivia'),
263
+ maxDirectives: readIntegerOption(args, '--max-directives'),
264
+ metadata: { cli: true }
265
+ })
266
+ : undefined;
267
+ return importNativeSource({
268
+ language,
269
+ parser: readOption(args, '--parser'),
270
+ sourcePath: file,
271
+ sourceHash,
272
+ sourceText: source,
273
+ sourcePreservation,
274
+ nativeAstMetadata: { sourceBytes: source.length }
275
+ });
276
+ }
277
+
278
+ function readNativeImportForProjection(file, source, args) {
279
+ const parsed = tryParseJson(source);
280
+ if (!parsed) return importNativeFile(file, source, args);
281
+ if (parsed.kind === 'frontier.lang.universalAst') {
282
+ const nativeSource = parsed.nativeSources?.[0];
283
+ return {
284
+ id: parsed.metadata?.nativeImportId ?? `import_${idFragment(parsed.id)}`,
285
+ language: parsed.metadata?.sourceLanguage ?? nativeSource?.language ?? readOption(args, '--language') ?? inferLanguage(file),
286
+ sourcePath: parsed.metadata?.sourcePath ?? nativeSource?.sourcePath,
287
+ universalAst: parsed,
288
+ nativeSource,
289
+ nativeAst: nativeSource?.ast,
290
+ semanticIndex: parsed.semanticIndex,
291
+ sourceMaps: parsed.sourceMaps,
292
+ losses: parsed.losses,
293
+ evidence: parsed.evidence,
294
+ metadata: parsed.metadata ?? {}
295
+ };
296
+ }
297
+ return parsed;
298
+ }
299
+
300
+ function tryParseJson(source) {
301
+ const trimmed = source.trim();
302
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return undefined;
303
+ try {
304
+ return JSON.parse(trimmed);
305
+ } catch {
306
+ return undefined;
307
+ }
308
+ }
309
+
220
310
  function isIgnoredCorpusDirectory(name) {
221
311
  return name === 'node_modules' || name === '.git' || name === 'dist' || name === 'coverage' || name === '.next';
222
312
  }
@@ -229,8 +319,23 @@ function idFragment(value) {
229
319
  return String(value ?? 'unknown').replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '').toLowerCase() || 'unknown';
230
320
  }
231
321
 
232
- function help(io) { io.log('frontier-lang <parse|check|hash|ast|capabilities|to-json|from-json|import|roundtrip|corpus-roundtrip|emit|emit-ts|emit-js|emit-rust|emit-python|emit-c> <file> [--target target] [--language language] [--parser parser] [--platform platform] [--ast] [--out file] [--strict-effects]'); }
322
+ function help(io) { io.log('frontier-lang <parse|check|hash|ast|capabilities|to-json|from-json|import|project-native|native-coverage|roundtrip|corpus-roundtrip|emit|emit-ts|emit-js|emit-rust|emit-python|emit-c> <file> [--target target] [--language language] [--parser parser] [--platform platform] [--ast] [--sidecar] [--sidecar-only] [--source-only] [--stubs] [--out file] [--strict-effects]'); }
233
323
  function readOption(args, flag) { const index = args.indexOf(flag); return index >= 0 ? args[index + 1] : undefined; }
324
+ function readIntegerOption(args, flag) {
325
+ const value = readOption(args, flag);
326
+ if (value === undefined) return undefined;
327
+ const parsed = Number.parseInt(value, 10);
328
+ return Number.isFinite(parsed) ? parsed : undefined;
329
+ }
330
+ function sourcePreservationOptionsRequested(args) {
331
+ return args.includes('--omit-source-text')
332
+ || args.includes('--no-tokens')
333
+ || args.includes('--no-trivia')
334
+ || args.includes('--no-directives')
335
+ || args.includes('--max-tokens')
336
+ || args.includes('--max-trivia')
337
+ || args.includes('--max-directives');
338
+ }
234
339
  function inferLanguage(file) {
235
340
  if (!file) return 'unknown';
236
341
  if (/\.[cm]?tsx?$/.test(file)) return 'typescript';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-cli",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
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,7 +54,7 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@shapeshift-labs/frontier-lang-checker": "0.3.3",
57
- "@shapeshift-labs/frontier-lang-compiler": "0.2.7",
57
+ "@shapeshift-labs/frontier-lang-compiler": "0.2.10",
58
58
  "@shapeshift-labs/frontier-lang-kernel": "0.3.3",
59
59
  "@shapeshift-labs/frontier-lang-parser": "0.3.3"
60
60
  },