tangled
alpha
login
or
join now
danabra.mov
/
typelex
56
fork
atom
An experimental TypeSpec syntax for Lexicon
56
fork
atom
overview
issues
1
pulls
2
pipelines
0.2.15
danabra.mov
5 months ago
82424d08
c24bc9bd
+42
-129
3 changed files
expand all
collapse all
unified
split
packages
cli
package.json
website
src
components
ComparisonBlock.astro
pages
index.astro
+1
-1
packages/cli/package.json
···
1
1
{
2
2
"name": "@typelex/cli",
3
3
-
"version": "0.2.14",
3
3
+
"version": "0.2.15",
4
4
"main": "dist/index.js",
5
5
"type": "module",
6
6
"bin": {
+15
-10
packages/website/src/components/ComparisonBlock.astro
···
9
9
10
10
interface Props {
11
11
code: string;
12
12
+
hero?: boolean;
12
13
}
13
14
14
14
-
const { code } = Astro.props;
15
15
+
const { code, hero = false } = Astro.props;
15
16
16
17
// Create temporary file for compilation
17
18
const tmpDir = mkdtempSync(join(tmpdir(), 'typelex-'));
···
23
24
24
25
try {
25
26
lexiconJson = await compileToJson(tmpFile);
26
26
-
lexicon = stringify(JSON.parse(lexiconJson), { maxLength: 80 });
27
27
+
lexicon = stringify(JSON.parse(lexiconJson), { maxLength: hero ? 50 : 80 });
27
28
} finally {
28
29
rmSync(tmpDir, { recursive: true, force: true });
29
30
}
···
31
32
const typelexHtml = await highlightCode(code, 'typespec');
32
33
const lexiconHtml = await highlightCode(lexicon, 'json');
33
34
const playgroundUrl = createPlaygroundUrl(code);
35
35
+
36
36
+
const panelClass = hero ? 'hero-panel' : 'code-panel';
37
37
+
const headerClass = hero ? 'hero-header' : 'code-header';
38
38
+
const blockClass = hero ? 'hero-code' : 'code-block';
34
39
---
35
40
36
36
-
<div class="comparison">
41
41
+
<figure class:list={[hero ? 'hero-comparison' : 'comparison']}>
37
42
<div class="comparison-content">
38
38
-
<div class="code-panel">
39
39
-
<p class="code-header">
43
43
+
<div class={panelClass}>
44
44
+
<p class={headerClass}>
40
45
Typelex
41
46
<a href={playgroundUrl} target="_blank" rel="noopener noreferrer" class="code-playground-link" aria-label="Open in playground">
42
47
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
···
45
50
</svg>
46
51
</a>
47
52
</p>
48
48
-
<div class="code-block" set:html={typelexHtml} />
53
53
+
<div class={blockClass} set:html={typelexHtml} />
49
54
</div>
50
50
-
<div class="code-panel">
51
51
-
<p class="code-header">
55
55
+
<div class={panelClass}>
56
56
+
<p class={headerClass}>
52
57
Lexicon
53
58
</p>
54
54
-
<div class="code-block" set:html={lexiconHtml} />
59
59
+
<div class={blockClass} set:html={lexiconHtml} />
55
60
</div>
56
61
</div>
57
57
-
</div>
62
62
+
</figure>
+26
-118
packages/website/src/pages/index.astro
···
1
1
---
2
2
import BaseLayout from '../layouts/BaseLayout.astro';
3
3
+
import ComparisonBlock from '../components/ComparisonBlock.astro';
3
4
import { highlightCode } from '../utils/shiki';
4
4
-
import { compileToJson } from '../utils/compile';
5
5
import { createPlaygroundUrl } from '../utils/playground-url';
6
6
-
import stringify from 'json-stringify-pretty-compact';
7
7
-
import { mkdtempSync, writeFileSync, rmSync } from 'fs';
8
8
-
import { join } from 'path';
9
9
-
import { tmpdir } from 'os';
10
6
11
7
// Define examples inline
12
8
const examples = [
13
9
{
14
10
title: "Records and properties",
15
15
-
typelex: `import "@typelex/emitter";
11
11
+
code: `import "@typelex/emitter";
16
12
17
13
namespace fm.teal.alpha.feed.play {
18
14
@rec("tid")
19
15
model Main {
20
16
@maxItems(10)
21
17
artistNames?: string[];
22
22
-
18
18
+
23
19
@required
24
20
@minLength(1)
25
21
@maxLength(256)
···
32
28
},
33
29
{
34
30
title: "Refs and unions",
35
35
-
typelex: `import "@typelex/emitter";
31
31
+
code: `import "@typelex/emitter";
36
32
37
33
namespace app.bsky.feed.post {
38
34
@rec("tid")
···
67
63
},
68
64
{
69
65
title: "Queries and params",
70
70
-
typelex: `import "@typelex/emitter";
66
66
+
code: `import "@typelex/emitter";
71
67
72
68
namespace com.atproto.repo.listRecords {
73
69
@query
···
100
96
},
101
97
];
102
98
103
103
-
// Compile examples
104
104
-
const highlighted = await Promise.all(
105
105
-
examples.map(async (ex) => {
106
106
-
// Create temporary file for compilation
107
107
-
const tmpDir = mkdtempSync(join(tmpdir(), 'typelex-'));
108
108
-
const tmpFile = join(tmpDir, 'example.tsp');
109
109
-
writeFileSync(tmpFile, ex.typelex);
110
110
-
111
111
-
try {
112
112
-
const lexiconJson = await compileToJson(tmpFile);
113
113
-
const lexicon = stringify(JSON.parse(lexiconJson), { maxLength: 80 });
114
114
-
115
115
-
return {
116
116
-
...ex,
117
117
-
typelexHtml: await highlightCode(ex.typelex, 'typespec'),
118
118
-
lexiconHtml: await highlightCode(lexicon, 'json'),
119
119
-
playgroundUrl: createPlaygroundUrl(ex.typelex),
120
120
-
};
121
121
-
} finally {
122
122
-
rmSync(tmpDir, { recursive: true, force: true });
123
123
-
}
124
124
-
})
125
125
-
);
126
126
-
---
127
127
-
128
128
-
<BaseLayout title="typelex – An experimental TypeSpec syntax for Lexicon" transparentNav={true}>
129
129
-
<main class="container">
130
130
-
<header>
131
131
-
<h1>typelex</h1>
132
132
-
<p class="tagline">An experimental <a href="https://typespec.io" target="_blank" rel="noopener noreferrer">TypeSpec</a> syntax for <a href="https://atproto.com/specs/lexicon" target="_blank" rel="noopener noreferrer">Lexicon</a></p>
133
133
-
134
134
-
<figure class="hero-comparison">
135
135
-
<div class="comparison-content">
136
136
-
<div class="hero-panel">
137
137
-
<p class="hero-header">
138
138
-
Typelex
139
139
-
<a href={createPlaygroundUrl(`import "@typelex/emitter";
99
99
+
const heroCode = `import "@typelex/emitter";
140
100
141
101
namespace app.bsky.actor.profile {
142
102
@rec("self")
···
149
109
@maxGraphemes(256)
150
110
description?: string;
151
111
}
152
152
-
}`)} target="_blank" rel="noopener noreferrer" class="code-playground-link" aria-label="Open in playground">
153
153
-
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
154
154
-
<path d="M6.5 3.5C6.5 3.22386 6.72386 3 7 3H13C13.2761 3 13.5 3.22386 13.5 3.5V9.5C13.5 9.77614 13.2761 10 13 10C12.7239 10 12.5 9.77614 12.5 9.5V4.70711L6.85355 10.3536C6.65829 10.5488 6.34171 10.5488 6.14645 10.3536C5.95118 10.1583 5.95118 9.84171 6.14645 9.64645L11.7929 4H7C6.72386 4 6.5 3.77614 6.5 3.5Z" fill="currentColor"/>
155
155
-
<path d="M3 5.5C3 4.67157 3.67157 4 4.5 4H5C5.27614 4 5.5 4.22386 5.5 4.5C5.5 4.77614 5.27614 5 5 5H4.5C4.22386 5 4 5.22386 4 5.5V11.5C4 11.7761 4.22386 12 4.5 12H10.5C10.7761 12 11 11.7761 11 11.5V11C11 10.7239 11.2239 10.5 11.5 10.5C11.7761 10.5 12 10.7239 12 11V11.5C12 12.3284 11.3284 13 10.5 13H4.5C3.67157 13 3 12.3284 3 11.5V5.5Z" fill="currentColor"/>
156
156
-
</svg>
157
157
-
</a>
158
158
-
</p>
159
159
-
<div class="hero-code" set:html={await highlightCode(`import "@typelex/emitter";
112
112
+
}`;
160
113
161
161
-
namespace app.bsky.actor.profile {
162
162
-
@rec("self")
163
163
-
model Main {
164
164
-
@maxLength(64)
165
165
-
@maxGraphemes(64)
166
166
-
displayName?: string;
114
114
+
const installCode = `import "@typelex/emitter";
115
115
+
import "./externals.tsp";
167
116
168
168
-
@maxLength(256)
117
117
+
namespace com.myapp.example.profile {
118
118
+
/** My profile. */
119
119
+
@rec("literal:self")
120
120
+
model Main {
121
121
+
/** Free-form profile description.*/
169
122
@maxGraphemes(256)
170
123
description?: string;
171
124
}
172
172
-
}`, 'typespec')} />
173
173
-
</div>
174
174
-
<div class="hero-panel">
175
175
-
<p class="hero-header">
176
176
-
Lexicon
177
177
-
</p>
178
178
-
<div class="hero-code" set:html={await highlightCode(stringify({
179
179
-
"lexicon": 1,
180
180
-
"id": "app.bsky.actor.profile",
181
181
-
"defs": {
182
182
-
"main": {
183
183
-
"type": "record",
184
184
-
"key": "self",
185
185
-
"record": {
186
186
-
"type": "object",
187
187
-
"properties": {
188
188
-
"displayName": {
189
189
-
"type": "string",
190
190
-
"maxLength": 64,
191
191
-
"maxGraphemes": 64
192
192
-
},
193
193
-
"description": {
194
194
-
"type": "string",
195
195
-
"maxLength": 256,
196
196
-
"maxGraphemes": 256
197
197
-
}
198
198
-
}
199
199
-
}
200
200
-
}
201
201
-
}
202
202
-
}, { maxLength: 50 }), 'json')} />
203
203
-
</div>
204
204
-
</div>
205
205
-
</figure>
125
125
+
}`;
126
126
+
---
127
127
+
128
128
+
<BaseLayout title="typelex – An experimental TypeSpec syntax for Lexicon" transparentNav={true}>
129
129
+
<main class="container">
130
130
+
<header>
131
131
+
<h1>typelex</h1>
132
132
+
<p class="tagline">An experimental <a href="https://typespec.io" target="_blank" rel="noopener noreferrer">TypeSpec</a> syntax for <a href="https://atproto.com/specs/lexicon" target="_blank" rel="noopener noreferrer">Lexicon</a></p>
133
133
+
134
134
+
<ComparisonBlock code={heroCode} hero={true} />
206
135
207
136
<p class="hero-description">
208
137
Typelex lets you write AT <a target="_blank" href="https://atproto.com/specs/lexicon">Lexicons</a> in a more readable syntax. <br />
···
219
148
220
149
<hr class="separator" />
221
150
222
222
-
{highlighted.map(({ title, typelexHtml, lexiconHtml, playgroundUrl }) => (
151
151
+
{examples.map(({ title, code }) => (
223
152
<section>
224
153
<h2>{title}</h2>
225
225
-
<figure class="comparison">
226
226
-
<div class="comparison-content">
227
227
-
<div class="code-panel">
228
228
-
<p class="code-header">
229
229
-
Typelex
230
230
-
<a href={playgroundUrl} target="_blank" rel="noopener noreferrer" class="code-playground-link" aria-label="Open in playground">
231
231
-
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
232
232
-
<path d="M6.5 3.5C6.5 3.22386 6.72386 3 7 3H13C13.2761 3 13.5 3.22386 13.5 3.5V9.5C13.5 9.77614 13.2761 10 13 10C12.7239 10 12.5 9.77614 12.5 9.5V4.70711L6.85355 10.3536C6.65829 10.5488 6.34171 10.5488 6.14645 10.3536C5.95118 10.1583 5.95118 9.84171 6.14645 9.64645L11.7929 4H7C6.72386 4 6.5 3.77614 6.5 3.5Z" fill="currentColor"/>
233
233
-
<path d="M3 5.5C3 4.67157 3.67157 4 4.5 4H5C5.27614 4 5.5 4.22386 5.5 4.5C5.5 4.77614 5.27614 5 5 5H4.5C4.22386 5 4 5.22386 4 5.5V11.5C4 11.7761 4.22386 12 4.5 12H10.5C10.7761 12 11 11.7761 11 11.5V11C11 10.7239 11.2239 10.5 11.5 10.5C11.7761 10.5 12 10.7239 12 11V11.5C12 12.3284 11.3284 13 10.5 13H4.5C3.67157 13 3 12.3284 3 11.5V5.5Z" fill="currentColor"/>
234
234
-
</svg>
235
235
-
</a>
236
236
-
</p>
237
237
-
<div class="code-block" set:html={typelexHtml} />
238
238
-
</div>
239
239
-
<div class="code-panel">
240
240
-
<p class="code-header">
241
241
-
Lexicon
242
242
-
</p>
243
243
-
<div class="code-block" set:html={lexiconHtml} />
244
244
-
</div>
245
245
-
</div>
246
246
-
</figure>
154
154
+
<ComparisonBlock code={code} />
247
155
</section>
248
156
))}
249
157