@atcute/lex-cli 2.5.3 → 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 (51) hide show
  1. package/dist/codegen.d.ts +1 -7
  2. package/dist/codegen.d.ts.map +1 -1
  3. package/dist/codegen.js +7 -16
  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 +19 -1
  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.js.map +1 -1
  34. package/dist/pull-sources/atproto.d.ts +3 -11
  35. package/dist/pull-sources/atproto.d.ts.map +1 -1
  36. package/dist/pull-sources/atproto.js.map +1 -1
  37. package/dist/pull-sources/git.d.ts +3 -7
  38. package/dist/pull-sources/git.d.ts.map +1 -1
  39. package/dist/pull-sources/git.js.map +1 -1
  40. package/dist/shared-options.d.ts +1 -1
  41. package/package.json +9 -9
  42. package/src/cli.ts +3 -3
  43. package/src/codegen.ts +7 -27
  44. package/src/commands/export.ts +8 -20
  45. package/src/commands/generate.ts +21 -17
  46. package/src/commands/pull.ts +17 -23
  47. package/src/config.ts +16 -1
  48. package/src/formatter.ts +145 -0
  49. package/src/index.ts +1 -1
  50. package/src/pull-sources/atproto.ts +2 -2
  51. package/src/pull-sources/git.ts +3 -3
package/src/codegen.ts CHANGED
@@ -16,8 +16,6 @@ import type {
16
16
  } from '@atcute/lexicon-doc';
17
17
  import { formatLexiconRef, parseLexiconRef, type ParsedLexiconRef } from '@atcute/lexicon-doc';
18
18
 
19
- import * as prettier from 'prettier';
20
-
21
19
  export interface SourceFile {
22
20
  filename: string;
23
21
  code: string;
@@ -34,13 +32,6 @@ export interface LexiconApiOptions {
34
32
  modules: {
35
33
  importSuffix: string;
36
34
  };
37
- prettier: {
38
- cwd: string;
39
- };
40
- }
41
-
42
- export interface LexiconApiResult {
43
- files: SourceFile[];
44
35
  }
45
36
 
46
37
  type DocumentMap = Map<string, LexiconDoc>;
@@ -68,7 +59,7 @@ const resolveExternalImport = (nsid: string, mappings: ImportMapping[]): ImportM
68
59
 
69
60
  const PURE = `/*#__PURE__*/`;
70
61
 
71
- export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<LexiconApiResult> => {
62
+ export function* generateLexiconApi(opts: LexiconApiOptions): Generator<SourceFile> {
72
63
  const importExt = opts.modules?.importSuffix;
73
64
 
74
65
  const documents = opts.documents.toSorted((a, b) => {
@@ -83,7 +74,6 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
83
74
  });
84
75
 
85
76
  const map: DocumentMap = new Map(documents.map((doc) => [doc.id, doc]));
86
- const files: SourceFile[] = [];
87
77
  const generatedIds = new Set<string>();
88
78
 
89
79
  for (const doc of documents) {
@@ -336,7 +326,7 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
336
326
  if (file.exports) {
337
327
  generatedIds.add(doc.id);
338
328
 
339
- files.push({
329
+ yield {
340
330
  filename: filename,
341
331
  code:
342
332
  file.imports +
@@ -354,7 +344,7 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
354
344
  file.sinterfaces +
355
345
  `\n\n` +
356
346
  file.ambients,
357
- });
347
+ };
358
348
  }
359
349
  }
360
350
 
@@ -369,23 +359,12 @@ export const generateLexiconApi = async (opts: LexiconApiOptions): Promise<Lexic
369
359
  code += `export * as ${toTitleCase(doc.id)} from ${lit(`./types/${doc.id.replaceAll('.', '/')}${importExt}`)};\n`;
370
360
  }
371
361
 
372
- files.push({
362
+ yield {
373
363
  filename: 'index.ts',
374
364
  code: code,
375
- });
376
- }
377
-
378
- if (opts.prettier) {
379
- const config = await prettier.resolveConfig(opts.prettier.cwd, { editorconfig: true });
380
-
381
- for (const file of files) {
382
- const formatted = await prettier.format(file.code, { ...config, parser: 'typescript' });
383
- file.code = formatted;
384
- }
365
+ };
385
366
  }
386
-
387
- return { files };
388
- };
367
+ }
389
368
 
390
369
  const generateXrpcQuery = (imports: ImportSet, path: ParsedLexiconRef, spec: LexXrpcQuery): string => {
391
370
  const params = generateXrpcParameters(imports, path, spec.parameters);
@@ -729,6 +708,7 @@ const generateType = (
729
708
  const refPath = resolvePath(path, ref);
730
709
  return { path: refPath, uri: formatLexiconRef(refPath) };
731
710
  })
711
+ // oxlint-disable-next-line unicorn/no-array-sort -- map already clones
732
712
  .sort((a, b) => {
733
713
  if (a.uri < b.uri) {
734
714
  return -1;
@@ -8,11 +8,11 @@ import { message } from '@optique/core/message';
8
8
  import { type InferValue } from '@optique/core/parser';
9
9
  import { command, constant } from '@optique/core/primitives';
10
10
  import pc from 'picocolors';
11
- import prettier from 'prettier';
12
11
 
13
- import { loadConfig, type ExportConfig, type NormalizedConfig } from '../config.js';
14
- import { loadLexicons } from '../lexicon-loader.js';
15
- import { sharedOptions } from '../shared-options.js';
12
+ import { loadConfig, type ExportConfig, type NormalizedConfig } from '../config.ts';
13
+ import { createFormatter, type Formatter } from '../formatter.ts';
14
+ import { loadLexicons } from '../lexicon-loader.ts';
15
+ import { sharedOptions } from '../shared-options.ts';
16
16
 
17
17
  export const exportCommandSchema = command(
18
18
  'export',
@@ -44,27 +44,17 @@ const ensureExportConfig = (config: NormalizedConfig): ExportConfig => {
44
44
  return config.export;
45
45
  };
46
46
 
47
- /**
48
- * writes a lexicon document to disk as formatted JSON
49
- * @param outdir output directory
50
- * @param nsid the NSID of the lexicon
51
- * @param doc the lexicon document
52
- * @param prettierConfig prettier configuration
53
- */
54
47
  const writeLexicon = async (
55
48
  outdir: string,
56
49
  nsid: string,
57
50
  doc: LexiconDoc,
58
- prettierConfig: prettier.Options | null,
51
+ formatter: Formatter,
59
52
  ): Promise<void> => {
60
53
  const nsidPath = nsid.replaceAll('.', '/');
61
54
  const target = path.join(outdir, `${nsidPath}.json`);
62
55
  const dirname = path.dirname(target);
63
56
 
64
- const code = await prettier.format(JSON.stringify(doc, null, 2), {
65
- ...prettierConfig,
66
- parser: 'json',
67
- });
57
+ const code = await formatter.format(JSON.stringify(doc, null, 2), target);
68
58
 
69
59
  await fs.mkdir(dirname, { recursive: true });
70
60
  await fs.writeFile(target, code);
@@ -81,7 +71,7 @@ export const runExport = async (args: ExportCommand): Promise<void> => {
81
71
  // use export.files if specified, otherwise fall back to root files config
82
72
  const files = exportConfig.files ?? config.files;
83
73
  const outdir = path.resolve(config.root, exportConfig.outdir);
84
- const prettierConfig = await prettier.resolveConfig(config.root, { editorconfig: true });
74
+ const formatter = await createFormatter(config.formatter, config.root);
85
75
 
86
76
  // load lexicons from files
87
77
  const loaded = await loadLexicons(files, config.root);
@@ -99,9 +89,7 @@ export const runExport = async (args: ExportCommand): Promise<void> => {
99
89
  await fs.mkdir(outdir, { recursive: true });
100
90
 
101
91
  // write each lexicon as JSON
102
- for (const { nsid, doc } of loaded) {
103
- await writeLexicon(outdir, nsid, doc, prettierConfig);
104
- }
92
+ await Promise.all(loaded.map(({ nsid, doc }) => writeLexicon(outdir, nsid, doc, formatter)));
105
93
 
106
94
  console.log(pc.green(`exported ${loaded.length} lexicon(s) to ${outdir}`));
107
95
  };
@@ -7,11 +7,12 @@ import { type InferValue } from '@optique/core/parser';
7
7
  import { command, constant } from '@optique/core/primitives';
8
8
  import pc from 'picocolors';
9
9
 
10
- import { generateLexiconApi, type ImportMapping } from '../codegen.js';
11
- import { loadConfig } from '../config.js';
12
- import { loadLexicons } from '../lexicon-loader.js';
13
- import { packageJsonSchema } from '../lexicon-metadata.js';
14
- import { sharedOptions } from '../shared-options.js';
10
+ import { generateLexiconApi, type ImportMapping } from '../codegen.ts';
11
+ import { loadConfig } from '../config.ts';
12
+ import { createFormatter } from '../formatter.ts';
13
+ import { loadLexicons } from '../lexicon-loader.ts';
14
+ import { packageJsonSchema } from '../lexicon-metadata.ts';
15
+ import { sharedOptions } from '../shared-options.ts';
15
16
 
16
17
  /**
17
18
  * resolves package imports to ImportMapping[]
@@ -147,24 +148,27 @@ export const runGenerate = async (args: GenerateCommand): Promise<void> => {
147
148
  const loaded = await loadLexicons(config.files, config.root);
148
149
  const documents = loaded.map((l) => l.doc);
149
150
 
150
- const generationResult = await generateLexiconApi({
151
+ const outdir = path.join(config.root, config.outdir);
152
+ const formatter = await createFormatter(config.formatter, config.root);
153
+ const pending: Promise<void>[] = [];
154
+
155
+ for (const file of generateLexiconApi({
151
156
  documents: documents,
152
157
  mappings: allMappings,
153
158
  modules: {
154
159
  importSuffix: config.modules?.importSuffix ?? '.js',
155
160
  },
156
- prettier: {
157
- cwd: process.cwd(),
158
- },
159
- });
160
-
161
- const outdir = path.join(config.root, config.outdir);
162
-
163
- for (const file of generationResult.files) {
161
+ })) {
164
162
  const filename = path.join(outdir, file.filename);
165
- const dirname = path.dirname(filename);
166
163
 
167
- await fs.mkdir(dirname, { recursive: true });
168
- await fs.writeFile(filename, file.code);
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
+ );
169
171
  }
172
+
173
+ await Promise.all(pending);
170
174
  };
@@ -8,13 +8,13 @@ import { message } from '@optique/core/message';
8
8
  import { type InferValue } from '@optique/core/parser';
9
9
  import { command, constant } from '@optique/core/primitives';
10
10
  import pc from 'picocolors';
11
- import prettier from 'prettier';
12
11
 
13
- import { loadConfig, type NormalizedConfig, type PullConfig, type SourceConfig } from '../config.js';
14
- import { pullAtprotoSource } from '../pull-sources/atproto.js';
15
- import { pullGitSource } from '../pull-sources/git.js';
16
- import type { PullResult, PulledLexicon, SourceLocation } from '../pull-sources/types.js';
17
- import { sharedOptions } from '../shared-options.js';
12
+ import { loadConfig, type NormalizedConfig, type PullConfig, type SourceConfig } from '../config.ts';
13
+ import { createFormatter, type Formatter } from '../formatter.ts';
14
+ import { pullAtprotoSource } from '../pull-sources/atproto.ts';
15
+ import { pullGitSource } from '../pull-sources/git.ts';
16
+ import type { PullResult, PulledLexicon, SourceLocation } from '../pull-sources/types.ts';
17
+ import { sharedOptions } from '../shared-options.ts';
18
18
 
19
19
  export const pullCommandSchema = command(
20
20
  'pull',
@@ -110,16 +110,13 @@ const writeLexicon = async (
110
110
  outdir: string,
111
111
  nsid: string,
112
112
  doc: LexiconDoc,
113
- prettierConfig: prettier.Options | null,
113
+ formatter: Formatter,
114
114
  ): Promise<void> => {
115
115
  const nsidPath = nsid.replaceAll('.', '/');
116
116
  const target = path.join(outdir, `${nsidPath}.json`);
117
117
  const dirname = path.dirname(target);
118
118
 
119
- const code = await prettier.format(JSON.stringify(doc, null, 2), {
120
- ...prettierConfig,
121
- parser: 'json',
122
- });
119
+ const code = await formatter.format(JSON.stringify(doc, null, 2), target);
123
120
 
124
121
  await fs.mkdir(dirname, { recursive: true });
125
122
  await fs.writeFile(target, code);
@@ -139,7 +136,7 @@ const pullSource = async (source: SourceConfig): Promise<PullResult> => {
139
136
  const writeSourceReadme = async (
140
137
  outdir: string,
141
138
  revisions: SourceRevision[],
142
- prettierConfig: prettier.Options | null,
139
+ formatter: Formatter,
143
140
  ): Promise<void> => {
144
141
  const lines = [
145
142
  '# lexicon sources',
@@ -173,12 +170,10 @@ const writeSourceReadme = async (
173
170
  lines.push('');
174
171
 
175
172
  const content = lines.join('\n');
176
- const formatted = await prettier.format(content, {
177
- ...prettierConfig,
178
- parser: 'markdown',
179
- });
173
+ const target = path.join(outdir, 'README.md');
174
+ const formatted = await formatter.format(content, target);
180
175
 
181
- await fs.writeFile(path.join(outdir, 'README.md'), formatted);
176
+ await fs.writeFile(target, formatted);
182
177
  };
183
178
 
184
179
  /**
@@ -190,7 +185,7 @@ export const runPull = async (args: PullCommand): Promise<void> => {
190
185
  const pullConfig = ensurePullConfig(config);
191
186
 
192
187
  const outdir = path.resolve(config.root, pullConfig.outdir);
193
- const prettierConfig = await prettier.resolveConfig(config.root, { editorconfig: true });
188
+ const formatter = await createFormatter(config.formatter, config.root);
194
189
 
195
190
  const seen = new Map<string, SourceLocation>();
196
191
  const collected: PulledLexicon[] = [];
@@ -224,9 +219,8 @@ export const runPull = async (args: PullCommand): Promise<void> => {
224
219
 
225
220
  await fs.mkdir(outdir, { recursive: true });
226
221
 
227
- for (const entry of collected) {
228
- await writeLexicon(outdir, entry.nsid, entry.doc, prettierConfig);
229
- }
230
-
231
- await writeSourceReadme(outdir, sourceRevisions, prettierConfig);
222
+ await Promise.all([
223
+ ...collected.map((entry) => writeLexicon(outdir, entry.nsid, entry.doc, formatter)),
224
+ writeSourceReadme(outdir, sourceRevisions, formatter),
225
+ ]);
232
226
  };
package/src/config.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as fs from 'node:fs/promises';
2
+ import { availableParallelism } from 'node:os';
2
3
  import * as path from 'node:path';
3
4
  import * as url from 'node:url';
4
5
 
@@ -8,7 +9,7 @@ import { isHandle, isNsid } from '@atcute/lexicons/syntax';
8
9
  import * as v from '@badrap/valita';
9
10
  import pc from 'picocolors';
10
11
 
11
- import type { ImportMapping } from './codegen.js';
12
+ import type { ImportMapping } from './codegen.ts';
12
13
 
13
14
  const gitSourceConfigSchema = v.object({
14
15
  type: v.literal('git'),
@@ -63,6 +64,18 @@ const exportConfigSchema = v.object({
63
64
  clean: v.boolean().optional(),
64
65
  });
65
66
 
67
+ const formatterConfigSchema = v.union(
68
+ v.object({ type: v.literal('prettier') }),
69
+ v.object({
70
+ type: v.literal('command'),
71
+ command: v.string().assert((value) => value.length > 0, `must not be empty`),
72
+ concurrency: v
73
+ .number()
74
+ .assert((value) => Number.isInteger(value) && value > 0, `must be a positive integer`)
75
+ .optional(() => availableParallelism()),
76
+ }),
77
+ );
78
+
66
79
  export type GitSourceConfig = v.Infer<typeof gitSourceConfigSchema>;
67
80
  export type AtprotoNsidsSourceConfig = v.Infer<typeof atprotoNsidsSourceConfigSchema>;
68
81
  export type AtprotoAuthoritySourceConfig = v.Infer<typeof atprotoAuthoritySourceConfigSchema>;
@@ -70,6 +83,7 @@ export type AtprotoSourceConfig = v.Infer<typeof atprotoSourceConfigSchema>;
70
83
  export type SourceConfig = v.Infer<typeof sourceConfigSchema>;
71
84
  export type PullConfig = v.Infer<typeof pullConfigSchema>;
72
85
  export type ExportConfig = v.Infer<typeof exportConfigSchema>;
86
+ export type FormatterConfig = v.Infer<typeof formatterConfigSchema>;
73
87
 
74
88
  const isValidLexiconPattern = (pattern: string): boolean => {
75
89
  if (pattern.endsWith('.*')) {
@@ -126,6 +140,7 @@ export const lexiconConfigSchema = v.object({
126
140
  })
127
141
  .partial()
128
142
  .optional(),
143
+ formatter: formatterConfigSchema.optional((): FormatterConfig => ({ type: 'prettier' })),
129
144
  pull: pullConfigSchema.optional(),
130
145
  export: exportConfigSchema.optional(),
131
146
  });
@@ -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
 
@@ -20,9 +20,9 @@ import {
20
20
 
21
21
  import pc from 'picocolors';
22
22
 
23
- import type { AtprotoSourceConfig } from '../config.js';
23
+ import type { AtprotoSourceConfig } from '../config.ts';
24
24
 
25
- import type { PullResult, SourceLocation } from './types.js';
25
+ import type { PullResult, SourceLocation } from './types.ts';
26
26
 
27
27
  /**
28
28
  * discovers all published lexicons for an authority by listing records in the
@@ -6,10 +6,10 @@ import type { LexiconDoc } from '@atcute/lexicon-doc';
6
6
 
7
7
  import pc from 'picocolors';
8
8
 
9
- import type { GitSourceConfig } from '../config.js';
10
- import { runGit, GitError } from '../git.js';
9
+ import type { GitSourceConfig } from '../config.ts';
10
+ import { runGit, GitError } from '../git.ts';
11
11
 
12
- import type { PullResult, PulledLexicon, SourceLocation } from './types.js';
12
+ import type { PullResult, PulledLexicon, SourceLocation } from './types.ts';
13
13
 
14
14
  /**
15
15
  * pulls lexicon documents from a git repository source