From 1359a9c46bfc55aa7e0a990cfb5bd548df002a6e Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 16 Feb 2023 23:05:35 +0100 Subject: [PATCH 1/6] feat: add support for monorepos and JS-Controller 5 --- dist/index.js | 88 ++++++++++++++++++++++++++++++++---------------- src/index.ts | 93 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 125 insertions(+), 56 deletions(-) diff --git a/dist/index.js b/dist/index.js index df0bb1f1..472322b0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -89,6 +89,12 @@ class DevServer { default: 'latest', description: 'Define which version of admin to be used', }, + entrypoint: { + type: 'string', + alias: 'e', + default: '.', + description: 'For monorepos only - Defines the path relative to the current directory, where the adapter is located.', + }, backupFile: { type: 'string', alias: 'b', @@ -101,7 +107,7 @@ class DevServer { default: false, description: 'Use symlinks instead of packing and installing the current adapter for a smoother dev experience. Requires JS-Controller 5+.', }, - }, async (args) => await this.setup(args.adminPort, { ['iobroker.js-controller']: args.jsController, ['iobroker.admin']: args.admin }, args.backupFile, !!args.force, args.symlinks)) + }, async (args) => await this.setup(args.adminPort, { ['iobroker.js-controller']: args.jsController, ['iobroker.admin']: args.admin }, args.entrypoint, args.backupFile, !!args.force, args.symlinks)) .command(['update [profile]', 'ud'], 'Update ioBroker and its dependencies to the latest versions', {}, async () => await this.update()) .command(['run [profile]', 'r'], 'Run ioBroker dev-server, the adapter will not run, but you may test the Admin UI with hot-reload', {}, async () => await this.run()) .command(['watch [profile]', 'w'], 'Run ioBroker dev-server and start the adapter in "watch" mode. The adapter will automatically restart when its source code changes. You may attach a debugger to the running adapter.', { @@ -149,7 +155,7 @@ class DevServer { .middleware(async (argv) => await this.setLogger(argv)) .middleware(async () => await this.checkVersion()) .middleware(async (argv) => await this.setDirectories(argv)) - .middleware(async () => await this.parseConfig()) + .middleware(async (argv) => await this.parseConfig(argv)) .wrap(Math.min(100, parser.terminalWidth())) .help().argv; } @@ -236,27 +242,43 @@ class DevServer { } } if (!profileName.match(/^[a-z0-9_-]+$/i)) { - throw new Error(`Invaid profile name: "${profileName}", it may only contain a-z, 0-9, _ and -.`); + throw new Error(`Invalid profile name: "${profileName}", it may only contain a-z, 0-9, _ and -.`); } this.profileName = profileName; this.log.debug(`Using profile name "${this.profileName}"`); this.profileDir = path.join(this.tempDir, profileName); - this.adapterName = await this.findAdapterName(); } - async parseConfig() { + async parseConfig(argv) { let pkg; try { pkg = await (0, fs_extra_1.readJson)(path.join(this.profileDir, 'package.json')); + this.config = pkg['dev-server']; } catch (_a) { // not all commands need the config - return; } - this.config = pkg['dev-server']; + this.adapterName = await this.findAdapterName(argv.entrypoint); + } + async isMonorepo() { + try { + const pkg = await (0, fs_extra_1.readJson)(path.join(this.rootDir, 'package.json')); + return pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0; + } + catch (_a) { + return false; + } } - async findAdapterName() { + async findAdapterName(entrypoint) { + var _a, _b; + this.entrypoint = path.join(this.rootDir, (_b = entrypoint !== null && entrypoint !== void 0 ? entrypoint : (_a = this.config) === null || _a === void 0 ? void 0 : _a.entrypoint) !== null && _b !== void 0 ? _b : '.'); + if (await this.isMonorepo()) { + if (this.entrypoint === this.rootDir) { + this.log.error('The current directory is a monorepo. You must specify where the adapter is located using the "--entrypoint" option during setup.'); + return this.exit(-1); + } + } try { - const ioPackage = await (0, fs_extra_1.readJson)(path.join(this.rootDir, 'io-package.json')); + const ioPackage = await (0, fs_extra_1.readJson)(path.join(this.entrypoint, 'io-package.json')); const adapterName = ioPackage.common.name; this.log.debug(`Using adapter name "${adapterName}"`); return adapterName; @@ -271,7 +293,7 @@ class DevServer { return this.adapterName === 'js-controller'; } readPackageJson() { - return (0, fs_extra_1.readJson)(path.join(this.rootDir, 'package.json')); + return (0, fs_extra_1.readJson)(path.join(this.entrypoint, 'package.json')); } getPort(adminPort, offset) { let port = adminPort + offset; @@ -281,7 +303,7 @@ class DevServer { return port; } ////////////////// Command Handlers ////////////////// - async setup(adminPort, dependencies, backupFile, force, useSymlinks = false) { + async setup(adminPort, dependencies, entrypoint, backupFile, force, useSymlinks = false) { if (force) { this.log.notice(`Deleting ${this.profileDir}`); await this.rimraf(this.profileDir); @@ -291,7 +313,7 @@ class DevServer { this.log.debug(`Use --force to set it up from scratch (all data will be lost).`); return; } - await this.setupDevServer(adminPort, dependencies, backupFile, useSymlinks); + await this.setupDevServer(adminPort, dependencies, entrypoint, backupFile, useSymlinks); const commands = ['run', 'watch', 'debug']; this.log.box(`dev-server was sucessfully set up in\n${this.profileDir}.\n\n` + `You may now execute one of the following commands\n\n${commands @@ -513,7 +535,7 @@ class DevServer { ws: true, })); } - else if ((0, fs_extra_1.existsSync)(path.resolve(this.rootDir, 'admin/jsonConfig.json'))) { + else if ((0, fs_extra_1.existsSync)(path.resolve(this.entrypoint, 'admin/jsonConfig.json'))) { // JSON config await this.createJsonConfigProxy(app, this.config); } @@ -583,7 +605,7 @@ class DevServer { const browserSyncPort = this.getPort(config.adminPort, HIDDEN_BROWSER_SYNC_PORT_OFFSET); const bs = this.startBrowserSync(browserSyncPort, false); // whenever jsonConfig.json changes, we upload the new file - const jsonConfig = path.resolve(this.rootDir, 'admin/jsonConfig.json'); + const jsonConfig = path.resolve(this.entrypoint, 'admin/jsonConfig.json'); bs.watch(jsonConfig, undefined, async (e) => { var _a; if (e === 'change') { @@ -624,7 +646,7 @@ class DevServer { if (scripts['watch:react']) { await this.startReact('watch:react'); hasReact = true; - if ((0, fs_extra_1.existsSync)(path.resolve(this.rootDir, 'admin/.watch'))) { + if ((0, fs_extra_1.existsSync)(path.resolve(this.entrypoint, 'admin/.watch'))) { // rewrite the build directory to the .watch directory, // because "watch:react" no longer updates the build directory automatically pathRewrite[`^/adapter/${this.adapterName}/build/`] = '/.watch/'; @@ -658,12 +680,12 @@ class DevServer { this.log.notice(`Creating or patching sourcemaps in ${outDir}`); const sourcemaps = await this.findFiles('map', true); if (sourcemaps.length === 0) { - this.log.debug(`Couldn't find any sourcemaps in ${this.rootDir},\nwill try to reverse map .js files`); + this.log.debug(`Couldn't find any sourcemaps in ${this.entrypoint},\nwill try to reverse map .js files`); // search all .js files that exist in the node module in the temp directory as well as in the root directory and // create sourcemap files for each of them const jsFiles = await this.findFiles('js', true); await Promise.all(jsFiles.map(async (js) => { - const src = path.join(this.rootDir, js); + const src = path.join(this.entrypoint, js); const dest = path.join(outDir, js); await this.addSourcemap(src, dest, false); })); @@ -672,7 +694,7 @@ class DevServer { // copy all *.map files to the node module in the temp directory and // change their sourceRoot so they can be found in the development directory await Promise.all(sourcemaps.map(async (sourcemap) => { - const src = path.join(this.rootDir, sourcemap); + const src = path.join(this.entrypoint, sourcemap); const dest = path.join(outDir, sourcemap); await this.patchSourcemap(src, dest); })); @@ -741,6 +763,7 @@ class DevServer { return patterns; } async findFiles(extension, excludeAdmin) { + // TODO: Maybe we need to set cwd to this.entrypoint? return await (0, fast_glob_1.default)(this.getFilePatterns(extension, excludeAdmin), { cwd: this.rootDir }); } async createIdentitySourcemap(filename) { @@ -769,14 +792,14 @@ class DevServer { async startReact(scriptName) { this.log.notice('Starting React build'); this.log.debug('Waiting for first successful React build...'); - await this.spawnAndAwaitOutput('npm', ['run', scriptName], this.rootDir, /(built in|done in|watching (files )?for)/i, { + await this.spawnAndAwaitOutput('npm', ['run', scriptName], this.entrypoint, /(built in|done in|watching (files )?for)/i, { shell: true, }); } startBrowserSync(port, hasReact) { this.log.notice('Starting browser-sync'); const bs = browser_sync_1.default.create(); - const adminPath = path.resolve(this.rootDir, 'admin/'); + const adminPath = path.resolve(this.entrypoint, 'admin/'); const config = { server: { baseDir: adminPath, directory: true }, port: port, @@ -872,16 +895,18 @@ class DevServer { async startTscWatch() { this.log.notice('Starting tsc --watch'); this.log.debug('Waiting for first successful tsc build...'); - await this.spawnAndAwaitOutput('npm', ['run', 'watch:ts'], this.rootDir, /watching (files )?for/i, { shell: true }); + await this.spawnAndAwaitOutput('npm', ['run', 'watch:ts'], this.entrypoint, /watching (files )?for/i, { + shell: true, + }); } startFileSync(destinationDir) { - this.log.notice(`Starting file system sync from ${this.rootDir}`); - const inSrc = (filename) => path.join(this.rootDir, filename); + this.log.notice(`Starting file system sync from ${this.entrypoint}`); + const inSrc = (filename) => path.join(this.entrypoint, filename); const inDest = (filename) => path.join(destinationDir, filename); return new Promise((resolve, reject) => { const patterns = this.getFilePatterns(['js', 'map'], true); const ignoreFiles = []; - const watcher = chokidar_1.default.watch(patterns, { cwd: this.rootDir }); + const watcher = chokidar_1.default.watch(patterns, { cwd: this.entrypoint }); let ready = false; let initialEventPromises = []; watcher.on('error', reject); @@ -1028,12 +1053,13 @@ class DevServer { const debigPid = await this.waitForNodeChildProcess(parseInt(match[1])); this.log.box(`Debugger is now available on process id ${debigPid}`); } - async setupDevServer(adminPort, dependencies, backupFile, useSymlinks) { - await this.buildLocalAdapter(); + async setupDevServer(adminPort, dependencies, entrypoint, backupFile, useSymlinks) { + // await this.buildLocalAdapter(); this.log.notice(`Setting up in ${this.profileDir}`); this.config = { adminPort, useSymlinks, + entrypoint, }; // create the data directory const dataDir = path.join(this.profileDir, 'iobroker-data'); @@ -1124,6 +1150,9 @@ class DevServer { useSymlinks, }, }; + if (entrypoint !== '.') { + pkg['dev-server'].entrypoint = entrypoint; + } await (0, fs_extra_1.writeJson)(path.join(this.profileDir, 'package.json'), pkg, { spaces: 2 }); // Tell npm to link the local adapter folder instead of creating a copy if (useSymlinks) { @@ -1285,6 +1314,7 @@ class DevServer { const pkg = await this.readPackageJson(); if ((_a = pkg.scripts) === null || _a === void 0 ? void 0 : _a.build) { this.log.notice(`Build iobroker.${this.adapterName}`); + // TODO: Figure out if we need to build in the root or the entrypoint directory this.execSync('npm run build', this.rootDir); } } @@ -1293,7 +1323,7 @@ class DevServer { this.log.notice(`Install local iobroker.${this.adapterName}`); if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.useSymlinks) { // This is the expected relative path - const relativePath = path.relative(this.profileDir, this.rootDir); + const relativePath = path.relative(this.profileDir, this.entrypoint); // Check if it is already used in package.json const tempPkg = await (0, fs_extra_1.readJson)(path.join(this.profileDir, 'package.json')); const depPath = (_b = tempPkg.dependencies) === null || _b === void 0 ? void 0 : _b[`iobroker.${this.adapterName}`]; @@ -1303,10 +1333,10 @@ class DevServer { } } else { - const { stdout } = await this.getExecOutput('npm pack', this.rootDir); + const { stdout } = await this.getExecOutput('npm pack', this.entrypoint); const filename = stdout.trim(); this.log.info(`Packed to ${filename}`); - const fullPath = path.join(this.rootDir, filename); + const fullPath = path.join(this.entrypoint, filename); this.execSync(`npm install "${fullPath}"`, this.profileDir); await this.rimraf(fullPath); } diff --git a/src/index.ts b/src/index.ts index 65b9d4c2..5003102a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,6 +53,8 @@ const DEFAULT_PROFILE_NAME = 'default'; interface DevServerConfig { adminPort: number; useSymlinks: boolean; + /** The directory relative to the rootDir where the adapter/controller is located. Useful for monorepos. */ + entrypoint: string; } type CoreDependency = 'iobroker.js-controller' | 'iobroker.admin'; @@ -61,6 +63,7 @@ type DependencyVersions = Partial>; class DevServer { private log!: Logger; private rootDir!: string; + private entrypoint!: string; private adapterName!: string; private tempDir!: string; private profileName!: string; @@ -101,6 +104,13 @@ class DevServer { default: 'latest', description: 'Define which version of admin to be used', }, + entrypoint: { + type: 'string', + alias: 'e', + default: '.', + description: + 'For monorepos only - Defines the path relative to the current directory, where the adapter is located.', + }, backupFile: { type: 'string', alias: 'b', @@ -119,6 +129,7 @@ class DevServer { await this.setup( args.adminPort, { ['iobroker.js-controller']: args.jsController, ['iobroker.admin']: args.admin }, + args.entrypoint, args.backupFile, !!args.force, args.symlinks, @@ -207,7 +218,7 @@ class DevServer { .middleware(async (argv) => await this.setLogger(argv)) .middleware(async () => await this.checkVersion()) .middleware(async (argv) => await this.setDirectories(argv)) - .middleware(async () => await this.parseConfig()) + .middleware(async (argv) => await this.parseConfig(argv as any)) .wrap(Math.min(100, parser.terminalWidth())) .help().argv; } @@ -306,30 +317,48 @@ class DevServer { } if (!profileName.match(/^[a-z0-9_-]+$/i)) { - throw new Error(`Invaid profile name: "${profileName}", it may only contain a-z, 0-9, _ and -.`); + throw new Error(`Invalid profile name: "${profileName}", it may only contain a-z, 0-9, _ and -.`); } this.profileName = profileName; this.log.debug(`Using profile name "${this.profileName}"`); this.profileDir = path.join(this.tempDir, profileName); - this.adapterName = await this.findAdapterName(); } - private async parseConfig(): Promise { + private async parseConfig(argv: { entrypoint?: string }): Promise { let pkg: Record; try { pkg = await readJson(path.join(this.profileDir, 'package.json')); + this.config = pkg['dev-server']; } catch { // not all commands need the config - return; } - this.config = pkg['dev-server']; + this.adapterName = await this.findAdapterName(argv.entrypoint); } - private async findAdapterName(): Promise { + private async isMonorepo(): Promise { try { - const ioPackage = await readJson(path.join(this.rootDir, 'io-package.json')); + const pkg = await readJson(path.join(this.rootDir, 'package.json')); + return pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0; + } catch { + return false; + } + } + + private async findAdapterName(entrypoint?: string): Promise { + this.entrypoint = path.join(this.rootDir, entrypoint ?? this.config?.entrypoint ?? '.'); + if (await this.isMonorepo()) { + if (this.entrypoint === this.rootDir) { + this.log.error( + 'The current directory is a monorepo. You must specify where the adapter is located using the "--entrypoint" option during setup.', + ); + return this.exit(-1); + } + } + + try { + const ioPackage = await readJson(path.join(this.entrypoint, 'io-package.json')); const adapterName = ioPackage.common.name; this.log.debug(`Using adapter name "${adapterName}"`); return adapterName; @@ -345,7 +374,7 @@ class DevServer { } private readPackageJson(): Promise { - return readJson(path.join(this.rootDir, 'package.json')); + return readJson(path.join(this.entrypoint, 'package.json')); } private getPort(adminPort: number, offset: number): number { @@ -361,6 +390,7 @@ class DevServer { async setup( adminPort: number, dependencies: DependencyVersions, + entrypoint: string, backupFile?: string, force?: boolean, useSymlinks = false, @@ -376,7 +406,7 @@ class DevServer { return; } - await this.setupDevServer(adminPort, dependencies, backupFile, useSymlinks); + await this.setupDevServer(adminPort, dependencies, entrypoint, backupFile, useSymlinks); const commands = ['run', 'watch', 'debug']; this.log.box( @@ -640,7 +670,7 @@ class DevServer { ws: true, }), ); - } else if (existsSync(path.resolve(this.rootDir, 'admin/jsonConfig.json'))) { + } else if (existsSync(path.resolve(this.entrypoint, 'admin/jsonConfig.json'))) { // JSON config await this.createJsonConfigProxy(app, this.config); } else { @@ -713,7 +743,7 @@ class DevServer { const bs = this.startBrowserSync(browserSyncPort, false); // whenever jsonConfig.json changes, we upload the new file - const jsonConfig = path.resolve(this.rootDir, 'admin/jsonConfig.json'); + const jsonConfig = path.resolve(this.entrypoint, 'admin/jsonConfig.json'); bs.watch(jsonConfig, undefined, async (e) => { if (e === 'change') { const content = await readFile(jsonConfig); @@ -765,7 +795,7 @@ class DevServer { await this.startReact('watch:react'); hasReact = true; - if (existsSync(path.resolve(this.rootDir, 'admin/.watch'))) { + if (existsSync(path.resolve(this.entrypoint, 'admin/.watch'))) { // rewrite the build directory to the .watch directory, // because "watch:react" no longer updates the build directory automatically pathRewrite[`^/adapter/${this.adapterName}/build/`] = '/.watch/'; @@ -806,14 +836,14 @@ class DevServer { this.log.notice(`Creating or patching sourcemaps in ${outDir}`); const sourcemaps = await this.findFiles('map', true); if (sourcemaps.length === 0) { - this.log.debug(`Couldn't find any sourcemaps in ${this.rootDir},\nwill try to reverse map .js files`); + this.log.debug(`Couldn't find any sourcemaps in ${this.entrypoint},\nwill try to reverse map .js files`); // search all .js files that exist in the node module in the temp directory as well as in the root directory and // create sourcemap files for each of them const jsFiles = await this.findFiles('js', true); await Promise.all( jsFiles.map(async (js) => { - const src = path.join(this.rootDir, js); + const src = path.join(this.entrypoint, js); const dest = path.join(outDir, js); await this.addSourcemap(src, dest, false); }), @@ -825,7 +855,7 @@ class DevServer { // change their sourceRoot so they can be found in the development directory await Promise.all( sourcemaps.map(async (sourcemap) => { - const src = path.join(this.rootDir, sourcemap); + const src = path.join(this.entrypoint, sourcemap); const dest = path.join(outDir, sourcemap); await this.patchSourcemap(src, dest); }), @@ -899,6 +929,7 @@ class DevServer { } private async findFiles(extension: string, excludeAdmin: boolean): Promise { + // TODO: Maybe we need to set cwd to this.entrypoint? return await fg(this.getFilePatterns(extension, excludeAdmin), { cwd: this.rootDir }); } @@ -935,7 +966,7 @@ class DevServer { await this.spawnAndAwaitOutput( 'npm', ['run', scriptName], - this.rootDir, + this.entrypoint, /(built in|done in|watching (files )?for)/i, { shell: true, @@ -947,7 +978,7 @@ class DevServer { this.log.notice('Starting browser-sync'); const bs = browserSync.create(); - const adminPath = path.resolve(this.rootDir, 'admin/'); + const adminPath = path.resolve(this.entrypoint, 'admin/'); const config: browserSync.Options = { server: { baseDir: adminPath, directory: true }, port: port, @@ -1054,17 +1085,19 @@ class DevServer { private async startTscWatch(): Promise { this.log.notice('Starting tsc --watch'); this.log.debug('Waiting for first successful tsc build...'); - await this.spawnAndAwaitOutput('npm', ['run', 'watch:ts'], this.rootDir, /watching (files )?for/i, { shell: true }); + await this.spawnAndAwaitOutput('npm', ['run', 'watch:ts'], this.entrypoint, /watching (files )?for/i, { + shell: true, + }); } private startFileSync(destinationDir: string): Promise { - this.log.notice(`Starting file system sync from ${this.rootDir}`); - const inSrc = (filename: string): string => path.join(this.rootDir, filename); + this.log.notice(`Starting file system sync from ${this.entrypoint}`); + const inSrc = (filename: string): string => path.join(this.entrypoint, filename); const inDest = (filename: string): string => path.join(destinationDir, filename); return new Promise((resolve, reject) => { const patterns = this.getFilePatterns(['js', 'map'], true); const ignoreFiles = [] as string[]; - const watcher = chokidar.watch(patterns, { cwd: this.rootDir }); + const watcher = chokidar.watch(patterns, { cwd: this.entrypoint }); let ready = false; let initialEventPromises: Promise[] = []; watcher.on('error', reject); @@ -1220,15 +1253,17 @@ class DevServer { async setupDevServer( adminPort: number, dependencies: DependencyVersions, + entrypoint: string, backupFile: string | undefined, useSymlinks: boolean, ): Promise { - await this.buildLocalAdapter(); + // await this.buildLocalAdapter(); this.log.notice(`Setting up in ${this.profileDir}`); this.config = { adminPort, useSymlinks, + entrypoint, }; // create the data directory @@ -1320,8 +1355,11 @@ class DevServer { 'dev-server': { adminPort, useSymlinks, - }, + } as Record, }; + if (entrypoint !== '.') { + pkg['dev-server'].entrypoint = entrypoint; + } await writeJson(path.join(this.profileDir, 'package.json'), pkg, { spaces: 2 }); // Tell npm to link the local adapter folder instead of creating a copy @@ -1502,6 +1540,7 @@ class DevServer { const pkg = await this.readPackageJson(); if (pkg.scripts?.build) { this.log.notice(`Build iobroker.${this.adapterName}`); + // TODO: Figure out if we need to build in the root or the entrypoint directory this.execSync('npm run build', this.rootDir); } } @@ -1511,7 +1550,7 @@ class DevServer { if (this.config?.useSymlinks) { // This is the expected relative path - const relativePath = path.relative(this.profileDir, this.rootDir); + const relativePath = path.relative(this.profileDir, this.entrypoint); // Check if it is already used in package.json const tempPkg = await readJson(path.join(this.profileDir, 'package.json')); const depPath = tempPkg.dependencies?.[`iobroker.${this.adapterName}`]; @@ -1520,10 +1559,10 @@ class DevServer { this.execSync(`npm install "${relativePath}"`, this.profileDir); } } else { - const { stdout } = await this.getExecOutput('npm pack', this.rootDir); + const { stdout } = await this.getExecOutput('npm pack', this.entrypoint); const filename = stdout.trim(); this.log.info(`Packed to ${filename}`); - const fullPath = path.join(this.rootDir, filename); + const fullPath = path.join(this.entrypoint, filename); this.execSync(`npm install "${fullPath}"`, this.profileDir); await this.rimraf(fullPath); } From 661c1d98e8d59b4b44c31e646673fd01fab7b4dc Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 16 Feb 2023 23:34:34 +0100 Subject: [PATCH 2/6] fix: prevent setcap call, forgot a --preserve-symlinks --- dist/index.js | 6 +++++- src/index.ts | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 472322b0..1f1672fe 100644 --- a/dist/index.js +++ b/dist/index.js @@ -56,7 +56,7 @@ const EventEmitter = require("events"); const DEFAULT_TEMP_DIR_NAME = '.dev-server'; const CORE_MODULE = 'iobroker.js-controller'; const IOBROKER_CLI = 'node_modules/iobroker.js-controller/iobroker.js'; -const IOBROKER_COMMAND = `node ${IOBROKER_CLI}`; +const IOBROKER_COMMAND = `node --preserve-symlinks-main --preserve-symlinks ${IOBROKER_CLI}`; const DEFAULT_ADMIN_PORT = 8081; const HIDDEN_ADMIN_PORT_OFFSET = 12345; const HIDDEN_BROWSER_SYNC_PORT_OFFSET = 14345; @@ -483,6 +483,10 @@ class DevServer { } } async startJsController() { + // Store the current Node.js version, so JS-Controller doesn't try to `sudo setcap` + await this.withDb(async (db) => { + await db.setState(`system.host.${(0, os_1.hostname)()}.nodeVersion`, process.versions.node); + }); const proc = await this.spawn('node', [ '--inspect=127.0.0.1:9228', '--preserve-symlinks', diff --git a/src/index.ts b/src/index.ts index 5003102a..731bf858 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,7 +42,7 @@ import EventEmitter = require('events'); const DEFAULT_TEMP_DIR_NAME = '.dev-server'; const CORE_MODULE = 'iobroker.js-controller'; const IOBROKER_CLI = 'node_modules/iobroker.js-controller/iobroker.js'; -const IOBROKER_COMMAND = `node ${IOBROKER_CLI}`; +const IOBROKER_COMMAND = `node --preserve-symlinks-main --preserve-symlinks ${IOBROKER_CLI}`; const DEFAULT_ADMIN_PORT = 8081; const HIDDEN_ADMIN_PORT_OFFSET = 12345; const HIDDEN_BROWSER_SYNC_PORT_OFFSET = 14345; @@ -605,6 +605,11 @@ class DevServer { } async startJsController(): Promise { + // Store the current Node.js version, so JS-Controller doesn't try to `sudo setcap` + await this.withDb(async (db) => { + await db.setState(`system.host.${hostname()}.nodeVersion`, process.versions.node); + }); + const proc = await this.spawn( 'node', [ From 7264ad9b943b152d1e87585b94e67c555569202c Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Fri, 17 Feb 2023 23:51:32 +0100 Subject: [PATCH 3/6] remember and watch workspaces, ignore files written by JS-controller --- dist/index.js | 32 ++++++++++++++++++++++++++------ src/index.ts | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/dist/index.js b/dist/index.js index 1f1672fe..577ebbae 100644 --- a/dist/index.js +++ b/dist/index.js @@ -124,7 +124,7 @@ class DevServer { doNotWatch: { type: 'string', alias: 'w', - description: 'Do not watch the given files or directories for changes (provide paths relative to the adapter base directory.', + description: 'Do not watch the given files or directories for changes (provide paths relative to the adapter base directory).', }, }, async (args) => await this.watch(!args.noStart, !!args.noInstall, args.doNotWatch)) .command(['debug [profile]', 'd'], 'Run ioBroker dev-server and start the adapter from ioBroker in "debug" mode. You may attach a debugger to the running adapter.', { @@ -259,7 +259,7 @@ class DevServer { } this.adapterName = await this.findAdapterName(argv.entrypoint); } - async isMonorepo() { + async checkMonorepo() { try { const pkg = await (0, fs_extra_1.readJson)(path.join(this.rootDir, 'package.json')); return pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0; @@ -271,7 +271,17 @@ class DevServer { async findAdapterName(entrypoint) { var _a, _b; this.entrypoint = path.join(this.rootDir, (_b = entrypoint !== null && entrypoint !== void 0 ? entrypoint : (_a = this.config) === null || _a === void 0 ? void 0 : _a.entrypoint) !== null && _b !== void 0 ? _b : '.'); - if (await this.isMonorepo()) { + // check if we are in a monorepo + try { + const pkg = await (0, fs_extra_1.readJson)(path.join(this.rootDir, 'package.json')); + if (pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0) { + this.workspaces = pkg.workspaces; + } + } + catch (_c) { + // ignore + } + if (this.workspaces) { if (this.entrypoint === this.rootDir) { this.log.error('The current directory is a monorepo. You must specify where the adapter is located using the "--entrypoint" option during setup.'); return this.exit(-1); @@ -886,9 +896,16 @@ class DevServer { // This is not necessary when using symlinks await this.startFileSync(adapterRunDir); } + // In monorepos make sure to watch all packages + const additionalWatchDirs = []; + if (this.workspaces) { + const directories = await (0, fast_glob_1.default)(this.workspaces, { onlyDirectories: true, cwd: this.rootDir, absolute: true }); + // TODO: Check if we need to account for backslashes on Windows + additionalWatchDirs.push(...directories.map((d) => (d.endsWith(path.sep) ? d : d + path.sep)).filter((d) => d !== this.entrypoint)); + } if (startAdapter) { await this.delay(3000); - await this.startNodemon(adapterRunDir, pkg.main, doNotWatch); + await this.startNodemon(adapterRunDir, pkg.main, doNotWatch, additionalWatchDirs); } else { this.log.box(`You can now start the adapter manually by running\n ` + @@ -972,7 +989,7 @@ class DevServer { }); }); } - async startNodemon(baseDir, scriptName, doNotWatch) { + async startNodemon(baseDir, scriptName, doNotWatch, additionalWatchDirs = []) { const script = path.resolve(baseDir, scriptName); this.log.notice(`Starting nodemon for ${script}`); let isExiting = false; @@ -984,6 +1001,9 @@ class DevServer { path.join(baseDir, 'admin'), // avoid recursively following symlinks path.join(baseDir, '.dev-server'), + // Do not watch some files that JS-Controller typically writes to: + 'iobroker.js-controller/pids.txt', + 'iobroker.js-controller/data/**', ]; if (doNotWatch.length > 0) { doNotWatch.forEach((entry) => ignoreList.push(path.join(baseDir, entry))); @@ -994,7 +1014,7 @@ class DevServer { verbose: true, // dump: true, // this will output the entire config and not do anything colours: false, - watch: [baseDir], + watch: [baseDir, ...additionalWatchDirs], ignore: ignoreList, ignoreRoot: [], delay: 2000, diff --git a/src/index.ts b/src/index.ts index 731bf858..19983e2b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,6 +64,7 @@ class DevServer { private log!: Logger; private rootDir!: string; private entrypoint!: string; + private workspaces: string[] | undefined; private adapterName!: string; private tempDir!: string; private profileName!: string; @@ -165,7 +166,7 @@ class DevServer { type: 'string', alias: 'w', description: - 'Do not watch the given files or directories for changes (provide paths relative to the adapter base directory.', + 'Do not watch the given files or directories for changes (provide paths relative to the adapter base directory).', }, }, async (args) => await this.watch(!args.noStart, !!args.noInstall, args.doNotWatch), @@ -337,7 +338,7 @@ class DevServer { this.adapterName = await this.findAdapterName(argv.entrypoint); } - private async isMonorepo(): Promise { + private async checkMonorepo(): Promise { try { const pkg = await readJson(path.join(this.rootDir, 'package.json')); return pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0; @@ -348,7 +349,18 @@ class DevServer { private async findAdapterName(entrypoint?: string): Promise { this.entrypoint = path.join(this.rootDir, entrypoint ?? this.config?.entrypoint ?? '.'); - if (await this.isMonorepo()) { + + // check if we are in a monorepo + try { + const pkg = await readJson(path.join(this.rootDir, 'package.json')); + if (pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0) { + this.workspaces = pkg.workspaces; + } + } catch { + // ignore + } + + if (this.workspaces) { if (this.entrypoint === this.rootDir) { this.log.error( 'The current directory is a monorepo. You must specify where the adapter is located using the "--entrypoint" option during setup.', @@ -1075,9 +1087,19 @@ class DevServer { await this.startFileSync(adapterRunDir); } + // In monorepos make sure to watch all packages + const additionalWatchDirs = []; + if (this.workspaces) { + const directories = await fg(this.workspaces, { onlyDirectories: true, cwd: this.rootDir, absolute: true }); + // TODO: Check if we need to account for backslashes on Windows + additionalWatchDirs.push( + ...directories.map((d) => (d.endsWith(path.sep) ? d : d + path.sep)).filter((d) => d !== this.entrypoint), + ); + } + if (startAdapter) { await this.delay(3000); - await this.startNodemon(adapterRunDir, pkg.main, doNotWatch); + await this.startNodemon(adapterRunDir, pkg.main, doNotWatch, additionalWatchDirs); } else { this.log.box( `You can now start the adapter manually by running\n ` + @@ -1160,7 +1182,12 @@ class DevServer { }); } - private async startNodemon(baseDir: string, scriptName: string, doNotWatch: string[]): Promise { + private async startNodemon( + baseDir: string, + scriptName: string, + doNotWatch: string[], + additionalWatchDirs: string[] = [], + ): Promise { const script = path.resolve(baseDir, scriptName); this.log.notice(`Starting nodemon for ${script}`); @@ -1175,6 +1202,9 @@ class DevServer { path.join(baseDir, 'admin'), // avoid recursively following symlinks path.join(baseDir, '.dev-server'), + // Do not watch some files that JS-Controller typically writes to: + 'iobroker.js-controller/pids.txt', + 'iobroker.js-controller/data/**', ]; if (doNotWatch.length > 0) { doNotWatch.forEach((entry) => ignoreList.push(path.join(baseDir, entry))); @@ -1186,7 +1216,7 @@ class DevServer { verbose: true, // dump: true, // this will output the entire config and not do anything colours: false, - watch: [baseDir], + watch: [baseDir, ...additionalWatchDirs], ignore: ignoreList, ignoreRoot: [], delay: 2000, From c9b9dc43da64a4a75fe2c8987d2182049f3b5db6 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Sat, 18 Feb 2023 00:03:40 +0100 Subject: [PATCH 4/6] refactor watch/debug commands, distinguish building from npm installing --- dist/index.js | 73 +++++++++++++++++++++++++++++++------------ src/index.ts | 85 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 118 insertions(+), 40 deletions(-) diff --git a/dist/index.js b/dist/index.js index 577ebbae..286b056f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -116,29 +116,48 @@ class DevServer { alias: 'n', description: 'Do not start the adapter itself, only watch for changes and sync them.', }, + noBuild: { + type: 'boolean', + alias: 'b', + description: 'Do not build the adapter before starting.', + }, noInstall: { type: 'boolean', alias: 'x', - description: 'Do not build and install the adapter before starting.', + description: 'Do not install the adapter before starting. Implies --noBuild.', }, doNotWatch: { type: 'string', alias: 'w', description: 'Do not watch the given files or directories for changes (provide paths relative to the adapter base directory).', }, - }, async (args) => await this.watch(!args.noStart, !!args.noInstall, args.doNotWatch)) + }, async (args) => await this.watch({ + start: !args.noStart, + install: !args.noInstall, + build: !args.noBuild, + ignore: args.doNotWatch, + })) .command(['debug [profile]', 'd'], 'Run ioBroker dev-server and start the adapter from ioBroker in "debug" mode. You may attach a debugger to the running adapter.', { wait: { type: 'boolean', alias: 'w', description: 'Start the adapter only once the debugger is attached.', }, + noBuild: { + type: 'boolean', + alias: 'b', + description: 'Do not build the adapter before starting.', + }, noInstall: { type: 'boolean', alias: 'x', - description: 'Do not build and install the adapter before starting.', + description: 'Do not build and install the adapter before starting. Implies --noBuild.', }, - }, async (args) => await this.debug(!!args.wait, !!args.noInstall)) + }, async (args) => await this.debug({ + wait: !!args.wait, + install: !args.noInstall, + build: !args.noBuild, + })) .command(['upload [profile]', 'ul'], 'Upload the current version of your adapter to the ioBroker dev-server. This is only required if you changed something relevant in your io-package.json', {}, async () => await this.upload()) .command(['backup [profile]', 'b'], 'Create an ioBroker backup to the given file.', {}, async (args) => await this.backup(args.filename)) .command(['profile', 'p'], 'List all dev-server profiles that exist in the current directory.', {}, async () => await this.profile()) @@ -331,12 +350,15 @@ class DevServer { .join('\n')}\n\nto use dev-server.`); } async update() { + var _a; await this.checkSetup(); this.log.notice('Updating everything...'); this.execSync('npm update --loglevel error', this.profileDir); this.uploadAdapter('admin'); - await this.buildLocalAdapter(); - await this.installLocalAdapter(); + if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.useSymlinks)) { + await this.buildLocalAdapter(); + await this.installLocalAdapter(); + } if (!this.isJSController()) this.uploadAdapter(this.adapterName); this.log.box(`dev-server was sucessfully updated.`); @@ -346,37 +368,48 @@ class DevServer { await this.startJsController(); await this.startServer(); } - async watch(startAdapter, noInstall, doNotWatch) { - let doNotWatchArr = []; - if (typeof doNotWatch === 'string') { - doNotWatchArr.push(doNotWatch); + async watch(options) { + const { start, install, build, ignore } = options; + let ignorePaths = []; + if (typeof ignore === 'string') { + ignorePaths.push(ignore); } - else if (Array.isArray(doNotWatch)) { - doNotWatchArr = doNotWatch; + else if (Array.isArray(ignore)) { + ignorePaths = ignore; } await this.checkSetup(); - if (!noInstall) { - await this.buildLocalAdapter(); + if (install) { + if (build) { + await this.buildLocalAdapter(); + } await this.installLocalAdapter(); } if (this.isJSController()) { // this watches actually js-controller - await this.startAdapterWatch(startAdapter, doNotWatchArr); + await this.startAdapterWatch(start, ignorePaths); await this.startServer(); } else { await this.startJsController(); await this.startServer(); - await this.startAdapterWatch(startAdapter, doNotWatchArr); + await this.startAdapterWatch(start, ignorePaths); } } - async debug(wait, noInstall) { + async debug(options) { + var _a; + const { wait, install, build } = options; await this.checkSetup(); - if (!noInstall) { - await this.buildLocalAdapter(); + if (install) { + if (build) { + await this.buildLocalAdapter(); + } await this.installLocalAdapter(); } - await this.copySourcemaps(); + // When using symlinks, copying the sourcemaps is not necessary + // TODO: Setup launch config instead? + if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.useSymlinks)) { + await this.copySourcemaps(); + } if (this.isJSController()) { await this.startJsControllerDebug(wait); await this.startServer(); diff --git a/src/index.ts b/src/index.ts index 19983e2b..868aa3ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -157,10 +157,15 @@ class DevServer { alias: 'n', description: 'Do not start the adapter itself, only watch for changes and sync them.', }, + noBuild: { + type: 'boolean', + alias: 'b', + description: 'Do not build the adapter before starting.', + }, noInstall: { type: 'boolean', alias: 'x', - description: 'Do not build and install the adapter before starting.', + description: 'Do not install the adapter before starting. Implies --noBuild.', }, doNotWatch: { type: 'string', @@ -169,7 +174,13 @@ class DevServer { 'Do not watch the given files or directories for changes (provide paths relative to the adapter base directory).', }, }, - async (args) => await this.watch(!args.noStart, !!args.noInstall, args.doNotWatch), + async (args) => + await this.watch({ + start: !args.noStart, + install: !args.noInstall, + build: !args.noBuild, + ignore: args.doNotWatch, + }), ) .command( ['debug [profile]', 'd'], @@ -180,13 +191,23 @@ class DevServer { alias: 'w', description: 'Start the adapter only once the debugger is attached.', }, + noBuild: { + type: 'boolean', + alias: 'b', + description: 'Do not build the adapter before starting.', + }, noInstall: { type: 'boolean', alias: 'x', - description: 'Do not build and install the adapter before starting.', + description: 'Do not build and install the adapter before starting. Implies --noBuild.', }, }, - async (args) => await this.debug(!!args.wait, !!args.noInstall), + async (args) => + await this.debug({ + wait: !!args.wait, + install: !args.noInstall, + build: !args.noBuild, + }), ) .command( ['upload [profile]', 'ul'], @@ -436,8 +457,10 @@ class DevServer { this.execSync('npm update --loglevel error', this.profileDir); this.uploadAdapter('admin'); - await this.buildLocalAdapter(); - await this.installLocalAdapter(); + if (!this.config?.useSymlinks) { + await this.buildLocalAdapter(); + await this.installLocalAdapter(); + } if (!this.isJSController()) this.uploadAdapter(this.adapterName); this.log.box(`dev-server was sucessfully updated.`); @@ -449,37 +472,59 @@ class DevServer { await this.startServer(); } - async watch(startAdapter: boolean, noInstall: boolean, doNotWatch: string | string[] | undefined): Promise { - let doNotWatchArr: string[] = []; - if (typeof doNotWatch === 'string') { - doNotWatchArr.push(doNotWatch); - } else if (Array.isArray(doNotWatch)) { - doNotWatchArr = doNotWatch; + async watch(options: { + start: boolean; + install: boolean; + build: boolean; + ignore: string | string[] | undefined; + }): Promise { + const { start, install, build, ignore } = options; + + let ignorePaths: string[] = []; + if (typeof ignore === 'string') { + ignorePaths.push(ignore); + } else if (Array.isArray(ignore)) { + ignorePaths = ignore; } await this.checkSetup(); - if (!noInstall) { - await this.buildLocalAdapter(); + + if (install) { + if (build) { + await this.buildLocalAdapter(); + } await this.installLocalAdapter(); } + if (this.isJSController()) { // this watches actually js-controller - await this.startAdapterWatch(startAdapter, doNotWatchArr); + await this.startAdapterWatch(start, ignorePaths); await this.startServer(); } else { await this.startJsController(); await this.startServer(); - await this.startAdapterWatch(startAdapter, doNotWatchArr); + await this.startAdapterWatch(start, ignorePaths); } } - async debug(wait: boolean, noInstall: boolean): Promise { + async debug(options: { wait: boolean; install: boolean; build: boolean }): Promise { + const { wait, install, build } = options; + await this.checkSetup(); - if (!noInstall) { - await this.buildLocalAdapter(); + + if (install) { + if (build) { + await this.buildLocalAdapter(); + } await this.installLocalAdapter(); } - await this.copySourcemaps(); + + // When using symlinks, copying the sourcemaps is not necessary + // TODO: Setup launch config instead? + if (!this.config?.useSymlinks) { + await this.copySourcemaps(); + } + if (this.isJSController()) { await this.startJsControllerDebug(wait); await this.startServer(); From 36fed9dba07f012e1b7d42c09508297de664df81 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Sat, 18 Feb 2023 01:16:50 +0100 Subject: [PATCH 5/6] fix: normalize workspace dirs and the entrypoint --- dist/index.js | 11 +---------- src/index.ts | 11 +---------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/dist/index.js b/dist/index.js index 286b056f..3f9c8715 100644 --- a/dist/index.js +++ b/dist/index.js @@ -278,15 +278,6 @@ class DevServer { } this.adapterName = await this.findAdapterName(argv.entrypoint); } - async checkMonorepo() { - try { - const pkg = await (0, fs_extra_1.readJson)(path.join(this.rootDir, 'package.json')); - return pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0; - } - catch (_a) { - return false; - } - } async findAdapterName(entrypoint) { var _a, _b; this.entrypoint = path.join(this.rootDir, (_b = entrypoint !== null && entrypoint !== void 0 ? entrypoint : (_a = this.config) === null || _a === void 0 ? void 0 : _a.entrypoint) !== null && _b !== void 0 ? _b : '.'); @@ -934,7 +925,7 @@ class DevServer { if (this.workspaces) { const directories = await (0, fast_glob_1.default)(this.workspaces, { onlyDirectories: true, cwd: this.rootDir, absolute: true }); // TODO: Check if we need to account for backslashes on Windows - additionalWatchDirs.push(...directories.map((d) => (d.endsWith(path.sep) ? d : d + path.sep)).filter((d) => d !== this.entrypoint)); + additionalWatchDirs.push(...directories.map((d) => d.replace(/[\\/]$/, '')).filter((d) => d !== this.entrypoint.replace(/[\\/]$/, ''))); } if (startAdapter) { await this.delay(3000); diff --git a/src/index.ts b/src/index.ts index 868aa3ff..169d745b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -359,15 +359,6 @@ class DevServer { this.adapterName = await this.findAdapterName(argv.entrypoint); } - private async checkMonorepo(): Promise { - try { - const pkg = await readJson(path.join(this.rootDir, 'package.json')); - return pkg.private === true && Array.isArray(pkg.workspaces) && pkg.workspaces.length > 0; - } catch { - return false; - } - } - private async findAdapterName(entrypoint?: string): Promise { this.entrypoint = path.join(this.rootDir, entrypoint ?? this.config?.entrypoint ?? '.'); @@ -1138,7 +1129,7 @@ class DevServer { const directories = await fg(this.workspaces, { onlyDirectories: true, cwd: this.rootDir, absolute: true }); // TODO: Check if we need to account for backslashes on Windows additionalWatchDirs.push( - ...directories.map((d) => (d.endsWith(path.sep) ? d : d + path.sep)).filter((d) => d !== this.entrypoint), + ...directories.map((d) => d.replace(/[\\/]$/, '')).filter((d) => d !== this.entrypoint.replace(/[\\/]$/, '')), ); } From 10e45f0afb1e93355bfa628c3972200557dad56d Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Tue, 28 Mar 2023 21:39:48 +0200 Subject: [PATCH 6/6] fix: npmignore, pass DB location to jsc --- dist/index.js | 12 +++++++++--- src/index.ts | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/dist/index.js b/dist/index.js index 3f9c8715..92ec1f5f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -266,6 +266,8 @@ class DevServer { this.profileName = profileName; this.log.debug(`Using profile name "${this.profileName}"`); this.profileDir = path.join(this.tempDir, profileName); + // This will be passed to js-controller, so there is no confusion where the DB is located + process.env.IOBROKER_DATA_DIR = path.join(this.profileDir, 'iobroker-data'); } async parseConfig(argv) { let pkg; @@ -802,6 +804,7 @@ class DevServer { } async findFiles(extension, excludeAdmin) { // TODO: Maybe we need to set cwd to this.entrypoint? + // We should encourage people to use symlinks instead though, so this would become unnecessary anyways. return await (0, fast_glob_1.default)(this.getFilePatterns(extension, excludeAdmin), { cwd: this.rootDir }); } async createIdentitySourcemap(filename) { @@ -1098,8 +1101,8 @@ class DevServer { if (!match) { return; } - const debigPid = await this.waitForNodeChildProcess(parseInt(match[1])); - this.log.box(`Debugger is now available on process id ${debigPid}`); + const debugPid = await this.waitForNodeChildProcess(parseInt(match[1])); + this.log.box(`Debugger is now available on process id ${debugPid}`); } async setupDevServer(adminPort, dependencies, entrypoint, backupFile, useSymlinks) { // await this.buildLocalAdapter(); @@ -1206,7 +1209,10 @@ class DevServer { if (useSymlinks) { await (0, fs_extra_1.writeFile)(path.join(this.profileDir, '.npmrc'), 'install-links=false', 'utf8'); } - await this.verifyIgnoreFiles(); + // Don't verify ignore files in monorepos, we're too likely to give wrong info there + if (!this.workspaces) { + await this.verifyIgnoreFiles(); + } this.log.notice('Installing js-controller and admin...'); this.execSync('npm install --loglevel error --production', this.profileDir); if (backupFile) { diff --git a/src/index.ts b/src/index.ts index 169d745b..ffb50212 100644 --- a/src/index.ts +++ b/src/index.ts @@ -345,6 +345,8 @@ class DevServer { this.profileName = profileName; this.log.debug(`Using profile name "${this.profileName}"`); this.profileDir = path.join(this.tempDir, profileName); + // This will be passed to js-controller, so there is no confusion where the DB is located + process.env.IOBROKER_DATA_DIR = path.join(this.profileDir, 'iobroker-data'); } private async parseConfig(argv: { entrypoint?: string }): Promise { @@ -983,6 +985,7 @@ class DevServer { private async findFiles(extension: string, excludeAdmin: boolean): Promise { // TODO: Maybe we need to set cwd to this.entrypoint? + // We should encourage people to use symlinks instead though, so this would become unnecessary anyways. return await fg(this.getFilePatterns(extension, excludeAdmin), { cwd: this.rootDir }); } @@ -1316,9 +1319,9 @@ class DevServer { return; } - const debigPid = await this.waitForNodeChildProcess(parseInt(match[1])); + const debugPid = await this.waitForNodeChildProcess(parseInt(match[1])); - this.log.box(`Debugger is now available on process id ${debigPid}`); + this.log.box(`Debugger is now available on process id ${debugPid}`); } async setupDevServer( @@ -1438,7 +1441,10 @@ class DevServer { await writeFile(path.join(this.profileDir, '.npmrc'), 'install-links=false', 'utf8'); } - await this.verifyIgnoreFiles(); + // Don't verify ignore files in monorepos, we're too likely to give wrong info there + if (!this.workspaces) { + await this.verifyIgnoreFiles(); + } this.log.notice('Installing js-controller and admin...'); this.execSync('npm install --loglevel error --production', this.profileDir);