Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions lib/resolver/dist/__test__/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
export {};
49 changes: 49 additions & 0 deletions lib/resolver/dist/__test__/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions lib/resolver/dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/// <reference types="node" />
import * as url from "url";
export interface Args {
lib: string[];
path: string[];
}
export declare const enum SourceType {
Local = 0,
URL = 1,
Package = 2
}
export declare function resolve(fromSourcePath: string, toSourcePath: string, args: Args): Address[];
declare type FileReader = (name: string, baseDir?: string) => string | null;
/** Base class for addresses. */
export declare abstract class Address {
file: string;
constructor(file: string);
abstract resolveSync(readFile: FileReader): string | null;
}
/** Address to a file in the module or in a local library */
export declare class Module extends Address {
root: string;
constructor(file: string);
resolveSync(readFile: FileReader): string | null;
}
export declare class Package extends Address {
protected packageFolder: string;
constructor(file: string, packageFolder?: string);
resolveSync(readFile: FileReader): string | null;
}
export declare class URL extends Address {
url: url.Url;
constructor(file: string);
resolveSync(readFile: FileReader): string | null;
}
export declare function resloveLibFolders(toSourcePath: string, args: Args): Address[];
export declare function resolveRelative(from: string, to: string): Address[];
export {};
220 changes: 220 additions & 0 deletions lib/resolver/dist/index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/resolver/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "./dist";
1 change: 1 addition & 0 deletions lib/resolver/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("./dist");
7 changes: 7 additions & 0 deletions lib/resolver/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"scripts": {
"test": "ts-node ./src/__tests__/index.ts",
"build:snapshot": "npm run test -- --create",
"build": "tsc"
}
}
1 change: 1 addition & 0 deletions lib/resolver/snapshots/local-relative.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"local-relative","tests":[{"input":["/","./hello"],"output":[{"file":"hello.ts","root":"/"},{"file":"index.ts","root":"/hello"}],"args":{}}]}
47 changes: 47 additions & 0 deletions lib/resolver/src/__test__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env node
import { resolve, Module, Package, URL, resolveRelative, Address, Args } from "..";
import { deepEqual } from "assert";
import * as fs from "fs";

interface Test {
input: string[];
output: Address[];
args: Args;
}

const snapshots = [
{
name: "local-relative",
tests: [
{
input: ["/", "./hello"], // add from and to sources here,
output: resolveRelative("/", "hello"),
args: {}
}
]
}
];

var failed = false;
for (const snapshot of snapshots) {
const creating = process.argv.includes("--create");
const path = __dirname +"/../../snapshots/" + snapshot.name + ".json";
if (creating) {
fs.writeFileSync(path, JSON.stringify(snapshot));
continue;
}
let tests: Test[] = require(path).tests;
var test = tests[0]
var result: Address[] = [];
try {
for (let i = 0; i < snapshot.tests.length; i++) {
test = tests[i];
result = resolve(test.input[0], test.input[1], test.args);
deepEqual(result, test.output, "Don't match.");
}
} catch (e) {
console.error(`Snapshot: ${snapshot.name} failed.
expected: ${JSON.stringify(test.output)}
actual: ${JSON.stringify(result)}`);
}
}
239 changes: 239 additions & 0 deletions lib/resolver/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import * as path from "path";
import * as url from "url";
import * as os from "os";
const platform = os.platform();

const DEFAULT_ASC_FOLDER = "assembly";
const DEFAULT_PACKAGE_PATHS = ["node_modules"];

export interface Args {
lib: string[];
path: string[];
}

export const enum SourceType {
Local = 0,
URL = 1,
Package = 2,
}

function isURL(str: string): boolean {
return /^(?:https?:\/\/|ipfs:\/\/|file:\/\/|dweb:\/ipfs\/)/i.test(str);
}

export function resolve(fromSourcePath: string, toSourcePath: string, args: Args) {
return isURL(toSourcePath)
? resolveFromUrl(fromSourcePath, toSourcePath, args)
: resolveFromLocal(fromSourcePath, toSourcePath, args);
}

type FileReader = (name: string, baseDir?: string) => string | null;

/** Base class for addresses. */
export abstract class Address {
constructor(public file: string){}
abstract resolveSync(readFile: FileReader): string | null;

}
/** Address to a file in the module or in a local library */
export class Module extends Address {
root: string;
constructor(file: string){
super(path.basename(file));
this.root = path.dirname(file);
}

resolveSync(readFile:FileReader): string | null {
return readFile(this.file, this.root);
}

}

export class Package extends Address {
constructor(file: string, protected packageFolder = ""){
super(file);
}

resolveSync(readFile: FileReader): string | null {
// resolve it as a package
const fileRelativePath = this.file;
const packageJsonPath = path.join(this.packageFolder, "package.json");
const pkg = readFile(packageJsonPath);
let ascFolder = DEFAULT_ASC_FOLDER;
if (pkg) {
try {
const jsonContents = JSON.parse(pkg);
if (typeof jsonContents.ascMain === "string") {
ascFolder = path.dirname(jsonContents.ascMain);
}
} catch(ex) {

}
}
const absoluteModulePath = path.join(this.packageFolder, ascFolder, fileRelativePath);
return readFile(path.basename(absoluteModulePath), path.dirname(absoluteModulePath));
}
}

export class URL extends Address {
url: url.Url;

constructor(file: string){
super(file);
this.url = url.parse(file);
}

resolveSync(readFile: FileReader): string | null {
return readFile(url.fileURLToPath(this.url.toString()))
}
}

// resolveAsync(readFile, fetch, callback): void {
// if (this.type === SourceType.Package) {
// // resolve it as a package
// const moduleFolder = this.root;
// const fileRelativePath = this.file;
// let ascFolder = "assembly";
// readFile("package.json", moduleFolder, (err, pkg) => {
// if (pkg && !err) {
// try {
// const jsonContents = JSON.parse(pkg);
// if (typeof jsonContents.ascMain === "string") {
// ascFolder = path.dirname(jsonContents.ascMain);
// }
// } catch(ex) {

// }
// }
// const absoluteModulePath = path.join(moduleFolder, ascFolder, fileRelativePath);
// return readFile(path.basename(absoluteModulePath), path.dirname(absoluteModulePath), (err, file) => {
// if (err) callback(null);
// else callback(file.toString("utf8"));
// });
// });
// } else if (this.type === SourceType.URL) {
// fetch(this.url.toString(), (err, buffer) => {
// if (err) callback(null);
// else callback(buffer.toString("utf8"));
// });
// } else {
// readFile(this.file, this.root, (err, buffer) => {
// if (err) callback(null);
// else callback(buffer.toString("utf8"));
// });
// }
// }
// }



function resolveFromLocal(fromSourcePath: string, toSourcePath: string, args: Args): Address[] {
const isToSourceRelative = toSourcePath.startsWith(".");
const isFromSourcePathAbsolute = path.isAbsolute(fromSourcePath);

// If the source is relative, we can check two locations: folder/index.ts, and file.ts
if (isToSourceRelative) {
return resolveRelative(path.dirname(fromSourcePath), toSourcePath);
}

// We could be receiving a path from the compiler
toSourcePath = toSourcePath.replace(/^~lib\//, "");

// At this point, the source must be a package, url or lib entry. Check for urls first.
if (isURL(toSourcePath)) {
return [ new URL(toSourcePath) ];
}

// Create an Entry array to contain the results, starting with lib entries first
let results = resloveLibFolders(toSourcePath, args);

// Results is now an array of entries to try every lib folder,
// favoring lib.ts over lib/index.ts

// Now we can try package management locations
const toSourceEntries = toSourcePath.split("/");
const fromSourceEntries = path.dirname(fromSourcePath).split(path.sep);
const moduleFolders = DEFAULT_PACKAGE_PATHS.concat(args.path || []);
const fromSourceEntriesLength = fromSourceEntries.length;
const toSourceEntriesLength = toSourceEntries.length;

// assume the toSource could start in any of these folders
for (let k = 0; k < fromSourceEntriesLength; k++) {
const fromSourceFolder = (isFromSourcePathAbsolute && platform !== "win32" ? path.sep + path.sep : "")
+ fromSourceEntries.slice(0, fromSourceEntriesLength - k).join(path.sep) + path.sep;

// for each module folder (i.e. node_modules, bower_components, packages)
for (let j = 0; j < moduleFolders.length; j++) {
const moduleFolder = moduleFolders[j];

// Assume the package folder exists at i in the toSource
for (let i = 0; i < toSourceEntriesLength; i++) {
const index = toSourceEntriesLength - i;
const toSourceStart = toSourceEntries.slice(0, index).join(path.sep);
const toSourceEnd = toSourceEntries.slice(index).join(path.sep);

// baseDir is the package folder. Not the baseDir.
const packageFolder = path.join(fromSourceFolder, moduleFolder, toSourceStart);

// create two entries and assume file.ts first over file/index.ts
if (toSourceEnd !== "") {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason why this if exists is because the first entry assumes that the package folder is toSourceEntries.join(path.sep) in totality. The only thing it could possibly be at this point is {package}/assembly/index.ts. Perhaps we should remove this if statement and copy the second push out of the loop, starting said loop with i = 1.

results.push(
new Package(
toSourceEnd + ".ts",
packageFolder
)
);
}
results.push(
new Package(
toSourceEnd + path.sep + "index.ts",
packageFolder
)
);

}
}
}

return results;
}

// url resolution is limited to lib and relative path resolutionrequire
function resolveFromUrl(fromSourcePath: string, toSourcePath: string, args: Args): Address[] {
const isRelativePath = toSourcePath.startsWith(".");
if (isRelativePath) {
const resolved = url.resolve(fromSourcePath, toSourcePath);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know if this works completely. We should look into url joining algorithms :)

const fileResolved = resolved.endsWith("/")
? resolved.slice(0, -1)
: resolved;
const folderResolved = resolved.endsWith("/")
? resolved
: resolved + "/";
return [
new URL(fileResolved + ".ts"),
new URL(url.resolve(folderResolved, "./index.ts"))
];
}

// We could be receiving a path from the compiler
toSourcePath = toSourcePath.replace(/^~lib\//, "");

// Create an Entry array to contain the results, starting with lib entries first
return resloveLibFolders(toSourcePath, args);

}

export function resloveLibFolders(toSourcePath: string, args: Args): Address[] {
const results: Address[] = [];
const libFolders = args.lib || [];
return libFolders.reduce((result, libFolder) => result.concat(resolveRelative(libFolder, toSourcePath)), results);
}


export function resolveRelative(from: string, to: string): Address[] {
const filename = path.join(from, to)
return [
new Module(filename + ".ts"),
new Module(path.join(filename, "index.ts"))
];
}
Loading