Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove need for module resolution esnext (#142)
![the same thing we do every day, pinky: muck with ESM](https://github.com/fixie-ai/ai-jsx/assets/829827/89596f17-e02c-4c6c-992a-c2cdd08af9b2) Previously, consumers need to set `"moduleResolution": "nodenext"` in their `tsconfig.json`. Otherwise, importing from `ai-jsx` fails. I confirmed the original problem, and that this PR fixes it, in https://github.com/NickHeiner/esm-cjs-test. The reason for the problem: in the legacy module resolution scheme, an import to `ai-jsx/stream` would be resolved by looking for these files: ``` node_modules/ai-jsx/stream.js node_modules/ai-jsx/stream/index.js ``` We don't have files at that location, because we use the `package.json` `exports` field. But that field isn't respected in the legacy resolution scheme, so we got TS build errors. To fix it, I added a script that writes proxy files in the locations the legacy resolution algo expects to find them.
- Loading branch information
1 parent
d5f5c87
commit 4697540
Showing
8 changed files
with
137 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { loadJsonFile } from 'load-json-file'; | ||
import path from 'node:path'; | ||
import { fileURLToPath } from 'node:url'; | ||
import fs from 'fs/promises'; | ||
import ts from 'typescript'; | ||
|
||
const currentPath = path.dirname(fileURLToPath(import.meta.url)); | ||
const packageRoot = path.resolve(currentPath, '..', '..'); | ||
const pathToThisFile = path.relative(packageRoot, fileURLToPath(import.meta.url)); | ||
|
||
interface PackageJson { | ||
exports: Record< | ||
string, | ||
{ | ||
import: { | ||
types: string; | ||
default: string; | ||
}; | ||
require: { | ||
types: string; | ||
default: string; | ||
}; | ||
} | ||
>; | ||
} | ||
|
||
const packageJson = await loadJsonFile<PackageJson>(path.resolve(packageRoot, 'package.json')); | ||
|
||
async function writeFile(filePath: string, contents: string) { | ||
const dirname = path.dirname(filePath); | ||
await fs.mkdir(dirname, { recursive: true }); | ||
return fs.writeFile( | ||
path.join(packageRoot, filePath), | ||
`// Auto-generated by ${pathToThisFile}. Do not edit. | ||
// prettier-ignore | ||
/* eslint-disable */ | ||
${contents}\n`, | ||
'utf8' | ||
); | ||
} | ||
|
||
for (const [exportKey, exportValue] of Object.entries(packageJson.exports)) { | ||
const importObj = exportValue.import; | ||
const requireObj = exportValue.require; | ||
|
||
const filePath = exportKey === '.' ? 'index' : exportKey.slice('./'.length); | ||
|
||
const getPathToDest = (dest: string) => `./${path.relative(path.dirname(filePath), dest)}`; | ||
|
||
const program = ts.createProgram({ | ||
rootNames: [importObj.types], | ||
options: { | ||
module: ts.ModuleKind.ESNext, | ||
}, | ||
}); | ||
const checker = program.getTypeChecker(); | ||
const sourceFile = program.getSourceFile(importObj.types); | ||
if (!sourceFile) { | ||
throw new Error(`Failed to find source file: ${importObj.types}`); | ||
} | ||
|
||
const moduleSymbol = checker.getSymbolAtLocation(sourceFile); | ||
if (!moduleSymbol) { | ||
throw new Error(`Failed to find module symbol for file: ${importObj.types}`); | ||
} | ||
|
||
const exports = checker.getExportsOfModule(moduleSymbol); | ||
|
||
const namedExports: string[] = []; | ||
let hasDefaultExport = false; | ||
|
||
for (const exp of exports) { | ||
if ((exp.escapedName as string) === 'default') { | ||
hasDefaultExport = true; | ||
} else { | ||
namedExports.push(exp.escapedName as string); | ||
} | ||
} | ||
|
||
const foundAnyExport = hasDefaultExport || namedExports.length; | ||
|
||
if (!foundAnyExport) { | ||
throw new Error(`Could not find any exports for file ${importObj.types}`); | ||
} | ||
|
||
if (hasDefaultExport) { | ||
throw new Error('create-entry-points.ts does not support default exports yet.'); | ||
} | ||
|
||
const typeFileDestContents = `export { ${namedExports.join(', ')} } from '${getPathToDest(importObj.types)}'; | ||
`; | ||
|
||
await writeFile(`${filePath}.js`, `export * from '${getPathToDest(importObj.default)}'`); | ||
await writeFile(`${filePath}.d.ts`, typeFileDestContents); | ||
await writeFile(`${filePath}.cjs`, `module.exports = require('${getPathToDest(requireObj.default)}')`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"extends": "@tsconfig/node18", | ||
"include": ["**/*.ts"], | ||
"compilerOptions": { | ||
"resolveJsonModule": true, | ||
"outDir": "dist", | ||
"rootDir": ".", | ||
"module": "ESNext" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4697540
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
ai-jsx-docs – ./packages/docs
ai-jsx-docs-git-main-fixie-ai.vercel.app
ai-jsx-docs.vercel.app
ai-jsx-docs-fixie-ai.vercel.app
docs.ai-jsx.com
4697540
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
ai-jsx-tutorial-nextjs – ./packages/tutorial-nextjs
ai-jsx-tutorial-nextjs.vercel.app
ai-jsx-tutorial-nextjs-fixie-ai.vercel.app
ai-jsx-tutorial-nextjs-git-main-fixie-ai.vercel.app
4697540
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
ai-jsx-nextjs-demo – ./packages/nextjs-demo
ai-jsx-nextjs-demo.vercel.app
ai-jsx-nextjs-demo-fixie-ai.vercel.app
ai-jsx-nextjs-demo-git-main-fixie-ai.vercel.app