@atcute/lex-cli 2.5.2 → 2.6.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 (54) hide show
  1. package/dist/codegen.d.ts +1 -7
  2. package/dist/codegen.d.ts.map +1 -1
  3. package/dist/codegen.js +20 -42
  4. package/dist/codegen.js.map +1 -1
  5. package/dist/commands/export.d.ts +2 -6
  6. package/dist/commands/export.d.ts.map +1 -1
  7. package/dist/commands/export.js +5 -17
  8. package/dist/commands/export.js.map +1 -1
  9. package/dist/commands/generate.d.ts +2 -6
  10. package/dist/commands/generate.d.ts.map +1 -1
  11. package/dist/commands/generate.js +12 -10
  12. package/dist/commands/generate.js.map +1 -1
  13. package/dist/commands/pull.d.ts +2 -6
  14. package/dist/commands/pull.d.ts.map +1 -1
  15. package/dist/commands/pull.js +12 -17
  16. package/dist/commands/pull.js.map +1 -1
  17. package/dist/config.d.ts +17 -2
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +21 -3
  20. package/dist/config.js.map +1 -1
  21. package/dist/formatter.d.ts +19 -0
  22. package/dist/formatter.d.ts.map +1 -0
  23. package/dist/formatter.js +111 -0
  24. package/dist/formatter.js.map +1 -0
  25. package/dist/git.d.ts.map +1 -1
  26. package/dist/git.js.map +1 -1
  27. package/dist/index.d.ts +2 -66
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/lexicon-loader.d.ts.map +1 -1
  31. package/dist/lexicon-loader.js +9 -1
  32. package/dist/lexicon-loader.js.map +1 -1
  33. package/dist/lexicon-metadata.d.ts.map +1 -1
  34. package/dist/lexicon-metadata.js +1 -1
  35. package/dist/lexicon-metadata.js.map +1 -1
  36. package/dist/pull-sources/atproto.d.ts +3 -11
  37. package/dist/pull-sources/atproto.d.ts.map +1 -1
  38. package/dist/pull-sources/atproto.js.map +1 -1
  39. package/dist/pull-sources/git.d.ts +3 -7
  40. package/dist/pull-sources/git.d.ts.map +1 -1
  41. package/dist/pull-sources/git.js.map +1 -1
  42. package/dist/shared-options.d.ts +1 -1
  43. package/package.json +18 -15
  44. package/src/cli.ts +3 -3
  45. package/src/codegen.ts +47 -72
  46. package/src/commands/export.ts +9 -20
  47. package/src/commands/generate.ts +21 -17
  48. package/src/commands/pull.ts +18 -23
  49. package/src/config.ts +19 -4
  50. package/src/formatter.ts +145 -0
  51. package/src/index.ts +1 -1
  52. package/src/lexicon-metadata.ts +2 -2
  53. package/src/pull-sources/atproto.ts +4 -2
  54. package/src/pull-sources/git.ts +5 -3
@@ -0,0 +1,145 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { availableParallelism } from 'node:os';
3
+
4
+ import type { FormatterConfig } from './config.ts';
5
+
6
+ /** formats source code */
7
+ export interface Formatter {
8
+ /**
9
+ * formats the given code
10
+ * @param code source code to format
11
+ * @param filepath filepath hint for language detection and config resolution
12
+ * @returns formatted code
13
+ */
14
+ format(code: string, filepath: string): Promise<string>;
15
+ }
16
+
17
+ const inferPrettierParser = (filepath: string): string => {
18
+ if (filepath.endsWith('.ts') || filepath.endsWith('.tsx')) {
19
+ return 'typescript';
20
+ }
21
+ if (filepath.endsWith('.json')) {
22
+ return 'json';
23
+ }
24
+ if (filepath.endsWith('.md') || filepath.endsWith('.markdown')) {
25
+ return 'markdown';
26
+ }
27
+ return 'typescript';
28
+ };
29
+
30
+ // #region semaphore
31
+
32
+ interface Lock {
33
+ release(): void;
34
+ }
35
+
36
+ class Semaphore {
37
+ private waiting: (() => void)[] = [];
38
+ private active = 0;
39
+ private max: number;
40
+
41
+ constructor(max: number) {
42
+ this.max = max;
43
+ }
44
+
45
+ acquire(): Promise<Lock> {
46
+ const lock: Lock = {
47
+ release: () => {
48
+ this.active--;
49
+ const next = this.waiting.shift();
50
+ if (next) {
51
+ this.active++;
52
+ next();
53
+ }
54
+ },
55
+ };
56
+
57
+ if (this.active < this.max) {
58
+ this.active++;
59
+ return Promise.resolve(lock);
60
+ }
61
+
62
+ const { promise, resolve } = Promise.withResolvers<Lock>();
63
+ this.waiting.push(() => resolve(lock));
64
+ return promise;
65
+ }
66
+ }
67
+
68
+ // #endregion
69
+
70
+ /**
71
+ * creates a formatter from the given configuration
72
+ * @param config formatter configuration
73
+ * @param root project root for config resolution
74
+ * @returns a formatter instance
75
+ */
76
+ export const createFormatter = async (config: FormatterConfig, root: string): Promise<Formatter> => {
77
+ let inner: Formatter;
78
+ let concurrency: number;
79
+
80
+ if (config.type === 'prettier') {
81
+ const prettier = await import('prettier');
82
+ const prettierConfig = await prettier.resolveConfig(root, { editorconfig: true });
83
+
84
+ // prettier is in-process and CPU-bound, so concurrency only helps
85
+ // avoid buffering all files in memory at once
86
+ concurrency = availableParallelism();
87
+ inner = {
88
+ async format(code, filepath) {
89
+ return prettier.format(code, { ...prettierConfig, parser: inferPrettierParser(filepath) });
90
+ },
91
+ };
92
+ } else {
93
+ // the template uses {filepath} as a placeholder, which is passed as a
94
+ // positional argument to sh to avoid shell injection via filenames
95
+ const shellCmd = config.command.replaceAll('{filepath}', '"$1"');
96
+
97
+ concurrency = config.concurrency;
98
+ inner = {
99
+ format(code, filepath) {
100
+ return new Promise<string>((resolve, reject) => {
101
+ const child = spawn('sh', ['-c', shellCmd, 'sh', filepath], {
102
+ stdio: ['pipe', 'pipe', 'pipe'],
103
+ });
104
+
105
+ const stdoutChunks: Buffer[] = [];
106
+ const stderrChunks: Buffer[] = [];
107
+
108
+ child.stdout.on('data', (chunk: Buffer) => {
109
+ stdoutChunks.push(chunk);
110
+ });
111
+
112
+ child.stderr.on('data', (chunk: Buffer) => {
113
+ stderrChunks.push(chunk);
114
+ });
115
+
116
+ child.on('error', reject);
117
+
118
+ child.on('close', (exitCode: number | null) => {
119
+ if (exitCode !== 0) {
120
+ const stderr = Buffer.concat(stderrChunks).toString();
121
+ reject(new Error(`formatter exited with code ${exitCode}:\n${stderr}`));
122
+ } else {
123
+ resolve(Buffer.concat(stdoutChunks).toString());
124
+ }
125
+ });
126
+
127
+ child.stdin.end(code);
128
+ });
129
+ },
130
+ };
131
+ }
132
+
133
+ const semaphore = new Semaphore(concurrency);
134
+
135
+ return {
136
+ async format(code, filepath) {
137
+ const lock = await semaphore.acquire();
138
+ try {
139
+ return await inner.format(code, filepath);
140
+ } finally {
141
+ lock.release();
142
+ }
143
+ },
144
+ };
145
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { lexiconConfigSchema, type LexiconConfig } from './config.js';
1
+ import { lexiconConfigSchema, type LexiconConfig } from './config.ts';
2
2
 
3
3
  export type { LexiconConfig };
4
4
 
@@ -1,7 +1,7 @@
1
- import * as v from '@badrap/valita';
2
-
3
1
  import { isNsid } from '@atcute/lexicons/syntax';
4
2
 
3
+ import * as v from '@badrap/valita';
4
+
5
5
  export type LexiconMappingEntryType = 'namespace' | 'named';
6
6
  export type LexiconMappingPath = '.' | `./${string}`;
7
7
 
@@ -17,10 +17,12 @@ import {
17
17
  type AtprotoDid,
18
18
  type Nsid,
19
19
  } from '@atcute/lexicons/syntax';
20
+
20
21
  import pc from 'picocolors';
21
22
 
22
- import type { AtprotoSourceConfig } from '../config.js';
23
- import type { PullResult, SourceLocation } from './types.js';
23
+ import type { AtprotoSourceConfig } from '../config.ts';
24
+
25
+ import type { PullResult, SourceLocation } from './types.ts';
24
26
 
25
27
  /**
26
28
  * discovers all published lexicons for an authority by listing records in the
@@ -3,11 +3,13 @@ import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
4
 
5
5
  import type { LexiconDoc } from '@atcute/lexicon-doc';
6
+
6
7
  import pc from 'picocolors';
7
8
 
8
- import { runGit, GitError } from '../git.js';
9
- import type { GitSourceConfig } from '../config.js';
10
- import type { PullResult, PulledLexicon, SourceLocation } from './types.js';
9
+ import type { GitSourceConfig } from '../config.ts';
10
+ import { runGit, GitError } from '../git.ts';
11
+
12
+ import type { PullResult, PulledLexicon, SourceLocation } from './types.ts';
11
13
 
12
14
  /**
13
15
  * pulls lexicon documents from a git repository source