@atcute/lex-cli 2.4.0 → 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.
- package/README.md +40 -8
- 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 +13 -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 +13 -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 +13 -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/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/pull-sources/atproto.d.ts +9 -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 +11 -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 +10 -7
- 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 +199 -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
package/README.md
CHANGED
|
@@ -20,19 +20,24 @@ export default defineLexiconConfig({
|
|
|
20
20
|
then run the tool:
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
npm exec lex-cli generate
|
|
23
|
+
npm exec lex-cli generate
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## pulling lexicons
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
you can pull lexicon files from other sources.
|
|
29
|
+
|
|
30
|
+
### git sources
|
|
31
|
+
|
|
32
|
+
pull lexicons from git repositories using sparse checkout:
|
|
30
33
|
|
|
31
34
|
```ts
|
|
32
35
|
// file: lex.config.js
|
|
33
36
|
import { defineLexiconConfig } from '@atcute/lex-cli';
|
|
34
37
|
|
|
35
38
|
export default defineLexiconConfig({
|
|
39
|
+
files: ['lexicons/**/*.json'],
|
|
40
|
+
outdir: 'src/lexicons/',
|
|
36
41
|
pull: {
|
|
37
42
|
outdir: 'lexicons/',
|
|
38
43
|
clean: true,
|
|
@@ -45,23 +50,50 @@ export default defineLexiconConfig({
|
|
|
45
50
|
},
|
|
46
51
|
],
|
|
47
52
|
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### atproto sources
|
|
57
|
+
|
|
58
|
+
pull lexicons directly from the AT Protocol network.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
export default defineLexiconConfig({
|
|
48
62
|
files: ['lexicons/**/*.json'],
|
|
49
63
|
outdir: 'src/lexicons/',
|
|
64
|
+
pull: {
|
|
65
|
+
outdir: 'lexicons/',
|
|
66
|
+
sources: [
|
|
67
|
+
{
|
|
68
|
+
type: 'atproto',
|
|
69
|
+
mode: 'nsids',
|
|
70
|
+
nsids: ['app.bsky.feed.post', 'app.bsky.actor.profile'],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: 'atproto',
|
|
74
|
+
mode: 'authority',
|
|
75
|
+
authority: 'atproto-lexicons.bsky.social',
|
|
76
|
+
pattern: ['com.atproto.*'],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
50
80
|
});
|
|
51
81
|
```
|
|
52
82
|
|
|
83
|
+
### running the pull command
|
|
84
|
+
|
|
53
85
|
pull the lexicons to disk, then generate types from them:
|
|
54
86
|
|
|
55
87
|
```
|
|
56
|
-
npm exec lex-cli pull
|
|
57
|
-
npm exec lex-cli generate
|
|
88
|
+
npm exec lex-cli pull
|
|
89
|
+
npm exec lex-cli generate
|
|
58
90
|
```
|
|
59
91
|
|
|
60
92
|
## publishing your schemas
|
|
61
93
|
|
|
62
|
-
if you're packaging your generated schemas as a publishable library, add the `atcute:lexicons`
|
|
63
|
-
|
|
64
|
-
|
|
94
|
+
if you're packaging your generated schemas as a publishable library, add the `atcute:lexicons` field
|
|
95
|
+
to your package.json. this allows other projects to automatically discover and import your schemas
|
|
96
|
+
without manual configuration.
|
|
65
97
|
|
|
66
98
|
```json
|
|
67
99
|
{
|
package/dist/cli.js
CHANGED
|
@@ -1,175 +1,17 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { lexiconDoc, refineLexiconDoc } from '@atcute/lexicon-doc';
|
|
4
|
-
import { object } from '@optique/core/constructs';
|
|
5
|
-
import { command, constant, option } from '@optique/core/primitives';
|
|
6
1
|
import { or } from '@optique/core/constructs';
|
|
7
2
|
import { run } from '@optique/run';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { runPull } from './pull.js';
|
|
14
|
-
/**
|
|
15
|
-
* Resolves package imports to ImportMapping[]
|
|
16
|
-
*/
|
|
17
|
-
const resolveImportsToMappings = async (imports, configDirname) => {
|
|
18
|
-
const mappings = [];
|
|
19
|
-
for (const packageName of imports) {
|
|
20
|
-
// Walk up from config directory to find package in node_modules
|
|
21
|
-
let packageJson;
|
|
22
|
-
let currentDir = configDirname;
|
|
23
|
-
let found = false;
|
|
24
|
-
while (currentDir !== path.dirname(currentDir)) {
|
|
25
|
-
const candidatePath = path.join(currentDir, 'node_modules', packageName, 'package.json');
|
|
26
|
-
try {
|
|
27
|
-
const content = await fs.readFile(candidatePath, 'utf8');
|
|
28
|
-
packageJson = JSON.parse(content);
|
|
29
|
-
found = true;
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
catch (err) {
|
|
33
|
-
// Only continue to parent if file not found
|
|
34
|
-
if (err.code !== 'ENOENT') {
|
|
35
|
-
console.error(pc.bold(pc.red(`failed to read package.json for "${packageName}":`)));
|
|
36
|
-
console.error(err);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
// Not found, try parent directory
|
|
40
|
-
currentDir = path.dirname(currentDir);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (!found) {
|
|
44
|
-
console.error(pc.bold(pc.red(`failed to resolve package "${packageName}"`)));
|
|
45
|
-
console.error(`Could not find package in node_modules starting from ${configDirname}`);
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
// Validate package.json
|
|
49
|
-
const result = packageJsonSchema.try(packageJson, { mode: 'passthrough' });
|
|
50
|
-
if (!result.ok) {
|
|
51
|
-
console.error(pc.bold(pc.red(`invalid atcute:lexicons in "${packageName}":`)));
|
|
52
|
-
console.error(result.message);
|
|
53
|
-
for (const issue of result.issues) {
|
|
54
|
-
console.log(`- ${issue.code} at .${issue.path.join('.')}`);
|
|
55
|
-
}
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
const lexicons = result.value['atcute:lexicons'];
|
|
59
|
-
if (!lexicons?.mappings) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
// Convert mapping to ImportMapping[]
|
|
63
|
-
for (const [pattern, entry] of Object.entries(lexicons.mappings)) {
|
|
64
|
-
const isWildcard = pattern.endsWith('.*');
|
|
65
|
-
mappings.push({
|
|
66
|
-
nsid: [pattern],
|
|
67
|
-
imports: (nsid) => {
|
|
68
|
-
// Check if pattern matches
|
|
69
|
-
if (isWildcard) {
|
|
70
|
-
if (!nsid.startsWith(pattern.slice(0, -1))) {
|
|
71
|
-
throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
if (nsid !== pattern) {
|
|
76
|
-
throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const nsidPrefix = isWildcard ? pattern.slice(0, -2) : pattern;
|
|
80
|
-
const nsidRemainder = isWildcard ? nsid.slice(nsidPrefix.length + 1) : '';
|
|
81
|
-
let expandedPath = entry.path
|
|
82
|
-
.replaceAll('{{nsid}}', nsid.replaceAll('.', '/'))
|
|
83
|
-
.replaceAll('{{nsid_remainder}}', nsidRemainder.replaceAll('.', '/'))
|
|
84
|
-
.replaceAll('{{nsid_prefix}}', nsidPrefix.replaceAll('.', '/'));
|
|
85
|
-
if (expandedPath === '.') {
|
|
86
|
-
expandedPath = packageName;
|
|
87
|
-
}
|
|
88
|
-
else if (expandedPath.startsWith('./')) {
|
|
89
|
-
expandedPath = `${packageName}/${expandedPath.slice(2)}`;
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
type: entry.type,
|
|
93
|
-
from: expandedPath,
|
|
94
|
-
};
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return mappings;
|
|
100
|
-
};
|
|
101
|
-
const parser = or(command('generate', object({
|
|
102
|
-
type: constant('generate'),
|
|
103
|
-
config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
|
|
104
|
-
})), command('pull', object({
|
|
105
|
-
type: constant('pull'),
|
|
106
|
-
config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
|
|
107
|
-
})));
|
|
108
|
-
const result = run(parser, { programName: 'lex-cli' });
|
|
3
|
+
import { exportCommandSchema, runExport } from './commands/export.js';
|
|
4
|
+
import { generateCommandSchema, runGenerate } from './commands/generate.js';
|
|
5
|
+
import { pullCommandSchema, runPull } from './commands/pull.js';
|
|
6
|
+
const parser = or(generateCommandSchema, pullCommandSchema, exportCommandSchema);
|
|
7
|
+
const result = run(parser, { programName: 'lex-cli', help: 'both' });
|
|
109
8
|
if (result.type === 'generate') {
|
|
110
|
-
|
|
111
|
-
// Resolve imports to mappings
|
|
112
|
-
const importMappings = config.imports ? await resolveImportsToMappings(config.imports, config.root) : [];
|
|
113
|
-
const allMappings = [...importMappings, ...(config.mappings ?? [])];
|
|
114
|
-
const documents = [];
|
|
115
|
-
for await (const filename of fs.glob(config.files, { cwd: config.root })) {
|
|
116
|
-
let source;
|
|
117
|
-
try {
|
|
118
|
-
source = await fs.readFile(path.join(config.root, filename), 'utf8');
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
console.error(pc.bold(pc.red(`file read error with "${filename}"`)));
|
|
122
|
-
console.error(err);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
let json;
|
|
126
|
-
try {
|
|
127
|
-
json = JSON.parse(source);
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
console.error(pc.bold(pc.red(`json parse error in "${filename}"`)));
|
|
131
|
-
console.error(err);
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
const result = lexiconDoc.try(json, { mode: 'strip' });
|
|
135
|
-
if (!result.ok) {
|
|
136
|
-
console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`)));
|
|
137
|
-
console.error(result.message);
|
|
138
|
-
for (const issue of result.issues) {
|
|
139
|
-
console.log(`- ${issue.code} at .${issue.path.join('.')}`);
|
|
140
|
-
}
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
const issues = refineLexiconDoc(result.value, true);
|
|
144
|
-
if (issues.length > 0) {
|
|
145
|
-
console.error(pc.bold(pc.red(`lint validation failed for "${filename}"`)));
|
|
146
|
-
for (const issue of issues) {
|
|
147
|
-
console.log(`- ${issue.message} at .${issue.path.join('.')}`);
|
|
148
|
-
}
|
|
149
|
-
process.exit(1);
|
|
150
|
-
}
|
|
151
|
-
documents.push(result.value);
|
|
152
|
-
}
|
|
153
|
-
const generationResult = await generateLexiconApi({
|
|
154
|
-
documents: documents,
|
|
155
|
-
mappings: allMappings,
|
|
156
|
-
modules: {
|
|
157
|
-
importSuffix: config.modules?.importSuffix ?? '.js',
|
|
158
|
-
},
|
|
159
|
-
prettier: {
|
|
160
|
-
cwd: process.cwd(),
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
const outdir = path.join(config.root, config.outdir);
|
|
164
|
-
for (const file of generationResult.files) {
|
|
165
|
-
const filename = path.join(outdir, file.filename);
|
|
166
|
-
const dirname = path.dirname(filename);
|
|
167
|
-
await fs.mkdir(dirname, { recursive: true });
|
|
168
|
-
await fs.writeFile(filename, file.code);
|
|
169
|
-
}
|
|
9
|
+
await runGenerate(result);
|
|
170
10
|
}
|
|
171
11
|
else if (result.type === 'pull') {
|
|
172
|
-
|
|
173
|
-
|
|
12
|
+
await runPull(result);
|
|
13
|
+
}
|
|
14
|
+
else if (result.type === 'export') {
|
|
15
|
+
await runExport(result);
|
|
174
16
|
}
|
|
175
17
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,MAAM,GAAG,EAAE,CAAC,qBAAqB,EAAE,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;AAEjF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAErE,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;IAChC,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;KAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IACnC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC;KAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;IACrC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC"}
|
package/dist/codegen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEX,UAAU,EAWV,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,OAAO,GAAG,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEX,UAAU,EAWV,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,OAAO,GAAG,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;CACpB;AAkDD,eAAO,MAAM,kBAAkB,GAAU,MAAM,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAmT1F,CAAC"}
|
package/dist/codegen.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { dirname as getDirname, relative as getRelativePath } from 'node:path/posix';
|
|
2
2
|
import * as prettier from 'prettier';
|
|
3
3
|
const lit = JSON.stringify;
|
|
4
|
+
const toLexUri = (path) => {
|
|
5
|
+
const { nsid, defId } = path;
|
|
6
|
+
return defId === 'main' ? nsid : `${nsid}#${defId}`;
|
|
7
|
+
};
|
|
8
|
+
const resolvePath = (from, ref) => {
|
|
9
|
+
const index = ref.indexOf('#');
|
|
10
|
+
// nsid (no hash)
|
|
11
|
+
if (index === -1) {
|
|
12
|
+
return { nsid: ref, defId: 'main' };
|
|
13
|
+
}
|
|
14
|
+
// #defId (local ref)
|
|
15
|
+
if (index === 0) {
|
|
16
|
+
return { nsid: from.nsid, defId: ref.slice(1) };
|
|
17
|
+
}
|
|
18
|
+
// nsid#defId (full ref)
|
|
19
|
+
return { nsid: ref.slice(0, index), defId: ref.slice(index + 1) };
|
|
20
|
+
};
|
|
4
21
|
const resolveExternalImport = (nsid, mappings) => {
|
|
5
22
|
return mappings.find((mapping) => {
|
|
6
23
|
return mapping.nsid.some((pattern) => {
|
|
@@ -51,57 +68,57 @@ export const generateLexiconApi = async (opts) => {
|
|
|
51
68
|
});
|
|
52
69
|
for (const defId of sortedDefIds) {
|
|
53
70
|
const def = doc.defs[defId];
|
|
54
|
-
const
|
|
71
|
+
const path = { nsid: doc.id, defId };
|
|
55
72
|
const camelcased = toCamelCase(defId);
|
|
56
73
|
const varname = `${camelcased}Schema`;
|
|
57
74
|
let result;
|
|
58
75
|
switch (def.type) {
|
|
59
76
|
case 'query': {
|
|
60
|
-
result = generateXrpcQuery(imports,
|
|
77
|
+
result = generateXrpcQuery(imports, path, def);
|
|
61
78
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
62
79
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
63
80
|
file.ambients += ` interface XRPCQueries {\n`;
|
|
64
|
-
file.ambients += ` ${lit(
|
|
81
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
65
82
|
file.ambients += ` }\n`;
|
|
66
83
|
file.ambients += `}`;
|
|
67
84
|
break;
|
|
68
85
|
}
|
|
69
86
|
case 'procedure': {
|
|
70
|
-
result = generateXrpcProcedure(imports,
|
|
87
|
+
result = generateXrpcProcedure(imports, path, def);
|
|
71
88
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
72
89
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
73
90
|
file.ambients += ` interface XRPCProcedures {\n`;
|
|
74
|
-
file.ambients += ` ${lit(
|
|
91
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
75
92
|
file.ambients += ` }\n`;
|
|
76
93
|
file.ambients += `}`;
|
|
77
94
|
break;
|
|
78
95
|
}
|
|
79
96
|
case 'subscription': {
|
|
80
|
-
result = generateXrpcSubscription(imports,
|
|
97
|
+
result = generateXrpcSubscription(imports, path, def);
|
|
81
98
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
82
99
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
83
100
|
file.ambients += ` interface XRPCSubscriptions {\n`;
|
|
84
|
-
file.ambients += ` ${lit(
|
|
101
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
85
102
|
file.ambients += ` }\n`;
|
|
86
103
|
file.ambients += `}`;
|
|
87
104
|
break;
|
|
88
105
|
}
|
|
89
106
|
case 'object': {
|
|
90
|
-
result = generateObject(imports,
|
|
107
|
+
result = generateObject(imports, path, def);
|
|
91
108
|
break;
|
|
92
109
|
}
|
|
93
110
|
case 'record': {
|
|
94
|
-
result = generateRecord(imports,
|
|
111
|
+
result = generateRecord(imports, path, def);
|
|
95
112
|
file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
|
|
96
113
|
file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
|
|
97
114
|
file.ambients += ` interface Records {\n`;
|
|
98
|
-
file.ambients += ` ${lit(
|
|
115
|
+
file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
|
|
99
116
|
file.ambients += ` }\n`;
|
|
100
117
|
file.ambients += `}`;
|
|
101
118
|
break;
|
|
102
119
|
}
|
|
103
120
|
case 'token': {
|
|
104
|
-
result = `${PURE} v.literal(${lit(
|
|
121
|
+
result = `${PURE} v.literal(${lit(toLexUri(path))})`;
|
|
105
122
|
break;
|
|
106
123
|
}
|
|
107
124
|
case 'permission-set': {
|
|
@@ -109,7 +126,7 @@ export const generateLexiconApi = async (opts) => {
|
|
|
109
126
|
continue;
|
|
110
127
|
}
|
|
111
128
|
default: {
|
|
112
|
-
result = generateType(imports,
|
|
129
|
+
result = generateType(imports, path, def);
|
|
113
130
|
break;
|
|
114
131
|
}
|
|
115
132
|
}
|
|
@@ -279,32 +296,32 @@ export const generateLexiconApi = async (opts) => {
|
|
|
279
296
|
}
|
|
280
297
|
return { files };
|
|
281
298
|
};
|
|
282
|
-
const generateXrpcQuery = (imports,
|
|
283
|
-
const params = generateXrpcParameters(imports,
|
|
284
|
-
const output = generateXrpcBody(imports,
|
|
285
|
-
return `${PURE} v.query(${lit(
|
|
299
|
+
const generateXrpcQuery = (imports, path, spec) => {
|
|
300
|
+
const params = generateXrpcParameters(imports, path, spec.parameters);
|
|
301
|
+
const output = generateXrpcBody(imports, path, spec.output);
|
|
302
|
+
return `${PURE} v.query(${lit(toLexUri(path))}, {\n"params": ${params}, "output": ${output} })`;
|
|
286
303
|
};
|
|
287
|
-
const generateXrpcProcedure = (imports,
|
|
288
|
-
const params = generateXrpcParameters(imports,
|
|
289
|
-
const input = generateXrpcBody(imports,
|
|
290
|
-
const output = generateXrpcBody(imports,
|
|
291
|
-
return `${PURE} v.procedure(${lit(
|
|
304
|
+
const generateXrpcProcedure = (imports, path, spec) => {
|
|
305
|
+
const params = generateXrpcParameters(imports, path, spec.parameters);
|
|
306
|
+
const input = generateXrpcBody(imports, path, spec.input);
|
|
307
|
+
const output = generateXrpcBody(imports, path, spec.output);
|
|
308
|
+
return `${PURE} v.procedure(${lit(toLexUri(path))}, {\n"params": ${params}, "input": ${input}, "output": ${output} })`;
|
|
292
309
|
};
|
|
293
|
-
const generateXrpcSubscription = (imports,
|
|
310
|
+
const generateXrpcSubscription = (imports, path, spec) => {
|
|
294
311
|
const schema = spec.message?.schema;
|
|
295
|
-
const params = generateXrpcParameters(imports,
|
|
312
|
+
const params = generateXrpcParameters(imports, path, spec.parameters);
|
|
296
313
|
let inner = ``;
|
|
297
314
|
inner += `"params": ${params},`;
|
|
298
315
|
if (schema) {
|
|
299
|
-
const res = generateType(imports,
|
|
316
|
+
const res = generateType(imports, path, schema);
|
|
300
317
|
inner += `get "message" () { return ${res} },`;
|
|
301
318
|
}
|
|
302
319
|
else {
|
|
303
320
|
inner += `"message": null,`;
|
|
304
321
|
}
|
|
305
|
-
return `${PURE} v.subscription(${lit(
|
|
322
|
+
return `${PURE} v.subscription(${lit(toLexUri(path))}, {\n${inner}})`;
|
|
306
323
|
};
|
|
307
|
-
const generateXrpcBody = (imports,
|
|
324
|
+
const generateXrpcBody = (imports, path, spec) => {
|
|
308
325
|
if (spec === undefined) {
|
|
309
326
|
return `null`;
|
|
310
327
|
}
|
|
@@ -314,11 +331,11 @@ const generateXrpcBody = (imports, defUri, spec) => {
|
|
|
314
331
|
let inner = ``;
|
|
315
332
|
inner += `"type": "lex",`;
|
|
316
333
|
if (schema.type === 'object') {
|
|
317
|
-
const res = generateObject(imports,
|
|
334
|
+
const res = generateObject(imports, path, schema, 'none');
|
|
318
335
|
inner += `"schema": ${res},`;
|
|
319
336
|
}
|
|
320
337
|
else {
|
|
321
|
-
const res = generateType(imports,
|
|
338
|
+
const res = generateType(imports, path, schema);
|
|
322
339
|
inner += `get "schema" () { return ${res} },`;
|
|
323
340
|
}
|
|
324
341
|
return `{\n${inner}}`;
|
|
@@ -334,7 +351,7 @@ const generateXrpcBody = (imports, defUri, spec) => {
|
|
|
334
351
|
}
|
|
335
352
|
return `null`;
|
|
336
353
|
};
|
|
337
|
-
const generateXrpcParameters = (imports,
|
|
354
|
+
const generateXrpcParameters = (imports, path, spec) => {
|
|
338
355
|
if (spec === undefined) {
|
|
339
356
|
return `null`;
|
|
340
357
|
}
|
|
@@ -363,10 +380,10 @@ const generateXrpcParameters = (imports, defUri, spec) => {
|
|
|
363
380
|
required: spec.required,
|
|
364
381
|
properties: transformedProperties ?? originalProperties,
|
|
365
382
|
};
|
|
366
|
-
return generateObject(imports,
|
|
383
|
+
return generateObject(imports, path, mask, 'none');
|
|
367
384
|
};
|
|
368
|
-
const generateRecord = (imports,
|
|
369
|
-
const schema = generateObject(imports,
|
|
385
|
+
const generateRecord = (imports, path, spec) => {
|
|
386
|
+
const schema = generateObject(imports, path, spec.record, 'required');
|
|
370
387
|
let key = `${PURE} v.string()`;
|
|
371
388
|
if (spec.key) {
|
|
372
389
|
if (spec.key === 'tid') {
|
|
@@ -381,17 +398,17 @@ const generateRecord = (imports, defUri, spec) => {
|
|
|
381
398
|
}
|
|
382
399
|
return `${PURE} v.record(${key}, ${schema})`;
|
|
383
400
|
};
|
|
384
|
-
const generateObject = (imports,
|
|
401
|
+
const generateObject = (imports, path, spec, writeType = 'optional') => {
|
|
385
402
|
const required = new Set(spec.required);
|
|
386
403
|
const nullable = new Set(spec.nullable);
|
|
387
404
|
let inner = ``;
|
|
388
405
|
switch (writeType) {
|
|
389
406
|
case 'optional': {
|
|
390
|
-
inner += `"$type": ${PURE} v.optional(${PURE} v.literal(${lit(
|
|
407
|
+
inner += `"$type": ${PURE} v.optional(${PURE} v.literal(${lit(toLexUri(path))})),`;
|
|
391
408
|
break;
|
|
392
409
|
}
|
|
393
410
|
case 'required': {
|
|
394
|
-
inner += `"$type": ${PURE} v.literal(${lit(
|
|
411
|
+
inner += `"$type": ${PURE} v.literal(${lit(toLexUri(path))}),`;
|
|
395
412
|
break;
|
|
396
413
|
}
|
|
397
414
|
}
|
|
@@ -406,9 +423,9 @@ const generateObject = (imports, defUri, spec, writeType = 'optional') => {
|
|
|
406
423
|
});
|
|
407
424
|
for (const [prop, propSpec] of sortedEntries) {
|
|
408
425
|
const lazy = isRefVariant(propSpec.type === 'array' ? propSpec.items : propSpec);
|
|
409
|
-
const optional = !required.has(prop) && !('default' in propSpec);
|
|
426
|
+
const optional = !required.has(prop) && !('default' in propSpec && propSpec.default !== undefined);
|
|
410
427
|
const nulled = nullable.has(prop);
|
|
411
|
-
let call = generateType(imports,
|
|
428
|
+
let call = generateType(imports, path, propSpec, lazy);
|
|
412
429
|
if (nulled) {
|
|
413
430
|
call = `${PURE} v.nullable(${call})`;
|
|
414
431
|
}
|
|
@@ -524,53 +541,44 @@ const generateJsdocField = (spec) => {
|
|
|
524
541
|
}
|
|
525
542
|
return res;
|
|
526
543
|
};
|
|
527
|
-
const generateType = (imports,
|
|
544
|
+
const generateType = (imports, path, spec, lazy = false) => {
|
|
528
545
|
switch (spec.type) {
|
|
529
546
|
// LexRefVariant
|
|
530
547
|
case 'ref': {
|
|
531
|
-
const
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
return `${toCamelCase(id)}Schema`;
|
|
535
|
-
}
|
|
536
|
-
else {
|
|
537
|
-
const [ns, id = 'main'] = ref.split('#');
|
|
538
|
-
if (ns === stripHash(defUri)) {
|
|
539
|
-
return `${toCamelCase(id)}Schema`;
|
|
540
|
-
}
|
|
541
|
-
imports.add(ns);
|
|
542
|
-
return `${toTitleCase(ns)}.${toCamelCase(id)}Schema`;
|
|
548
|
+
const refPath = resolvePath(path, spec.ref);
|
|
549
|
+
if (refPath.nsid === path.nsid) {
|
|
550
|
+
return `${toCamelCase(refPath.defId)}Schema`;
|
|
543
551
|
}
|
|
552
|
+
imports.add(refPath.nsid);
|
|
553
|
+
return `${toTitleCase(refPath.nsid)}.${toCamelCase(refPath.defId)}Schema`;
|
|
544
554
|
}
|
|
545
555
|
case 'union': {
|
|
546
|
-
const
|
|
556
|
+
const refs = spec.refs
|
|
547
557
|
.map((ref) => {
|
|
548
|
-
|
|
549
|
-
|
|
558
|
+
const refPath = resolvePath(path, ref);
|
|
559
|
+
return { path: refPath, uri: toLexUri(refPath) };
|
|
560
|
+
})
|
|
561
|
+
.sort((a, b) => {
|
|
562
|
+
if (a.uri < b.uri) {
|
|
563
|
+
return -1;
|
|
550
564
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return `#${id}`;
|
|
565
|
+
if (a.uri > b.uri) {
|
|
566
|
+
return 1;
|
|
554
567
|
}
|
|
555
|
-
return
|
|
568
|
+
return 0;
|
|
556
569
|
})
|
|
557
|
-
.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
const id = ref.slice(1);
|
|
561
|
-
return `${toCamelCase(id)}Schema`;
|
|
562
|
-
}
|
|
563
|
-
else {
|
|
564
|
-
const [ns, id = 'main'] = ref.split('#');
|
|
565
|
-
imports.add(ns);
|
|
566
|
-
return `${toTitleCase(ns)}.${toCamelCase(id)}Schema`;
|
|
570
|
+
.map(({ path: refPath }) => {
|
|
571
|
+
if (refPath.nsid === path.nsid) {
|
|
572
|
+
return `${toCamelCase(refPath.defId)}Schema`;
|
|
567
573
|
}
|
|
574
|
+
imports.add(refPath.nsid);
|
|
575
|
+
return `${toTitleCase(refPath.nsid)}.${toCamelCase(refPath.defId)}Schema`;
|
|
568
576
|
});
|
|
569
577
|
return `${PURE} v.variant([${refs.join(', ')}]${spec.closed ? `, true` : ``})`;
|
|
570
578
|
}
|
|
571
579
|
// LexArray
|
|
572
580
|
case 'array': {
|
|
573
|
-
let item = generateType(imports,
|
|
581
|
+
let item = generateType(imports, path, spec.items);
|
|
574
582
|
if (!lazy && (spec.items.type === 'ref' || spec.items.type === 'union')) {
|
|
575
583
|
item = `(() => { return ${item}; })`;
|
|
576
584
|
}
|
|
@@ -740,16 +748,6 @@ const isRefVariant = (spec) => {
|
|
|
740
748
|
const type = spec.type;
|
|
741
749
|
return type === 'ref' || type === 'union';
|
|
742
750
|
};
|
|
743
|
-
const stripHash = (defUri) => {
|
|
744
|
-
const index = defUri.indexOf('#');
|
|
745
|
-
if (index === -1) {
|
|
746
|
-
return defUri;
|
|
747
|
-
}
|
|
748
|
-
return defUri.slice(0, index);
|
|
749
|
-
};
|
|
750
|
-
const stripMainHash = (defUri) => {
|
|
751
|
-
return defUri.endsWith('#main') ? defUri.slice(0, -'#main'.length) : defUri;
|
|
752
|
-
};
|
|
753
751
|
const toTitleCase = (v) => {
|
|
754
752
|
v = v.replace(/^([a-z])/gi, (_, g) => g.toUpperCase());
|
|
755
753
|
v = v.replace(/[.#-]([a-z])/gi, (_, g) => g.toUpperCase());
|