UNPKG

3.54 kBJavaScriptView Raw
1/**
2 * @param {object} exports
3 * @param {Set<string>} keys
4 */
5function loop(exports, keys) {
6 if (typeof exports === 'string') {
7 return exports;
8 }
9
10 if (exports) {
11 let idx, tmp;
12 if (Array.isArray(exports)) {
13 for (idx=0; idx < exports.length; idx++) {
14 if (tmp = loop(exports[idx], keys)) return tmp;
15 }
16 } else {
17 for (idx in exports) {
18 if (keys.has(idx)) {
19 return loop(exports[idx], keys);
20 }
21 }
22 }
23 }
24}
25
26/**
27 * @param {string} name The package name
28 * @param {string} entry The target entry, eg "."
29 * @param {number} [condition] Unmatched condition?
30 */
31function bail(name, entry, condition) {
32 throw new Error(
33 condition
34 ? `No known conditions for "${entry}" entry in "${name}" package`
35 : `Missing "${entry}" export in "${name}" package`
36 );
37}
38
39/**
40 * @param {string} name the package name
41 * @param {string} entry the target path/import
42 */
43function toName(name, entry) {
44 return entry === name ? '.'
45 : entry[0] === '.' ? entry
46 : entry.replace(new RegExp('^' + name + '\/'), './');
47}
48
49/**
50 * @param {object} pkg package.json contents
51 * @param {string} [entry] entry name or import path
52 * @param {object} [options]
53 * @param {boolean} [options.browser]
54 * @param {boolean} [options.require]
55 * @param {string[]} [options.conditions]
56 * @param {boolean} [options.unsafe]
57 */
58export function resolve(pkg, entry='.', options={}) {
59 let { name, exports } = pkg;
60
61 if (exports) {
62 let { browser, require, unsafe, conditions=[] } = options;
63
64 let target = toName(name, entry);
65 if (target[0] !== '.') target = './' + target;
66
67 if (typeof exports === 'string') {
68 return target === '.' ? exports : bail(name, target);
69 }
70
71 let allows = new Set(['default', ...conditions]);
72 unsafe || allows.add(require ? 'require' : 'import');
73 unsafe || allows.add(browser ? 'browser' : 'node');
74
75 let key, tmp, isSingle=false;
76
77 for (key in exports) {
78 isSingle = key[0] !== '.';
79 break;
80 }
81
82 if (isSingle) {
83 return target === '.'
84 ? loop(exports, allows) || bail(name, target, 1)
85 : bail(name, target);
86 }
87
88 if (tmp = exports[target]) {
89 return loop(tmp, allows) || bail(name, target, 1);
90 }
91
92 for (key in exports) {
93 tmp = key[key.length - 1];
94 if (tmp === '/' && target.startsWith(key)) {
95 return (tmp = loop(exports[key], allows))
96 ? (tmp + target.substring(key.length))
97 : bail(name, target, 1);
98 }
99 if (tmp === '*' && target.startsWith(key.slice(0, -1))) {
100 // do not trigger if no *content* to inject
101 if (target.substring(key.length - 1).length > 0) {
102 return (tmp = loop(exports[key], allows))
103 ? tmp.replace('*', target.substring(key.length - 1))
104 : bail(name, target, 1);
105 }
106 }
107 }
108
109 return bail(name, target);
110 }
111}
112
113/**
114 * @param {object} pkg
115 * @param {object} [options]
116 * @param {string|boolean} [options.browser]
117 * @param {string[]} [options.fields]
118 */
119export function legacy(pkg, options={}) {
120 let i=0, value,
121 browser = options.browser,
122 fields = options.fields || ['module', 'main'];
123
124 if (browser && !fields.includes('browser')) {
125 fields.unshift('browser');
126 }
127
128 for (; i < fields.length; i++) {
129 if (value = pkg[fields[i]]) {
130 if (typeof value == 'string') {
131 //
132 } else if (typeof value == 'object' && fields[i] == 'browser') {
133 if (typeof browser == 'string') {
134 value = value[browser=toName(pkg.name, browser)];
135 if (value == null) return browser;
136 }
137 } else {
138 continue;
139 }
140
141 return typeof value == 'string'
142 ? ('./' + value.replace(/^\.?\//, ''))
143 : value;
144 }
145 }
146}