-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
RootScan.ts
108 lines (93 loc) · 3.49 KB
/
RootScan.ts
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
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
/**
* Represents a partial package.json object.
*/
type PartialPackageJson = Partial<{
main: string;
module: string;
type: 'commonjs' | 'module';
}>;
/**
* Represents the root data.
*/
export interface RootData {
/**
* The root directory.
*/
root: string;
/**
* The type of the module system used.
* It can be either 'ESM' or 'CommonJS'.
*/
type: 'ESM' | 'CommonJS';
}
let data: RootData | null = null;
/**
* Returns the directory name of a given path by joining the current working directory (cwd) with the joinable path.
* @private
* @param cwd - The current working directory.
* @param joinablePath - The path to be joined with the cwd.
* @returns The directory name of the joined path.
*/
function dirnameWithPath(cwd: string, joinablePath: string) {
return dirname(join(cwd, joinablePath));
}
export function getRootData(): RootData {
return (data ??= parseRootData());
}
/**
* Retrieves the root data of the project.
*
* This function reads the `package.json` file in the current working directory and determines the root path and type
* of the project.
*
* - If the `package.json` file is not found or cannot be parsed, it assumes the project is using CommonJS and
* the current working directory is used as the root
*
* - If the project `type` is specified as `"commonjs"` or `"module"` in the `package.json`, it uses the corresponding
* `main` or `module` file path as the root.
*
* - If there is no `main` or `module` then it uses the current working directory as the root, while retaining the
* matching `CommonJS` or `ESM` based on the `type`
*
* - If the main or module file path is not specified, it uses the current working directory as the root.
*
* The following table shows how different situations resolve to different root data
*
* | fields | resolved as |
* |--------------------------|-------------|
* | type=commonjs && main | CommonJS |
* | type=commonjs && module | CommonJS |
* | type=module && main | ESM |
* | type=module && module | ESM |
* | type=undefined && main | CommonJS |
* | type=undefined && module | ESM |
* | no package.json on cwd | CommonJS |
*
* @returns The root data object containing the root path and the type of the project.
*/
export function parseRootData(): RootData {
const cwd = process.cwd();
let file: PartialPackageJson | undefined;
try {
file = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')) as PartialPackageJson;
} catch (error) {
return { root: cwd, type: 'CommonJS' };
}
const { main: packageMain, module: packageModule, type: packageType } = file;
const lowerCasedType = packageType?.toLowerCase() as PartialPackageJson['type'];
if (lowerCasedType === 'commonjs') {
if (packageMain) return { root: dirnameWithPath(cwd, packageMain), type: 'CommonJS' };
if (packageModule) return { root: dirnameWithPath(cwd, packageModule), type: 'CommonJS' };
return { root: cwd, type: 'CommonJS' };
}
if (lowerCasedType === 'module') {
if (packageMain) return { root: dirnameWithPath(cwd, packageMain), type: 'ESM' };
if (packageModule) return { root: dirnameWithPath(cwd, packageModule), type: 'ESM' };
return { root: cwd, type: 'ESM' };
}
if (packageMain) return { root: dirnameWithPath(cwd, packageMain), type: 'CommonJS' };
if (packageModule) return { root: dirnameWithPath(cwd, packageModule), type: 'ESM' };
return { root: cwd, type: 'CommonJS' };
}