-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
123 lines (105 loc) · 3.67 KB
/
index.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
import fs from 'fs';
import path from 'path';
import { red, bold, yellow, underline } from 'kleur/colors';
import { legacy } from 'resolve.exports';
import ts from 'typescript';
import { analyzeModuleGraph } from './analyze-module-graph.js';
import { gatherFilesFromPackageExports } from './package-exports.js';
import { analyzeFile } from './analyze-file.js';
import {
DEFAULTS,
getCliConfig,
getUserConfig,
} from './config.js';
/**
* @typedef {{
* level: 'error' | 'warning' | 'info';
* message: string;
* id: 'barrel-file' | 're-export-all' | 'import-all' | 'module-graph-size';
* data?: any;
* loc?: {
* start: number,
* end: number
* }
* }} Diagnostic
*
* @typedef {{
* currentFile: string,
* options: {
* cwd: string,
* maxModuleGraphSize: number,
* amountOfExportsToConsiderModuleAsBarrel: number,
* info: boolean,
* rollup: object
* }
* }} Context
*/
export async function barrelBegone(programmaticConfig = {}) {
const cliConfig = getCliConfig(process.argv);
const userConfig = await getUserConfig(
cliConfig?.cwd ?? DEFAULTS.cwd
);
/** Merged config, CLI args overrule userConfig, programmatic options overrule everything */
const finalOptions = {
...DEFAULTS,
...userConfig,
...cliConfig,
...programmaticConfig,
};
/**
* @type {{[key: string]: Diagnostic[]}}
*/
const diagnostics = {};
/** @type {Context} */
const context = {
currentFile: '',
options: {
...finalOptions
}
}
console.log(bold("\n🛢️ BARREL BEGONE 🛢️\n"));
const packageJsonPath = path.join(context.options.cwd, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8').toString());
let files = [];
if (packageJson.exports) {
files = gatherFilesFromPackageExports(packageJson.exports, { cwd: context.options.cwd }).filter(f => !f.value.includes('package.json') && (f.value.endsWith('.js') || f.value.endsWith('.mjs')));
console.log('Analyzing the following entrypoints based on the "exports" field in package.json:');
} else {
const value = legacy(packageJson, { cwd: context.options.cwd });
files = [{key: '.', value}];
if (value === undefined) {
console.log(`No entrypoints found in the "module", "main", or "exports" field, exiting...`);
return;
}
console.log('Analyzing the following entrypoints based on either the "module" or "main" field in package.json:');
}
files.forEach(({key, value}) => console.log(`- "${bold(key)}": "${underline(bold(value))}"`));
console.log();
for (const {_, value} of files) {
const filePath = path.join(context.options.cwd, value);
context.currentFile = filePath;
diagnostics[filePath] = [];
await analyzeModuleGraph(filePath, context, diagnostics);
const source = ts.createSourceFile(filePath, fs.readFileSync(filePath, 'utf8'), ts.ScriptTarget.ES2015, true);
const { diagnostics: d } = analyzeFile(source, filePath, {
amountOfExportsToConsiderModuleAsBarrel: context.options.amountOfExportsToConsiderModuleAsBarrel,
});
diagnostics[filePath].unshift(...d);
if (diagnostics[filePath].length) {
console.log('\n❌' + ' ' + bold(value));
} else {
console.log('\n✅' + ' ' + bold(value));
console.log(` No issues found.`);
}
for (const diagnostic of diagnostics[filePath]) {
if (diagnostic.level === 'error') {
console.log(` ${bold(red('[ERROR]'))}: ${diagnostic.message}`);
} else if (diagnostic.level === 'warning') {
console.log(` ${bold(yellow('[WARNING]'))}: ${diagnostic.message}`);
} else {
console.log(` ${bold('[INFO]')}: ${diagnostic.message}`);
}
}
}
return diagnostics;
}