-
Notifications
You must be signed in to change notification settings - Fork 5
/
types-entry.js
138 lines (116 loc) · 3.97 KB
/
types-entry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
'use strict';
const path = require('path');
const fs = require('fs');
const log = require('./log.js');
module.exports = function findTypesEntry(packagePath, isopack, remote) {
const result = analyzeResources(isopack, remote);
const mainModules = result.mainModules;
const definitionFiles = result.definitionFiles;
const packageTypesConfigs = result.packageTypesConfigs;
log('examining', packagePath);
if (!remote && mainModules.size === 1) {
let mainModule = getFirstValue(mainModules);
// Unlike remote packages, for local packages we are okay with using
// a ts file as the types entry. Typescript can't be configured to ignore
// problems with types in ts files, but for local packages you probably
// want to know about problems.
if (mainModule.path.endsWith('.ts')) {
log('can use main module directly - local');
return mainModule.file;
}
}
if (packageTypesConfigs.length > 0) {
let configResource = packageTypesConfigs[0];
let config = readTypesConfig(packagePath, configResource);
log('read config', config);
if (
config.typesEntry &&
fs.existsSync(config.typesEntry)
) {
log('can use types entry');
let relative = path.relative(packagePath, config.typesEntry);
if (
relative.startsWith('..') ||
relative.startsWith('/')
) {
throw new Error(`typesEntry is not inside of package. package: ${packagePath}, typesEntry: ${config.typesEntry}`)
}
// TODO: warn if published package, and file is a ts file instead of d.ts
return relative.split(path.sep).join(path.posix.sep);
} else {
log('not exists', config.typesEntry);
}
}
if (definitionFiles.size === 1) {
log('can use definition file', packagePath);
return getFirstValue(definitionFiles).file;
} else if (mainModules.size === 1 && definitionFiles.size > 0) {
let mainModule = getFirstValue(mainModules);
let name = mainModule.path.slice(0, mainModule.path.lastIndexOf('.'));
let definitionNames = [
name + '.d.ts'
];
if (name.startsWith('src/')) {
definitionNames.push(
name.replace('src/', 'lib/') + '.d.ts'
)
}
// Check if definition file for the main module
for (const resource of definitionFiles.values()) {
if (definitionNames.includes(resource.path)) {
log('found matching definitions', resource);
log('can use main module definition', packagePath);
return resource.file;
}
}
}
return null;
}
function getFirstValue(map) {
return map.values().next().value;
}
function analyzeResources(isopack, remote) {
let mainModules = new Map();
let definitionFiles = new Map();
let rootPackageTypes = null;
let generatedPackageTypes = null;
for (const unibuild of isopack.unibuilds) {
for (const resource of unibuild.resources) {
if (resource.fileOptions && resource.fileOptions.mainModule) {
mainModules.set(resource.path, resource);
}
if (resource.file.endsWith('.d.ts')) {
definitionFiles.set(resource.hash, resource);
}
if (resource.path === 'package-types.json') {
rootPackageTypes = resource;
}
if (resource.path === '__types/package-types.json') {
generatedPackageTypes = resource;
}
}
}
let packageTypesConfigs = remote ?
[generatedPackageTypes, rootPackageTypes] :
[rootPackageTypes, generatedPackageTypes];
return {
mainModules,
definitionFiles,
// Sort in order of priority
// .types/package-types.json overrides package-types.json
packageTypesConfigs: packageTypesConfigs.filter(c => c)
};
}
function readTypesConfig(packagePath, resource) {
let fullPath = path.resolve(packagePath, resource.file);
let content = fs.readFileSync(fullPath, 'utf-8');
let config = JSON.parse(content);
if (config.typesEntry) {
config.typesEntry = path.resolve(
packagePath,
path.dirname(resource.file),
config.typesEntry
)
}
return config;
}