@atcute/lex-cli 2.6.0 → 2.7.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/dist/formatter.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { availableParallelism } from 'node:os';
2
+ import { createLspClient } from './lsp-client.js';
3
3
  const inferPrettierParser = (filepath) => {
4
4
  if (filepath.endsWith('.ts') || filepath.endsWith('.tsx')) {
5
5
  return 'typescript';
@@ -47,65 +47,76 @@ class Semaphore {
47
47
  * @returns a formatter instance
48
48
  */
49
49
  export const createFormatter = async (config, root) => {
50
- let inner;
51
- let concurrency;
52
- if (config.type === 'prettier') {
53
- const prettier = await import('prettier');
54
- const prettierConfig = await prettier.resolveConfig(root, { editorconfig: true });
55
- // prettier is in-process and CPU-bound, so concurrency only helps
56
- // avoid buffering all files in memory at once
57
- concurrency = availableParallelism();
58
- inner = {
59
- async format(code, filepath) {
60
- return prettier.format(code, { ...prettierConfig, parser: inferPrettierParser(filepath) });
61
- },
62
- };
63
- }
64
- else {
65
- // the template uses {filepath} as a placeholder, which is passed as a
66
- // positional argument to sh to avoid shell injection via filenames
67
- const shellCmd = config.command.replaceAll('{filepath}', '"$1"');
68
- concurrency = config.concurrency;
69
- inner = {
70
- format(code, filepath) {
71
- return new Promise((resolve, reject) => {
72
- const child = spawn('sh', ['-c', shellCmd, 'sh', filepath], {
73
- stdio: ['pipe', 'pipe', 'pipe'],
74
- });
75
- const stdoutChunks = [];
76
- const stderrChunks = [];
77
- child.stdout.on('data', (chunk) => {
78
- stdoutChunks.push(chunk);
79
- });
80
- child.stderr.on('data', (chunk) => {
81
- stderrChunks.push(chunk);
82
- });
83
- child.on('error', reject);
84
- child.on('close', (exitCode) => {
85
- if (exitCode !== 0) {
86
- const stderr = Buffer.concat(stderrChunks).toString();
87
- reject(new Error(`formatter exited with code ${exitCode}:\n${stderr}`));
88
- }
89
- else {
90
- resolve(Buffer.concat(stdoutChunks).toString());
91
- }
92
- });
93
- child.stdin.end(code);
94
- });
95
- },
96
- };
50
+ switch (config.type) {
51
+ case 'prettier': {
52
+ const prettier = await import('prettier');
53
+ const prettierConfig = await prettier.resolveConfig(root, { editorconfig: true });
54
+ return {
55
+ async format(code, filepath) {
56
+ return prettier.format(code, { ...prettierConfig, parser: inferPrettierParser(filepath) });
57
+ },
58
+ async dispose() { },
59
+ };
60
+ }
61
+ case 'command': {
62
+ // the template uses {filepath} as a placeholder, which is passed as a
63
+ // positional argument to sh to avoid shell injection via filenames
64
+ const shellCmd = config.command.replaceAll('{filepath}', '"$1"');
65
+ const semaphore = new Semaphore(config.concurrency);
66
+ return {
67
+ async format(code, filepath) {
68
+ const lock = await semaphore.acquire();
69
+ try {
70
+ return await new Promise((resolve, reject) => {
71
+ const child = spawn('sh', ['-c', shellCmd, 'sh', filepath], {
72
+ stdio: ['pipe', 'pipe', 'pipe'],
73
+ });
74
+ const stdoutChunks = [];
75
+ const stderrChunks = [];
76
+ child.stdout.on('data', (chunk) => {
77
+ stdoutChunks.push(chunk);
78
+ });
79
+ child.stderr.on('data', (chunk) => {
80
+ stderrChunks.push(chunk);
81
+ });
82
+ child.on('error', reject);
83
+ child.on('close', (exitCode) => {
84
+ if (exitCode !== 0) {
85
+ const stderr = Buffer.concat(stderrChunks).toString();
86
+ reject(new Error(`formatter exited with code ${exitCode}:\n${stderr}`));
87
+ }
88
+ else {
89
+ resolve(Buffer.concat(stdoutChunks).toString());
90
+ }
91
+ });
92
+ child.stdin.end(code);
93
+ });
94
+ }
95
+ finally {
96
+ lock.release();
97
+ }
98
+ },
99
+ async dispose() { },
100
+ };
101
+ }
102
+ case 'lsp': {
103
+ const client = await createLspClient(config.command, root);
104
+ const semaphore = new Semaphore(1);
105
+ return {
106
+ async format(code, filepath) {
107
+ const lock = await semaphore.acquire();
108
+ try {
109
+ return await client.formatDocument(code, filepath);
110
+ }
111
+ finally {
112
+ lock.release();
113
+ }
114
+ },
115
+ async dispose() {
116
+ await client.dispose();
117
+ },
118
+ };
119
+ }
97
120
  }
98
- const semaphore = new Semaphore(concurrency);
99
- return {
100
- async format(code, filepath) {
101
- const lock = await semaphore.acquire();
102
- try {
103
- return await inner.format(code, filepath);
104
- }
105
- finally {
106
- lock.release();
107
- }
108
- },
109
- };
110
121
  };
111
122
  //# sourceMappingURL=formatter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** LSP client for formatting documents */
2
+ export interface LspClient {
3
+ /**
4
+ * formats a document via LSP textDocument/formatting
5
+ * @param code source code to format
6
+ * @param filepath filepath for language detection and URI
7
+ * @returns formatted code
8
+ */
9
+ formatDocument(code: string, filepath: string): Promise<string>;
10
+ /** shuts down the LSP server */
11
+ dispose(): Promise<void>;
12
+ }
13
+ /**
14
+ * creates an LSP client that communicates with a formatter over stdio
15
+ * @param command shell command to spawn the LSP server
16
+ * @param root project root for LSP rootUri
17
+ * @returns an initialized LSP client ready for formatting
18
+ */
19
+ export declare const createLspClient: (command: string, root: string) => Promise<LspClient>;
20
+ //# sourceMappingURL=lsp-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lsp-client.d.ts","sourceRoot":"","sources":["../src/lsp-client.ts"],"names":[],"mappings":"AAsFA,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACzB;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,gCAAgC;IAChC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,YAAmB,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,SAAS,CA0LtF,CAAC"}
@@ -0,0 +1,203 @@
1
+ import { spawn } from 'node:child_process';
2
+ import * as url from 'node:url';
3
+ import { getUtf8Length } from '@atcute/uint8array';
4
+ // #endregion
5
+ // #region text edits
6
+ const applyTextEdits = (code, edits) => {
7
+ if (edits.length === 0) {
8
+ return code;
9
+ }
10
+ // build line start offsets
11
+ const lineStarts = [0];
12
+ for (let i = 0; i < code.length; i++) {
13
+ if (code[i] === '\n') {
14
+ lineStarts.push(i + 1);
15
+ }
16
+ }
17
+ const positionToOffset = (pos) => {
18
+ return (lineStarts[pos.line] ?? code.length) + pos.character;
19
+ };
20
+ // sort edits in reverse document order so earlier positions stay valid
21
+ const sorted = edits.toSorted((a, b) => {
22
+ const lineDiff = b.range.start.line - a.range.start.line;
23
+ if (lineDiff !== 0) {
24
+ return lineDiff;
25
+ }
26
+ return b.range.start.character - a.range.start.character;
27
+ });
28
+ let result = code;
29
+ for (const edit of sorted) {
30
+ const start = positionToOffset(edit.range.start);
31
+ const end = positionToOffset(edit.range.end);
32
+ result = result.slice(0, start) + edit.newText + result.slice(end);
33
+ }
34
+ return result;
35
+ };
36
+ // #endregion
37
+ const inferLanguageId = (filepath) => {
38
+ if (filepath.endsWith('.ts') || filepath.endsWith('.tsx')) {
39
+ return 'typescript';
40
+ }
41
+ if (filepath.endsWith('.json')) {
42
+ return 'json';
43
+ }
44
+ if (filepath.endsWith('.md') || filepath.endsWith('.markdown')) {
45
+ return 'markdown';
46
+ }
47
+ return 'typescript';
48
+ };
49
+ /**
50
+ * creates an LSP client that communicates with a formatter over stdio
51
+ * @param command shell command to spawn the LSP server
52
+ * @param root project root for LSP rootUri
53
+ * @returns an initialized LSP client ready for formatting
54
+ */
55
+ export const createLspClient = async (command, root) => {
56
+ const child = spawn('sh', ['-c', command], {
57
+ stdio: ['pipe', 'pipe', 'pipe'],
58
+ });
59
+ // prevent EPIPE crash when child exits mid-write; actual errors
60
+ // are handled by the close/error handlers below
61
+ child.stdin.on('error', () => { });
62
+ // drain stderr so a chatty server doesn't block on a full pipe buffer
63
+ child.stderr.resume();
64
+ // #region JSON-RPC framing
65
+ const pending = new Map();
66
+ let nextId = 1;
67
+ let exited = false;
68
+ const sendMessage = (message) => {
69
+ if (exited) {
70
+ return;
71
+ }
72
+ const json = JSON.stringify(message);
73
+ const byteLength = getUtf8Length(json);
74
+ child.stdin.write(`Content-Length: ${byteLength}\r\n\r\n${json}`);
75
+ };
76
+ const sendRequest = (method, params) => {
77
+ if (exited) {
78
+ return Promise.reject(new Error(`LSP server has already exited`));
79
+ }
80
+ const id = nextId++;
81
+ const deferred = Promise.withResolvers();
82
+ pending.set(id, deferred);
83
+ sendMessage({ jsonrpc: '2.0', id, method, params });
84
+ return deferred.promise;
85
+ };
86
+ const sendNotification = (method, params) => {
87
+ sendMessage({ jsonrpc: '2.0', method, params });
88
+ };
89
+ // incremental message parser
90
+ const HEADER_SEPARATOR = Buffer.from('\r\n\r\n');
91
+ let buffer = Buffer.alloc(0);
92
+ let contentLength = -1;
93
+ const processBuffer = () => {
94
+ while (true) {
95
+ if (contentLength === -1) {
96
+ const separatorIndex = buffer.indexOf(HEADER_SEPARATOR);
97
+ if (separatorIndex === -1) {
98
+ break;
99
+ }
100
+ const header = buffer.toString('utf8', 0, separatorIndex);
101
+ const match = header.match(/Content-Length:\s*(\d+)/i);
102
+ buffer = buffer.subarray(separatorIndex + 4);
103
+ if (!match) {
104
+ continue;
105
+ }
106
+ contentLength = parseInt(match[1], 10);
107
+ }
108
+ if (buffer.length < contentLength) {
109
+ break;
110
+ }
111
+ const body = buffer.toString('utf8', 0, contentLength);
112
+ buffer = buffer.subarray(contentLength);
113
+ contentLength = -1;
114
+ let message;
115
+ try {
116
+ message = JSON.parse(body);
117
+ }
118
+ catch {
119
+ continue;
120
+ }
121
+ if (message.id != null) {
122
+ const entry = pending.get(message.id);
123
+ if (entry) {
124
+ pending.delete(message.id);
125
+ if (message.error) {
126
+ entry.reject(new Error(`LSP error ${message.error.code}: ${message.error.message}`));
127
+ }
128
+ else {
129
+ entry.resolve(message.result);
130
+ }
131
+ }
132
+ else if (message.method != null) {
133
+ // server-initiated request — reply with MethodNotFound so it doesn't hang
134
+ sendMessage({
135
+ jsonrpc: '2.0',
136
+ id: message.id,
137
+ error: { code: -32601, message: `method not found` },
138
+ });
139
+ }
140
+ }
141
+ }
142
+ };
143
+ child.stdout.on('data', (chunk) => {
144
+ buffer = buffer.length > 0 ? Buffer.concat([buffer, chunk]) : chunk;
145
+ processBuffer();
146
+ });
147
+ const rejectPending = (error) => {
148
+ for (const [, entry] of pending) {
149
+ entry.reject(error);
150
+ }
151
+ pending.clear();
152
+ };
153
+ child.on('error', (err) => {
154
+ exited = true;
155
+ rejectPending(err);
156
+ });
157
+ child.on('close', (exitCode) => {
158
+ exited = true;
159
+ rejectPending(new Error(`LSP server exited unexpectedly with code ${exitCode}`));
160
+ });
161
+ // #endregion
162
+ // #region initialize handshake
163
+ const rootUri = url.pathToFileURL(root).href;
164
+ await sendRequest('initialize', {
165
+ processId: process.pid,
166
+ clientInfo: { name: 'lex-cli' },
167
+ rootUri,
168
+ capabilities: {},
169
+ });
170
+ sendNotification('initialized');
171
+ // #endregion
172
+ return {
173
+ async formatDocument(code, filepath) {
174
+ const uri = url.pathToFileURL(filepath).href;
175
+ const languageId = inferLanguageId(filepath);
176
+ sendNotification('textDocument/didOpen', {
177
+ textDocument: { uri, languageId, version: 1, text: code },
178
+ });
179
+ const edits = (await sendRequest('textDocument/formatting', {
180
+ textDocument: { uri },
181
+ options: { tabSize: 2, insertSpaces: false },
182
+ }));
183
+ sendNotification('textDocument/didClose', {
184
+ textDocument: { uri },
185
+ });
186
+ if (!edits || edits.length === 0) {
187
+ return code;
188
+ }
189
+ return applyTextEdits(code, edits);
190
+ },
191
+ async dispose() {
192
+ if (!exited) {
193
+ await sendRequest('shutdown', null);
194
+ sendNotification('exit');
195
+ }
196
+ if (!exited) {
197
+ child.kill();
198
+ await new Promise((resolve) => child.on('close', resolve));
199
+ }
200
+ },
201
+ };
202
+ };
203
+ //# sourceMappingURL=lsp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lsp-client.js","sourceRoot":"","sources":["../src/lsp-client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atcute/lex-cli",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "cli tool to generate type definitions for atcute",
5
5
  "license": "0BSD",
6
6
  "repository": {
@@ -31,13 +31,14 @@
31
31
  "prettier": "^3.8.1",
32
32
  "@atcute/identity": "^1.1.4",
33
33
  "@atcute/identity-resolver": "^1.2.2",
34
- "@atcute/lexicon-doc": "^2.1.2",
34
+ "@atcute/lexicon-doc": "^2.2.0",
35
35
  "@atcute/lexicon-resolver": "^0.1.6",
36
- "@atcute/lexicons": "^1.2.9"
36
+ "@atcute/lexicons": "^1.3.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^25.5.2",
40
- "tschema": "^3.2.0"
40
+ "tschema": "^3.2.0",
41
+ "@atcute/uint8array": "^1.1.1"
41
42
  },
42
43
  "scripts": {
43
44
  "build": "pnpm run generate:schema && tsgo",
package/src/codegen.ts CHANGED
@@ -59,6 +59,32 @@ const resolveExternalImport = (nsid: string, mappings: ImportMapping[]): ImportM
59
59
 
60
60
  const PURE = `/*#__PURE__*/`;
61
61
 
62
+ const simplifyAccept = (accept: string[] | undefined): string[] | undefined => {
63
+ if (accept === undefined || accept.length === 0 || accept.includes('*/*')) {
64
+ return undefined;
65
+ }
66
+
67
+ const wildcards = new Set<string>();
68
+ for (const mime of accept) {
69
+ if (mime.endsWith('/*')) {
70
+ wildcards.add(mime.slice(0, mime.indexOf('/')));
71
+ }
72
+ }
73
+
74
+ if (wildcards.size === 0) {
75
+ return accept;
76
+ }
77
+
78
+ const simplified = accept.filter((mime) => {
79
+ if (mime.endsWith('/*')) {
80
+ return true;
81
+ }
82
+ return !wildcards.has(mime.slice(0, mime.indexOf('/')));
83
+ });
84
+
85
+ return simplified.length > 0 ? simplified : undefined;
86
+ };
87
+
62
88
  export function* generateLexiconApi(opts: LexiconApiOptions): Generator<SourceFile> {
63
89
  const importExt = opts.modules?.importSuffix;
64
90
 
@@ -658,9 +684,10 @@ const generateJsdocField = (spec: LexUserType | LexRefVariant | LexUnknown) => {
658
684
  break;
659
685
  }
660
686
  case 'blob': {
661
- if (spec.accept) {
662
- const accept = spec.accept.map((mime) => mime.replace(/\*\//g, '*\\/')).join(', ');
663
- lines.push(`@accept ${accept}`);
687
+ const accept = simplifyAccept(spec.accept);
688
+ if (accept) {
689
+ const formatted = accept.map((mime) => mime.replace(/\*\//g, '*\\/')).join(', ');
690
+ lines.push(`@accept ${formatted}`);
664
691
  }
665
692
  if (spec.maxSize !== undefined) {
666
693
  lines.push(`@maxSize ${spec.maxSize}`);
@@ -890,7 +917,24 @@ const generateType = (
890
917
 
891
918
  // LexBlob
892
919
  case 'blob': {
893
- return `${PURE} v.blob()`;
920
+ let pipe: string[] = [];
921
+
922
+ if (spec.maxSize !== undefined) {
923
+ pipe.push(`${PURE} v.blobSize(${lit(spec.maxSize)})`);
924
+ }
925
+
926
+ const accept = simplifyAccept(spec.accept);
927
+ if (accept !== undefined) {
928
+ pipe.push(`${PURE} v.blobAccept(${lit(accept)})`);
929
+ }
930
+
931
+ let call = `${PURE} v.blob()`;
932
+
933
+ if (pipe.length !== 0) {
934
+ call = `${PURE} v.constrain(${call}, [ ${pipe.join(', ')} ])`;
935
+ }
936
+
937
+ return call;
894
938
  }
895
939
 
896
940
  // LexIpldType
@@ -73,23 +73,27 @@ export const runExport = async (args: ExportCommand): Promise<void> => {
73
73
  const outdir = path.resolve(config.root, exportConfig.outdir);
74
74
  const formatter = await createFormatter(config.formatter, config.root);
75
75
 
76
- // load lexicons from files
77
- const loaded = await loadLexicons(files, config.root);
76
+ try {
77
+ // load lexicons from files
78
+ const loaded = await loadLexicons(files, config.root);
78
79
 
79
- if (loaded.length === 0) {
80
- console.warn(pc.yellow(`warning: no lexicons found to export`));
81
- return;
82
- }
80
+ if (loaded.length === 0) {
81
+ console.warn(pc.yellow(`warning: no lexicons found to export`));
82
+ return;
83
+ }
83
84
 
84
- // clean output directory if requested
85
- if (exportConfig.clean) {
86
- await fs.rm(outdir, { recursive: true, force: true });
87
- }
85
+ // clean output directory if requested
86
+ if (exportConfig.clean) {
87
+ await fs.rm(outdir, { recursive: true, force: true });
88
+ }
88
89
 
89
- await fs.mkdir(outdir, { recursive: true });
90
+ await fs.mkdir(outdir, { recursive: true });
90
91
 
91
- // write each lexicon as JSON
92
- await Promise.all(loaded.map(({ nsid, doc }) => writeLexicon(outdir, nsid, doc, formatter)));
92
+ // write each lexicon as JSON
93
+ await Promise.all(loaded.map(({ nsid, doc }) => writeLexicon(outdir, nsid, doc, formatter)));
93
94
 
94
- console.log(pc.green(`exported ${loaded.length} lexicon(s) to ${outdir}`));
95
+ console.log(pc.green(`exported ${loaded.length} lexicon(s) to ${outdir}`));
96
+ } finally {
97
+ await formatter.dispose();
98
+ }
95
99
  };
@@ -150,25 +150,30 @@ export const runGenerate = async (args: GenerateCommand): Promise<void> => {
150
150
 
151
151
  const outdir = path.join(config.root, config.outdir);
152
152
  const formatter = await createFormatter(config.formatter, config.root);
153
- const pending: Promise<void>[] = [];
154
-
155
- for (const file of generateLexiconApi({
156
- documents: documents,
157
- mappings: allMappings,
158
- modules: {
159
- importSuffix: config.modules?.importSuffix ?? '.js',
160
- },
161
- })) {
162
- const filename = path.join(outdir, file.filename);
163
-
164
- pending.push(
165
- (async () => {
166
- const formatted = await formatter.format(file.code, filename);
167
- await fs.mkdir(path.dirname(filename), { recursive: true });
168
- await fs.writeFile(filename, formatted);
169
- })(),
170
- );
171
- }
172
153
 
173
- await Promise.all(pending);
154
+ try {
155
+ const pending: Promise<void>[] = [];
156
+
157
+ for (const file of generateLexiconApi({
158
+ documents: documents,
159
+ mappings: allMappings,
160
+ modules: {
161
+ importSuffix: config.modules?.importSuffix ?? '.js',
162
+ },
163
+ })) {
164
+ const filename = path.join(outdir, file.filename);
165
+
166
+ pending.push(
167
+ (async () => {
168
+ const formatted = await formatter.format(file.code, filename);
169
+ await fs.mkdir(path.dirname(filename), { recursive: true });
170
+ await fs.writeFile(filename, formatted);
171
+ })(),
172
+ );
173
+ }
174
+
175
+ await Promise.all(pending);
176
+ } finally {
177
+ await formatter.dispose();
178
+ }
174
179
  };