@atcute/lex-cli 2.3.3 → 2.5.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.
Files changed (59) hide show
  1. package/README.md +70 -4
  2. package/dist/cli.js +13 -162
  3. package/dist/cli.js.map +1 -1
  4. package/dist/codegen.d.ts.map +1 -1
  5. package/dist/codegen.js +76 -78
  6. package/dist/codegen.js.map +1 -1
  7. package/dist/commands/export.d.ts +13 -0
  8. package/dist/commands/export.d.ts.map +1 -0
  9. package/dist/commands/export.js +76 -0
  10. package/dist/commands/export.js.map +1 -0
  11. package/dist/commands/generate.d.ts +13 -0
  12. package/dist/commands/generate.d.ts.map +1 -0
  13. package/dist/commands/generate.js +136 -0
  14. package/dist/commands/generate.js.map +1 -0
  15. package/dist/commands/pull.d.ts +13 -0
  16. package/dist/commands/pull.d.ts.map +1 -0
  17. package/dist/commands/pull.js +163 -0
  18. package/dist/commands/pull.js.map +1 -0
  19. package/dist/config.d.ts +99 -1
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +82 -25
  22. package/dist/config.js.map +1 -1
  23. package/dist/git.d.ts +27 -0
  24. package/dist/git.d.ts.map +1 -0
  25. package/dist/git.js +73 -0
  26. package/dist/git.js.map +1 -0
  27. package/dist/lexicon-loader.d.ts +17 -0
  28. package/dist/lexicon-loader.d.ts.map +1 -0
  29. package/dist/lexicon-loader.js +167 -0
  30. package/dist/lexicon-loader.js.map +1 -0
  31. package/dist/pull-sources/atproto.d.ts +9 -0
  32. package/dist/pull-sources/atproto.d.ts.map +1 -0
  33. package/dist/pull-sources/atproto.js +192 -0
  34. package/dist/pull-sources/atproto.js.map +1 -0
  35. package/dist/pull-sources/git.d.ts +11 -0
  36. package/dist/pull-sources/git.d.ts.map +1 -0
  37. package/dist/pull-sources/git.js +80 -0
  38. package/dist/pull-sources/git.js.map +1 -0
  39. package/dist/pull-sources/types.d.ts +16 -0
  40. package/dist/pull-sources/types.d.ts.map +1 -0
  41. package/dist/pull-sources/types.js +2 -0
  42. package/dist/pull-sources/types.js.map +1 -0
  43. package/dist/shared-options.d.ts +6 -0
  44. package/dist/shared-options.d.ts.map +1 -0
  45. package/dist/shared-options.js +11 -0
  46. package/dist/shared-options.js.map +1 -0
  47. package/package.json +10 -7
  48. package/src/cli.ts +11 -198
  49. package/src/codegen.ts +90 -88
  50. package/src/commands/export.ts +106 -0
  51. package/src/commands/generate.ts +170 -0
  52. package/src/commands/pull.ts +231 -0
  53. package/src/config.ts +102 -30
  54. package/src/git.ts +104 -0
  55. package/src/lexicon-loader.ts +199 -0
  56. package/src/pull-sources/atproto.ts +243 -0
  57. package/src/pull-sources/git.ts +103 -0
  58. package/src/pull-sources/types.ts +18 -0
  59. package/src/shared-options.ts +13 -0
@@ -0,0 +1,192 @@
1
+ import { getPdsEndpoint, isAtprotoDid } from '@atcute/identity';
2
+ import { CompositeDidDocumentResolver, CompositeHandleResolver, DohJsonHandleResolver, PlcDidDocumentResolver, WebDidDocumentResolver, WellKnownHandleResolver, } from '@atcute/identity-resolver';
3
+ import { refineLexiconDoc } from '@atcute/lexicon-doc';
4
+ import { DohJsonLexiconAuthorityResolver, LexiconSchemaResolver } from '@atcute/lexicon-resolver';
5
+ import { isHandle, isNsid, parseCanonicalResourceUri, } from '@atcute/lexicons/syntax';
6
+ import pc from 'picocolors';
7
+ /**
8
+ * discovers all published lexicons for an authority by listing records in the
9
+ * com.atproto.lexicon.schema collection
10
+ * @param authority the authority DID
11
+ * @param didResolver DID document resolver
12
+ * @returns array of NSID strings
13
+ */
14
+ const discoverLexiconsForAuthority = async (authority, didResolver) => {
15
+ // resolve DID to get PDS endpoint
16
+ const didDocument = await didResolver.resolve(authority);
17
+ const pdsEndpoint = getPdsEndpoint(didDocument);
18
+ if (!pdsEndpoint) {
19
+ throw new Error(`no pds service in did document; did=${authority}`);
20
+ }
21
+ // call com.atproto.repo.listRecords to get all lexicon schema records
22
+ const nsids = [];
23
+ let cursor;
24
+ do {
25
+ const url = new URL('/xrpc/com.atproto.repo.listRecords', pdsEndpoint);
26
+ url.searchParams.set('repo', authority);
27
+ url.searchParams.set('collection', 'com.atproto.lexicon.schema');
28
+ url.searchParams.set('limit', '100');
29
+ if (cursor) {
30
+ url.searchParams.set('cursor', cursor);
31
+ }
32
+ const response = await fetch(url, {
33
+ headers: { accept: 'application/json' },
34
+ });
35
+ if (!response.ok) {
36
+ throw new Error(`http ${response.status} when listing records`);
37
+ }
38
+ const data = (await response.json());
39
+ // extract NSIDs from record keys (the rkey in at://did/collection/rkey)
40
+ for (const record of data.records) {
41
+ const r = parseCanonicalResourceUri(record.uri);
42
+ if (!r.ok) {
43
+ continue;
44
+ }
45
+ const nsid = r.value.rkey;
46
+ if (!isNsid(nsid)) {
47
+ continue;
48
+ }
49
+ nsids.push(nsid);
50
+ }
51
+ cursor = data.cursor;
52
+ } while (cursor);
53
+ return nsids;
54
+ };
55
+ /**
56
+ * pulls lexicon documents from AT Protocol network resolution
57
+ * @param source atproto source configuration
58
+ * @returns pulled lexicons and ISO timestamp
59
+ */
60
+ export const pullAtprotoSource = async (source) => {
61
+ // create resolver instances (reusable across NSIDs)
62
+ const authorityResolver = new DohJsonLexiconAuthorityResolver({
63
+ dohUrl: 'https://cloudflare-dns.com/dns-query',
64
+ });
65
+ const didResolver = new CompositeDidDocumentResolver({
66
+ methods: {
67
+ plc: new PlcDidDocumentResolver(),
68
+ web: new WebDidDocumentResolver(),
69
+ },
70
+ });
71
+ const schemaResolver = new LexiconSchemaResolver({
72
+ didDocumentResolver: didResolver,
73
+ });
74
+ const handleResolver = new CompositeHandleResolver({
75
+ strategy: 'race',
76
+ methods: {
77
+ http: new WellKnownHandleResolver(),
78
+ dns: new DohJsonHandleResolver({
79
+ dohUrl: 'https://cloudflare-dns.com/dns-query',
80
+ }),
81
+ },
82
+ });
83
+ const pulled = new Map();
84
+ const errors = [];
85
+ let nsids;
86
+ let authorityDid = null;
87
+ let sourceDesc;
88
+ let sourceName = null;
89
+ if (source.mode === 'nsids') {
90
+ nsids = source.nsids;
91
+ sourceDesc = `atproto (${nsids.length} nsids)`;
92
+ }
93
+ else {
94
+ // mode 2: authority-based
95
+ // step 2a: resolve authority (handle -> DID if needed)
96
+ let resolvedDid;
97
+ const handle = isHandle(source.authority) ? source.authority : null;
98
+ try {
99
+ if (isAtprotoDid(source.authority)) {
100
+ resolvedDid = source.authority;
101
+ }
102
+ else if (handle) {
103
+ resolvedDid = await handleResolver.resolve(handle);
104
+ }
105
+ else {
106
+ console.error(pc.bold(pc.red(`invalid authority: ${source.authority}`)));
107
+ console.error(`must be a valid DID or handle`);
108
+ process.exit(1);
109
+ }
110
+ authorityDid = resolvedDid;
111
+ sourceDesc = `atproto (authority: ${authorityDid})`;
112
+ sourceName = handle ?? authorityDid;
113
+ }
114
+ catch (err) {
115
+ console.error(pc.bold(pc.red(`failed to resolve authority: ${source.authority}`)));
116
+ console.error(err);
117
+ process.exit(1);
118
+ }
119
+ // step 2b: discover all lexicons for this authority
120
+ try {
121
+ nsids = await discoverLexiconsForAuthority(authorityDid, didResolver);
122
+ }
123
+ catch (err) {
124
+ console.error(pc.bold(pc.red(`failed to discover lexicons for ${sourceName}`)));
125
+ console.error(err);
126
+ process.exit(1);
127
+ }
128
+ // step 2c: filter by pattern if specified
129
+ if (source.pattern) {
130
+ nsids = nsids.filter((nsid) => {
131
+ return source.pattern.some((pattern) => {
132
+ if (pattern.endsWith('.*')) {
133
+ const prefix = pattern.slice(0, -2);
134
+ return nsid === prefix || nsid.startsWith(prefix + '.');
135
+ }
136
+ return nsid === pattern;
137
+ });
138
+ });
139
+ }
140
+ if (nsids.length === 0) {
141
+ console.warn(pc.yellow(`warning: no lexicons found for ${sourceName}`));
142
+ }
143
+ }
144
+ // fetch each NSID
145
+ let fetchedCount = 0;
146
+ for (const nsid of nsids) {
147
+ try {
148
+ // step 1: resolve authority from NSID (DNS)
149
+ const resolvedAuthority = await authorityResolver.resolve(nsid);
150
+ // step 2: cross-verify authority if in authority-based mode
151
+ if (authorityDid && resolvedAuthority !== authorityDid) {
152
+ throw new Error(`authority mismatch: NSID ${nsid} claims authority ${resolvedAuthority} but expected ${authorityDid}`);
153
+ }
154
+ // step 3: fetch schema from authority's PDS
155
+ const resolved = await schemaResolver.resolve(resolvedAuthority, nsid);
156
+ // step 4: lint the lexicon document
157
+ const issues = refineLexiconDoc(resolved.schema, true);
158
+ if (issues.length > 0) {
159
+ const messages = issues.map((i) => ` ${i.path}: ${i.message}`).join('\n');
160
+ throw new Error(`lint validation failed:\n${messages}`);
161
+ }
162
+ // create in-memory location (no file on disk yet)
163
+ const location = {
164
+ absolutePath: `<atproto:${nsid}>`,
165
+ relativePath: `${nsid}.json`,
166
+ sourceDescription: sourceDesc,
167
+ };
168
+ pulled.set(nsid, {
169
+ nsid,
170
+ doc: resolved.schema,
171
+ location,
172
+ });
173
+ fetchedCount++;
174
+ console.log(`${pc.green('+')} ${nsid}`);
175
+ }
176
+ catch (err) {
177
+ // best-effort: collect errors but continue
178
+ errors.push({ nsid, error: err });
179
+ }
180
+ }
181
+ // report all errors at end
182
+ if (errors.length > 0) {
183
+ console.warn(pc.yellow(`\nwarning: failed to fetch ${errors.length} lexicon(s):`));
184
+ for (const { nsid, error } of errors) {
185
+ console.warn(` - ${nsid}: ${error.message}`);
186
+ }
187
+ }
188
+ const suffix = sourceName ? ` from ${pc.cyan(sourceName)}` : '';
189
+ console.log(`pulled ${pc.cyan(fetchedCount.toString())} lexicons${suffix}`);
190
+ return { pulled };
191
+ };
192
+ //# sourceMappingURL=atproto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atproto.js","sourceRoot":"","sources":["../../src/pull-sources/atproto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { LexiconDoc } from '@atcute/lexicon-doc';
2
+ import type { GitSourceConfig } from '../config.js';
3
+ import type { PullResult, SourceLocation } from './types.js';
4
+ /**
5
+ * pulls lexicon documents from a git repository source
6
+ * @param source git source configuration
7
+ * @param parseLexiconFile function to parse and validate lexicon files
8
+ * @returns pulled lexicons and commit hash
9
+ */
10
+ export declare const pullGitSource: (source: GitSourceConfig, parseLexiconFile: (loc: SourceLocation) => Promise<LexiconDoc>) => Promise<PullResult>;
11
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/pull-sources/git.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAiB,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACzB,QAAQ,eAAe,EACvB,kBAAkB,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,UAAU,CAAC,KAC5D,OAAO,CAAC,UAAU,CAkFpB,CAAC"}
@@ -0,0 +1,80 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import pc from 'picocolors';
5
+ import { runGit, GitError } from '../git.js';
6
+ /**
7
+ * pulls lexicon documents from a git repository source
8
+ * @param source git source configuration
9
+ * @param parseLexiconFile function to parse and validate lexicon files
10
+ * @returns pulled lexicons and commit hash
11
+ */
12
+ export const pullGitSource = async (source, parseLexiconFile) => {
13
+ const tempParent = await fs.mkdtemp(path.join(os.tmpdir(), 'lex-cli-pull-'));
14
+ const cloneDir = path.join(tempParent, 'repo');
15
+ try {
16
+ await runGit([
17
+ 'clone',
18
+ '--filter=blob:none',
19
+ '--depth',
20
+ '1',
21
+ '--sparse',
22
+ ...(source.ref ? ['--branch', source.ref, '--single-branch'] : []),
23
+ source.remote,
24
+ cloneDir,
25
+ ], { timeoutMs: 60_000 });
26
+ }
27
+ catch (err) {
28
+ if (err instanceof GitError) {
29
+ console.error(pc.bold(pc.red(`git clone failed for ${source.remote}:`)));
30
+ console.error(err.stderr || err.message);
31
+ process.exit(1);
32
+ }
33
+ throw err;
34
+ }
35
+ try {
36
+ await runGit(['-C', cloneDir, 'sparse-checkout', 'set', '--no-cone', ...source.pattern], {
37
+ timeoutMs: 30_000,
38
+ });
39
+ }
40
+ catch (err) {
41
+ if (err instanceof GitError) {
42
+ console.error(pc.bold(pc.red(`git sparse-checkout failed for ${source.remote}:`)));
43
+ console.error(err.stderr || err.message);
44
+ process.exit(1);
45
+ }
46
+ throw err;
47
+ }
48
+ const pulled = new Map();
49
+ for await (const filename of fs.glob(source.pattern, { cwd: cloneDir })) {
50
+ const absolute = path.join(cloneDir, filename);
51
+ const stat = await fs.stat(absolute);
52
+ if (!stat.isFile()) {
53
+ continue;
54
+ }
55
+ const location = {
56
+ absolutePath: absolute,
57
+ relativePath: filename,
58
+ sourceDescription: source.remote,
59
+ };
60
+ const doc = await parseLexiconFile(location);
61
+ pulled.set(doc.id, { nsid: doc.id, doc, location });
62
+ }
63
+ // get the commit hash
64
+ let rev;
65
+ try {
66
+ const result = await runGit(['-C', cloneDir, 'rev-parse', 'HEAD'], { timeoutMs: 10_000 });
67
+ rev = result.stdout.trim();
68
+ }
69
+ catch (err) {
70
+ if (err instanceof GitError) {
71
+ console.error(pc.bold(pc.red(`git rev-parse failed for ${source.remote}:`)));
72
+ console.error(err.stderr || err.message);
73
+ process.exit(1);
74
+ }
75
+ throw err;
76
+ }
77
+ await fs.rm(tempParent, { recursive: true, force: true });
78
+ return { pulled, rev };
79
+ };
80
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/pull-sources/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { LexiconDoc } from '@atcute/lexicon-doc';
2
+ export interface SourceLocation {
3
+ absolutePath: string;
4
+ relativePath: string;
5
+ sourceDescription: string;
6
+ }
7
+ export interface PulledLexicon {
8
+ nsid: string;
9
+ doc: LexiconDoc;
10
+ location: SourceLocation;
11
+ }
12
+ export interface PullResult {
13
+ pulled: Map<string, PulledLexicon>;
14
+ rev?: string;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/pull-sources/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,UAAU,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pull-sources/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export declare const sharedOptions: import("@optique/core/parser").Parser<{
2
+ readonly config: string | undefined;
3
+ }, {
4
+ readonly config: [import("@optique/core/valueparser").ValueParserResult<string> | undefined] | undefined;
5
+ }>;
6
+ //# sourceMappingURL=shared-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-options.d.ts","sourceRoot":"","sources":["../src/shared-options.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa;;;;EAMxB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { object } from '@optique/core/constructs';
2
+ import { message } from '@optique/core/message';
3
+ import { optional } from '@optique/core/modifiers';
4
+ import { option } from '@optique/core/primitives';
5
+ import { path as pathParser } from '@optique/run/valueparser';
6
+ export const sharedOptions = object(`Global options`, {
7
+ config: optional(option('-c', '--config', pathParser({ metavar: 'CONFIG' }), {
8
+ description: message `path to the lexicon configuration file. defaults to searching for lex.config.js or lex.config.ts in the current directory.`,
9
+ })),
10
+ });
11
+ //# sourceMappingURL=shared-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-options.js","sourceRoot":"","sources":["../src/shared-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,gBAAgB,EAAE;IACrD,MAAM,EAAE,QAAQ,CACf,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE;QAC3D,WAAW,EAAE,OAAO,CAAA,4HAA4H;KAChJ,CAAC,CACF;CACD,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@atcute/lex-cli",
4
- "version": "2.3.3",
4
+ "version": "2.5.0",
5
5
  "description": "cli tool to generate type definitions for atcute",
6
6
  "license": "0BSD",
7
7
  "repository": {
@@ -22,16 +22,19 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@badrap/valita": "^0.4.6",
25
- "@optique/core": "^0.6.2",
26
- "@optique/run": "^0.6.2",
25
+ "@optique/core": "^0.6.3",
26
+ "@optique/run": "^0.6.3",
27
27
  "picocolors": "^1.1.1",
28
28
  "prettier": "^3.6.2",
29
- "@atcute/lexicon-doc": "^2.0.0"
29
+ "@atcute/identity": "^1.1.3",
30
+ "@atcute/lexicon-doc": "^2.0.3",
31
+ "@atcute/identity-resolver": "^1.1.4",
32
+ "@atcute/lexicon-resolver": "^0.1.4",
33
+ "@atcute/lexicons": "^1.2.5"
30
34
  },
31
35
  "devDependencies": {
32
- "@types/node": "^22.19.0",
33
- "tschema": "^3.2.0",
34
- "@atcute/lexicons": "^1.2.4"
36
+ "@types/node": "^22.19.1",
37
+ "tschema": "^3.2.0"
35
38
  },
36
39
  "scripts": {
37
40
  "build": "pnpm run generate:schema && tsc",
package/src/cli.ts CHANGED
@@ -1,205 +1,18 @@
1
- import * as fs from 'node:fs/promises';
2
- import * as path from 'node:path';
3
-
4
- import { lexiconDoc, refineLexiconDoc, type LexiconDoc } from '@atcute/lexicon-doc';
5
-
6
- import { object } from '@optique/core/constructs';
7
- import { command, constant, option } from '@optique/core/primitives';
1
+ import { or } from '@optique/core/constructs';
8
2
  import { run } from '@optique/run';
9
- import { path as pathParser } from '@optique/run/valueparser';
10
- import pc from 'picocolors';
11
-
12
- import { generateLexiconApi, type ImportMapping } from './codegen.js';
13
- import { loadConfig } from './config.js';
14
- import { packageJsonSchema } from './lexicon-metadata.js';
15
-
16
- /**
17
- * Resolves package imports to ImportMapping[]
18
- */
19
- const resolveImportsToMappings = async (
20
- imports: string[],
21
- configDirname: string,
22
- ): Promise<ImportMapping[]> => {
23
- const mappings: ImportMapping[] = [];
24
-
25
- for (const packageName of imports) {
26
- // Walk up from config directory to find package in node_modules
27
- let packageJson: unknown;
28
- let currentDir = configDirname;
29
- let found = false;
30
-
31
- while (currentDir !== path.dirname(currentDir)) {
32
- const candidatePath = path.join(currentDir, 'node_modules', packageName, 'package.json');
33
- try {
34
- const content = await fs.readFile(candidatePath, 'utf8');
35
- packageJson = JSON.parse(content);
36
- found = true;
37
- break;
38
- } catch (err: any) {
39
- // Only continue to parent if file not found
40
- if (err.code !== 'ENOENT') {
41
- console.error(pc.bold(pc.red(`failed to read package.json for "${packageName}":`)));
42
- console.error(err);
43
- process.exit(1);
44
- }
45
-
46
- // Not found, try parent directory
47
- currentDir = path.dirname(currentDir);
48
- }
49
- }
50
-
51
- if (!found) {
52
- console.error(pc.bold(pc.red(`failed to resolve package "${packageName}"`)));
53
- console.error(`Could not find package in node_modules starting from ${configDirname}`);
54
- process.exit(1);
55
- }
56
-
57
- // Validate package.json
58
- const result = packageJsonSchema.try(packageJson, { mode: 'passthrough' });
59
- if (!result.ok) {
60
- console.error(pc.bold(pc.red(`invalid atcute:lexicons in "${packageName}":`)));
61
- console.error(result.message);
62
-
63
- for (const issue of result.issues) {
64
- console.log(`- ${issue.code} at .${issue.path.join('.')}`);
65
- }
66
-
67
- process.exit(1);
68
- }
69
-
70
- const lexicons = result.value['atcute:lexicons'];
71
- if (!lexicons?.mappings) {
72
- continue;
73
- }
74
-
75
- // Convert mapping to ImportMapping[]
76
- for (const [pattern, entry] of Object.entries(lexicons.mappings)) {
77
- const isWildcard = pattern.endsWith('.*');
78
-
79
- mappings.push({
80
- nsid: [pattern],
81
- imports: (nsid: string) => {
82
- // Check if pattern matches
83
- if (isWildcard) {
84
- if (!nsid.startsWith(pattern.slice(0, -1))) {
85
- throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
86
- }
87
- } else {
88
- if (nsid !== pattern) {
89
- throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
90
- }
91
- }
92
-
93
- const nsidPrefix = isWildcard ? pattern.slice(0, -2) : pattern;
94
- const nsidRemainder = isWildcard ? nsid.slice(nsidPrefix.length + 1) : '';
95
-
96
- let expandedPath = entry.path
97
- .replaceAll('{{nsid}}', nsid.replaceAll('.', '/'))
98
- .replaceAll('{{nsid_remainder}}', nsidRemainder.replaceAll('.', '/'))
99
- .replaceAll('{{nsid_prefix}}', nsidPrefix.replaceAll('.', '/'));
100
-
101
- if (expandedPath === '.') {
102
- expandedPath = packageName;
103
- } else if (expandedPath.startsWith('./')) {
104
- expandedPath = `${packageName}/${expandedPath.slice(2)}`;
105
- }
106
-
107
- return {
108
- type: entry.type,
109
- from: expandedPath,
110
- };
111
- },
112
- });
113
- }
114
- }
115
3
 
116
- return mappings;
117
- };
4
+ import { exportCommandSchema, runExport } from './commands/export.js';
5
+ import { generateCommandSchema, runGenerate } from './commands/generate.js';
6
+ import { pullCommandSchema, runPull } from './commands/pull.js';
118
7
 
119
- const parser = command(
120
- 'generate',
121
- object({
122
- type: constant('generate'),
123
- config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
124
- }),
125
- );
8
+ const parser = or(generateCommandSchema, pullCommandSchema, exportCommandSchema);
126
9
 
127
- const result = run(parser, { programName: 'lex-cli' });
10
+ const result = run(parser, { programName: 'lex-cli', help: 'both' });
128
11
 
129
12
  if (result.type === 'generate') {
130
- const config = await loadConfig(result.config);
131
-
132
- // Resolve imports to mappings
133
- const importMappings = config.imports ? await resolveImportsToMappings(config.imports, config.root) : [];
134
- const allMappings = [...importMappings, ...(config.mappings ?? [])];
135
-
136
- const documents: LexiconDoc[] = [];
137
-
138
- for await (const filename of fs.glob(config.files, { cwd: config.root })) {
139
- let source: string;
140
- try {
141
- source = await fs.readFile(path.join(config.root, filename), 'utf8');
142
- } catch (err) {
143
- console.error(pc.bold(pc.red(`file read error with "${filename}"`)));
144
- console.error(err);
145
-
146
- process.exit(1);
147
- }
148
-
149
- let json: unknown;
150
- try {
151
- json = JSON.parse(source);
152
- } catch (err) {
153
- console.error(pc.bold(pc.red(`json parse error in "${filename}"`)));
154
- console.error(err);
155
-
156
- process.exit(1);
157
- }
158
-
159
- const result = lexiconDoc.try(json, { mode: 'strip' });
160
- if (!result.ok) {
161
- console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`)));
162
- console.error(result.message);
163
-
164
- for (const issue of result.issues) {
165
- console.log(`- ${issue.code} at .${issue.path.join('.')}`);
166
- }
167
-
168
- process.exit(1);
169
- }
170
-
171
- const issues = refineLexiconDoc(result.value, true);
172
- if (issues.length > 0) {
173
- console.error(pc.bold(pc.red(`lint validation failed for "${filename}"`)));
174
-
175
- for (const issue of issues) {
176
- console.log(`- ${issue.message} at .${issue.path.join('.')}`);
177
- }
178
-
179
- process.exit(1);
180
- }
181
-
182
- documents.push(result.value);
183
- }
184
-
185
- const generationResult = await generateLexiconApi({
186
- documents: documents,
187
- mappings: allMappings,
188
- modules: {
189
- importSuffix: config.modules?.importSuffix ?? '.js',
190
- },
191
- prettier: {
192
- cwd: process.cwd(),
193
- },
194
- });
195
-
196
- const outdir = path.join(config.root, config.outdir);
197
-
198
- for (const file of generationResult.files) {
199
- const filename = path.join(outdir, file.filename);
200
- const dirname = path.dirname(filename);
201
-
202
- await fs.mkdir(dirname, { recursive: true });
203
- await fs.writeFile(filename, file.code);
204
- }
13
+ await runGenerate(result);
14
+ } else if (result.type === 'pull') {
15
+ await runPull(result);
16
+ } else if (result.type === 'export') {
17
+ await runExport(result);
205
18
  }