mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
7.0 KiB
165 lines
7.0 KiB
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.workspaceRules = void 0;
|
|
exports.loadWorkspaceRules = loadWorkspaceRules;
|
|
const devkit_1 = require("@nx/devkit");
|
|
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
|
const internal_1 = require("@nx/js/src/internal");
|
|
const fs_1 = require("fs");
|
|
const path_1 = require("path");
|
|
const constants_1 = require("./constants");
|
|
// ESM import() cannot resolve directories to index files like require() can
|
|
const INDEX_FILE_EXTENSIONS = [
|
|
'.ts',
|
|
'.mts',
|
|
'.cts',
|
|
'.js',
|
|
'.mjs',
|
|
'.cjs',
|
|
];
|
|
function resolveDirectoryEntryFile(directory) {
|
|
for (const ext of INDEX_FILE_EXTENSIONS) {
|
|
const candidatePath = (0, path_1.join)(directory, `index${ext}`);
|
|
if ((0, fs_1.existsSync)(candidatePath)) {
|
|
return candidatePath;
|
|
}
|
|
}
|
|
throw new Error(`No index file found in directory: ${directory}. ` +
|
|
`Expected one of: ${INDEX_FILE_EXTENSIONS.map((ext) => `index${ext}`).join(', ')}`);
|
|
}
|
|
function normalizePath(path) {
|
|
return `${(0, path_1.normalize)(path).replace(/[\/\\]$/g, '')}${path_1.sep}`;
|
|
}
|
|
function findTsConfig(directory, tsConfigPath) {
|
|
let effectiveTsConfigPath = tsConfigPath;
|
|
const normalizedWorkspaceRoot = normalizePath(devkit_1.workspaceRoot);
|
|
if (effectiveTsConfigPath) {
|
|
if (!(0, path_1.isAbsolute)(effectiveTsConfigPath)) {
|
|
effectiveTsConfigPath = (0, path_1.resolve)(devkit_1.workspaceRoot, effectiveTsConfigPath);
|
|
}
|
|
if (!effectiveTsConfigPath.startsWith(normalizedWorkspaceRoot)) {
|
|
console.warn(`TypeScript config "${effectiveTsConfigPath}" is outside the workspace root "${devkit_1.workspaceRoot}". Falling back to automatic tsconfig detection.`);
|
|
effectiveTsConfigPath = undefined;
|
|
}
|
|
}
|
|
if (!effectiveTsConfigPath) {
|
|
let currentDir = directory;
|
|
while (currentDir.startsWith(normalizedWorkspaceRoot)) {
|
|
const candidatePath = (0, path_1.join)(currentDir, 'tsconfig.json');
|
|
if ((0, fs_1.existsSync)(candidatePath)) {
|
|
effectiveTsConfigPath = candidatePath;
|
|
break;
|
|
}
|
|
const parentDir = (0, path_1.dirname)(currentDir);
|
|
if (normalizePath(parentDir) === normalizedWorkspaceRoot) {
|
|
break;
|
|
}
|
|
currentDir = parentDir;
|
|
}
|
|
}
|
|
if (!tsConfigPath && !effectiveTsConfigPath) {
|
|
console.warn(`No TypeScript config found. Crawled up to workspace root "${devkit_1.workspaceRoot}" ` +
|
|
`from directory "${directory}" without finding a tsconfig.json file. Provide ` +
|
|
`a tsconfig.json file path to the loadWorkspaceRules function.`);
|
|
}
|
|
return effectiveTsConfigPath;
|
|
}
|
|
/**
|
|
* Load ESLint rules from a directory.
|
|
*
|
|
* This utility allows loading custom ESLint rules from any directory within the workspace,
|
|
* not just the default `tools/eslint-rules` location. It's useful for:
|
|
* - Loading rules from a custom directory structure
|
|
* - Loading rules from npm workspace packages (e.g., linked packages via npm/yarn/pnpm workspaces)
|
|
* - Loading rules from multiple directories with different configurations
|
|
*
|
|
* The directory must contain an index file (index.ts, index.js, etc.) that exports the rules.
|
|
*
|
|
* @param directory - The directory path to load rules from. Can be absolute or relative to workspace root.
|
|
* @param tsConfigPath - Optional path to tsconfig.json for TypeScript compilation.
|
|
* If not provided, will search for tsconfig.json starting from
|
|
* the directory and traversing up to the workspace root.
|
|
* @returns An object containing the loaded ESLint rules (without any prefix).
|
|
* Returns an empty object if the directory doesn't exist or loading fails.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Load rules from a custom directory (relative to workspace root)
|
|
* const customRules = await loadWorkspaceRules('packages/my-eslint-plugin/rules');
|
|
*
|
|
* // Load rules with a specific tsconfig
|
|
* const customRules = await loadWorkspaceRules(
|
|
* 'packages/my-eslint-plugin/rules',
|
|
* 'packages/my-eslint-plugin/tsconfig.json'
|
|
* );
|
|
*
|
|
* // Or use absolute paths
|
|
* const customRules = loadWorkspaceRules('/absolute/path/to/rules');
|
|
* ```
|
|
*/
|
|
async function loadWorkspaceRules(directory, tsConfigPath) {
|
|
const resolvedDirectory = (0, path_1.isAbsolute)(directory)
|
|
? directory
|
|
: (0, path_1.resolve)(devkit_1.workspaceRoot, directory);
|
|
if (!resolvedDirectory.startsWith(normalizePath(devkit_1.workspaceRoot))) {
|
|
console.warn(`Directory "${resolvedDirectory}" is outside the workspace root "${devkit_1.workspaceRoot}". ESLint rules can only be loaded from within the workspace.`);
|
|
return {};
|
|
}
|
|
if (!(0, fs_1.existsSync)(resolvedDirectory)) {
|
|
console.warn(`Directory "${resolvedDirectory}" does not exist. Skipping loading ESLint rules from this directory.`);
|
|
return {};
|
|
}
|
|
let registrationCleanup = null;
|
|
try {
|
|
const effectiveTsConfigPath = findTsConfig(resolvedDirectory, tsConfigPath);
|
|
if (effectiveTsConfigPath) {
|
|
registrationCleanup = (0, internal_1.registerTsProject)(effectiveTsConfigPath);
|
|
}
|
|
const entryFile = resolveDirectoryEntryFile(resolvedDirectory);
|
|
// Only rules are supported (not configs, processors, etc.)
|
|
const module = await (0, config_utils_1.loadConfigFile)(entryFile);
|
|
const rules = module.rules || module;
|
|
return rules;
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
return {};
|
|
}
|
|
finally {
|
|
if (registrationCleanup) {
|
|
registrationCleanup();
|
|
}
|
|
}
|
|
}
|
|
exports.workspaceRules = (() => {
|
|
// If `tools/eslint-rules` folder doesn't exist, there is no point trying to register and load it
|
|
if (!(0, fs_1.existsSync)(constants_1.WORKSPACE_PLUGIN_DIR)) {
|
|
return {};
|
|
}
|
|
// Register `tools/eslint-rules` for TS transpilation
|
|
const registrationCleanup = (0, internal_1.registerTsProject)((0, path_1.join)(constants_1.WORKSPACE_PLUGIN_DIR, 'tsconfig.json'));
|
|
try {
|
|
/**
|
|
* Currently we only support applying the rules from the user's workspace plugin object
|
|
* (i.e. not other things that plugings can expose like configs, processors etc)
|
|
*/
|
|
const { rules } = require(constants_1.WORKSPACE_PLUGIN_DIR);
|
|
// Apply the namespace to the resolved rules
|
|
const namespacedRules = {};
|
|
for (const [ruleName, ruleConfig] of Object.entries(rules)) {
|
|
namespacedRules[`${constants_1.WORKSPACE_RULE_PREFIX}-${ruleName}`] = ruleConfig;
|
|
// keep the old namespaced rules for backwards compatibility
|
|
namespacedRules[`${constants_1.WORKSPACE_RULE_PREFIX}/${ruleName}`] = ruleConfig;
|
|
}
|
|
return namespacedRules;
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
return {};
|
|
}
|
|
finally {
|
|
if (registrationCleanup) {
|
|
registrationCleanup();
|
|
}
|
|
}
|
|
})();
|
|
|