2019-06-23 03:35:23 -07:00
|
|
|
import {
|
2022-11-23 07:20:28 -08:00
|
|
|
CUSTOM_EXTENSION_ENV,
|
|
|
|
UserSettings,
|
|
|
|
CustomDirectoryLoader,
|
|
|
|
DirectoryLoader,
|
|
|
|
PackageDirectoryLoader,
|
|
|
|
LazyPackageDirectoryLoader,
|
|
|
|
Types,
|
|
|
|
} from 'n8n-core';
|
|
|
|
import type {
|
2021-05-21 21:41:06 -07:00
|
|
|
ILogger,
|
2022-11-23 07:20:28 -08:00
|
|
|
INodesAndCredentials,
|
|
|
|
KnownNodesAndCredentials,
|
|
|
|
LoadedNodesAndCredentials,
|
2019-06-23 03:35:23 -07:00
|
|
|
} from 'n8n-workflow';
|
2022-11-23 07:20:28 -08:00
|
|
|
import { LoggerProxy, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2019-06-24 03:47:44 -07:00
|
|
|
import {
|
|
|
|
access as fsAccess,
|
2022-11-23 07:20:28 -08:00
|
|
|
copyFile,
|
|
|
|
mkdir,
|
2019-06-24 03:47:44 -07:00
|
|
|
readdir as fsReaddir,
|
|
|
|
stat as fsStat,
|
2022-11-23 07:20:28 -08:00
|
|
|
writeFile,
|
2021-08-20 14:48:02 -07:00
|
|
|
} from 'fs/promises';
|
2022-04-08 14:32:08 -07:00
|
|
|
import path from 'path';
|
2022-11-09 06:25:00 -08:00
|
|
|
import config from '@/config';
|
|
|
|
import { InstalledPackages } from '@db/entities/InstalledPackages';
|
|
|
|
import { InstalledNodes } from '@db/entities/InstalledNodes';
|
2022-11-23 07:20:28 -08:00
|
|
|
import { executeCommand } from '@/CommunityNodes/helpers';
|
|
|
|
import { CLI_DIR, GENERATED_STATIC_DIR, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
2022-07-20 07:24:03 -07:00
|
|
|
import {
|
|
|
|
persistInstalledPackageData,
|
|
|
|
removePackageFromDatabase,
|
2022-11-09 06:25:00 -08:00
|
|
|
} from '@/CommunityNodes/packageModel';
|
2022-11-23 07:20:28 -08:00
|
|
|
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
|
|
|
known: KnownNodesAndCredentials = { nodes: {}, credentials: {} };
|
2021-06-17 22:58:26 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
loaded: LoadedNodesAndCredentials = { nodes: {}, credentials: {} };
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
types: Types = { nodes: [], credentials: [] };
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
excludeNodes = config.getEnv('nodes.exclude');
|
2021-08-29 11:58:11 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
includeNodes = config.getEnv('nodes.include');
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2021-05-21 21:41:06 -07:00
|
|
|
logger: ILogger;
|
2021-05-21 20:51:38 -07:00
|
|
|
|
2019-08-08 11:38:25 -07:00
|
|
|
async init() {
|
2022-07-20 07:24:03 -07:00
|
|
|
// Make sure the imported modules can resolve dependencies fine.
|
2022-08-03 09:10:59 -07:00
|
|
|
const delimiter = process.platform === 'win32' ? ';' : ':';
|
|
|
|
process.env.NODE_PATH = module.paths.join(delimiter);
|
2022-11-23 07:20:28 -08:00
|
|
|
|
2022-07-20 07:24:03 -07:00
|
|
|
// @ts-ignore
|
2022-11-23 07:20:28 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2022-07-20 07:24:03 -07:00
|
|
|
module.constructor._initPaths();
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
await mkdir(path.join(GENERATED_STATIC_DIR, 'icons/nodes'), { recursive: true });
|
|
|
|
await mkdir(path.join(GENERATED_STATIC_DIR, 'icons/credentials'), { recursive: true });
|
2022-07-20 07:24:03 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
await this.loadNodesFromBasePackages();
|
|
|
|
await this.loadNodesFromDownloadedPackages();
|
|
|
|
await this.loadNodesFromCustomDirectories();
|
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
async generateTypesForFrontend() {
|
|
|
|
const credentialsOverwrites = CredentialsOverwrites().getAll();
|
|
|
|
for (const credential of this.types.credentials) {
|
|
|
|
if (credential.name in credentialsOverwrites) {
|
|
|
|
credential.__overwrittenProperties = Object.keys(credentialsOverwrites[credential.name]);
|
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
// pre-render all the node and credential types as static json files
|
|
|
|
await mkdir(path.join(GENERATED_STATIC_DIR, 'types'), { recursive: true });
|
|
|
|
|
|
|
|
const writeStaticJSON = async (name: string, data: any[]) => {
|
|
|
|
const filePath = path.join(GENERATED_STATIC_DIR, `types/${name}.json`);
|
|
|
|
const payload = `[\n${data.map((entry) => JSON.stringify(entry)).join(',\n')}\n]`;
|
|
|
|
await writeFile(filePath, payload, { encoding: 'utf-8' });
|
|
|
|
};
|
2022-07-20 07:24:03 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
await writeStaticJSON('nodes', this.types.nodes);
|
|
|
|
await writeStaticJSON('credentials', this.types.credentials);
|
2022-07-20 07:24:03 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
async loadNodesFromBasePackages() {
|
|
|
|
const nodeModulesPath = await this.getNodeModulesPath();
|
|
|
|
const nodePackagePaths = await this.getN8nNodePackages(nodeModulesPath);
|
|
|
|
|
|
|
|
for (const packagePath of nodePackagePaths) {
|
|
|
|
await this.runDirectoryLoader(LazyPackageDirectoryLoader, packagePath);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-07-20 07:24:03 -07:00
|
|
|
async loadNodesFromDownloadedPackages(): Promise<void> {
|
|
|
|
const nodePackages = [];
|
|
|
|
try {
|
|
|
|
// Read downloaded nodes and credentials
|
2022-11-23 07:20:28 -08:00
|
|
|
const downloadedNodesDir = UserSettings.getUserN8nFolderDownloadedNodesPath();
|
|
|
|
const downloadedNodesDirModules = path.join(downloadedNodesDir, 'node_modules');
|
|
|
|
await fsAccess(downloadedNodesDirModules);
|
|
|
|
const downloadedPackages = await this.getN8nNodePackages(downloadedNodesDirModules);
|
2022-07-20 07:24:03 -07:00
|
|
|
nodePackages.push(...downloadedPackages);
|
2022-11-04 09:34:47 -07:00
|
|
|
} catch (error) {
|
|
|
|
// Folder does not exist so ignore and return
|
|
|
|
return;
|
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
|
|
for (const packagePath of nodePackages) {
|
|
|
|
try {
|
2022-11-23 07:20:28 -08:00
|
|
|
await this.runDirectoryLoader(PackageDirectoryLoader, packagePath);
|
2022-11-04 09:34:47 -07:00
|
|
|
} catch (error) {
|
|
|
|
ErrorReporter.error(error);
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
async loadNodesFromCustomDirectories(): Promise<void> {
|
2019-06-23 03:35:23 -07:00
|
|
|
// Read nodes and credentials from custom directories
|
|
|
|
const customDirectories = [];
|
|
|
|
|
|
|
|
// Add "custom" folder in user-n8n folder
|
|
|
|
customDirectories.push(UserSettings.getUserN8nFolderCustomExtensionPath());
|
|
|
|
|
|
|
|
// Add folders from special environment variable
|
|
|
|
if (process.env[CUSTOM_EXTENSION_ENV] !== undefined) {
|
2021-08-29 11:58:11 -07:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
2019-06-23 03:35:23 -07:00
|
|
|
const customExtensionFolders = process.env[CUSTOM_EXTENSION_ENV]!.split(';');
|
2022-09-09 09:08:08 -07:00
|
|
|
customDirectories.push(...customExtensionFolders);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const directory of customDirectories) {
|
2022-11-23 07:20:28 -08:00
|
|
|
await this.runDirectoryLoader(CustomDirectoryLoader, directory);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns all the names of the packages which could
|
|
|
|
* contain n8n nodes
|
|
|
|
*
|
|
|
|
*/
|
2022-07-20 07:24:03 -07:00
|
|
|
async getN8nNodePackages(baseModulesPath: string): Promise<string[]> {
|
2020-06-03 10:40:39 -07:00
|
|
|
const getN8nNodePackagesRecursive = async (relativePath: string): Promise<string[]> => {
|
|
|
|
const results: string[] = [];
|
2022-07-20 07:24:03 -07:00
|
|
|
const nodeModulesPath = `${baseModulesPath}/${relativePath}`;
|
2021-04-30 19:22:15 -07:00
|
|
|
for (const file of await fsReaddir(nodeModulesPath)) {
|
2020-06-03 10:40:39 -07:00
|
|
|
const isN8nNodesPackage = file.indexOf('n8n-nodes-') === 0;
|
|
|
|
const isNpmScopedPackage = file.indexOf('@') === 0;
|
|
|
|
if (!isN8nNodesPackage && !isNpmScopedPackage) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-04-30 19:22:15 -07:00
|
|
|
if (!(await fsStat(nodeModulesPath)).isDirectory()) {
|
2020-06-03 10:40:39 -07:00
|
|
|
continue;
|
|
|
|
}
|
2021-08-29 11:58:11 -07:00
|
|
|
if (isN8nNodesPackage) {
|
2022-07-20 07:24:03 -07:00
|
|
|
results.push(`${baseModulesPath}/${relativePath}${file}`);
|
2021-08-29 11:58:11 -07:00
|
|
|
}
|
2020-06-03 10:40:39 -07:00
|
|
|
if (isNpmScopedPackage) {
|
2021-08-29 11:58:11 -07:00
|
|
|
results.push(...(await getN8nNodePackagesRecursive(`${relativePath}${file}/`)));
|
2020-06-03 10:40:39 -07:00
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
2020-06-03 10:40:39 -07:00
|
|
|
return results;
|
2020-06-03 10:58:55 -07:00
|
|
|
};
|
2020-06-03 10:40:39 -07:00
|
|
|
return getN8nNodePackagesRecursive('');
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2022-07-20 07:24:03 -07:00
|
|
|
async loadNpmModule(packageName: string, version?: string): Promise<InstalledPackages> {
|
2022-11-04 09:34:47 -07:00
|
|
|
const downloadFolder = UserSettings.getUserN8nFolderDownloadedNodesPath();
|
2022-07-20 07:24:03 -07:00
|
|
|
const command = `npm install ${packageName}${version ? `@${version}` : ''}`;
|
|
|
|
|
|
|
|
await executeCommand(command);
|
|
|
|
|
|
|
|
const finalNodeUnpackedPath = path.join(downloadFolder, 'node_modules', packageName);
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
const { loadedNodes, packageJson } = await this.runDirectoryLoader(
|
|
|
|
PackageDirectoryLoader,
|
|
|
|
finalNodeUnpackedPath,
|
|
|
|
);
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
|
|
if (loadedNodes.length > 0) {
|
|
|
|
// Save info to DB
|
|
|
|
try {
|
|
|
|
const installedPackage = await persistInstalledPackageData(
|
2022-11-23 07:20:28 -08:00
|
|
|
packageJson.name,
|
|
|
|
packageJson.version,
|
2022-07-20 07:24:03 -07:00
|
|
|
loadedNodes,
|
2022-11-23 07:20:28 -08:00
|
|
|
this.loaded.nodes,
|
|
|
|
packageJson.author?.name,
|
|
|
|
packageJson.author?.email,
|
2022-07-20 07:24:03 -07:00
|
|
|
);
|
|
|
|
this.attachNodesToNodeTypes(installedPackage.installedNodes);
|
2022-11-23 07:20:28 -08:00
|
|
|
await this.generateTypesForFrontend();
|
2022-07-20 07:24:03 -07:00
|
|
|
return installedPackage;
|
|
|
|
} catch (error) {
|
2022-11-23 07:20:28 -08:00
|
|
|
LoggerProxy.error('Failed to save installed packages and nodes', {
|
|
|
|
error: error as Error,
|
|
|
|
packageName,
|
|
|
|
});
|
2022-07-20 07:24:03 -07:00
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Remove this package since it contains no loadable nodes
|
|
|
|
const removeCommand = `npm remove ${packageName}`;
|
|
|
|
try {
|
|
|
|
await executeCommand(removeCommand);
|
2022-11-23 07:20:28 -08:00
|
|
|
} catch (_) {}
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
|
|
throw new Error(RESPONSE_ERROR_MESSAGES.PACKAGE_DOES_NOT_CONTAIN_NODES);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async removeNpmModule(packageName: string, installedPackage: InstalledPackages): Promise<void> {
|
|
|
|
const command = `npm remove ${packageName}`;
|
|
|
|
|
|
|
|
await executeCommand(command);
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
await removePackageFromDatabase(installedPackage);
|
|
|
|
|
|
|
|
await this.generateTypesForFrontend();
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
|
|
this.unloadNodes(installedPackage.installedNodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
async updateNpmModule(
|
|
|
|
packageName: string,
|
|
|
|
installedPackage: InstalledPackages,
|
|
|
|
): Promise<InstalledPackages> {
|
2022-11-04 09:34:47 -07:00
|
|
|
const downloadFolder = UserSettings.getUserN8nFolderDownloadedNodesPath();
|
2022-07-20 07:24:03 -07:00
|
|
|
|
2022-08-22 04:11:51 -07:00
|
|
|
const command = `npm i ${packageName}@latest`;
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
|
|
try {
|
|
|
|
await executeCommand(command);
|
|
|
|
} catch (error) {
|
2022-11-23 07:20:28 -08:00
|
|
|
if (error instanceof Error && error.message === RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND) {
|
2022-07-20 07:24:03 -07:00
|
|
|
throw new Error(`The npm package "${packageName}" could not be found.`);
|
|
|
|
}
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.unloadNodes(installedPackage.installedNodes);
|
|
|
|
|
|
|
|
const finalNodeUnpackedPath = path.join(downloadFolder, 'node_modules', packageName);
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
const { loadedNodes, packageJson } = await this.runDirectoryLoader(
|
|
|
|
PackageDirectoryLoader,
|
|
|
|
finalNodeUnpackedPath,
|
|
|
|
);
|
2022-07-20 07:24:03 -07:00
|
|
|
|
|
|
|
if (loadedNodes.length > 0) {
|
|
|
|
// Save info to DB
|
|
|
|
try {
|
|
|
|
await removePackageFromDatabase(installedPackage);
|
|
|
|
|
|
|
|
const newlyInstalledPackage = await persistInstalledPackageData(
|
2022-11-23 07:20:28 -08:00
|
|
|
packageJson.name,
|
|
|
|
packageJson.version,
|
2022-07-20 07:24:03 -07:00
|
|
|
loadedNodes,
|
2022-11-23 07:20:28 -08:00
|
|
|
this.loaded.nodes,
|
|
|
|
packageJson.author?.name,
|
|
|
|
packageJson.author?.email,
|
2022-07-20 07:24:03 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
this.attachNodesToNodeTypes(newlyInstalledPackage.installedNodes);
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
await this.generateTypesForFrontend();
|
|
|
|
|
2022-07-20 07:24:03 -07:00
|
|
|
return newlyInstalledPackage;
|
|
|
|
} catch (error) {
|
2022-11-23 07:20:28 -08:00
|
|
|
LoggerProxy.error('Failed to save installed packages and nodes', {
|
|
|
|
error: error as Error,
|
|
|
|
packageName,
|
|
|
|
});
|
2022-07-20 07:24:03 -07:00
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Remove this package since it contains no loadable nodes
|
|
|
|
const removeCommand = `npm remove ${packageName}`;
|
|
|
|
try {
|
|
|
|
await executeCommand(removeCommand);
|
2022-11-23 07:20:28 -08:00
|
|
|
} catch (_) {}
|
2022-07-20 07:24:03 -07:00
|
|
|
throw new Error(RESPONSE_ERROR_MESSAGES.PACKAGE_DOES_NOT_CONTAIN_NODES);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
private unloadNodes(installedNodes: InstalledNodes[]): void {
|
|
|
|
installedNodes.forEach((installedNode) => {
|
|
|
|
delete this.loaded.nodes[installedNode.type];
|
|
|
|
});
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
private attachNodesToNodeTypes(installedNodes: InstalledNodes[]): void {
|
|
|
|
const loadedNodes = this.loaded.nodes;
|
|
|
|
installedNodes.forEach((installedNode) => {
|
|
|
|
const { type, sourcePath } = loadedNodes[installedNode.type];
|
|
|
|
loadedNodes[installedNode.type] = { type, sourcePath };
|
|
|
|
});
|
2021-06-17 22:58:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-11-23 07:20:28 -08:00
|
|
|
* Run a loader of source files of nodes and credentials in a directory.
|
2021-06-17 22:58:26 -07:00
|
|
|
*/
|
2022-11-23 07:20:28 -08:00
|
|
|
private async runDirectoryLoader<T extends DirectoryLoader>(
|
|
|
|
constructor: new (...args: ConstructorParameters<typeof DirectoryLoader>) => T,
|
|
|
|
dir: string,
|
|
|
|
) {
|
|
|
|
const loader = new constructor(dir, this.excludeNodes, this.includeNodes);
|
|
|
|
await loader.loadAll();
|
|
|
|
|
|
|
|
// list of node & credential types that will be sent to the frontend
|
|
|
|
const { types } = loader;
|
|
|
|
this.types.nodes = this.types.nodes.concat(types.nodes);
|
|
|
|
this.types.credentials = this.types.credentials.concat(types.credentials);
|
|
|
|
|
|
|
|
// Copy over all icons and set `iconUrl` for the frontend
|
|
|
|
const iconPromises: Array<Promise<void>> = [];
|
|
|
|
for (const node of types.nodes) {
|
|
|
|
if (node.icon?.startsWith('file:')) {
|
|
|
|
const icon = node.icon.substring(5);
|
|
|
|
const iconUrl = `icons/nodes/${node.name}${path.extname(icon)}`;
|
|
|
|
delete node.icon;
|
|
|
|
node.iconUrl = iconUrl;
|
|
|
|
iconPromises.push(copyFile(path.join(dir, icon), path.join(GENERATED_STATIC_DIR, iconUrl)));
|
2021-06-17 22:58:26 -07:00
|
|
|
}
|
|
|
|
}
|
2022-11-23 07:20:28 -08:00
|
|
|
for (const credential of types.credentials) {
|
|
|
|
if (credential.icon?.startsWith('file:')) {
|
|
|
|
const icon = credential.icon.substring(5);
|
|
|
|
const iconUrl = `icons/credentials/${credential.name}${path.extname(icon)}`;
|
|
|
|
delete credential.icon;
|
|
|
|
credential.iconUrl = iconUrl;
|
|
|
|
iconPromises.push(copyFile(path.join(dir, icon), path.join(GENERATED_STATIC_DIR, iconUrl)));
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
2022-11-23 07:20:28 -08:00
|
|
|
await Promise.all(iconPromises);
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
// Nodes and credentials that have been loaded immediately
|
|
|
|
for (const nodeTypeName in loader.nodeTypes) {
|
|
|
|
this.loaded.nodes[nodeTypeName] = loader.nodeTypes[nodeTypeName];
|
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
for (const credentialTypeName in loader.credentialTypes) {
|
|
|
|
this.loaded.credentials[credentialTypeName] = loader.credentialTypes[credentialTypeName];
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
// Nodes and credentials that will be lazy loaded
|
|
|
|
if (loader instanceof LazyPackageDirectoryLoader) {
|
|
|
|
const { packageName, known } = loader;
|
|
|
|
|
|
|
|
for (const type in known.nodes) {
|
|
|
|
const { className, sourcePath } = known.nodes[type];
|
|
|
|
this.known.nodes[`${packageName}.${type}`] = {
|
|
|
|
className,
|
|
|
|
sourcePath: path.join(dir, sourcePath),
|
|
|
|
};
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
for (const type in known.credentials) {
|
|
|
|
const { className, sourcePath } = known.credentials[type];
|
|
|
|
this.known.credentials[type] = { className, sourcePath: path.join(dir, sourcePath) };
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
2022-07-20 07:24:03 -07:00
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
return loader;
|
2022-07-20 07:24:03 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
private async getNodeModulesPath(): Promise<string> {
|
|
|
|
// Get the path to the node-modules folder to be later able
|
|
|
|
// to load the credentials and nodes
|
|
|
|
const checkPaths = [
|
|
|
|
// In case "n8n" package is in same node_modules folder.
|
|
|
|
path.join(CLI_DIR, '..', 'n8n-workflow'),
|
|
|
|
// In case "n8n" package is the root and the packages are
|
|
|
|
// in the "node_modules" folder underneath it.
|
|
|
|
path.join(CLI_DIR, 'node_modules', 'n8n-workflow'),
|
|
|
|
// In case "n8n" package is installed using npm/yarn workspaces
|
|
|
|
// the node_modules folder is in the root of the workspace.
|
|
|
|
path.join(CLI_DIR, '..', '..', 'node_modules', 'n8n-workflow'),
|
|
|
|
];
|
|
|
|
for (const checkPath of checkPaths) {
|
|
|
|
try {
|
|
|
|
await fsAccess(checkPath);
|
|
|
|
// Folder exists, so use it.
|
|
|
|
return path.dirname(checkPath);
|
|
|
|
} catch (_) {} // Folder does not exist so get next one
|
|
|
|
}
|
|
|
|
throw new Error('Could not find "node_modules" folder!');
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let packagesInformationInstance: LoadNodesAndCredentialsClass | undefined;
|
|
|
|
|
2022-11-23 07:20:28 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
2019-06-23 03:35:23 -07:00
|
|
|
export function LoadNodesAndCredentials(): LoadNodesAndCredentialsClass {
|
|
|
|
if (packagesInformationInstance === undefined) {
|
|
|
|
packagesInformationInstance = new LoadNodesAndCredentialsClass();
|
|
|
|
}
|
|
|
|
|
|
|
|
return packagesInformationInstance;
|
|
|
|
}
|