-
-
Notifications
You must be signed in to change notification settings - Fork 550
Expand file tree
/
Copy pathmodule-type-classifier.ts
More file actions
102 lines (90 loc) · 2.8 KB
/
module-type-classifier.ts
File metadata and controls
102 lines (90 loc) · 2.8 KB
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
import { getPatternFromSpec } from './ts-internals';
import { cachedLookup, normalizeSlashes } from './util';
import type { ModuleTypeOverride, ModuleTypes } from '.';
// Logic to support our `moduleTypes` option, which allows overriding node's default ESM / CJS
// classification of `.js` files based on package.json `type` field.
/**
* Seperate internal type because `auto` is clearer than `package`, but changing
* the public API is a breaking change.
* @internal
*/
export type InternalModuleTypeOverride = 'cjs' | 'esm' | 'auto';
/** @internal */
export interface ModuleTypeClassification {
moduleType: InternalModuleTypeOverride;
}
/** @internal */
export interface ModuleTypeClassifierOptions {
basePath?: string;
patterns?: ModuleTypes;
}
/** @internal */
export type ModuleTypeClassifier = ReturnType<
typeof createModuleTypeClassifier
>;
/**
* @internal
* May receive non-normalized options -- basePath and patterns -- and will normalize them
* internally.
* However, calls to `classifyModule` must pass pre-normalized paths!
*/
export function createModuleTypeClassifier(
options: ModuleTypeClassifierOptions
) {
const { patterns, basePath: _basePath } = options;
const basePath =
_basePath !== undefined
? normalizeSlashes(_basePath).replace(/\/$/, '')
: undefined;
const patternTypePairs = Object.entries(patterns ?? []).map(
([_pattern, type]) => {
const pattern = normalizeSlashes(_pattern);
return { pattern: parsePattern(basePath!, pattern), type };
}
);
const classifications: Record<ModuleTypeOverride, ModuleTypeClassification> =
{
package: {
moduleType: 'auto',
},
cjs: {
moduleType: 'cjs',
},
esm: {
moduleType: 'esm',
},
};
const auto = classifications.package;
// Passed path must be normalized!
function classifyModuleNonCached(path: string): ModuleTypeClassification {
const matched = matchPatterns(patternTypePairs, (_) => _.pattern, path);
if (matched) return classifications[matched.type];
return auto;
}
const classifyModule = cachedLookup(classifyModuleNonCached);
function classifyModuleAuto(path: String) {
return auto;
}
return {
classifyModuleByModuleTypeOverrides: patternTypePairs.length
? classifyModule
: classifyModuleAuto,
};
}
function parsePattern(basePath: string, patternString: string): RegExp {
const pattern = getPatternFromSpec(patternString, basePath);
return pattern !== undefined ? new RegExp(pattern) : /(?:)/;
}
function matchPatterns<T>(
objects: T[],
getPattern: (t: T) => RegExp,
candidate: string
): T | undefined {
for (let i = objects.length - 1; i >= 0; i--) {
const object = objects[i];
const pattern = getPattern(object);
if (pattern?.test(candidate)) {
return object;
}
}
}