@atcute/lex-cli 2.4.0 → 2.5.1
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 +107 -10
- package/dist/cli.js +10 -168
- package/dist/cli.js.map +1 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +76 -78
- package/dist/codegen.js.map +1 -1
- package/dist/commands/export.d.ts +17 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +76 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/generate.d.ts +17 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +136 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/pull.d.ts +17 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/{pull.js → commands/pull.js} +35 -81
- package/dist/commands/pull.js.map +1 -0
- package/dist/config.d.ts +68 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +54 -3
- package/dist/config.js.map +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lexicon-loader.d.ts +17 -0
- package/dist/lexicon-loader.d.ts.map +1 -0
- package/dist/lexicon-loader.js +167 -0
- package/dist/lexicon-loader.js.map +1 -0
- package/dist/lexicon-metadata.js.map +1 -1
- package/dist/pull-sources/atproto.d.ts +17 -0
- package/dist/pull-sources/atproto.d.ts.map +1 -0
- package/dist/pull-sources/atproto.js +192 -0
- package/dist/pull-sources/atproto.js.map +1 -0
- package/dist/pull-sources/git.d.ts +15 -0
- package/dist/pull-sources/git.d.ts.map +1 -0
- package/dist/pull-sources/git.js +80 -0
- package/dist/pull-sources/git.js.map +1 -0
- package/dist/pull-sources/types.d.ts +16 -0
- package/dist/pull-sources/types.d.ts.map +1 -0
- package/dist/pull-sources/types.js +2 -0
- package/dist/pull-sources/types.js.map +1 -0
- package/dist/shared-options.d.ts +6 -0
- package/dist/shared-options.d.ts.map +1 -0
- package/dist/shared-options.js +11 -0
- package/dist/shared-options.js.map +1 -0
- package/package.json +12 -9
- package/src/cli.ts +9 -210
- package/src/codegen.ts +90 -88
- package/src/commands/export.ts +106 -0
- package/src/commands/generate.ts +170 -0
- package/src/{pull.ts → commands/pull.ts} +49 -116
- package/src/config.ts +67 -4
- package/src/lexicon-loader.ts +201 -0
- package/src/pull-sources/atproto.ts +243 -0
- package/src/pull-sources/git.ts +103 -0
- package/src/pull-sources/types.ts +18 -0
- package/src/shared-options.ts +13 -0
- package/dist/pull.d.ts +0 -7
- package/dist/pull.d.ts.map +0 -1
- package/dist/pull.js.map +0 -1
|
@@ -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.
|
|
4
|
+
"version": "2.5.1",
|
|
5
5
|
"description": "cli tool to generate type definitions for atcute",
|
|
6
6
|
"license": "0BSD",
|
|
7
7
|
"repository": {
|
|
@@ -22,19 +22,22 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@badrap/valita": "^0.4.6",
|
|
25
|
-
"@optique/core": "^0.6.
|
|
26
|
-
"@optique/run": "^0.6.
|
|
25
|
+
"@optique/core": "^0.6.3",
|
|
26
|
+
"@optique/run": "^0.6.3",
|
|
27
27
|
"picocolors": "^1.1.1",
|
|
28
|
-
"prettier": "^3.
|
|
29
|
-
"@atcute/lexicon-doc": "^2.0.
|
|
28
|
+
"prettier": "^3.7.1",
|
|
29
|
+
"@atcute/lexicon-doc": "^2.0.5",
|
|
30
|
+
"@atcute/identity": "^1.1.3",
|
|
31
|
+
"@atcute/identity-resolver": "^1.2.0",
|
|
32
|
+
"@atcute/lexicon-resolver": "^0.1.5",
|
|
33
|
+
"@atcute/lexicons": "^1.2.5"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^22.19.
|
|
33
|
-
"tschema": "^3.2.0"
|
|
34
|
-
"@atcute/lexicons": "^1.2.5"
|
|
36
|
+
"@types/node": "^22.19.1",
|
|
37
|
+
"tschema": "^3.2.0"
|
|
35
38
|
},
|
|
36
39
|
"scripts": {
|
|
37
|
-
"build": "pnpm run generate:schema &&
|
|
40
|
+
"build": "pnpm run generate:schema && tsgo",
|
|
38
41
|
"generate:schema": "node scripts/generate-schema.js",
|
|
39
42
|
"prepublish": "rm -rf dist; pnpm run build"
|
|
40
43
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,219 +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';
|
|
8
1
|
import { or } from '@optique/core/constructs';
|
|
9
2
|
import { run } from '@optique/run';
|
|
10
|
-
import { path as pathParser } from '@optique/run/valueparser';
|
|
11
|
-
import pc from 'picocolors';
|
|
12
|
-
|
|
13
|
-
import { generateLexiconApi, type ImportMapping } from './codegen.js';
|
|
14
|
-
import { loadConfig } from './config.js';
|
|
15
|
-
import { packageJsonSchema } from './lexicon-metadata.js';
|
|
16
|
-
import { runPull } from './pull.js';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Resolves package imports to ImportMapping[]
|
|
20
|
-
*/
|
|
21
|
-
const resolveImportsToMappings = async (
|
|
22
|
-
imports: string[],
|
|
23
|
-
configDirname: string,
|
|
24
|
-
): Promise<ImportMapping[]> => {
|
|
25
|
-
const mappings: ImportMapping[] = [];
|
|
26
|
-
|
|
27
|
-
for (const packageName of imports) {
|
|
28
|
-
// Walk up from config directory to find package in node_modules
|
|
29
|
-
let packageJson: unknown;
|
|
30
|
-
let currentDir = configDirname;
|
|
31
|
-
let found = false;
|
|
32
|
-
|
|
33
|
-
while (currentDir !== path.dirname(currentDir)) {
|
|
34
|
-
const candidatePath = path.join(currentDir, 'node_modules', packageName, 'package.json');
|
|
35
|
-
try {
|
|
36
|
-
const content = await fs.readFile(candidatePath, 'utf8');
|
|
37
|
-
packageJson = JSON.parse(content);
|
|
38
|
-
found = true;
|
|
39
|
-
break;
|
|
40
|
-
} catch (err: any) {
|
|
41
|
-
// Only continue to parent if file not found
|
|
42
|
-
if (err.code !== 'ENOENT') {
|
|
43
|
-
console.error(pc.bold(pc.red(`failed to read package.json for "${packageName}":`)));
|
|
44
|
-
console.error(err);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Not found, try parent directory
|
|
49
|
-
currentDir = path.dirname(currentDir);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!found) {
|
|
54
|
-
console.error(pc.bold(pc.red(`failed to resolve package "${packageName}"`)));
|
|
55
|
-
console.error(`Could not find package in node_modules starting from ${configDirname}`);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Validate package.json
|
|
60
|
-
const result = packageJsonSchema.try(packageJson, { mode: 'passthrough' });
|
|
61
|
-
if (!result.ok) {
|
|
62
|
-
console.error(pc.bold(pc.red(`invalid atcute:lexicons in "${packageName}":`)));
|
|
63
|
-
console.error(result.message);
|
|
64
|
-
|
|
65
|
-
for (const issue of result.issues) {
|
|
66
|
-
console.log(`- ${issue.code} at .${issue.path.join('.')}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const lexicons = result.value['atcute:lexicons'];
|
|
73
|
-
if (!lexicons?.mappings) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Convert mapping to ImportMapping[]
|
|
78
|
-
for (const [pattern, entry] of Object.entries(lexicons.mappings)) {
|
|
79
|
-
const isWildcard = pattern.endsWith('.*');
|
|
80
|
-
|
|
81
|
-
mappings.push({
|
|
82
|
-
nsid: [pattern],
|
|
83
|
-
imports: (nsid: string) => {
|
|
84
|
-
// Check if pattern matches
|
|
85
|
-
if (isWildcard) {
|
|
86
|
-
if (!nsid.startsWith(pattern.slice(0, -1))) {
|
|
87
|
-
throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
if (nsid !== pattern) {
|
|
91
|
-
throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const nsidPrefix = isWildcard ? pattern.slice(0, -2) : pattern;
|
|
96
|
-
const nsidRemainder = isWildcard ? nsid.slice(nsidPrefix.length + 1) : '';
|
|
97
|
-
|
|
98
|
-
let expandedPath = entry.path
|
|
99
|
-
.replaceAll('{{nsid}}', nsid.replaceAll('.', '/'))
|
|
100
|
-
.replaceAll('{{nsid_remainder}}', nsidRemainder.replaceAll('.', '/'))
|
|
101
|
-
.replaceAll('{{nsid_prefix}}', nsidPrefix.replaceAll('.', '/'));
|
|
102
|
-
|
|
103
|
-
if (expandedPath === '.') {
|
|
104
|
-
expandedPath = packageName;
|
|
105
|
-
} else if (expandedPath.startsWith('./')) {
|
|
106
|
-
expandedPath = `${packageName}/${expandedPath.slice(2)}`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
type: entry.type,
|
|
111
|
-
from: expandedPath,
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
3
|
|
|
118
|
-
|
|
119
|
-
};
|
|
4
|
+
import { exportCommandSchema, runExport } from './commands/export.js';
|
|
5
|
+
import { generateCommandSchema, runGenerate } from './commands/generate.js';
|
|
6
|
+
import { pullCommandSchema, runPull } from './commands/pull.js';
|
|
120
7
|
|
|
121
|
-
const parser = or(
|
|
122
|
-
command(
|
|
123
|
-
'generate',
|
|
124
|
-
object({
|
|
125
|
-
type: constant('generate'),
|
|
126
|
-
config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
|
|
127
|
-
}),
|
|
128
|
-
),
|
|
129
|
-
command(
|
|
130
|
-
'pull',
|
|
131
|
-
object({
|
|
132
|
-
type: constant('pull'),
|
|
133
|
-
config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
|
|
134
|
-
}),
|
|
135
|
-
),
|
|
136
|
-
);
|
|
8
|
+
const parser = or(generateCommandSchema, pullCommandSchema, exportCommandSchema);
|
|
137
9
|
|
|
138
|
-
const result = run(parser, { programName: 'lex-cli' });
|
|
10
|
+
const result = run(parser, { programName: 'lex-cli', help: 'both' });
|
|
139
11
|
|
|
140
12
|
if (result.type === 'generate') {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Resolve imports to mappings
|
|
144
|
-
const importMappings = config.imports ? await resolveImportsToMappings(config.imports, config.root) : [];
|
|
145
|
-
const allMappings = [...importMappings, ...(config.mappings ?? [])];
|
|
146
|
-
|
|
147
|
-
const documents: LexiconDoc[] = [];
|
|
148
|
-
|
|
149
|
-
for await (const filename of fs.glob(config.files, { cwd: config.root })) {
|
|
150
|
-
let source: string;
|
|
151
|
-
try {
|
|
152
|
-
source = await fs.readFile(path.join(config.root, filename), 'utf8');
|
|
153
|
-
} catch (err) {
|
|
154
|
-
console.error(pc.bold(pc.red(`file read error with "${filename}"`)));
|
|
155
|
-
console.error(err);
|
|
156
|
-
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let json: unknown;
|
|
161
|
-
try {
|
|
162
|
-
json = JSON.parse(source);
|
|
163
|
-
} catch (err) {
|
|
164
|
-
console.error(pc.bold(pc.red(`json parse error in "${filename}"`)));
|
|
165
|
-
console.error(err);
|
|
166
|
-
|
|
167
|
-
process.exit(1);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const result = lexiconDoc.try(json, { mode: 'strip' });
|
|
171
|
-
if (!result.ok) {
|
|
172
|
-
console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`)));
|
|
173
|
-
console.error(result.message);
|
|
174
|
-
|
|
175
|
-
for (const issue of result.issues) {
|
|
176
|
-
console.log(`- ${issue.code} at .${issue.path.join('.')}`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
process.exit(1);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const issues = refineLexiconDoc(result.value, true);
|
|
183
|
-
if (issues.length > 0) {
|
|
184
|
-
console.error(pc.bold(pc.red(`lint validation failed for "${filename}"`)));
|
|
185
|
-
|
|
186
|
-
for (const issue of issues) {
|
|
187
|
-
console.log(`- ${issue.message} at .${issue.path.join('.')}`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
process.exit(1);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
documents.push(result.value);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const generationResult = await generateLexiconApi({
|
|
197
|
-
documents: documents,
|
|
198
|
-
mappings: allMappings,
|
|
199
|
-
modules: {
|
|
200
|
-
importSuffix: config.modules?.importSuffix ?? '.js',
|
|
201
|
-
},
|
|
202
|
-
prettier: {
|
|
203
|
-
cwd: process.cwd(),
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const outdir = path.join(config.root, config.outdir);
|
|
208
|
-
|
|
209
|
-
for (const file of generationResult.files) {
|
|
210
|
-
const filename = path.join(outdir, file.filename);
|
|
211
|
-
const dirname = path.dirname(filename);
|
|
212
|
-
|
|
213
|
-
await fs.mkdir(dirname, { recursive: true });
|
|
214
|
-
await fs.writeFile(filename, file.code);
|
|
215
|
-
}
|
|
13
|
+
await runGenerate(result);
|
|
216
14
|
} else if (result.type === 'pull') {
|
|
217
|
-
|
|
218
|
-
|
|
15
|
+
await runPull(result);
|
|
16
|
+
} else if (result.type === 'export') {
|
|
17
|
+
await runExport(result);
|
|
219
18
|
}
|
package/src/codegen.ts
CHANGED
|
@@ -49,6 +49,33 @@ type Literal = string | number | boolean;
|
|
|
49
49
|
|
|
50
50
|
const lit: (val: Literal | Literal[]) => string = JSON.stringify;
|
|
51
51
|
|
|
52
|
+
interface LexPath {
|
|
53
|
+
nsid: string;
|
|
54
|
+
defId: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const toLexUri = (path: LexPath): string => {
|
|
58
|
+
const { nsid, defId } = path;
|
|
59
|
+
return defId === 'main' ? nsid : `${nsid}#${defId}`;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const resolvePath = (from: LexPath, ref: string): LexPath => {
|
|
63
|
+
const index = ref.indexOf('#');
|
|
64
|
+
|
|
65
|
+
// nsid (no hash)
|
|
66
|
+
if (index === -1) {
|
|
67
|
+
return { nsid: ref, defId: 'main' };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// #defId (local ref)
|
|
71
|
+
if (index === 0) {
|
|
72
|
+
return { nsid: from.nsid, defId: ref.slice(1) };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// nsid#defId (full ref)
|
|
76
|
+
return { nsid: ref.slice(0, index), defId: ref.slice(index + 1) };
|
|
77
|
+
};
|
|
78
|
+
|
|
52
79
|
const resolveExternalImport = (nsid: string, mappings: ImportMapping[]): ImportMapping | undefined => {
|
|
53
80
|
return mappings.find((mapping) => {
|
|
54
81
|
return mapping.nsid.some((pattern) => {
|
|
@@ -111,7 +138,7 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
|
|
|
111
138
|
|
|
112
139
|
for (const defId of sortedDefIds) {
|
|
113
140
|
const def = doc.defs[defId];
|
|
114
|
-
const
|
|
141
|
+
const path: LexPath = { nsid: doc.id, defId };
|
|
115
142
|
|
|
116
143
|
const camelcased = toCamelCase(defId);
|
|
117
144
|
const varname = `${camelcased}Schema`;
|
|
@@ -119,59 +146,59 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
|
|
|
119
146
|
let result: string;
|
|
120
147
|
switch (def.type) {
|
|
121
148
|
case 'query': {
|
|
122
|
-
result = generateXrpcQuery(imports,
|
|
149
|
+
result = generateXrpcQuery(imports, path, def);
|
|
123
150
|
|
|
124
151
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
125
152
|
|
|
126
153
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
127
154
|
file.ambients += ` interface XRPCQueries {\n`;
|
|
128
|
-
file.ambients += ` ${lit(
|
|
155
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
129
156
|
file.ambients += ` }\n`;
|
|
130
157
|
file.ambients += `}`;
|
|
131
158
|
break;
|
|
132
159
|
}
|
|
133
160
|
case 'procedure': {
|
|
134
|
-
result = generateXrpcProcedure(imports,
|
|
161
|
+
result = generateXrpcProcedure(imports, path, def);
|
|
135
162
|
|
|
136
163
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
137
164
|
|
|
138
165
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
139
166
|
file.ambients += ` interface XRPCProcedures {\n`;
|
|
140
|
-
file.ambients += ` ${lit(
|
|
167
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
141
168
|
file.ambients += ` }\n`;
|
|
142
169
|
file.ambients += `}`;
|
|
143
170
|
break;
|
|
144
171
|
}
|
|
145
172
|
case 'subscription': {
|
|
146
|
-
result = generateXrpcSubscription(imports,
|
|
173
|
+
result = generateXrpcSubscription(imports, path, def);
|
|
147
174
|
|
|
148
175
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
149
176
|
|
|
150
177
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
151
178
|
file.ambients += ` interface XRPCSubscriptions {\n`;
|
|
152
|
-
file.ambients += ` ${lit(
|
|
179
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
153
180
|
file.ambients += ` }\n`;
|
|
154
181
|
file.ambients += `}`;
|
|
155
182
|
break;
|
|
156
183
|
}
|
|
157
184
|
case 'object': {
|
|
158
|
-
result = generateObject(imports,
|
|
185
|
+
result = generateObject(imports, path, def);
|
|
159
186
|
break;
|
|
160
187
|
}
|
|
161
188
|
case 'record': {
|
|
162
|
-
result = generateRecord(imports,
|
|
189
|
+
result = generateRecord(imports, path, def);
|
|
163
190
|
|
|
164
191
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
165
192
|
|
|
166
193
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
167
194
|
file.ambients += ` interface Records {\n`;
|
|
168
|
-
file.ambients += ` ${lit(
|
|
195
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
169
196
|
file.ambients += ` }\n`;
|
|
170
197
|
file.ambients += `}`;
|
|
171
198
|
break;
|
|
172
199
|
}
|
|
173
200
|
case 'token': {
|
|
174
|
-
result = `${PURE} v.literal(${lit(
|
|
201
|
+
result = `${PURE} v.literal(${lit(toLexUri(path))})`;
|
|
175
202
|
break;
|
|
176
203
|
}
|
|
177
204
|
case 'permission-set': {
|
|
@@ -179,7 +206,7 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
|
|
|
179
206
|
continue;
|
|
180
207
|
}
|
|
181
208
|
default: {
|
|
182
|
-
result = generateType(imports,
|
|
209
|
+
result = generateType(imports, path, def);
|
|
183
210
|
break;
|
|
184
211
|
}
|
|
185
212
|
}
|
|
@@ -372,42 +399,42 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
|
|
|
372
399
|
return { files };
|
|
373
400
|
};
|
|
374
401
|
|
|
375
|
-
const generateXrpcQuery = (imports: ImportSet,
|
|
376
|
-
const params = generateXrpcParameters(imports,
|
|
377
|
-
const output = generateXrpcBody(imports,
|
|
402
|
+
const generateXrpcQuery = (imports: ImportSet, path: LexPath, spec: LexXrpcQuery): string => {
|
|
403
|
+
const params = generateXrpcParameters(imports, path, spec.parameters);
|
|
404
|
+
const output = generateXrpcBody(imports, path, spec.output);
|
|
378
405
|
|
|
379
|
-
return `${PURE} v.query(${lit(
|
|
406
|
+
return `${PURE} v.query(${lit(toLexUri(path))}, {\n"params": ${params}, "output": ${output} })`;
|
|
380
407
|
};
|
|
381
408
|
|
|
382
|
-
const generateXrpcProcedure = (imports: ImportSet,
|
|
383
|
-
const params = generateXrpcParameters(imports,
|
|
384
|
-
const input = generateXrpcBody(imports,
|
|
385
|
-
const output = generateXrpcBody(imports,
|
|
409
|
+
const generateXrpcProcedure = (imports: ImportSet, path: LexPath, spec: LexXrpcProcedure): string => {
|
|
410
|
+
const params = generateXrpcParameters(imports, path, spec.parameters);
|
|
411
|
+
const input = generateXrpcBody(imports, path, spec.input);
|
|
412
|
+
const output = generateXrpcBody(imports, path, spec.output);
|
|
386
413
|
|
|
387
|
-
return `${PURE} v.procedure(${lit(
|
|
414
|
+
return `${PURE} v.procedure(${lit(toLexUri(path))}, {\n"params": ${params}, "input": ${input}, "output": ${output} })`;
|
|
388
415
|
};
|
|
389
416
|
|
|
390
|
-
const generateXrpcSubscription = (imports: ImportSet,
|
|
417
|
+
const generateXrpcSubscription = (imports: ImportSet, path: LexPath, spec: LexXrpcSubscription): string => {
|
|
391
418
|
const schema = spec.message?.schema;
|
|
392
419
|
|
|
393
|
-
const params = generateXrpcParameters(imports,
|
|
420
|
+
const params = generateXrpcParameters(imports, path, spec.parameters);
|
|
394
421
|
|
|
395
422
|
let inner = ``;
|
|
396
423
|
|
|
397
424
|
inner += `"params": ${params},`;
|
|
398
425
|
|
|
399
426
|
if (schema) {
|
|
400
|
-
const res = generateType(imports,
|
|
427
|
+
const res = generateType(imports, path, schema);
|
|
401
428
|
|
|
402
429
|
inner += `get "message" () { return ${res} },`;
|
|
403
430
|
} else {
|
|
404
431
|
inner += `"message": null,`;
|
|
405
432
|
}
|
|
406
433
|
|
|
407
|
-
return `${PURE} v.subscription(${lit(
|
|
434
|
+
return `${PURE} v.subscription(${lit(toLexUri(path))}, {\n${inner}})`;
|
|
408
435
|
};
|
|
409
436
|
|
|
410
|
-
const generateXrpcBody = (imports: ImportSet,
|
|
437
|
+
const generateXrpcBody = (imports: ImportSet, path: LexPath, spec: LexXrpcBody | undefined): string => {
|
|
411
438
|
if (spec === undefined) {
|
|
412
439
|
return `null`;
|
|
413
440
|
}
|
|
@@ -421,11 +448,11 @@ const generateXrpcBody = (imports: ImportSet, defUri: string, spec: LexXrpcBody
|
|
|
421
448
|
inner += `"type": "lex",`;
|
|
422
449
|
|
|
423
450
|
if (schema.type === 'object') {
|
|
424
|
-
const res = generateObject(imports,
|
|
451
|
+
const res = generateObject(imports, path, schema, 'none');
|
|
425
452
|
|
|
426
453
|
inner += `"schema": ${res},`;
|
|
427
454
|
} else {
|
|
428
|
-
const res = generateType(imports,
|
|
455
|
+
const res = generateType(imports, path, schema);
|
|
429
456
|
|
|
430
457
|
inner += `get "schema" () { return ${res} },`;
|
|
431
458
|
}
|
|
@@ -452,7 +479,7 @@ const generateXrpcBody = (imports: ImportSet, defUri: string, spec: LexXrpcBody
|
|
|
452
479
|
|
|
453
480
|
const generateXrpcParameters = (
|
|
454
481
|
imports: ImportSet,
|
|
455
|
-
|
|
482
|
+
path: LexPath,
|
|
456
483
|
spec: LexXrpcParameters | undefined,
|
|
457
484
|
): string => {
|
|
458
485
|
if (spec === undefined) {
|
|
@@ -489,11 +516,11 @@ const generateXrpcParameters = (
|
|
|
489
516
|
properties: transformedProperties ?? originalProperties,
|
|
490
517
|
};
|
|
491
518
|
|
|
492
|
-
return generateObject(imports,
|
|
519
|
+
return generateObject(imports, path, mask, 'none');
|
|
493
520
|
};
|
|
494
521
|
|
|
495
|
-
const generateRecord = (imports: ImportSet,
|
|
496
|
-
const schema = generateObject(imports,
|
|
522
|
+
const generateRecord = (imports: ImportSet, path: LexPath, spec: LexRecord): string => {
|
|
523
|
+
const schema = generateObject(imports, path, spec.record, 'required');
|
|
497
524
|
|
|
498
525
|
let key = `${PURE} v.string()`;
|
|
499
526
|
if (spec.key) {
|
|
@@ -511,7 +538,7 @@ const generateRecord = (imports: ImportSet, defUri: string, spec: LexRecord): st
|
|
|
511
538
|
|
|
512
539
|
const generateObject = (
|
|
513
540
|
imports: ImportSet,
|
|
514
|
-
|
|
541
|
+
path: LexPath,
|
|
515
542
|
spec: LexObject,
|
|
516
543
|
writeType: 'required' | 'optional' | 'none' = 'optional',
|
|
517
544
|
): string => {
|
|
@@ -522,11 +549,11 @@ const generateObject = (
|
|
|
522
549
|
|
|
523
550
|
switch (writeType) {
|
|
524
551
|
case 'optional': {
|
|
525
|
-
inner += `"$type": ${PURE} v.optional(${PURE} v.literal(${lit(
|
|
552
|
+
inner += `"$type": ${PURE} v.optional(${PURE} v.literal(${lit(toLexUri(path))})),`;
|
|
526
553
|
break;
|
|
527
554
|
}
|
|
528
555
|
case 'required': {
|
|
529
|
-
inner += `"$type": ${PURE} v.literal(${lit(
|
|
556
|
+
inner += `"$type": ${PURE} v.literal(${lit(toLexUri(path))}),`;
|
|
530
557
|
break;
|
|
531
558
|
}
|
|
532
559
|
}
|
|
@@ -544,10 +571,10 @@ const generateObject = (
|
|
|
544
571
|
|
|
545
572
|
for (const [prop, propSpec] of sortedEntries) {
|
|
546
573
|
const lazy = isRefVariant(propSpec.type === 'array' ? propSpec.items : propSpec);
|
|
547
|
-
const optional = !required.has(prop) && !('default' in propSpec);
|
|
574
|
+
const optional = !required.has(prop) && !('default' in propSpec && propSpec.default !== undefined);
|
|
548
575
|
const nulled = nullable.has(prop);
|
|
549
576
|
|
|
550
|
-
let call = generateType(imports,
|
|
577
|
+
let call = generateType(imports, path, propSpec, lazy);
|
|
551
578
|
|
|
552
579
|
if (nulled) {
|
|
553
580
|
call = `${PURE} v.nullable(${call})`;
|
|
@@ -678,62 +705,50 @@ const generateJsdocField = (spec: LexUserType | LexRefVariant | LexUnknown) => {
|
|
|
678
705
|
return res;
|
|
679
706
|
};
|
|
680
707
|
|
|
681
|
-
const generateType = (imports: ImportSet,
|
|
708
|
+
const generateType = (imports: ImportSet, path: LexPath, spec: LexDefinableField, lazy = false): string => {
|
|
682
709
|
switch (spec.type) {
|
|
683
710
|
// LexRefVariant
|
|
684
711
|
case 'ref': {
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
if (ref.startsWith('#')) {
|
|
688
|
-
const id = ref.slice(1);
|
|
689
|
-
|
|
690
|
-
return `${toCamelCase(id)}Schema`;
|
|
691
|
-
} else {
|
|
692
|
-
const [ns, id = 'main'] = ref.split('#');
|
|
693
|
-
if (ns === stripHash(defUri)) {
|
|
694
|
-
return `${toCamelCase(id)}Schema`;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
imports.add(ns);
|
|
712
|
+
const refPath = resolvePath(path, spec.ref);
|
|
698
713
|
|
|
699
|
-
|
|
714
|
+
if (refPath.nsid === path.nsid) {
|
|
715
|
+
return `${toCamelCase(refPath.defId)}Schema`;
|
|
700
716
|
}
|
|
717
|
+
|
|
718
|
+
imports.add(refPath.nsid);
|
|
719
|
+
return `${toTitleCase(refPath.nsid)}.${toCamelCase(refPath.defId)}Schema`;
|
|
701
720
|
}
|
|
702
721
|
case 'union': {
|
|
703
|
-
const
|
|
704
|
-
.map((ref)
|
|
705
|
-
|
|
706
|
-
|
|
722
|
+
const refs = spec.refs
|
|
723
|
+
.map((ref) => {
|
|
724
|
+
const refPath = resolvePath(path, ref);
|
|
725
|
+
return { path: refPath, uri: toLexUri(refPath) };
|
|
726
|
+
})
|
|
727
|
+
.sort((a, b) => {
|
|
728
|
+
if (a.uri < b.uri) {
|
|
729
|
+
return -1;
|
|
707
730
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
if (ns === stripHash(defUri)) {
|
|
711
|
-
return `#${id}`;
|
|
731
|
+
if (a.uri > b.uri) {
|
|
732
|
+
return 1;
|
|
712
733
|
}
|
|
713
734
|
|
|
714
|
-
return
|
|
735
|
+
return 0;
|
|
715
736
|
})
|
|
716
|
-
.
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const id = ref.slice(1);
|
|
721
|
-
|
|
722
|
-
return `${toCamelCase(id)}Schema`;
|
|
723
|
-
} else {
|
|
724
|
-
const [ns, id = 'main'] = ref.split('#');
|
|
725
|
-
imports.add(ns);
|
|
737
|
+
.map(({ path: refPath }): string => {
|
|
738
|
+
if (refPath.nsid === path.nsid) {
|
|
739
|
+
return `${toCamelCase(refPath.defId)}Schema`;
|
|
740
|
+
}
|
|
726
741
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
742
|
+
imports.add(refPath.nsid);
|
|
743
|
+
return `${toTitleCase(refPath.nsid)}.${toCamelCase(refPath.defId)}Schema`;
|
|
744
|
+
});
|
|
730
745
|
|
|
731
746
|
return `${PURE} v.variant([${refs.join(', ')}]${spec.closed ? `, true` : ``})`;
|
|
732
747
|
}
|
|
733
748
|
|
|
734
749
|
// LexArray
|
|
735
750
|
case 'array': {
|
|
736
|
-
let item = generateType(imports,
|
|
751
|
+
let item = generateType(imports, path, spec.items);
|
|
737
752
|
if (!lazy && (spec.items.type === 'ref' || spec.items.type === 'union')) {
|
|
738
753
|
item = `(() => { return ${item}; })`;
|
|
739
754
|
}
|
|
@@ -924,19 +939,6 @@ const isRefVariant = (spec: LexDefinableField): spec is LexRefVariant => {
|
|
|
924
939
|
return type === 'ref' || type === 'union';
|
|
925
940
|
};
|
|
926
941
|
|
|
927
|
-
const stripHash = (defUri: string): string => {
|
|
928
|
-
const index = defUri.indexOf('#');
|
|
929
|
-
if (index === -1) {
|
|
930
|
-
return defUri;
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
return defUri.slice(0, index);
|
|
934
|
-
};
|
|
935
|
-
|
|
936
|
-
const stripMainHash = (defUri: string): string => {
|
|
937
|
-
return defUri.endsWith('#main') ? defUri.slice(0, -'#main'.length) : defUri;
|
|
938
|
-
};
|
|
939
|
-
|
|
940
942
|
const toTitleCase = (v: string): string => {
|
|
941
943
|
v = v.replace(/^([a-z])/gi, (_, g) => g.toUpperCase());
|
|
942
944
|
v = v.replace(/[.#-]([a-z])/gi, (_, g) => g.toUpperCase());
|