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

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 +1 -1
  2. package/dist/index.js +141 -2
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -190,7 +190,7 @@ npm install -g @shapeshift-labs/frontier-lang-cli
190
190
  frontier-lang check examples/todo.frontier
191
191
  ```
192
192
 
193
- Commands: `parse`, `check`, `hash`, `emit`, `emit-ts`, `emit-js`, `emit-rust`, `emit-python`, and `emit-c`.
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`.
194
194
 
195
195
  ```sh
196
196
  frontier-lang emit app.frontier --target rust --out app.rs
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, realpathSync, writeFileSync } from 'node:fs';
2
+ import { readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
5
  import { parseFrontierFile, parseFrontierSource } from '@shapeshift-labs/frontier-lang-parser';
5
6
  import { checkDocument } from '@shapeshift-labs/frontier-lang-checker';
@@ -18,6 +19,9 @@ export async function runCli(argv = process.argv.slice(2), io = console) {
18
19
  const [command, file, ...rest] = argv;
19
20
  if (!command || command === 'help' || command === '--help') return help(io);
20
21
  if (!file && command !== 'version') throw new Error(`Missing input file for ${command}`);
22
+ if (command === 'corpus-roundtrip') {
23
+ return outputMaybeFile(io, rest, runCorpusRoundtrip(file, rest));
24
+ }
21
25
  const source = file ? readFileSync(file, 'utf8') : '';
22
26
  if (command === 'from-json') {
23
27
  const envelope = readUniversalAstJson(source);
@@ -92,7 +96,140 @@ function outputMaybeFile(io, args, value) {
92
96
  const outIndex = args.indexOf('--out');
93
97
  if (outIndex >= 0 && args[outIndex + 1]) writeFileSync(args[outIndex + 1], json + '\n'); else io.log(json);
94
98
  }
95
- function help(io) { io.log('frontier-lang <parse|check|hash|ast|capabilities|to-json|from-json|import|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]'); }
99
+ function runCorpusRoundtrip(inputPath, args) {
100
+ const target = readOption(args, '--target') ?? 'typescript';
101
+ const entries = collectCorpusEntries(inputPath);
102
+ const files = entries.map((entry) => corpusRoundtripFile(entry, { target, parser: readOption(args, '--parser') }));
103
+ const failed = files.filter((fileResult) => !fileResult.ok);
104
+ return {
105
+ kind: 'frontier.lang.corpusRoundtrip',
106
+ version: 1,
107
+ inputPath,
108
+ target,
109
+ total: files.length,
110
+ passed: files.length - failed.length,
111
+ failed: failed.length,
112
+ sourceMapCount: files.reduce((sum, fileResult) => sum + (fileResult.sourceMapCount ?? 0), 0),
113
+ lossCount: files.reduce((sum, fileResult) => sum + (fileResult.lossCount ?? 0), 0),
114
+ readiness: files.reduce((counts, fileResult) => {
115
+ for (const readiness of fileResult.mergeReadiness ?? []) counts[readiness] = (counts[readiness] ?? 0) + 1;
116
+ return counts;
117
+ }, {}),
118
+ files
119
+ };
120
+ }
121
+
122
+ function corpusRoundtripFile(entry, options) {
123
+ const path = entry.path;
124
+ try {
125
+ const source = readFileSync(path, 'utf8');
126
+ const language = entry.language ?? inferLanguage(path);
127
+ if (/\.frontier$/i.test(path)) {
128
+ const document = parseFrontierFile(path, source);
129
+ const envelope = createUniversalAstFromDocument(document, {
130
+ evidence: [{ id: `frontier_lang_cli_corpus_${idFragment(path)}`, kind: 'test', status: 'passed', path, summary: 'Corpus Frontier source parsed into universal AST.' }]
131
+ });
132
+ const encoded = writeUniversalAstJson(envelope);
133
+ const decoded = readUniversalAstJson(encoded);
134
+ const result = compileFrontierDocument(decoded.document, { target: options.target });
135
+ return {
136
+ path,
137
+ language: 'frontier',
138
+ kind: 'frontierSource',
139
+ ok: result.ok && decoded.document.id === document.id,
140
+ hash: result.hash,
141
+ sourceMapCount: envelope.sourceMaps?.length ?? 0,
142
+ lossCount: envelope.losses?.length ?? 0,
143
+ evidenceCount: envelope.evidence?.length ?? 0,
144
+ diagnostics: result.diagnostics,
145
+ outputBytes: result.output.length,
146
+ jsonBytes: encoded.length
147
+ };
148
+ }
149
+ const imported = importNativeSource({
150
+ language,
151
+ parser: entry.parser ?? options.parser,
152
+ sourcePath: path,
153
+ sourceText: source
154
+ });
155
+ const encoded = writeUniversalAstJson(imported.universalAst);
156
+ const decoded = readUniversalAstJson(encoded);
157
+ return {
158
+ path,
159
+ language,
160
+ kind: 'nativeSource',
161
+ ok: decoded.document.id === imported.document.id,
162
+ sourceMapCount: imported.sourceMaps?.length ?? 0,
163
+ sourceMapMappingCount: (imported.sourceMaps ?? []).reduce((sum, sourceMap) => sum + (sourceMap.mappings?.length ?? 0), 0),
164
+ lossCount: imported.losses?.length ?? 0,
165
+ evidenceCount: imported.evidence?.length ?? 0,
166
+ symbolCount: imported.semanticIndex?.symbols?.length ?? 0,
167
+ occurrenceCount: imported.semanticIndex?.occurrences?.length ?? 0,
168
+ mergeReadiness: (imported.mergeCandidates ?? []).map((candidate) => candidate.readiness),
169
+ jsonBytes: encoded.length
170
+ };
171
+ } catch (error) {
172
+ return {
173
+ path,
174
+ language: entry.language ?? inferLanguage(path),
175
+ kind: 'error',
176
+ ok: false,
177
+ error: error instanceof Error ? error.message : String(error)
178
+ };
179
+ }
180
+ }
181
+
182
+ function collectCorpusEntries(inputPath) {
183
+ const absolute = resolve(inputPath);
184
+ const stat = statSync(absolute);
185
+ if (stat.isDirectory()) {
186
+ return collectCorpusDirectory(absolute).map((path) => ({ path }));
187
+ }
188
+ if (/\.json$/i.test(absolute)) {
189
+ return readCorpusManifest(absolute);
190
+ }
191
+ return [{ path: absolute }];
192
+ }
193
+
194
+ function collectCorpusDirectory(root) {
195
+ const files = [];
196
+ for (const item of readdirSync(root, { withFileTypes: true })) {
197
+ const path = join(root, item.name);
198
+ if (item.isDirectory()) {
199
+ if (!isIgnoredCorpusDirectory(item.name)) files.push(...collectCorpusDirectory(path));
200
+ continue;
201
+ }
202
+ if (item.isFile() && isCorpusSourceFile(path)) files.push(path);
203
+ }
204
+ return files.sort();
205
+ }
206
+
207
+ function readCorpusManifest(path) {
208
+ const manifest = JSON.parse(readFileSync(path, 'utf8'));
209
+ const base = dirname(path);
210
+ const rawEntries = Array.isArray(manifest) ? manifest : manifest.files ?? manifest.entries ?? [];
211
+ return rawEntries.map((entry) => {
212
+ if (typeof entry === 'string') return { path: resolve(base, entry) };
213
+ return {
214
+ ...entry,
215
+ path: resolve(base, entry.path ?? entry.file)
216
+ };
217
+ });
218
+ }
219
+
220
+ function isIgnoredCorpusDirectory(name) {
221
+ return name === 'node_modules' || name === '.git' || name === 'dist' || name === 'coverage' || name === '.next';
222
+ }
223
+
224
+ function isCorpusSourceFile(path) {
225
+ 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);
226
+ }
227
+
228
+ function idFragment(value) {
229
+ return String(value ?? 'unknown').replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '').toLowerCase() || 'unknown';
230
+ }
231
+
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]'); }
96
233
  function readOption(args, flag) { const index = args.indexOf(flag); return index >= 0 ? args[index + 1] : undefined; }
97
234
  function inferLanguage(file) {
98
235
  if (!file) return 'unknown';
@@ -107,6 +244,8 @@ function inferLanguage(file) {
107
244
  if (/\.kt$/.test(file)) return 'kotlin';
108
245
  if (/\.cs$/.test(file)) return 'csharp';
109
246
  if (/\.swift$/.test(file)) return 'swift';
247
+ if (/\.php$/.test(file)) return 'php';
248
+ if (/\.rb$|\.rake$/.test(file)) return 'ruby';
110
249
  return 'unknown';
111
250
  }
112
251
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-cli",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
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",
@@ -53,10 +53,10 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@shapeshift-labs/frontier-lang-checker": "0.3.2",
57
- "@shapeshift-labs/frontier-lang-compiler": "0.2.6",
58
- "@shapeshift-labs/frontier-lang-kernel": "0.3.2",
59
- "@shapeshift-labs/frontier-lang-parser": "0.3.2"
56
+ "@shapeshift-labs/frontier-lang-checker": "0.3.3",
57
+ "@shapeshift-labs/frontier-lang-compiler": "0.2.7",
58
+ "@shapeshift-labs/frontier-lang-kernel": "0.3.3",
59
+ "@shapeshift-labs/frontier-lang-parser": "0.3.3"
60
60
  },
61
61
  "bin": {
62
62
  "frontier-lang": "dist/index.js"