[kbn-pm] Enable intermediate build directory for projects (#16839)

This commit is contained in:
Kim Joar Bekkelund 2018-02-21 13:54:27 +01:00 committed by GitHub
parent 7b9a8e0557
commit 8de986040f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 243 additions and 32 deletions

View file

@ -6787,14 +6787,10 @@ var _writePkg = __webpack_require__(290);
var _writePkg2 = _interopRequireDefault(_writePkg);
var _path = __webpack_require__(1);
var _path2 = _interopRequireDefault(_path);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function readPackageJson(dir) {
return (0, _readPkg2.default)(_path2.default.join(dir, 'package.json'), { normalize: false });
return (0, _readPkg2.default)(dir, { normalize: false });
}
function writePackageJson(path, json) {
return (0, _writePkg2.default)(path, json);
@ -9642,9 +9638,22 @@ class Project {
}
throw new _errors.CliError(`[${this.name}] depends on [${project.name}], but it's not using the local package. ${updateMsg}`, meta);
}
getBuildConfig() {
return this.json.kibana && this.json.kibana.build || {};
}
/**
* Whether a package should not be included in the Kibana build artifact.
*/
skipFromBuild() {
const json = this.json;
return json.kibana && json.kibana.build && json.kibana.build.skip === true;
return this.getBuildConfig().skip === true;
}
/**
* Returns the directory that should be copied into the Kibana build artifact.
* This config can be specified to only include the project's build artifacts
* instead of everything located in the project directory.
*/
getIntermediateBuildDirectory() {
return _path2.default.resolve(this.path, this.getBuildConfig().intermediateBuildDirectory || '.');
}
hasScript(name) {
return name in this.scripts;
@ -40994,25 +41003,38 @@ let buildProject = (() => {
return _ref4.apply(this, arguments);
};
})();
/**
* Copy all the project's files from its "intermediate build directory" and
* into the build. The intermediate directory can either be the root of the
* project or some other location defined in the project's `package.json`.
*
* When copying all the files into the build, we exclude `node_modules` because
* we want the Kibana build to be responsible for actually installing all
* dependencies. The primary reason for allowing the Kibana build process to
* manage dependencies is that it will "dedupe" them, so we don't include
* unnecessary copies of dependencies.
*/
let copyToBuild = (() => {
var _ref5 = _asyncToGenerator(function* (project, kibanaRoot, buildRoot) {
// We want the package to have the same relative location within the build
const relativeProjectPath = (0, _path.relative)(kibanaRoot, project.path);
const buildProjectPath = (0, _path.resolve)(buildRoot, relativeProjectPath);
// When copying all the files into the build, we exclude `package.json` as we
// write a separate "production-ready" `package.json` below, and we exclude
// `node_modules` because we want the Kibana build to actually install all
// dependencies. The primary reason for allowing the Kibana build process to
// install the dependencies is that it will "dedupe" them, so we don't include
// unnecessary copies of dependencies.
yield (0, _cpy2.default)(['**/*', '!package.json', '!node_modules/**'], buildProjectPath, {
cwd: project.path,
yield (0, _cpy2.default)(['**/*', '!node_modules/**'], buildProjectPath, {
cwd: project.getIntermediateBuildDirectory(),
parents: true,
nodir: true,
dot: true
});
const packageJson = project.json;
// If a project is using an intermediate build directory, we special-case our
// handling of `package.json`, as the project build process might have copied
// (a potentially modified) `package.json` into the intermediate build
// directory already. If so, we want to use that `package.json` as the basis
// for creating the production-ready `package.json`. If it's not present in
// the intermediate build, we fall back to using the project's already defined
// `package.json`.
const packageJson = (yield (0, _fs.isFile)((0, _path.join)(buildProjectPath, 'package.json'))) ? yield (0, _package_json.readPackageJson)(buildProjectPath) : project.json;
const preparedPackageJson = (0, _package_json.createProductionPackageJson)(packageJson);
yield (0, _package_json.writePackageJson)(buildProjectPath, preparedPackageJson);
});

View file

@ -1,5 +1,5 @@
import del from 'del';
import { relative, resolve } from 'path';
import { relative, resolve, join } from 'path';
import copy from 'cpy';
import { getProjectPaths } from '../config';
@ -11,8 +11,9 @@ import {
import {
createProductionPackageJson,
writePackageJson,
readPackageJson,
} from '../utils/package_json';
import { isDirectory } from '../utils/fs';
import { isDirectory, isFile } from '../utils/fs';
import { Project } from '../utils/project';
export async function buildProductionProjects({
@ -76,6 +77,17 @@ async function buildProject(project: Project) {
}
}
/**
* Copy all the project's files from its "intermediate build directory" and
* into the build. The intermediate directory can either be the root of the
* project or some other location defined in the project's `package.json`.
*
* When copying all the files into the build, we exclude `node_modules` because
* we want the Kibana build to be responsible for actually installing all
* dependencies. The primary reason for allowing the Kibana build process to
* manage dependencies is that it will "dedupe" them, so we don't include
* unnecessary copies of dependencies.
*/
async function copyToBuild(
project: Project,
kibanaRoot: string,
@ -85,20 +97,24 @@ async function copyToBuild(
const relativeProjectPath = relative(kibanaRoot, project.path);
const buildProjectPath = resolve(buildRoot, relativeProjectPath);
// When copying all the files into the build, we exclude `package.json` as we
// write a separate "production-ready" `package.json` below, and we exclude
// `node_modules` because we want the Kibana build to actually install all
// dependencies. The primary reason for allowing the Kibana build process to
// install the dependencies is that it will "dedupe" them, so we don't include
// unnecessary copies of dependencies.
await copy(['**/*', '!package.json', '!node_modules/**'], buildProjectPath, {
cwd: project.path,
await copy(['**/*', '!node_modules/**'], buildProjectPath, {
cwd: project.getIntermediateBuildDirectory(),
parents: true,
nodir: true,
dot: true,
});
const packageJson = project.json;
// If a project is using an intermediate build directory, we special-case our
// handling of `package.json`, as the project build process might have copied
// (a potentially modified) `package.json` into the intermediate build
// directory already. If so, we want to use that `package.json` as the basis
// for creating the production-ready `package.json`. If it's not present in
// the intermediate build, we fall back to using the project's already defined
// `package.json`.
const packageJson = (await isFile(join(buildProjectPath, 'package.json')))
? await readPackageJson(buildProjectPath)
: project.json;
const preparedPackageJson = createProductionPackageJson(packageJson);
await writePackageJson(buildProjectPath, preparedPackageJson);
}

View file

@ -0,0 +1,5 @@
{
"name": "@elastic/baz",
"version": "1.0.0",
"main": "./index.js"
}

View file

@ -0,0 +1 @@
console.log('@elastic/baz');

View file

@ -0,0 +1,20 @@
{
"name": "@elastic/baz",
"version": "1.0.0",
"private": true,
"main": "./code.js",
"dependencies": {
"noop3": "999.999.999"
},
"devDependencies": {
"shx": "^0.2.2"
},
"scripts": {
"build": "shx cp code.js build/index.js"
},
"kibana": {
"build": {
"intermediateBuildDirectory": "build"
}
}
}

View file

@ -0,0 +1,17 @@
{
"name": "@elastic/quux",
"version": "1.0.0",
"private": true,
"main": "./quux.js",
"devDependencies": {
"shx": "^0.2.2"
},
"scripts": {
"build": "shx mkdir build; shx cp quux.js build/index.js"
},
"kibana": {
"build": {
"intermediateBuildDirectory": "build"
}
}
}

View file

@ -0,0 +1 @@
console.log('@elastic/quux');

View file

@ -6,9 +6,81 @@ Array [
"packages/bar/src/index.js",
"packages/bar/target/index.js",
"packages/bar/yarn.lock",
"packages/baz/index.js",
"packages/baz/package.json",
"packages/foo/package.json",
"packages/foo/src/index.js",
"packages/foo/target/index.js",
"packages/foo/yarn.lock",
"packages/quux/index.js",
"packages/quux/package.json",
]
`;
exports[`kbn-pm production builds and copies projects for production: packages/bar/package.json 1`] = `
Object {
"dependencies": Object {
"lodash": "4.17.4",
},
"devDependencies": Object {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.6.1",
},
"main": "./target/index.js",
"name": "@elastic/bar",
"private": true,
"scripts": Object {
"build": "babel --presets env --out-dir target src",
},
"version": "1.0.0",
}
`;
exports[`kbn-pm production builds and copies projects for production: packages/baz/package.json 1`] = `
Object {
"main": "./index.js",
"name": "@elastic/baz",
"version": "1.0.0",
}
`;
exports[`kbn-pm production builds and copies projects for production: packages/foo/package.json 1`] = `
Object {
"dependencies": Object {
"@elastic/bar": "file:../bar",
"lodash": "4.17.4",
},
"devDependencies": Object {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.6.1",
"moment": "2.20.1",
},
"main": "./target/index.js",
"name": "@elastic/foo",
"private": true,
"scripts": Object {
"build": "babel --presets env --out-dir target src",
},
"version": "1.0.0",
}
`;
exports[`kbn-pm production builds and copies projects for production: packages/quux/package.json 1`] = `
Object {
"devDependencies": Object {
"shx": "^0.2.2",
},
"kibana": Object {
"build": Object {
"intermediateBuildDirectory": "build",
},
},
"main": "./quux.js",
"name": "@elastic/quux",
"private": true,
"scripts": Object {
"build": "shx mkdir build; shx cp quux.js build/index.js",
},
"version": "1.0.0",
}
`;

View file

@ -1,10 +1,11 @@
import tempy from 'tempy';
import copy from 'cpy';
import { resolve } from 'path';
import { resolve, relative, join } from 'path';
import globby from 'globby';
import { buildProductionProjects } from '../build_production_projects';
import { getProjects } from '../../utils/projects';
import { readPackageJson } from '../../utils/package_json';
describe('kbn-pm production', function() {
test(
@ -38,7 +39,15 @@ describe('kbn-pm production', function() {
});
expect(files.sort()).toMatchSnapshot();
for (const file of files) {
if (file.endsWith('package.json')) {
expect(await readPackageJson(join(buildRoot, file))).toMatchSnapshot(
file
);
}
}
},
60 * 1000
2 * 60 * 1000
);
});

View file

@ -7,7 +7,7 @@ export type PackageDependencies = { [key: string]: string };
export type PackageScripts = { [key: string]: string };
export function readPackageJson(dir: string) {
return readPkg(path.join(dir, 'package.json'), { normalize: false });
return readPkg(dir, { normalize: false });
}
export function writePackageJson(path: string, json: PackageJson) {

View file

@ -143,3 +143,28 @@ describe('#getExecutables()', () => {
).toThrowErrorMatchingSnapshot();
});
});
describe('#getIntermediateBuildDirectory', () => {
test('is the same as the project path when not specified', () => {
const project = createProjectWith({}, 'packages/my-project');
const path = project.getIntermediateBuildDirectory();
expect(path).toBe(project.path);
});
test('appends the `intermediateBuildDirectory` to project path when specified', () => {
const project = createProjectWith(
{
kibana: {
build: {
intermediateBuildDirectory: 'quux',
},
},
},
'packages/my-project'
);
const path = project.getIntermediateBuildDirectory();
expect(path).toBe(join(project.path, 'quux'));
});
});

View file

@ -16,6 +16,11 @@ import {
} from './package_json';
import { CliError } from './errors';
interface BuildConfig {
skip?: boolean;
intermediateBuildDirectory?: string;
}
export class Project {
static async fromPath(path: string) {
const pkgJson = await readPackageJson(path);
@ -86,9 +91,27 @@ export class Project {
);
}
getBuildConfig(): BuildConfig {
return (this.json.kibana && this.json.kibana.build) || {};
}
/**
* Whether a package should not be included in the Kibana build artifact.
*/
skipFromBuild() {
const json = this.json;
return json.kibana && json.kibana.build && json.kibana.build.skip === true;
return this.getBuildConfig().skip === true;
}
/**
* Returns the directory that should be copied into the Kibana build artifact.
* This config can be specified to only include the project's build artifacts
* instead of everything located in the project directory.
*/
getIntermediateBuildDirectory() {
return path.resolve(
this.path,
this.getBuildConfig().intermediateBuildDirectory || '.'
);
}
hasScript(name: string) {