a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky
atproto bluesky typescript npm
101
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(lex-cli): switch over from Clipanion to Optique

Mary 93b3e0a8 749f9045

+102 -94
+8
.changeset/sweet-bats-swim.md
··· 1 + --- 2 + '@atcute/lex-cli': patch 3 + --- 4 + 5 + switch from Clipanion to Optique 6 + 7 + lex-cli was previously using my personal fork of Clipanion and I wasn't quite interested in 8 + maintaining it any longer.
+2 -1
packages/lexicons/lex-cli/package.json
··· 26 26 "dependencies": { 27 27 "@atcute/lexicon-doc": "workspace:^", 28 28 "@badrap/valita": "^0.4.6", 29 - "@externdefs/collider": "^0.3.0", 29 + "@optique/core": "^0.6.1", 30 + "@optique/run": "^0.6.1", 30 31 "picocolors": "^1.1.1", 31 32 "prettier": "^3.6.2" 32 33 },
+72 -81
packages/lexicons/lex-cli/src/cli.ts
··· 2 2 import * as path from 'node:path'; 3 3 import * as url from 'node:url'; 4 4 5 - import { Builtins, Command, Option, Program } from '@externdefs/collider'; 5 + import { object } from '@optique/core/constructs'; 6 + import { command, constant, option } from '@optique/core/primitives'; 7 + import { run } from '@optique/run'; 8 + import { path as pathParser } from '@optique/run/valueparser'; 6 9 import pc from 'picocolors'; 7 10 8 11 import { lexiconDoc, type LexiconDoc } from '@atcute/lexicon-doc'; ··· 10 13 import { generateLexiconApi } from './codegen.js'; 11 14 import type { LexiconConfig } from './index.js'; 12 15 13 - const program = new Program({ binaryName: 'lex-cli' }); 16 + const parser = command( 17 + 'generate', 18 + object({ 19 + type: constant('generate'), 20 + config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })), 21 + }), 22 + ); 14 23 15 - program.register(Builtins.HelpCommand); 24 + const result = run(parser, { programName: 'lex-cli' }); 16 25 17 - program.register( 18 - class GenerateCommand extends Command { 19 - static override paths = [['generate']]; 26 + if (result.type === 'generate') { 27 + const configFilename = path.resolve(result.config); 28 + const configDirname = path.dirname(configFilename); 20 29 21 - static override usage = Command.Usage({ 22 - description: `Generates TypeScript schema`, 23 - }); 30 + let config: LexiconConfig; 31 + try { 32 + const configURL = url.pathToFileURL(configFilename); 33 + const configMod = (await import(configURL.href)) as { default: LexiconConfig }; 34 + config = configMod.default; 35 + } catch (err) { 36 + console.error(pc.bold(pc.red(`failed to import config:`))); 37 + console.error(err); 24 38 25 - config = Option.String(['-c', '--config'], { 26 - required: true, 27 - description: `Config file`, 28 - }); 39 + process.exit(1); 40 + } 29 41 30 - async execute(): Promise<number | void> { 31 - const configFilename = path.resolve(this.config); 32 - const configDirname = path.dirname(configFilename); 42 + const documents: LexiconDoc[] = []; 33 43 34 - let config: LexiconConfig; 35 - try { 36 - const configURL = url.pathToFileURL(configFilename); 37 - const configMod = (await import(configURL.href)) as { default: LexiconConfig }; 38 - config = configMod.default; 39 - } catch (err) { 40 - console.error(pc.bold(pc.red(`failed to import config:`))); 41 - console.error(err); 44 + for await (const filename of fs.glob(config.files, { cwd: configDirname })) { 45 + let source: string; 46 + try { 47 + source = await fs.readFile(path.join(configDirname, filename), 'utf8'); 48 + } catch (err) { 49 + console.error(pc.bold(pc.red(`file read error with "${filename}"`))); 50 + console.error(err); 42 51 43 - return 1; 44 - } 52 + process.exit(1); 53 + } 45 54 46 - const documents: LexiconDoc[] = []; 55 + let json: unknown; 56 + try { 57 + json = JSON.parse(source); 58 + } catch (err) { 59 + console.error(pc.bold(pc.red(`json parse error in "${filename}"`))); 60 + console.error(err); 47 61 48 - for await (const filename of fs.glob(config.files, { cwd: configDirname })) { 49 - let source: string; 50 - try { 51 - source = await fs.readFile(path.join(configDirname, filename), 'utf8'); 52 - } catch (err) { 53 - console.error(pc.bold(pc.red(`file read error with "${filename}"`))); 54 - console.error(err); 62 + process.exit(1); 63 + } 55 64 56 - return 1; 57 - } 58 - 59 - let json: unknown; 60 - try { 61 - json = JSON.parse(source); 62 - } catch (err) { 63 - console.error(pc.bold(pc.red(`json parse error in "${filename}"`))); 64 - console.error(err); 65 - 66 - return 1; 67 - } 68 - 69 - const result = lexiconDoc.try(json, { mode: 'strip' }); 70 - if (!result.ok) { 71 - console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`))); 72 - console.error(result.message); 73 - 74 - for (const issue of result.issues) { 75 - console.log(`- ${issue.code} at .${issue.path.join('.')}`); 76 - } 65 + const result = lexiconDoc.try(json, { mode: 'strip' }); 66 + if (!result.ok) { 67 + console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`))); 68 + console.error(result.message); 77 69 78 - return 1; 79 - } 80 - 81 - documents.push(result.value); 70 + for (const issue of result.issues) { 71 + console.log(`- ${issue.code} at .${issue.path.join('.')}`); 82 72 } 83 73 84 - const result = await generateLexiconApi({ 85 - documents: documents, 86 - mappings: config.mappings ?? [], 87 - modules: { 88 - importSuffix: config.modules?.importSuffix ?? '.js', 89 - }, 90 - prettier: { 91 - cwd: process.cwd(), 92 - }, 93 - }); 74 + process.exit(1); 75 + } 94 76 95 - const outdir = path.join(configDirname, config.outdir); 77 + documents.push(result.value); 78 + } 96 79 97 - for (const file of result.files) { 98 - const filename = path.join(outdir, file.filename); 99 - const dirname = path.dirname(filename); 80 + const generationResult = await generateLexiconApi({ 81 + documents: documents, 82 + mappings: config.mappings ?? [], 83 + modules: { 84 + importSuffix: config.modules?.importSuffix ?? '.js', 85 + }, 86 + prettier: { 87 + cwd: process.cwd(), 88 + }, 89 + }); 100 90 101 - await fs.mkdir(dirname, { recursive: true }); 102 - await fs.writeFile(filename, file.code); 103 - } 104 - } 105 - }, 106 - ); 91 + const outdir = path.join(configDirname, config.outdir); 92 + 93 + for (const file of generationResult.files) { 94 + const filename = path.join(outdir, file.filename); 95 + const dirname = path.dirname(filename); 107 96 108 - const exitCode = await program.run(process.argv.slice(2)); 109 - process.exitCode = exitCode; 97 + await fs.mkdir(dirname, { recursive: true }); 98 + await fs.writeFile(filename, file.code); 99 + } 100 + }
+20 -12
pnpm-lock.yaml
··· 463 463 '@badrap/valita': 464 464 specifier: ^0.4.6 465 465 version: 0.4.6 466 - '@externdefs/collider': 467 - specifier: ^0.3.0 468 - version: 0.3.0(@badrap/valita@0.4.6) 466 + '@optique/core': 467 + specifier: ^0.6.1 468 + version: 0.6.1 469 + '@optique/run': 470 + specifier: ^0.6.1 471 + version: 0.6.1 469 472 picocolors: 470 473 specifier: ^1.1.1 471 474 version: 1.1.1 ··· 1346 1349 cpu: [x64] 1347 1350 os: [win32] 1348 1351 1349 - '@externdefs/collider@0.3.0': 1350 - resolution: {integrity: sha512-x5CpeZ4c8n+1wMFthUMWSQKqCGcQo52/Qbda5ES+JFRRg/D8Ep6/JOvUUq5HExFuv/wW+6UYG2U/mXzw0IAd8Q==} 1351 - peerDependencies: 1352 - '@badrap/valita': ^0.4.4 1353 - 1354 1352 '@hapi/accept@6.0.3': 1355 1353 resolution: {integrity: sha512-p72f9k56EuF0n3MwlBNThyVE5PXX40g+aQh+C/xbKrfzahM2Oispv3AXmOIU51t3j77zay1qrX7IIziZXspMlw==} 1356 1354 ··· 1558 1556 '@nodelib/fs.walk@1.2.8': 1559 1557 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 1560 1558 engines: {node: '>= 8'} 1559 + 1560 + '@optique/core@0.6.1': 1561 + resolution: {integrity: sha512-e3J5zCL9aBAheZkLgYUoeAt1wKSkFbSn0+h0kB4VYabOGUnesVuSxZe00BY4jLnXr36HqaSf6ZTJi8bxbLH0Zg==} 1562 + engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} 1563 + 1564 + '@optique/run@0.6.1': 1565 + resolution: {integrity: sha512-P3E5ZAAwhAg8RYp2M4MPtwUr1wdZD7/IxHGKwW+Ie8eFbmfXnfvrygyqh5At0mQXVxbzKYlVrfl1vdoKVoxD7Q==} 1566 + engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} 1561 1567 1562 1568 '@pkgjs/parseargs@0.11.0': 1563 1569 resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} ··· 4874 4880 '@esbuild/win32-x64@0.25.9': 4875 4881 optional: true 4876 4882 4877 - '@externdefs/collider@0.3.0(@badrap/valita@0.4.6)': 4878 - dependencies: 4879 - '@badrap/valita': 0.4.6 4880 - 4881 4883 '@hapi/accept@6.0.3': 4882 4884 dependencies: 4883 4885 '@hapi/boom': 10.0.1 ··· 5065 5067 dependencies: 5066 5068 '@nodelib/fs.scandir': 2.1.5 5067 5069 fastq: 1.19.1 5070 + 5071 + '@optique/core@0.6.1': {} 5072 + 5073 + '@optique/run@0.6.1': 5074 + dependencies: 5075 + '@optique/core': 0.6.1 5068 5076 5069 5077 '@pkgjs/parseargs@0.11.0': 5070 5078 optional: true