mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(core): Unloading a community package should also unload all its files from require.cache (#16072)
This commit is contained in:
committed by
GitHub
parent
dfdc2237af
commit
8c63ca7d57
@@ -523,22 +523,15 @@ export class LoadNodesAndCredentials {
|
||||
const push = Container.get(Push);
|
||||
|
||||
Object.values(this.loaders).forEach(async (loader) => {
|
||||
const { directory } = loader;
|
||||
try {
|
||||
await fsPromises.access(loader.directory);
|
||||
await fsPromises.access(directory);
|
||||
} catch {
|
||||
// If directory doesn't exist, there is nothing to watch
|
||||
return;
|
||||
}
|
||||
|
||||
const realModulePath = path.join(await fsPromises.realpath(loader.directory), path.sep);
|
||||
const reloader = debounce(async () => {
|
||||
const modulesToUnload = Object.keys(require.cache).filter((filePath) =>
|
||||
filePath.startsWith(realModulePath),
|
||||
);
|
||||
modulesToUnload.forEach((filePath) => {
|
||||
delete require.cache[filePath];
|
||||
});
|
||||
|
||||
loader.reset();
|
||||
await loader.loadAll();
|
||||
await this.postProcessLoaders();
|
||||
@@ -549,11 +542,11 @@ export class LoadNodesAndCredentials {
|
||||
? ['**/nodes.json', '**/credentials.json']
|
||||
: ['**/*.js', '**/*.json'];
|
||||
const files = await glob(toWatch, {
|
||||
cwd: realModulePath,
|
||||
cwd: directory,
|
||||
ignore: ['node_modules/**'],
|
||||
});
|
||||
const watcher = watch(files, {
|
||||
cwd: realModulePath,
|
||||
cwd: directory,
|
||||
ignoreInitial: true,
|
||||
});
|
||||
watcher.on('add', reloader).on('change', reloader).on('unlink', reloader);
|
||||
|
||||
@@ -13,6 +13,7 @@ jest.mock('node:fs');
|
||||
jest.mock('node:fs/promises');
|
||||
const mockFs = mock<typeof fs>();
|
||||
const mockFsPromises = mock<typeof fsPromises>();
|
||||
fs.realpathSync = mockFs.realpathSync;
|
||||
fs.readFileSync = mockFs.readFileSync;
|
||||
fsPromises.readFile = mockFsPromises.readFile;
|
||||
|
||||
@@ -64,6 +65,7 @@ describe('DirectoryLoader', () => {
|
||||
let mockCredential1: ICredentialType, mockNode1: INodeType, mockNode2: INodeType;
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs.realpathSync.mockImplementation((path) => String(path));
|
||||
mockCredential1 = createCredential('credential1');
|
||||
mockNode1 = createNode('node1', 'credential1');
|
||||
mockNode2 = createNode('node2');
|
||||
@@ -330,6 +332,19 @@ describe('DirectoryLoader', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should resolve symlinks to real paths when directory is a symlink', () => {
|
||||
const symlinkPath = '/symlink/path';
|
||||
const realPath = '/real/path';
|
||||
mockFs.realpathSync.mockReturnValueOnce(realPath);
|
||||
|
||||
const loader = new CustomDirectoryLoader(symlinkPath);
|
||||
|
||||
expect(mockFs.realpathSync).toHaveBeenCalledWith(symlinkPath);
|
||||
expect(loader.directory).toBe(realPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reset()', () => {
|
||||
it('should reset all properties to their initial state', async () => {
|
||||
mockFs.readFileSync.calledWith(`${directory}/package.json`).mockReturnValue(packageJson);
|
||||
|
||||
@@ -17,6 +17,7 @@ import type {
|
||||
KnownNodesAndCredentials,
|
||||
} from 'n8n-workflow';
|
||||
import { ApplicationError, isSubNodeType } from 'n8n-workflow';
|
||||
import { realpathSync } from 'node:fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { UnrecognizedCredentialTypeError } from '@/errors/unrecognized-credential-type.error';
|
||||
@@ -83,13 +84,22 @@ export abstract class DirectoryLoader {
|
||||
readonly directory: string,
|
||||
protected excludeNodes: string[] = [],
|
||||
protected includeNodes: string[] = [],
|
||||
) {}
|
||||
) {
|
||||
// If `directory` is a symlink, we try to resolve it to its real path
|
||||
try {
|
||||
this.directory = realpathSync(directory);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (error.code !== 'ENOENT') throw error;
|
||||
}
|
||||
}
|
||||
|
||||
abstract packageName: string;
|
||||
|
||||
abstract loadAll(): Promise<void>;
|
||||
|
||||
reset() {
|
||||
this.unloadAll();
|
||||
this.loadedNodes = [];
|
||||
this.nodeTypes = {};
|
||||
this.credentialTypes = {};
|
||||
@@ -450,4 +460,13 @@ export abstract class DirectoryLoader {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private unloadAll() {
|
||||
const filesToUnload = Object.keys(require.cache).filter((filePath) =>
|
||||
filePath.startsWith(this.directory),
|
||||
);
|
||||
filesToUnload.forEach((filePath) => {
|
||||
delete require.cache[filePath];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user