diff --git a/package.json b/package.json index 63d3de4eac94..42d5f3ed5b4e 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,15 @@ "Tim Sullivan ", "Yuri Astrakhan " ], + "kibana": { + "clean": { + "extraPatterns": [ + "build", + "optimize", + ".eslintcache" + ] + } + }, "scripts": { "preinstall": "node ./preinstall_check", "kbn": "node scripts/kbn", @@ -406,4 +415,4 @@ "node": "8.11.4", "yarn": "^1.10.1" } -} \ No newline at end of file +} diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 4660a71d482e..67732b54fc72 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -7845,7 +7845,6 @@ class Project { this.path = projectPath; this.packageJsonLocation = (0, _path.resolve)(this.path, 'package.json'); this.nodeModulesLocation = (0, _path.resolve)(this.path, 'node_modules'); - this.optimizeLocation = (0, _path.resolve)(this.path, 'optimize'); this.targetLocation = (0, _path.resolve)(this.path, 'target'); this.productionDependencies = this.json.dependencies || {}; this.devDependencies = this.json.devDependencies || {}; @@ -7900,6 +7899,9 @@ class Project { getIntermediateBuildDirectory() { return (0, _path.resolve)(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); } + getCleanConfig() { + return this.json.kibana && this.json.kibana.clean || {}; + } hasScript(name) { return name in this.scripts; } @@ -23153,28 +23155,46 @@ function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, a const CleanCommand = exports.CleanCommand = { description: 'Remove the node_modules and target directories from all projects.', name: 'clean', - run(projects, projectGraph, { rootPath }) { + run(projects) { return _asyncToGenerator(function* () { - const directoriesToDelete = []; + const toDelete = []; for (const project of projects.values()) { if (yield (0, _fs.isDirectory)(project.nodeModulesLocation)) { - directoriesToDelete.push(project.nodeModulesLocation); + toDelete.push({ + cwd: project.path, + pattern: (0, _path.relative)(project.path, project.nodeModulesLocation) + }); } if (yield (0, _fs.isDirectory)(project.targetLocation)) { - directoriesToDelete.push(project.targetLocation); + toDelete.push({ + cwd: project.path, + pattern: (0, _path.relative)(project.path, project.targetLocation) + }); } - if (yield (0, _fs.isDirectory)(project.optimizeLocation)) { - directoriesToDelete.push(project.optimizeLocation); + const { extraPatterns } = project.getCleanConfig(); + if (extraPatterns) { + toDelete.push({ + cwd: project.path, + pattern: extraPatterns + }); } } - if (directoriesToDelete.length === 0) { - _log.log.write(_chalk2.default.bold.green('\n\nNo directories to delete')); + if (toDelete.length === 0) { + _log.log.write(_chalk2.default.bold.green('\n\nNothing to delete')); } else { - _log.log.write(_chalk2.default.bold.red('\n\nDeleting directories:\n')); - for (const dir of directoriesToDelete) { - const deleting = (0, _del2.default)(dir, { force: true }); - _ora2.default.promise(deleting, (0, _path.relative)(rootPath, dir)); - yield deleting; + _log.log.write(_chalk2.default.bold.red('\n\nDeleting:\n')); + const originalCwd = process.cwd(); + try { + for (const _ref of toDelete) { + const { pattern, cwd } = _ref; + + process.chdir(cwd); + const promise = (0, _del2.default)(pattern); + _ora2.default.promise(promise, (0, _path.relative)(originalCwd, (0, _path.join)(cwd, String(pattern)))); + yield promise; + } + } finally { + process.chdir(originalCwd); } } })(); diff --git a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap b/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap index 003d434978ef..53c942de0aff 100644 --- a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap +++ b/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap @@ -24,7 +24,6 @@ Array [ }, }, "nodeModulesLocation": "/packages/kbn-pm/src/commands/node_modules", - "optimizeLocation": "/packages/kbn-pm/src/commands/optimize", "packageJsonLocation": "/packages/kbn-pm/src/commands/package.json", "path": "/packages/kbn-pm/src/commands", "productionDependencies": Object { @@ -46,7 +45,6 @@ Array [ "version": "1.0.0", }, "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "optimizeLocation": "/packages/kbn-pm/src/commands/packages/bar/optimize", "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", "path": "/packages/kbn-pm/src/commands/packages/bar", "productionDependencies": Object {}, @@ -71,7 +69,6 @@ Array [ "version": "1.0.0", }, "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "optimizeLocation": "/packages/kbn-pm/src/commands/packages/bar/optimize", "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", "path": "/packages/kbn-pm/src/commands/packages/bar", "productionDependencies": Object {}, @@ -105,7 +102,6 @@ Array [ "version": "1.0.0", }, "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "optimizeLocation": "/packages/kbn-pm/src/commands/packages/bar/optimize", "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", "path": "/packages/kbn-pm/src/commands/packages/bar", "productionDependencies": Object {}, diff --git a/packages/kbn-pm/src/commands/clean.ts b/packages/kbn-pm/src/commands/clean.ts index 9c6f0853ec95..b3d433e22bd7 100644 --- a/packages/kbn-pm/src/commands/clean.ts +++ b/packages/kbn-pm/src/commands/clean.ts @@ -20,7 +20,7 @@ import chalk from 'chalk'; import del from 'del'; import ora from 'ora'; -import { relative } from 'path'; +import { join, relative } from 'path'; import { isDirectory } from '../utils/fs'; import { log } from '../utils/log'; @@ -30,31 +30,56 @@ export const CleanCommand: ICommand = { description: 'Remove the node_modules and target directories from all projects.', name: 'clean', - async run(projects, projectGraph, { rootPath }) { - const directoriesToDelete = []; + async run(projects) { + const toDelete = []; for (const project of projects.values()) { if (await isDirectory(project.nodeModulesLocation)) { - directoriesToDelete.push(project.nodeModulesLocation); + toDelete.push({ + cwd: project.path, + pattern: relative(project.path, project.nodeModulesLocation), + }); } if (await isDirectory(project.targetLocation)) { - directoriesToDelete.push(project.targetLocation); + toDelete.push({ + cwd: project.path, + pattern: relative(project.path, project.targetLocation), + }); } - if (await isDirectory(project.optimizeLocation)) { - directoriesToDelete.push(project.optimizeLocation); + const { extraPatterns } = project.getCleanConfig(); + if (extraPatterns) { + toDelete.push({ + cwd: project.path, + pattern: extraPatterns, + }); } } - if (directoriesToDelete.length === 0) { - log.write(chalk.bold.green('\n\nNo directories to delete')); + if (toDelete.length === 0) { + log.write(chalk.bold.green('\n\nNothing to delete')); } else { - log.write(chalk.bold.red('\n\nDeleting directories:\n')); + log.write(chalk.bold.red('\n\nDeleting:\n')); - for (const dir of directoriesToDelete) { - const deleting = del(dir, { force: true }); - ora.promise(deleting, relative(rootPath, dir)); - await deleting; + /** + * In order to avoid patterns like `/build` in packages from accidentally + * impacting files outside the package we use `process.chdir()` to change + * the cwd to the package and execute `del()` without the `force` option + * so it will check that each file being deleted is within the package. + * + * `del()` does support a `cwd` option, but it's only for resolving the + * patterns and does not impact the cwd check. + */ + const originalCwd = process.cwd(); + try { + for (const { pattern, cwd } of toDelete) { + process.chdir(cwd); + const promise = del(pattern); + ora.promise(promise, relative(originalCwd, join(cwd, String(pattern)))); + await promise; + } + } finally { + process.chdir(originalCwd); } } }, diff --git a/packages/kbn-pm/src/utils/project.ts b/packages/kbn-pm/src/utils/project.ts index 58cc047aa8c2..bdc6be48e0cd 100644 --- a/packages/kbn-pm/src/utils/project.ts +++ b/packages/kbn-pm/src/utils/project.ts @@ -32,11 +32,15 @@ import { } from './package_json'; import { installInDir, runScriptInPackage, runScriptInPackageStreaming } from './scripts'; -interface IBuildConfig { +interface BuildConfig { skip?: boolean; intermediateBuildDirectory?: string; } +interface CleanConfig { + extraPatterns?: string[]; +} + export class Project { public static async fromPath(path: string) { const pkgJson = await readPackageJson(path); @@ -46,7 +50,6 @@ export class Project { public readonly json: IPackageJson; public readonly packageJsonLocation: string; public readonly nodeModulesLocation: string; - public readonly optimizeLocation: string; public readonly targetLocation: string; public readonly path: string; public readonly allDependencies: IPackageDependencies; @@ -62,7 +65,6 @@ export class Project { this.packageJsonLocation = resolvePath(this.path, 'package.json'); this.nodeModulesLocation = resolvePath(this.path, 'node_modules'); - this.optimizeLocation = resolvePath(this.path, 'optimize'); this.targetLocation = resolvePath(this.path, 'target'); this.productionDependencies = this.json.dependencies || {}; @@ -117,7 +119,7 @@ export class Project { ); } - public getBuildConfig(): IBuildConfig { + public getBuildConfig(): BuildConfig { return (this.json.kibana && this.json.kibana.build) || {}; } @@ -130,6 +132,10 @@ export class Project { return resolvePath(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); } + public getCleanConfig(): CleanConfig { + return (this.json.kibana && this.json.kibana.clean) || {}; + } + public hasScript(name: string) { return name in this.scripts; }