diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 3484881975..61face1cab 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -14,7 +14,7 @@ "format:check": "biome ci .", "lint": "eslint nodes credentials utils --quiet", "lintfix": "eslint nodes credentials utils --fix", - "watch": "tsup --watch --tsconfig tsconfig.build.json --onSuccess \"pnpm copy-nodes-json && tsc-alias -p tsconfig.build.json && pnpm n8n-generate-metadata\"", + "watch": "tsup --watch nodes --watch credentials --watch utils --watch types --tsconfig tsconfig.build.json --onSuccess \"node ./scripts/post-build.js\"", "test": "jest", "test:dev": "jest --watch" }, diff --git a/packages/@n8n/nodes-langchain/scripts/post-build.js b/packages/@n8n/nodes-langchain/scripts/post-build.js new file mode 100644 index 0000000000..9450175799 --- /dev/null +++ b/packages/@n8n/nodes-langchain/scripts/post-build.js @@ -0,0 +1,23 @@ +/** + * Post-build script + * + * This is a separate script instead of inline npm commands because using "&&"" to chain commands in --onSuccess can cause the watch mode to hang + */ + +const { execSync } = require('child_process'); + +function runCommand(command) { + try { + execSync(command, { stdio: 'inherit' }); + } catch (error) { + console.error(`Command failed: ${command}`); + process.exit(1); + } +} + +// Run all post-build tasks +runCommand('npx tsc-alias -p tsconfig.build.json'); +runCommand('node scripts/copy-tokenizer-json.js .'); +runCommand('node ../../nodes-base/scripts/copy-nodes-json.js .'); +runCommand('pnpm n8n-copy-static-files'); +runCommand('pnpm n8n-generate-metadata'); diff --git a/packages/cli/package.json b/packages/cli/package.json index 64559bd930..b1caff3273 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -10,7 +10,7 @@ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pnpm run build:data", "build:data": "node scripts/build.mjs", "buildAndDev": "pnpm run build && pnpm run dev", - "dev": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon\"", + "dev": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon --delay 1\"", "dev:worker": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon worker\"", "dev:webhook": "concurrently -k -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold\" \"npm run watch\" \"nodemon webhook\"", "format": "biome format --write .", diff --git a/packages/cli/src/load-nodes-and-credentials.ts b/packages/cli/src/load-nodes-and-credentials.ts index 38678bdb6d..a3f5cc8454 100644 --- a/packages/cli/src/load-nodes-and-credentials.ts +++ b/packages/cli/src/load-nodes-and-credentials.ts @@ -26,6 +26,7 @@ import type { LoadedNodesAndCredentials, } from 'n8n-workflow'; import { deepCopy, NodeConnectionTypes, UnexpectedError, UserError } from 'n8n-workflow'; +import { type Stats } from 'node:fs'; import path from 'path'; import picocolors from 'picocolors'; @@ -532,24 +533,47 @@ export class LoadNodesAndCredentials { } const reloader = debounce(async () => { - loader.reset(); - await loader.loadAll(); - await this.postProcessLoaders(); - push.broadcast({ type: 'nodeDescriptionUpdated', data: {} }); + this.logger.info(`Hot reload triggered for ${loader.packageName}`); + try { + loader.reset(); + await loader.loadAll(); + await this.postProcessLoaders(); + push.broadcast({ type: 'nodeDescriptionUpdated', data: {} }); + } catch (error) { + this.logger.error(`Hot reload failed for ${loader.packageName}`); + } }, 100); - const toWatch = loader.isLazyLoaded - ? ['**/nodes.json', '**/credentials.json'] - : ['**/*.js', '**/*.json']; - const files = await glob(toWatch, { - cwd: directory, - ignore: ['node_modules/**'], - }); - const watcher = watch(files, { - cwd: directory, + // For lazy loaded packages, we need to watch the dist directory + const watchPath = loader.isLazyLoaded ? path.join(directory, 'dist') : directory; + + // Watch options for chokidar v4 + const watchOptions = { ignoreInitial: true, - }); - watcher.on('add', reloader).on('change', reloader).on('unlink', reloader); + cwd: directory, + // Filter which files to watch based on loader type + ignored: (filePath: string, stats?: Stats) => { + if (!stats) return false; + if (stats.isDirectory()) return false; + if (filePath.includes('node_modules')) return true; + + if (loader.isLazyLoaded) { + // Only watch nodes.json and credentials.json files + const basename = path.basename(filePath); + return basename !== 'nodes.json' && basename !== 'credentials.json'; + } + + // Watch all .js and .json files + return !filePath.endsWith('.js') && !filePath.endsWith('.json'); + }, + }; + + const watcher = watch(watchPath, watchOptions); + + // Watch for file changes and additions + // Not watching removals to prevent issues during build processes + watcher.on('change', reloader); + watcher.on('add', reloader); }); } }