WIP! A BB-style forum, on the ATmosphere!
We're still working... we'll be back soon when we have something to show off!
node
typescript
hono
htmx
atproto
1#!/usr/bin/env tsx
2/**
3 * Post-processing script to add .js extensions to relative imports in compiled JavaScript files.
4 * Required because @atproto/lex-cli generates TypeScript without file extensions,
5 * but Node ESM requires explicit extensions for relative imports.
6 */
7import { readdir, readFile, writeFile } from "node:fs/promises";
8import { join } from "node:path";
9
10async function* walkDir(dir: string): AsyncGenerator<string> {
11 const entries = await readdir(dir, { withFileTypes: true });
12 for (const entry of entries) {
13 const path = join(dir, entry.name);
14 if (entry.isDirectory()) {
15 yield* walkDir(path);
16 } else if (entry.isFile() && path.endsWith(".js")) {
17 yield path;
18 }
19 }
20}
21
22async function fixImports(filePath: string): Promise<void> {
23 const content = await readFile(filePath, "utf-8");
24
25 // Match relative imports without .js extension
26 // Examples: from '../../../lexicons' -> from '../../../lexicons.js'
27 // from './util' -> from './util.js'
28 const fixed = content.replace(
29 /from\s+(['"])(\.\.?\/.+?)\1/g,
30 (match, quote, importPath) => {
31 // Skip if already has extension
32 if (importPath.endsWith(".js") || importPath.endsWith(".json")) {
33 return match;
34 }
35 return `from ${quote}${importPath}.js${quote}`;
36 }
37 );
38
39 if (fixed !== content) {
40 await writeFile(filePath, fixed, "utf-8");
41 console.log(`Fixed imports in: ${filePath}`);
42 }
43}
44
45async function main() {
46 const distDir = join(process.cwd(), "dist/types");
47 console.log(`Fixing relative imports in ${distDir}...`);
48
49 let count = 0;
50 for await (const file of walkDir(distDir)) {
51 await fixImports(file);
52 count++;
53 }
54
55 console.log(`Processed ${count} JavaScript files.`);
56}
57
58main().catch((error) => {
59 console.error("Failed to fix imports:", error);
60 process.exit(1);
61});