Skip to content

Commit 4784cf4

Browse files
willemnealdcodeIO
authored andcommitted
Add node module resolution by default and use --path for custom package locations (#594)
1 parent 678593d commit 4784cf4

File tree

47 files changed

+361
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+361
-11
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ docs/
33
node_modules/
44
out/
55
raw/
6-
.history
6+
.history

cli/asc.js

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const colorsUtil = require("./util/colors");
2121
const optionsUtil = require("./util/options");
2222
const mkdirp = require("./util/mkdirp");
2323
const EOL = process.platform === "win32" ? "\r\n" : "\n";
24+
const SEP = process.platform === "win32" ? "\\" : "/";
2425

2526
// global.Binaryen = require("../lib/binaryen");
2627

@@ -229,6 +230,10 @@ exports.main = function main(argv, options, callback) {
229230
// Begin parsing
230231
var parser = null;
231232

233+
// Maps package names to parent directory
234+
var packages = new Map();
235+
var importPathMap = new Map();
236+
232237
// Include library files
233238
Object.keys(exports.libraryFiles).forEach(libPath => {
234239
if (libPath.indexOf("/") >= 0) return; // in sub-directory: imported on demand
@@ -254,13 +259,14 @@ exports.main = function main(argv, options, callback) {
254259
libFiles = [ path.basename(libDir) ];
255260
libDir = path.dirname(libDir);
256261
} else {
257-
libFiles = listFiles(libDir);
262+
libFiles = listFiles(libDir) || [];
258263
}
259264
for (let j = 0, l = libFiles.length; j < l; ++j) {
260265
let libPath = libFiles[j];
261266
let libText = readFile(libPath, libDir);
262267
if (libText === null) return callback(Error("Library file '" + libPath + "' not found."));
263268
stats.parseCount++;
269+
exports.libraryFiles[libPath.replace(/\.ts$/, "")] = libText;
264270
stats.parseTime += measure(() => {
265271
parser = assemblyscript.parseFile(
266272
libText,
@@ -272,13 +278,32 @@ exports.main = function main(argv, options, callback) {
272278
}
273279
}
274280
}
281+
args.path = args.path || [];
282+
// Find all valid node_module paths starting at baseDir
283+
function nodePaths(basePath, _path) {
284+
return basePath.split(SEP)
285+
.map((_, i, arr) => {
286+
let dir = arr.slice(0, i + 1).join(SEP) || SEP;
287+
let dirFrom = path.relative(baseDir, dir);
288+
return path.join(dirFrom, _path);
289+
})
290+
.filter(dir => listFiles(dir, baseDir))
291+
.reverse();
292+
}
293+
function getPaths(basePath) {
294+
let paths = args.path.map(p => nodePaths(basePath, p));
295+
return nodePaths(basePath, "node_modules").concat(...paths)
296+
}
275297

276298
// Parses the backlog of imported files after including entry files
277299
function parseBacklog() {
278-
var sourcePath, sourceText;
300+
var sourcePath, sourceText, sysPath;
301+
// dependee is the path of the file that depends on sourcePath
279302
while ((sourcePath = parser.nextFile()) != null) {
303+
dependee = importPathMap.get(assemblyscript.getDependee(parser, sourcePath)) || baseDir;
280304
sourceText = null;
281-
305+
sysPath = null;
306+
282307
// Load library file if explicitly requested
283308
if (sourcePath.startsWith(exports.libraryPrefix)) {
284309
const plainName = sourcePath.substring(exports.libraryPrefix.length);
@@ -294,11 +319,13 @@ exports.main = function main(argv, options, callback) {
294319
sourceText = readFile(plainName + ".ts", customLibDirs[i]);
295320
if (sourceText !== null) {
296321
sourcePath = exports.libraryPrefix + plainName + ".ts";
322+
sysPath = path.join(customLibDirs[i], plainName + ".ts");
297323
break;
298324
} else {
299325
sourceText = readFile(indexName + ".ts", customLibDirs[i]);
300326
if (sourceText !== null) {
301327
sourcePath = exports.libraryPrefix + indexName + ".ts";
328+
sysPath = path.join(customLibDirs[i], indexName + ".ts");
302329
break;
303330
}
304331
}
@@ -329,11 +356,13 @@ exports.main = function main(argv, options, callback) {
329356
sourceText = readFile(plainName + ".ts", dir);
330357
if (sourceText !== null) {
331358
sourcePath = exports.libraryPrefix + plainName + ".ts";
359+
sysPath = path.join(dir, plainName + ".ts");
332360
break;
333361
} else {
334362
sourceText = readFile(indexName + ".ts", dir);
335363
if (sourceText !== null) {
336364
sourcePath = exports.libraryPrefix + indexName + ".ts";
365+
sysPath = path.join(dir, indexName + ".ts");
337366
break;
338367
}
339368
}
@@ -342,9 +371,77 @@ exports.main = function main(argv, options, callback) {
342371
}
343372
}
344373
}
374+
/*
375+
In this case the library wasn't found so we check paths
376+
*/
377+
if (sourceText == null) {
378+
if (args.traceResolution) {
379+
stderr.write("Looking for " + sourcePath + " imported by " + dependee + " " + EOL);
380+
}
381+
paths = getPaths(path.join(baseDir, dependee));
382+
let _package = sourcePath.replace(/\~lib\/([^\/]*).*/, "$1");
383+
for (let _path of paths) {
384+
let ascMain = (() => {
385+
if (packages.has(_package)) {
386+
return packages.get(_package);
387+
}
388+
let p = path.join(_path, _package, "package.json");
389+
let res = readFile(p, baseDir);
390+
if (res) {
391+
let package_json;
392+
try {
393+
package_json = JSON.parse(res);
394+
} catch (e) {
395+
return callback(Error("Parsing " + p + " failed"));
396+
}
397+
let mainFile = package_json.ascMain;
398+
if (mainFile && (typeof mainFile === 'string')) {
399+
let newPackage = mainFile.replace(/(.*)\/index\.ts/, '$1');
400+
packages.set(_package, newPackage);
401+
return newPackage;
402+
}
403+
}
404+
return "assembly";
405+
})()
406+
let realPath = (_p) => {
407+
if (_p.startsWith(exports.libraryPrefix)){
408+
_p = _p.substring(exports.libraryPrefix.length);
409+
}
410+
let first = _p.substring(0, _p.indexOf("/"));
411+
let second = _p.substring(_p.indexOf("/") + 1);
412+
return path.join(_path, first, ascMain, second);
413+
}
414+
if (args.traceResolution) {
415+
stderr.write(" in " + realPath(sourcePath));
416+
}
417+
const plainName = sourcePath;
418+
const indexName = sourcePath + "/index";
419+
sourceText = readFile(realPath(plainName) + ".ts", baseDir);
420+
if (sourceText !== null) {
421+
sourcePath = plainName + ".ts";
422+
} else {
423+
sourceText = readFile(realPath(indexName) + ".ts", baseDir);
424+
if (sourceText !== null) {
425+
sourcePath = indexName + ".ts";
426+
}
427+
}
428+
if (sourceText !== null) {
429+
if (args.traceResolution) {
430+
stderr.write("\nFound at " + realPath(sourcePath) + EOL);
431+
}
432+
let newPath = path.join(_path, _package);
433+
sysPath = newPath;
434+
break;
435+
}
436+
if (args.traceResolution) {
437+
stderr.write(EOL);
438+
}
439+
}
440+
}
345441
if (sourceText == null) {
346442
return callback(Error("Import file '" + sourcePath + ".ts' not found."));
347443
}
444+
importPathMap.set(sourcePath.replace(/\.ts$/, ""), sysPath);
348445
stats.parseCount++;
349446
stats.parseTime += measure(() => {
350447
assemblyscript.parseFile(sourceText, sourcePath, false, parser);
@@ -418,6 +515,12 @@ exports.main = function main(argv, options, callback) {
418515
// Finish parsing
419516
const program = assemblyscript.finishParsing(parser);
420517

518+
// Print files and exit if listFiles
519+
if (args.listFiles) {
520+
stderr.write(program.sources.map(s => s.normalizedPath).sort().join(EOL) + EOL);
521+
return callback(null);
522+
}
523+
421524
// Set up optimization levels
422525
var optimizeLevel = 0;
423526
var shrinkLevel = 0;
@@ -718,11 +821,12 @@ exports.main = function main(argv, options, callback) {
718821
return callback(null);
719822

720823
function readFileNode(filename, baseDir) {
824+
let name = path.resolve(baseDir, filename);
721825
try {
722826
let text;
723827
stats.readCount++;
724828
stats.readTime += measure(() => {
725-
text = fs.readFileSync(path.join(baseDir, filename), { encoding: "utf8" });
829+
text = fs.readFileSync(name, { encoding: "utf8" });
726830
});
727831
return text;
728832
} catch (e) {
@@ -755,7 +859,7 @@ exports.main = function main(argv, options, callback) {
755859
});
756860
return files;
757861
} catch (e) {
758-
return [];
862+
return null;
759863
}
760864
}
761865

cli/asc.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,5 +216,22 @@
216216
"-O0z": { "value": { "optimizeLevel": 0, "shrinkLevel": 2 } },
217217
"-O1z": { "value": { "optimizeLevel": 1, "shrinkLevel": 2 } },
218218
"-O2z": { "value": { "optimizeLevel": 2, "shrinkLevel": 2 } },
219-
"-O3z": { "value": { "optimizeLevel": 3, "shrinkLevel": 2 } }
219+
"-O3z": { "value": { "optimizeLevel": 3, "shrinkLevel": 2 } },
220+
"path": {
221+
"description": [
222+
"Comma separated paths to look for dependencies.",
223+
"Looks for folders with package.json that includes a 'ascMain' field",
224+
"or defaults to having an '/assembly' folder."],
225+
"type": "S"
226+
},
227+
"traceResolution": {
228+
"description": "Enable tracing of package resolution.",
229+
"type": "b",
230+
"default": false
231+
},
232+
"listFiles": {
233+
"description": "List files to be compiled and exit.",
234+
"type": "b",
235+
"default": false
236+
}
220237
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@
4747
"check": "npm run check:config && npm run check:compiler",
4848
"check:config": "tsc --noEmit -p src --diagnostics --listFiles",
4949
"check:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as",
50-
"test": "npm run test:parser && npm run test:compiler",
50+
"test": "npm run test:parser && npm run test:compiler && npm run test:packages",
5151
"test:parser": "node tests/parser",
5252
"test:compiler": "node tests/compiler",
53+
"test:packages": "cd tests/packages && npm run test",
5354
"make": "npm run clean && npm test && npm run build && npm test",
5455
"all": "npm run check && npm run make",
5556
"docs": "typedoc --tsconfig tsconfig-docs.json --mode modules --name \"AssemblyScript Compiler API\" --out ./docs/api --ignoreCompilerErrors --excludeNotExported --excludePrivate --excludeExternals --exclude **/std/** --includeDeclarations --readme src/README.md",

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ export function nextFile(parser: Parser): string | null {
2626
return parser.nextFile();
2727
}
2828

29+
/** Obtains the path of the dependee of a given imported file. */
30+
export function getDependee(parser: Parser, file: string): string | null {
31+
return parser.getDependee(file);
32+
}
33+
2934
/** Obtains the next diagnostic message. Returns `null` once complete. */
3035
export function nextDiagnostic(parser: Parser): DiagnosticMessage | null {
3136
var program = parser.program;

src/parser.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ export class Parser extends DiagnosticEmitter {
102102
donelog: Set<string> = new Set();
103103
/** Optional handler to intercept comments while tokenizing. */
104104
onComment: CommentHandler | null = null;
105+
/** Current file being parsed. */
106+
currentSource: Source;
107+
/** Dependency map **/
108+
dependees: Map<string, Source> = new Map();
105109

106110
/** Constructs a new parser. */
107111
constructor() {
@@ -117,7 +121,6 @@ export class Parser extends DiagnosticEmitter {
117121
): void {
118122
var normalizedPath = normalizePath(path);
119123
var internalPath = mangleInternalPath(normalizedPath);
120-
121124
// check if already processed
122125
if (this.donelog.has(internalPath)) return;
123126
this.donelog.add(internalPath); // do not parse again
@@ -135,6 +138,7 @@ export class Parser extends DiagnosticEmitter {
135138
);
136139
var program = this.program;
137140
program.sources.push(source);
141+
this.currentSource = source;
138142

139143
// tokenize and parse
140144
var tn = new Tokenizer(source, program.diagnostics);
@@ -373,12 +377,22 @@ export class Parser extends DiagnosticEmitter {
373377
return backlog.length ? backlog.shift() : null;
374378
}
375379

380+
/** Obtains the dependee for a given import */
381+
getDependee(dependent: string): string | null {
382+
var source = this.dependees.get(dependent);
383+
if (source) {
384+
return source.internalPath;
385+
}
386+
return null;
387+
}
388+
376389
/** Finishes parsing and returns the program. */
377390
finish(): Program {
378391
if (this.backlog.length) throw new Error("backlog is not empty");
379392
this.backlog = [];
380393
this.seenlog.clear();
381394
this.donelog.clear();
395+
this.dependees.clear();
382396
return this.program;
383397
}
384398

@@ -2291,6 +2305,7 @@ export class Parser extends DiagnosticEmitter {
22912305
let ret = Node.createExportStatement(members, path, isDeclare, tn.range(startPos, tn.pos));
22922306
let internalPath = ret.internalPath;
22932307
if (internalPath !== null && !this.seenlog.has(internalPath)) {
2308+
this.dependees.set(internalPath, this.currentSource);
22942309
this.backlog.push(internalPath);
22952310
this.seenlog.add(internalPath);
22962311
}
@@ -2306,8 +2321,8 @@ export class Parser extends DiagnosticEmitter {
23062321
if (!source.exportPaths) source.exportPaths = new Set();
23072322
source.exportPaths.add(internalPath);
23082323
if (!this.seenlog.has(internalPath)) {
2324+
this.dependees.set(internalPath, this.currentSource);
23092325
this.backlog.push(internalPath);
2310-
this.seenlog.add(internalPath);
23112326
}
23122327
tn.skip(Token.SEMICOLON);
23132328
return ret;
@@ -2472,8 +2487,8 @@ export class Parser extends DiagnosticEmitter {
24722487
}
24732488
let internalPath = ret.internalPath;
24742489
if (!this.seenlog.has(internalPath)) {
2490+
this.dependees.set(internalPath, this.currentSource);
24752491
this.backlog.push(internalPath);
2476-
this.seenlog.add(internalPath);
24772492
}
24782493
tn.skip(Token.SEMICOLON);
24792494
return ret;

tests/packages/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
packages/*/package-lock.json
2+
package-lock.json
3+
!node_modules/
4+
!packages/**/*/node_modules/

tests/packages/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"scripts": {
3+
"test": "npm run a && npm run b && npm run c && npm run d && npm run e && npm run f && npm run g && npm run as",
4+
"a": "cd ./packages/a && node ../../../../bin/asc assembly/index.ts --noEmit --runtime stub --validate",
5+
"as": "cd ./packages/as && node ../../../../bin/asc as/index.ts --noEmit --runtime stub --validate",
6+
"b": "cd ./packages/b && node ../../../../bin/asc assembly/index.ts --noEmit --runtime stub && node ../../../../bin/asc assembly/index.ts --noEmit --runtime stub --listFiles",
7+
"c": "cd ./packages/c && node ../../../../bin/asc assembly/index.ts --noEmit --runtime stub --validate",
8+
"d": "cd ./packages/d && node ../../../../bin/asc assembly/index.ts --path packages --noEmit --runtime stub --validate --traceResolution",
9+
"e": "cd ./packages/d/packages/e && node ../../../../../../bin/asc assembly/index.ts --noEmit --runtime stub --validate",
10+
"f": "cd ./packages/d/packages/e/packages/f && node ../../../../../../../../bin/asc assembly/index.ts --noEmit --runtime stub --validate",
11+
"g": "cd ./packages/g && node test.js"
12+
},
13+
"author": "Willem Wyndham",
14+
"license": "Apache-2.0"
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function A(): string {
2+
return "A";
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./a";

tests/packages/packages/as/as/as.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function AS(str: string): string {
2+
return str + "S";
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./as";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"ascMain": "as/index.ts"
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { A } from "a";
2+
3+
export function B(): string {
4+
return "B";
5+
}
6+
7+
export function AB(): string {
8+
return A() + B();
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./b";

tests/packages/packages/b/node_modules/a/assembly/a.ts

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)