Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"url": "https://github.com/tr1ckydev/hyperimport/issues"
},
"homepage": "https://github.com/tr1ckydev/hyperimport#readme",
"dependencies": {
"tree-sitter": "^0.20.5",
"tree-sitter-rust": "^0.20.4"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably conditionally import per-lang packages instead of including this in deps

},
"devDependencies": {
"bun-types": "latest"
}
Expand Down
2 changes: 1 addition & 1 deletion preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const config: HyperImportConfig = (await import(`${cwd}/bunfig.toml`)).default.h
debugLog(config.debug, 3, "registering loaders...");

for (const loader of config.loaders ?? []) {
await import(`./src/loaders/${loader}.ts`).then(async l => {
await import(`./src/loaders/${loader}`).then(async l => {
const plugin = await new l.default(cwd).toPlugin();
Bun.plugin(plugin);
debugLog(config.debug, 2, plugin.name, "has been registered.");
Expand Down
9 changes: 7 additions & 2 deletions src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ export default class {
Bun.write(`${this.cwd}/@types/${filename}/lastModified`, lastModified(this.config.importPath));
const configWriter = Bun.file(`${this.cwd}/@types/${filename}/config.ts`).writer();
configWriter.write(`import { LoaderConfig, T } from "hyperimport";\nexport default {\n\tbuildCommand: ${JSON.stringify(this.config.buildCommand)},\n\toutDir: "${this.config.outDir}",\n\tsymbols: {`);
for (const symbol of nm(this.config.libPath)) {
configWriter.write(`\n\t\t${symbol}: {\n\t\t\targs: [],\n\t\t\treturns: T.void\n\t\t},`);
const symbols = nm(this.config.libPath);
const types = this._config.parseTypes?.(this.config.importPath, symbols);
for (const symbol of symbols) {
const type = types?.[symbol] ?? { args: [], returns: "T.void" };
const args = type.args.join(", ");
configWriter.write(`\n\t\t${symbol}: {\n\t\t\targs: [${args}],\n\t\t\treturns: ${type.returns}\n\t\t},`);
}
configWriter.write(`\n\t}\n} satisfies LoaderConfig.Main;`);
configWriter.end();
Expand Down Expand Up @@ -77,6 +81,7 @@ export default class {
const lmfile = `${this.cwd}/@types/${basename(this.config.importPath)}/lastModified`;
if (lm !== await Bun.file(lmfile).text()) {
await this.build();
await this.initConfigTypes();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should maybe be in a separate PR, it just regenerates the config types if the source has changed

Bun.write(lmfile, lm);
}
}
Expand Down
21 changes: 0 additions & 21 deletions src/loaders/rs.ts

This file was deleted.

37 changes: 37 additions & 0 deletions src/loaders/rs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { basename, join } from "path";
import Loader from "../../loader";

export default class extends Loader {
constructor() {
super("Rust Loader",
{
extension: "rs",
buildCommand: (importPath, outDir) => [
"rustc",
"--crate-type",
"cdylib",
importPath,
"--out-dir",
outDir
],
outDir: importPath => `build/${basename(importPath)}`,
parseTypes: (importPath, targets) => {
// Use Node to run tree-sitter due to Bun issue:
// https://github.com/oven-sh/bun/issues/4188
const types = Bun.spawnSync([
"node",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of node, tree-sitter provides wasm bindings which can be used https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/README.md

join(import.meta.dir, "parse.js"),
importPath,
...targets
]).stdout.toString();
try {
const result = JSON.parse(types);
return result;
} catch(e) {
return undefined;
}
}
}
);
}
}
70 changes: 70 additions & 0 deletions src/loaders/rs/parse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const fs = require("fs");
const Parser = require("tree-sitter");
const Rust = require("tree-sitter-rust");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conditional import + try to load for all the loaders specified in bunfig.toml


const path = process.argv[2];
const targets = process.argv.slice(3);

const parser = new Parser();
parser.setLanguage(Rust);

const source = fs.readFileSync(path, "utf8");
const tree = parser.parse(source);

const typeMap = {
"()": "T.void",
"bool": "T.bool",
"u8": "T.u8",
"u16": "T.u16",
"u32": "T.u32",
"u64": "T.u64",
"i8": "T.i8",
"i16": "T.i16",
"i32": "T.i32",
"i64": "T.i64",
"f32": "T.f32",
"f64": "T.f64",
"usize": "T.ptr",
"isize": "T.ptr",
"char": "T.u32"
};

function mapType(type) {
if(typeMap[type]) {
return typeMap[type];
}
return type;
}

const types = tree.rootNode.children.reduce((acc, node) => {
if(node.type === "function_item") {
const fnNameNode = node.children.find(child => child.type === "identifier");
if(fnNameNode && targets.includes(fnNameNode.text)) {
const parameters = node.children.find(child => child.type === "parameters");
const parameterTypes = parameters.children
.filter((child) => child.type === "parameter")
.map((child) => {
const primitiveType = child.children.find((child) => child.type === "primitive_type");
if(primitiveType) {
return mapType(primitiveType.text);
}
const pointerType = child.children.find((child) => child.type === "pointer_type");
if(pointerType) {
return "T.ptr";
}
const functionType = child.children.find((child) => child.type === "function_type");
if(functionType) {
return "T.function";
}
});
const returnType = node.children.find(child => child.type === "primitive_type" || child.type === "unit_type").text;
acc[fnNameNode.text] = {
args: parameterTypes,
returns: mapType(returnType)
};
}
}
return acc;
}, {});

process.stdout.write(JSON.stringify(types));
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export namespace LoaderConfig {
extension: string,
buildCommand?: (importPath: string, outDir: string) => string[],
outDir?: (importPath: string) => string,
parseTypes?: (importPath: string, targets: string[]) => Record<string, { args: string[], returns: string }> | undefined,
}

export interface Internal {
Expand Down