Skip to content

Commit 0b7c383

Browse files
committed
feat(package.json) WIP for #287
Resolution algorithm for picking only a single `.d.ts` by name
1 parent 45f6646 commit 0b7c383

File tree

2 files changed

+142
-15
lines changed

2 files changed

+142
-15
lines changed

dist/main/tsconfig/tsconfig.js

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ function getDefaultProject(srcFile) {
148148
compileOnSave: true
149149
};
150150
project.files = increaseProjectForReferenceAndImports(project.files);
151-
project.files = project.files.concat(getDefinitionsForNodeModules(dir));
151+
project.files = project.files.concat(getDefinitionsForNodeModules(dir, project.files));
152152
project.files = uniq(project.files.map(consistentPath));
153153
return {
154154
projectFileDirectory: dir,
@@ -239,7 +239,7 @@ function getProjectSync(pathOrSrcFile) {
239239
}
240240
project.compilerOptions = rawToTsCompilerOptions(projectSpec.compilerOptions, projectFileDirectory);
241241
project.files = increaseProjectForReferenceAndImports(project.files);
242-
project.files = project.files.concat(getDefinitionsForNodeModules(dir));
242+
project.files = project.files.concat(getDefinitionsForNodeModules(dir, project.files));
243243
project.files = uniq(project.files.map(consistentPath));
244244
projectFileDirectory = removeTrailingSlash(consistentPath(projectFileDirectory));
245245
return {
@@ -325,8 +325,40 @@ function increaseProjectForReferenceAndImports(files) {
325325
}
326326
return files;
327327
}
328-
function getDefinitionsForNodeModules(projectDir) {
329-
var definitions = [];
328+
function getDefinitionsForNodeModules(projectDir, files) {
329+
function versionStringToNumber(version) {
330+
var _a = version.split('.'), maj = _a[0], min = _a[1], patch = _a[2];
331+
return parseInt(maj) * 1000000 + parseInt(min);
332+
}
333+
var typings = {};
334+
var ourTypings = files
335+
.filter(function (f) { return path.basename(path.dirname(f)) == 'typings' && endsWith(f, '.d.ts')
336+
|| path.basename(path.dirname(path.dirname(f))) == 'typings' && endsWith(f, '.d.ts'); });
337+
ourTypings.forEach(function (f) { return typings[path.basename(f)] = { filePath: f, version: Infinity }; });
338+
var existing = createMap(files.map(consistentPath));
339+
function addAllReferencedFilesWithMaxVersion(file) {
340+
var dir = path.dirname(file);
341+
try {
342+
var content = fs.readFileSync(file).toString();
343+
}
344+
catch (ex) {
345+
return;
346+
}
347+
var preProcessedFileInfo = ts.preProcessFile(content, true);
348+
var files = preProcessedFileInfo.referencedFiles.map(function (fileReference) {
349+
var file = path.resolve(dir, fileReference.fileName);
350+
if (fs.existsSync(file)) {
351+
return file;
352+
}
353+
if (fs.existsSync(file + '.d.ts')) {
354+
return file + '.d.ts';
355+
}
356+
});
357+
files = files
358+
.filter(function (f) { return !typings[path.basename(f)] || typings[path.basename(f)].version > Infinity; });
359+
files.forEach(function (f) { return typings[path.basename(f)] = { filePath: f, version: Infinity }; });
360+
files.forEach(function (f) { return addAllReferencedFilesWithMaxVersion(f); });
361+
}
330362
try {
331363
var node_modules = travelUpTheDirectoryTreeTillYouFind(projectDir, 'node_modules', true);
332364
var moduleDirs = getDirs(node_modules);
@@ -335,16 +367,23 @@ function getDefinitionsForNodeModules(projectDir) {
335367
var package_json = JSON.parse(fs.readFileSync(moduleDir + "/package.json").toString());
336368
if (package_json.typescript) {
337369
if (package_json.typescript.definition) {
338-
definitions.push(path.resolve(moduleDir, './', package_json.typescript.definition));
370+
var file = path.resolve(moduleDir, './', package_json.typescript.definition);
371+
typings[path.basename(file)] = {
372+
filePath: file,
373+
version: Infinity
374+
};
375+
addAllReferencedFilesWithMaxVersion(file);
339376
}
340377
}
341378
}
342379
}
343380
catch (ex) {
344381
}
345-
return definitions;
382+
return Object.keys(typings)
383+
.map(function (typing) { return typings[typing].filePath; })
384+
.map(function (x) { return consistentPath(x); })
385+
.filter(function (x) { return !existing[x]; });
346386
}
347-
exports.getDefinitionsForNodeModules = getDefinitionsForNodeModules;
348387
function prettyJSON(object) {
349388
var cache = [];
350389
var value = JSON.stringify(object, function (key, value) {
@@ -447,3 +486,10 @@ function getDirs(rootDir) {
447486
}
448487
return dirs;
449488
}
489+
function createMap(arr) {
490+
return arr.reduce(function (result, key) {
491+
result[key] = true;
492+
return result;
493+
}, {});
494+
}
495+
exports.createMap = createMap;

lib/main/tsconfig/tsconfig.ts

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export function getDefaultProject(srcFile: string): TypeScriptProjectFileDetails
265265
};
266266

267267
project.files = increaseProjectForReferenceAndImports(project.files);
268-
project.files = project.files.concat(getDefinitionsForNodeModules(dir));
268+
project.files = project.files.concat(getDefinitionsForNodeModules(dir, project.files));
269269
project.files = uniq(project.files.map(consistentPath));
270270

271271
return {
@@ -393,7 +393,9 @@ export function getProjectSync(pathOrSrcFile: string): TypeScriptProjectFileDeta
393393

394394
// Expand files to include references
395395
project.files = increaseProjectForReferenceAndImports(project.files);
396-
project.files = project.files.concat(getDefinitionsForNodeModules(dir));
396+
397+
// Expand files to include node_modules / package.json / typescript.definition
398+
project.files = project.files.concat(getDefinitionsForNodeModules(dir, project.files));
397399

398400
// Normalize to "/" for all files
399401
// And take the uniq values
@@ -511,15 +513,73 @@ function increaseProjectForReferenceAndImports(files: string[]): string[] {
511513
return files;
512514
}
513515

516+
/** There can be only one typing by name */
517+
interface Typings {
518+
[name: string]: {
519+
filePath: string;
520+
/** Right now its just INF as we don't do version checks. First one wins! */
521+
version: number; // (Simple : maj * 1000000 + min). Don't care about patch
522+
};
523+
}
524+
514525
/**
515-
* TODO: exclude `tsd` definitions that we also have
526+
* Spec
527+
* We will expand on files making sure that we don't have a `typing` with the same name
528+
* Also if two node_modules reference a similar sub project (and also recursively) then the one with latest `version` field wins
516529
*/
517-
export function getDefinitionsForNodeModules(projectDir: string): string[] {
530+
function getDefinitionsForNodeModules(projectDir: string, files: string[]): string[] {
531+
532+
/** TODO use later when we care about versions */
533+
function versionStringToNumber(version: string): number {
534+
var [maj, min, patch] = version.split('.');
535+
return parseInt(maj) * 1000000 + parseInt(min);
536+
}
537+
538+
var typings: Typings = {};
539+
540+
// Find our `typings` (anything in a typings folder with extension `.d.ts` is considered a typing)
541+
// These are INF powerful
542+
var ourTypings = files
543+
.filter(f=> path.basename(path.dirname(f)) == 'typings' && endsWith(f, '.d.ts')
544+
|| path.basename(path.dirname(path.dirname(f))) == 'typings' && endsWith(f, '.d.ts'));
545+
ourTypings.forEach(f=> typings[path.basename(f)] = { filePath: f, version: Infinity });
546+
var existing = createMap(files.map(consistentPath));
547+
548+
function addAllReferencedFilesWithMaxVersion(file: string) {
549+
var dir = path.dirname(file);
550+
try {
551+
var content = fs.readFileSync(file).toString();
552+
}
553+
catch (ex) {
554+
// if we cannot read a file for whatever reason just quit
555+
return;
556+
}
557+
var preProcessedFileInfo = ts.preProcessFile(content, true);
558+
var files = preProcessedFileInfo.referencedFiles.map(fileReference => {
559+
// We assume reference paths are always relative
560+
var file = path.resolve(dir, fileReference.fileName);
561+
// Try by itself, .d.ts
562+
if (fs.existsSync(file)) {
563+
return file;
564+
}
565+
if (fs.existsSync(file + '.d.ts')) {
566+
return file + '.d.ts';
567+
}
568+
});
569+
570+
// Only ones we don't have by name yet
571+
// TODO: replace INF with an actual version
572+
files = files
573+
.filter(f => !typings[path.basename(f)] || typings[path.basename(f)].version > Infinity);
574+
// Add these
575+
files.forEach(f => typings[path.basename(f)] = { filePath: f, version: Infinity });
576+
// Keep expanding
577+
files.forEach(f=> addAllReferencedFilesWithMaxVersion(f));
578+
}
579+
518580
// Keep going up till we find node_modules
519581
// at that point read the `package.json` for each file in node_modules
520582
// And then if that package.json has `typescript.definition` we import that file
521-
var definitions = [];
522-
523583
try {
524584
var node_modules = travelUpTheDirectoryTreeTillYouFind(projectDir, 'node_modules', true);
525585

@@ -529,16 +589,27 @@ export function getDefinitionsForNodeModules(projectDir: string): string[] {
529589
var package_json = JSON.parse(fs.readFileSync(`${moduleDir}/package.json`).toString());
530590
if (package_json.typescript) {
531591
if (package_json.typescript.definition) {
532-
definitions.push(path.resolve(moduleDir, './', package_json.typescript.definition));
592+
let file = path.resolve(moduleDir, './', package_json.typescript.definition);
593+
594+
typings[path.basename(file)] = {
595+
filePath: file,
596+
version: Infinity
597+
};
598+
// Also add any files that this `.d.ts` references as long as they don't conflict with what we have
599+
addAllReferencedFilesWithMaxVersion(file);
533600
}
534601
}
535602
}
603+
536604
}
537605
catch (ex) {
538606
// this is best effort only at the moment
539607
}
540608

541-
return definitions;
609+
return Object.keys(typings)
610+
.map(typing => typings[typing].filePath)
611+
.map(x=> consistentPath(x))
612+
.filter(x=> !existing[x]);
542613
}
543614

544615
export function prettyJSON(object: any): string {
@@ -660,3 +731,13 @@ function getDirs(rootDir: string): string[] {
660731
}
661732
return dirs
662733
}
734+
735+
/**
736+
* Create a quick lookup map from list
737+
*/
738+
export function createMap(arr: string[]): { [string: string]: boolean } {
739+
return arr.reduce((result: { [string: string]: boolean }, key: string) => {
740+
result[key] = true;
741+
return result;
742+
}, <{ [string: string]: boolean }>{});
743+
}

0 commit comments

Comments
 (0)