knip
Version:
Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects
100 lines (99 loc) • 4.74 kB
JavaScript
import ts from 'typescript';
import { findDescendants, getDefaultImportName, getImportMap, stripQuotes } from "../../typescript/ast-helpers.js";
import { isFile, loadFile } from "../../util/fs.js";
import { toProductionEntry } from "../../util/input.js";
import { join } from "../../util/path.js";
export const getReactBabelPlugins = (sourceFile) => {
const babelPlugins = [];
const importMap = getImportMap(sourceFile);
const reactPluginNames = new Set();
for (const [importName, importPath] of importMap) {
if (importPath.includes('@vitejs/plugin-react')) {
reactPluginNames.add(importName);
}
}
if (reactPluginNames.size === 0) {
const defaultImportName = getDefaultImportName(importMap, '@vitejs/plugin-react');
if (defaultImportName) {
reactPluginNames.add(defaultImportName);
}
else {
reactPluginNames.add('react');
}
}
const callExpressions = findDescendants(sourceFile, node => ts.isCallExpression(node));
const defineConfigCall = callExpressions.find(node => ts.isIdentifier(node.expression) && node.expression.text === 'defineConfig');
if (!defineConfigCall || defineConfigCall.arguments.length === 0) {
return babelPlugins;
}
const configObject = defineConfigCall.arguments[0];
if (!ts.isObjectLiteralExpression(configObject)) {
return babelPlugins;
}
const pluginsProperty = configObject.properties.find(prop => ts.isPropertyAssignment(prop) && prop.name.getText() === 'plugins');
if (!pluginsProperty ||
!ts.isPropertyAssignment(pluginsProperty) ||
!ts.isArrayLiteralExpression(pluginsProperty.initializer)) {
return babelPlugins;
}
const pluginsArray = pluginsProperty.initializer;
for (const pluginElement of pluginsArray.elements) {
let isReactPlugin = false;
if (ts.isCallExpression(pluginElement)) {
if (ts.isIdentifier(pluginElement.expression)) {
isReactPlugin = reactPluginNames.has(pluginElement.expression.text);
}
if (isReactPlugin) {
if (pluginElement.arguments.length === 0 || !ts.isObjectLiteralExpression(pluginElement.arguments[0])) {
continue;
}
const reactConfig = pluginElement.arguments[0];
const babelProperty = reactConfig.properties.find(prop => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'babel');
if (!babelProperty ||
!ts.isPropertyAssignment(babelProperty) ||
!ts.isObjectLiteralExpression(babelProperty.initializer)) {
continue;
}
const babelObject = babelProperty.initializer;
const babelPluginsProperty = babelObject.properties.find(prop => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'plugins');
if (!babelPluginsProperty ||
!ts.isPropertyAssignment(babelPluginsProperty) ||
!ts.isArrayLiteralExpression(babelPluginsProperty.initializer)) {
continue;
}
const pluginsArray = babelPluginsProperty.initializer;
for (const element of pluginsArray.elements) {
if (ts.isStringLiteral(element)) {
babelPlugins.push(stripQuotes(element.text));
}
else if (ts.isArrayLiteralExpression(element) &&
element.elements.length > 0 &&
ts.isStringLiteral(element.elements[0])) {
babelPlugins.push(stripQuotes(element.elements[0].text));
}
}
}
}
}
return babelPlugins;
};
const moduleScriptPattern = /<script\b(?=[^>]*\btype\s*=\s*["']?module["']?)(?=[^>]*\bsrc\s*=\s*["']?([^"' >]+)["']?)[^>]*>/gi;
const normalizeModuleScriptSrc = (value) => value.trim().replace(/^\//, '');
const getModuleScriptSources = (html) => {
const matches = html.matchAll(moduleScriptPattern);
const sources = [];
for (const match of matches) {
const src = normalizeModuleScriptSrc(match[1]);
if (src)
sources.push(src);
}
return sources;
};
export const getIndexHtmlEntries = async (rootDir) => {
const indexPath = join(rootDir, 'index.html');
if (!isFile(indexPath))
return [];
const html = await loadFile(indexPath);
const entries = getModuleScriptSources(html).map(src => join(rootDir, src));
return entries.map(entry => toProductionEntry(entry));
};