[7.x] [plugin-helpers] improve 3rd party KP plugin support (#75019) (#76209)

Co-authored-by: Tyler Smalley <tylersmalley@me.com>
Co-authored-by: spalger <spalger@users.noreply.github.com>
This commit is contained in:
Spencer 2020-08-27 16:43:58 -07:00 committed by GitHub
parent ae0d8cb3bc
commit c8a93b688b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
79 changed files with 1147 additions and 1682 deletions

View file

@ -13,7 +13,7 @@ To get started copy and paste this example to `test/functional/config.js`:
["source","js"]
-----------
import { resolve } from 'path';
import { resolveKibanaPath } from '@kbn/plugin-helpers';
import { REPO_ROOT } from '@kbn/dev-utils';
import { MyServiceProvider } from './services/my_service';
import { MyAppPageProvider } from './services/my_app_page';
@ -24,7 +24,7 @@ export default async function ({ readConfigFile }) {
// read the {kib} config file so that we can utilize some of
// its services and PageObjects
const kibanaConfig = await readConfigFile(resolveKibanaPath('test/functional/config.js'));
const kibanaConfig = await readConfigFile(resolve(REPO_ROOT, 'test/functional/config.js'));
return {
// list paths to the files that contain your plugins tests

View file

@ -1,32 +1,38 @@
{
"name": "@kbn/dev-utils",
"main": "./target/index.js",
"version": "1.0.0",
"license": "Apache-2.0",
"private": true,
"license": "Apache-2.0",
"main": "./target/index.js",
"scripts": {
"build": "tsc",
"kbn:bootstrap": "yarn build",
"kbn:watch": "yarn build --watch"
},
"dependencies": {
"@babel/core": "^7.11.1",
"axios": "^0.19.0",
"chalk": "^4.1.0",
"cheerio": "0.22.0",
"dedent": "^0.7.0",
"execa": "^4.0.2",
"exit-hook": "^2.2.0",
"getopts": "^2.2.5",
"globby": "^8.0.1",
"load-json-file": "^6.2.0",
"normalize-path": "^3.0.0",
"markdown-it": "^10.0.0",
"moment": "^2.24.0",
"normalize-path": "^3.0.0",
"rxjs": "^6.5.5",
"strip-ansi": "^6.0.0",
"tree-kill": "^1.2.2",
"tslib": "^2.0.0"
"vinyl": "^2.2.0"
},
"devDependencies": {
"typescript": "4.0.2",
"@kbn/babel-preset": "1.0.0",
"@kbn/expect": "1.0.0",
"chance": "1.0.18"
"@types/vinyl": "^2.0.4",
"chance": "1.0.18",
"typescript": "4.0.2"
}
}

View file

@ -0,0 +1,59 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import File from 'vinyl';
import * as Babel from '@babel/core';
const transformedFiles = new WeakSet<File>();
/**
* Returns a promise that resolves when the file has been
* mutated so the contents of the file are tranformed with
* babel, include inline sourcemaps, and the filename has
* been updated to use .js.
*
* If the file was previously transformed with this function
* the promise will just resolve immediately.
*/
export async function transformFileWithBabel(file: File) {
if (!(file.contents instanceof Buffer)) {
throw new Error('file must be buffered');
}
if (transformedFiles.has(file)) {
return;
}
const source = file.contents.toString('utf8');
const result = await Babel.transformAsync(source, {
babelrc: false,
configFile: false,
sourceMaps: 'inline',
filename: file.path,
presets: [require.resolve('@kbn/babel-preset/node_preset')],
});
if (!result || typeof result.code !== 'string') {
throw new Error('babel transformation failed without an error...');
}
file.contents = Buffer.from(result.code);
file.extname = '.js';
transformedFiles.add(file);
}

View file

@ -41,3 +41,6 @@ export * from './stdio';
export * from './ci_stats_reporter';
export * from './plugin_list';
export * from './simple_kibana_platform_plugin_discovery';
export * from './streams';
export * from './babel';
export * from './parse_kibana_platform_plugin';

View file

@ -0,0 +1,59 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Path from 'path';
import loadJsonFile from 'load-json-file';
export interface KibanaPlatformPlugin {
readonly directory: string;
readonly manifestPath: string;
readonly manifest: {
id: string;
ui: boolean;
server: boolean;
[key: string]: unknown;
};
}
export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin {
if (!Path.isAbsolute(manifestPath)) {
throw new TypeError('expected new platform manifest path to be absolute');
}
const manifest = loadJsonFile.sync(manifestPath);
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
throw new TypeError('expected new platform plugin manifest to be a JSON encoded object');
}
if (typeof manifest.id !== 'string') {
throw new TypeError('expected new platform plugin manifest to have a string id');
}
return {
directory: Path.dirname(manifestPath),
manifestPath,
manifest: {
...manifest,
ui: !!manifest.ui,
server: !!manifest.server,
id: manifest.id,
},
};
}

View file

@ -52,8 +52,8 @@ export function mergeFlagOptions(global: FlagOptions = {}, local: FlagOptions =
boolean: [...(global.boolean || []), ...(local.boolean || [])],
string: [...(global.string || []), ...(local.string || [])],
default: {
...global.alias,
...local.alias,
...global.default,
...local.default,
},
help: local.help,

View file

@ -21,3 +21,4 @@ export * from './absolute_path_serializer';
export * from './strip_ansi_serializer';
export * from './recursive_serializer';
export * from './any_instance_serizlizer';
export * from './replace_serializer';

View file

@ -0,0 +1,36 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createRecursiveSerializer } from './recursive_serializer';
type Replacer = (substring: string, ...args: any[]) => string;
export function createReplaceSerializer(
toReplace: string | RegExp,
replaceWith: string | Replacer
) {
return createRecursiveSerializer(
typeof toReplace === 'string'
? (v: any) => typeof v === 'string' && v.includes(toReplace)
: (v: any) => typeof v === 'string' && toReplace.test(v),
typeof replaceWith === 'string'
? (v: string) => v.replace(toReplace, replaceWith)
: (v: string) => v.replace(toReplace, replaceWith)
);
}

View file

@ -20,67 +20,37 @@
import Path from 'path';
import globby from 'globby';
import loadJsonFile from 'load-json-file';
export interface KibanaPlatformPlugin {
readonly directory: string;
readonly manifestPath: string;
readonly manifest: {
id: string;
[key: string]: unknown;
};
}
import { parseKibanaPlatformPlugin } from './parse_kibana_platform_plugin';
/**
* Helper to find the new platform plugins.
*/
export function simpleKibanaPlatformPluginDiscovery(scanDirs: string[], paths: string[]) {
export function simpleKibanaPlatformPluginDiscovery(scanDirs: string[], pluginPaths: string[]) {
const patterns = Array.from(
new Set([
// find kibana.json files up to 5 levels within the scan dir
...scanDirs.reduce(
(acc: string[], dir) => [
...acc,
`${dir}/*/kibana.json`,
`${dir}/*/*/kibana.json`,
`${dir}/*/*/*/kibana.json`,
`${dir}/*/*/*/*/kibana.json`,
`${dir}/*/*/*/*/*/kibana.json`,
Path.resolve(dir, '*/kibana.json'),
Path.resolve(dir, '*/*/kibana.json'),
Path.resolve(dir, '*/*/*/kibana.json'),
Path.resolve(dir, '*/*/*/*/kibana.json'),
Path.resolve(dir, '*/*/*/*/*/kibana.json'),
],
[]
),
...paths.map((path) => `${path}/kibana.json`),
...pluginPaths.map((path) => Path.resolve(path, `kibana.json`)),
])
);
const manifestPaths = globby.sync(patterns, { absolute: true }).map((path) =>
// absolute paths returned from globby are using normalize or something so the path separators are `/` even on windows, Path.resolve solves this
// absolute paths returned from globby are using normalize or
// something so the path separators are `/` even on windows,
// Path.resolve solves this
Path.resolve(path)
);
return manifestPaths.map(
(manifestPath): KibanaPlatformPlugin => {
if (!Path.isAbsolute(manifestPath)) {
throw new TypeError('expected new platform manifest path to be absolute');
}
const manifest = loadJsonFile.sync(manifestPath);
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
throw new TypeError('expected new platform plugin manifest to be a JSON encoded object');
}
if (typeof manifest.id !== 'string') {
throw new TypeError('expected new platform plugin manifest to have a string id');
}
return {
directory: Path.dirname(manifestPath),
manifestPath,
manifest: {
...manifest,
id: manifest.id,
},
};
}
);
return manifestPaths.map(parseKibanaPlatformPlugin);
}

View file

@ -20,7 +20,6 @@
import { Transform } from 'stream';
import File from 'vinyl';
import { Minimatch } from 'minimatch';
interface BufferedFile extends File {
contents: Buffer;
@ -33,41 +32,31 @@ interface BufferedFile extends File {
* mutate the file, replace it with another file (return a new File
* object), or drop it from the stream (return null)
*/
export const tapFileStream = (
export const transformFileStream = (
fn: (file: BufferedFile) => File | void | null | Promise<File | void | null>
) =>
new Transform({
objectMode: true,
transform(file: BufferedFile, _, cb) {
Promise.resolve(file)
.then(fn)
.then(
(result) => {
// drop the file when null is returned
if (result === null) {
cb();
} else {
cb(undefined, result || file);
}
},
(error) => cb(error)
);
transform(file: File, _, cb) {
Promise.resolve()
.then(async () => {
if (file.isDirectory()) {
return cb(undefined, file);
}
if (!(file.contents instanceof Buffer)) {
throw new Error('files must be buffered to use transformFileStream()');
}
const result = await fn(file as BufferedFile);
if (result === null) {
// explicitly drop file if null is returned
cb();
} else {
cb(undefined, result || file);
}
})
.catch(cb);
},
});
export const excludeFiles = (globs: string[]) => {
const patterns = globs.map(
(g) =>
new Minimatch(g, {
matchBase: true,
})
);
return tapFileStream((file) => {
const path = file.relative.replace(/\.ejs$/, '');
const exclude = patterns.some((p) => p.match(path));
if (exclude) {
return null;
}
});
};

View file

@ -50,7 +50,7 @@ export function findKibanaPlatformPlugins(scanDirs: string[], paths: string[]) {
directory,
manifestPath,
id: manifest.id,
isUiPlugin: !!manifest.ui,
isUiPlugin: manifest.ui,
extraPublicDirs: extraPublicDirs || [],
};
}

View file

@ -0,0 +1,60 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Path from 'path';
import { REPO_ROOT } from '@kbn/dev-utils';
export interface PluginType {
thirdParty: boolean;
installDir: string;
}
export const PLUGIN_TYPE_OPTIONS: Array<{ name: string; value: PluginType }> = [
{
name: 'Installable plugin',
value: { thirdParty: true, installDir: Path.resolve(REPO_ROOT, 'plugins') },
},
{
name: 'Kibana Example',
value: { thirdParty: false, installDir: Path.resolve(REPO_ROOT, 'examples') },
},
{
name: 'Kibana OSS',
value: { thirdParty: false, installDir: Path.resolve(REPO_ROOT, 'src/plugins') },
},
{
name: 'Kibana OSS Functional Testing',
value: {
thirdParty: false,
installDir: Path.resolve(REPO_ROOT, 'test/plugin_functional/plugins'),
},
},
{
name: 'X-Pack',
value: { thirdParty: false, installDir: Path.resolve(REPO_ROOT, 'x-pack/plugins') },
},
{
name: 'X-Pack Functional Testing',
value: {
thirdParty: false,
installDir: Path.resolve(REPO_ROOT, 'x-pack/test/plugin_functional/plugins'),
},
},
];

View file

@ -23,15 +23,32 @@ import { promisify } from 'util';
import vfs from 'vinyl-fs';
import prettier from 'prettier';
import { REPO_ROOT } from '@kbn/dev-utils';
import { REPO_ROOT, transformFileStream } from '@kbn/dev-utils';
import ejs from 'ejs';
import { Minimatch } from 'minimatch';
import { snakeCase, camelCase, upperCamelCase } from './casing';
import { excludeFiles, tapFileStream } from './streams';
import { Answers } from './ask_questions';
const asyncPipeline = promisify(pipeline);
const excludeFiles = (globs: string[]) => {
const patterns = globs.map(
(g) =>
new Minimatch(g, {
matchBase: true,
})
);
return transformFileStream((file) => {
const path = file.relative.replace(/\.ejs$/, '');
const exclude = patterns.some((p) => p.match(path));
if (exclude) {
return null;
}
});
};
/**
* Stream all the files from the template directory, ignoring
* certain files based on the answers, process the .ejs templates
@ -82,7 +99,7 @@ export async function renderTemplates({
),
// render .ejs templates and rename to not use .ejs extension
tapFileStream((file) => {
transformFileStream((file) => {
if (file.extname !== '.ejs') {
return;
}
@ -108,7 +125,7 @@ export async function renderTemplates({
}),
// format each file with prettier
tapFileStream((file) => {
transformFileStream((file) => {
if (!file.extname) {
return;
}

View file

@ -7,3 +7,14 @@ A Kibana plugin
## Development
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.
<% if (thirdPartyPlugin) { %>
## Scripts
<dl>
<dt><code>yarn kbn bootstrap</code></dt>
<dd>Execute this to install node_modules and setup the dependencies in your plugin and in Kibana</dd>
<dt><code>yarn plugin-helpers build</code></dt>
<dd>Execute this to create a distributable version of this plugin that can be installed in Kibana</dd>
</dl>
<% } %>

View file

@ -3,6 +3,8 @@
"version": "0.0.0",
"private": true,
"scripts": {
"build": "yarn plugin-helpers build",
"plugin-helpers": "node ../../scripts/plugin_helpers",
"kbn": "node ../../scripts/kbn"
}
}

View file

@ -1,43 +1,32 @@
{
"name": "@kbn/plugin-helpers",
"version": "9.0.2",
"version": "1.0.0",
"private": true,
"description": "Just some helpers for kibana plugin devs.",
"license": "Apache-2.0",
"main": "target/lib/index.js",
"scripts": {
"kbn:bootstrap": "tsc"
},
"main": "target/index.js",
"bin": {
"plugin-helpers": "bin/plugin-helpers.js"
},
"scripts": {
"kbn:bootstrap": "rm -rf target && tsc",
"kbn:watch": "tsc --watch"
},
"dependencies": {
"@babel/core": "^7.11.1",
"argv-split": "^2.0.1",
"commander": "^2.9.0",
"@kbn/dev-utils": "1.0.0",
"@kbn/optimizer": "1.0.0",
"del": "^5.1.0",
"execa": "^4.0.2",
"globby": "^8.0.1",
"gulp-babel": "^8.0.0",
"gulp-rename": "1.4.0",
"gulp-zip": "5.0.1",
"gulp-zip": "^5.0.2",
"inquirer": "^1.2.2",
"minimatch": "^3.0.4",
"through2": "^2.0.3",
"through2-map": "^3.0.0",
"vinyl": "^2.2.0",
"load-json-file": "^6.2.0",
"vinyl-fs": "^3.0.3"
},
"devDependencies": {
"@types/gulp-rename": "^0.0.33",
"@types/decompress": "^4.2.3",
"@types/gulp-zip": "^4.0.1",
"@types/inquirer": "^6.5.0",
"@types/through2": "^2.0.35",
"@types/through2-map": "^3.0.0",
"@types/vinyl": "^2.0.4",
"decompress": "^4.2.1",
"typescript": "4.0.2"
},
"peerDependencies": {
"@kbn/babel-preset": "1.0.0"
}
}

View file

@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
@ -19,10 +17,16 @@
* under the License.
*/
const nodeMajorVersion = parseFloat(process.version.replace(/^v(\d+)\..+/, '$1'));
if (nodeMajorVersion < 6) {
console.error('FATAL: kibana-plugin-helpers requires node 6+');
process.exit(1);
}
import { ToolingLog } from '@kbn/dev-utils';
require('../target/cli');
import { Plugin } from './load_kibana_platform_plugin';
import { Config } from './config';
export interface BuildContext {
log: ToolingLog;
plugin: Plugin;
config: Config;
sourceDir: string;
buildDir: string;
kibanaVersion: string;
}

View file

@ -17,59 +17,86 @@
* under the License.
*/
import Fs from 'fs';
import Path from 'path';
import program from 'commander';
import { RunWithCommands, createFlagError, createFailError } from '@kbn/dev-utils';
import { createCommanderAction } from './lib/commander_action';
import { docs } from './lib/docs';
import { enableCollectingUnknownOptions } from './lib/enable_collecting_unknown_options';
import { findKibanaJson } from './find_kibana_json';
import { loadKibanaPlatformPlugin } from './load_kibana_platform_plugin';
import * as Tasks from './tasks';
import { BuildContext } from './build_context';
import { resolveKibanaVersion } from './resolve_kibana_version';
import { loadConfig } from './config';
const pkg = JSON.parse(Fs.readFileSync(Path.resolve(__dirname, '../package.json'), 'utf8'));
program.version(pkg.version);
export function runCli() {
new RunWithCommands({
description: 'Some helper tasks for plugin-authors',
})
.command({
name: 'build',
description: `
Copies files from the source into a zip archive that can be distributed for
installation into production Kibana installs. The archive includes the non-
development npm dependencies and builds itself using raw files in the source
directory so make sure they are clean/up to date. The resulting archive can
be found at:
enableCollectingUnknownOptions(
program
.command('start')
.description('Start kibana and have it include this plugin')
.on('--help', docs('start'))
.action(
createCommanderAction('start', (command) => ({
flags: command.unknownOptions,
}))
)
);
build/{plugin.id}-{kibanaVersion}.zip
program
.command('build [files...]')
.description('Build a distributable archive')
.on('--help', docs('build'))
.option('--skip-archive', "Don't create the zip file, leave the build path alone")
.option(
'-d, --build-destination <path>',
'Target path for the build output, absolute or relative to the plugin root'
)
.option('-b, --build-version <version>', 'Version for the build output')
.option('-k, --kibana-version <version>', 'Kibana version for the build output')
.action(
createCommanderAction('build', (command, files) => ({
buildDestination: command.buildDestination,
buildVersion: command.buildVersion,
kibanaVersion: command.kibanaVersion,
skipArchive: Boolean(command.skipArchive),
files,
}))
);
`,
flags: {
boolean: ['skip-archive'],
string: ['kibana-version'],
alias: {
k: 'kibana-version',
},
help: `
--skip-archive Don't create the zip file, just create the build/kibana directory
--kibana-version, -v Kibana version that the
`,
},
async run({ log, flags }) {
const versionFlag = flags['kibana-version'];
if (versionFlag !== undefined && typeof versionFlag !== 'string') {
throw createFlagError('expected a single --kibana-version flag');
}
program
.command('test:mocha [files...]')
.description('Run the server tests using mocha')
.on('--help', docs('test/mocha'))
.action(
createCommanderAction('testMocha', (command, files) => ({
files,
}))
);
const skipArchive = flags['skip-archive'];
if (skipArchive !== undefined && typeof skipArchive !== 'boolean') {
throw createFlagError('expected a single --skip-archive flag');
}
program.parse(process.argv);
const pluginDir = await findKibanaJson(process.cwd());
if (!pluginDir) {
throw createFailError(
`Unable to find Kibana Platform plugin in [${process.cwd()}] or any of its parent directories. Has it been migrated properly? Does it have a kibana.json file?`
);
}
const plugin = loadKibanaPlatformPlugin(pluginDir);
const config = await loadConfig(log, plugin);
const kibanaVersion = await resolveKibanaVersion(versionFlag, plugin);
const sourceDir = plugin.directory;
const buildDir = Path.resolve(plugin.directory, 'build/kibana', plugin.manifest.id);
const context: BuildContext = {
log,
plugin,
config,
sourceDir,
buildDir,
kibanaVersion,
};
await Tasks.initTargets(context);
await Tasks.optimize(context);
await Tasks.writeServerFiles(context);
await Tasks.yarnInstall(context);
if (skipArchive !== true) {
await Tasks.createArchive(context);
}
},
})
.execute();
}

View file

@ -0,0 +1,83 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Path from 'path';
import loadJsonFile from 'load-json-file';
import { ToolingLog } from '@kbn/dev-utils';
import { Plugin } from './load_kibana_platform_plugin';
export interface Config {
skipInstallDependencies: boolean;
serverSourcePatterns?: string[];
}
const isArrayOfStrings = (v: any): v is string[] =>
Array.isArray(v) && v.every((p) => typeof p === 'string');
export async function loadConfig(log: ToolingLog, plugin: Plugin): Promise<Config> {
try {
const path = Path.resolve(plugin.directory, '.kibana-plugin-helpers.json');
const file = await loadJsonFile(path);
if (!(typeof file === 'object' && file && !Array.isArray(file))) {
throw new TypeError(`expected config at [${path}] to be an object`);
}
const {
skipInstallDependencies = false,
buildSourcePatterns,
serverSourcePatterns,
...rest
} = file;
if (typeof skipInstallDependencies !== 'boolean') {
throw new TypeError(`expected [skipInstallDependencies] at [${path}] to be a boolean`);
}
if (buildSourcePatterns) {
log.warning(
`DEPRECATED: rename [buildSourcePatterns] to [serverSourcePatterns] in [${path}]`
);
}
const ssp = buildSourcePatterns || serverSourcePatterns;
if (ssp !== undefined && !isArrayOfStrings(ssp)) {
throw new TypeError(`expected [serverSourcePatterns] at [${path}] to be an array of strings`);
}
if (Object.keys(rest).length) {
throw new TypeError(`unexpected key in [${path}]: ${Object.keys(rest).join(', ')}`);
}
log.info(`Loaded config file from [${path}]`);
return {
skipInstallDependencies,
serverSourcePatterns: ssp,
};
} catch (error) {
if (error.code === 'ENOENT') {
return {
skipInstallDependencies: false,
};
}
throw error;
}
}

View file

@ -17,21 +17,21 @@
* under the License.
*/
import { pluginConfig, PluginConfig } from './plugin_config';
import { tasks, Tasks } from './tasks';
import Path from 'path';
import Fs from 'fs';
import { promisify } from 'util';
export interface TaskContext {
plugin: PluginConfig;
run: typeof run;
options?: any;
}
const existsAsync = promisify(Fs.exists);
export function run(name: keyof Tasks, options?: any) {
const action = tasks[name];
if (!action) {
throw new Error('Invalid task: "' + name + '"');
export async function findKibanaJson(directory: string): Promise<string | undefined> {
if (await existsAsync(Path.resolve(directory, 'kibana.json'))) {
return directory;
}
const plugin = pluginConfig();
return action({ plugin, run, options });
const parent = Path.dirname(directory);
if (parent === directory) {
return undefined;
}
return findKibanaJson(parent);
}

View file

@ -17,4 +17,4 @@
* under the License.
*/
export * from './start_task';
export * from './cli';

View file

@ -0,0 +1,123 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Path from 'path';
import Fs from 'fs';
import execa from 'execa';
import { createStripAnsiSerializer, REPO_ROOT, createReplaceSerializer } from '@kbn/dev-utils';
import decompress from 'decompress';
import del from 'del';
import globby from 'globby';
import loadJsonFile from 'load-json-file';
const PLUGIN_DIR = Path.resolve(REPO_ROOT, 'plugins/foo_test_plugin');
const PLUGIN_BUILD_DIR = Path.resolve(PLUGIN_DIR, 'build');
const PLUGIN_ARCHIVE = Path.resolve(PLUGIN_BUILD_DIR, `fooTestPlugin-7.5.0.zip`);
const TMP_DIR = Path.resolve(__dirname, '__tmp__');
expect.addSnapshotSerializer(createReplaceSerializer(/[\d\.]+ sec/g, '<time>'));
expect.addSnapshotSerializer(createReplaceSerializer(/\d+(\.\d+)?[sm]/g, '<time>'));
expect.addSnapshotSerializer(createReplaceSerializer(/yarn (\w+) v[\d\.]+/g, 'yarn $1 <version>'));
expect.addSnapshotSerializer(createStripAnsiSerializer());
beforeEach(async () => {
await del([PLUGIN_DIR, TMP_DIR]);
Fs.mkdirSync(TMP_DIR);
});
afterEach(async () => await del([PLUGIN_DIR, TMP_DIR]));
it('builds a generated plugin into a viable archive', async () => {
const generateProc = await execa(
process.execPath,
['scripts/generate_plugin', '-y', '--name', 'fooTestPlugin'],
{
cwd: REPO_ROOT,
all: true,
}
);
expect(generateProc.all).toMatchInlineSnapshot(`
" succ 🎉
Your plugin has been created in plugins/foo_test_plugin
"
`);
const buildProc = await execa(
process.execPath,
['../../scripts/plugin_helpers', 'build', '--kibana-version', '7.5.0'],
{
cwd: PLUGIN_DIR,
all: true,
}
);
expect(buildProc.all).toMatchInlineSnapshot(`
" info deleting the build and target directories
info running @kbn/optimizer
info initialized, 0 bundles cached
info starting worker [1 bundle]
succ 1 bundles compiled successfully after <time>
info copying source into the build and converting with babel
info running yarn to install dependencies
info compressing plugin into [fooTestPlugin-7.5.0.zip]"
`);
await decompress(PLUGIN_ARCHIVE, TMP_DIR);
const files = await globby(['**/*'], { cwd: TMP_DIR });
files.sort((a, b) => a.localeCompare(b));
expect(files).toMatchInlineSnapshot(`
Array [
"kibana/fooTestPlugin/common/index.js",
"kibana/fooTestPlugin/kibana.json",
"kibana/fooTestPlugin/package.json",
"kibana/fooTestPlugin/server/index.js",
"kibana/fooTestPlugin/server/plugin.js",
"kibana/fooTestPlugin/server/routes/index.js",
"kibana/fooTestPlugin/server/types.js",
"kibana/fooTestPlugin/target/public/fooTestPlugin.chunk.1.js",
"kibana/fooTestPlugin/target/public/fooTestPlugin.chunk.1.js.br",
"kibana/fooTestPlugin/target/public/fooTestPlugin.chunk.1.js.gz",
"kibana/fooTestPlugin/target/public/fooTestPlugin.plugin.js",
"kibana/fooTestPlugin/target/public/fooTestPlugin.plugin.js.br",
"kibana/fooTestPlugin/target/public/fooTestPlugin.plugin.js.gz",
"kibana/fooTestPlugin/translations/ja-JP.json",
"kibana/fooTestPlugin/tsconfig.json",
]
`);
expect(loadJsonFile.sync(Path.resolve(TMP_DIR, 'kibana', 'fooTestPlugin', 'kibana.json')))
.toMatchInlineSnapshot(`
Object {
"id": "fooTestPlugin",
"kibanaVersion": "7.5.0",
"optionalPlugins": Array [],
"requiredPlugins": Array [
"navigation",
],
"server": true,
"ui": true,
"version": "1.0.0",
}
`);
});

View file

@ -1,43 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`commander action exits with status 1 when task throws error asynchronously 1`] = `
Array [
Array [
"Task \\"mockTask\\" failed:
Error: async error thrown
...stack trace...
",
],
]
`;
exports[`commander action exits with status 1 when task throws synchronously 1`] = `
Array [
Array [
"Task \\"mockTask\\" failed:
Error: sync error thrown
...stack trace...
",
],
]
`;
exports[`commander action passes args to getOptions, calls run() with taskName and options 1`] = `
Array [
Array [
"taskName",
Object {
"args": Array [
"f",
"a",
"b",
"c",
"d",
"e",
],
},
],
]
`;

View file

@ -1,87 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createCommanderAction } from './commander_action';
import { run } from './run';
jest.mock('./run');
const STACK_TRACE_RE = /\n(?:\s+at .+(?:\n|$))+/g;
expect.addSnapshotSerializer({
print(val, serialize) {
return serialize(val.replace(STACK_TRACE_RE, '\n ...stack trace...\n'));
},
test(val) {
return typeof val === 'string' && STACK_TRACE_RE.test(val);
},
});
beforeAll(() => {
jest.spyOn(process.stderr, 'write').mockImplementation(() => {});
jest.spyOn(process, 'exit').mockImplementation(() => {});
});
beforeEach(() => {
run.mockReset();
jest.clearAllMocks();
});
afterAll(() => {
jest.restoreAllMocks();
});
describe('commander action', () => {
it('creates a function', async () => {
expect(typeof createCommanderAction()).toBe('function');
});
it('passes args to getOptions, calls run() with taskName and options', async () => {
const action = createCommanderAction('taskName', (...args) => ({ args }));
await action('a', 'b', 'c', 'd', 'e', 'f');
expect(run).toHaveBeenCalledTimes(1);
expect(run.mock.calls).toMatchSnapshot();
});
it('exits with status 1 when task throws synchronously', async () => {
run.mockImplementation(() => {
throw new Error('sync error thrown');
});
await createCommanderAction('mockTask')();
expect(process.stderr.write).toHaveBeenCalledTimes(1);
expect(process.stderr.write.mock.calls).toMatchSnapshot();
expect(process.exit).toHaveBeenCalledTimes(1);
expect(process.exit).toHaveBeenCalledWith(1);
});
it('exits with status 1 when task throws error asynchronously', async () => {
run.mockImplementation(async () => {
throw new Error('async error thrown');
});
await createCommanderAction('mockTask')();
expect(process.stderr.write).toHaveBeenCalledTimes(1);
expect(process.stderr.write.mock.calls).toMatchSnapshot();
expect(process.exit).toHaveBeenCalledTimes(1);
expect(process.exit).toHaveBeenCalledWith(1);
});
});

View file

@ -1,36 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { run } from './run';
import { Tasks } from './tasks';
type GetOptions = (command: any, ...args: string[]) => any;
export function createCommanderAction(taskName: keyof Tasks, getOptions: GetOptions = () => {}) {
return async (...args: string[]) => {
try {
// command is the last arg passed by commander, but we move it to the front of the list
const command = args.pop();
await run(taskName, getOptions(command, ...args));
} catch (error) {
process.stderr.write(`Task "${taskName}" failed:\n\n${error.stack || error.message}\n`);
process.exit(1);
}
};
}

View file

@ -1,69 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { resolve } from 'path';
import { readFileSync } from 'fs';
const configFileNames = ['.kibana-plugin-helpers.json', '.kibana-plugin-helpers.dev.json'];
interface Config {
[key: string]: unknown;
}
const configCache = new Map<string, Config>();
export function configFile(root: string = process.cwd()) {
if (configCache.has(root)) {
return configCache.get(root)!;
}
// config files to read from, in the order they are merged together
let config: Config = {};
for (const name of configFileNames) {
try {
config = JSON.parse(readFileSync(resolve(root, name), 'utf8'));
} catch (e) {
// rethrow error unless it's complaining about file not existing
if (e.code !== 'ENOENT') {
throw e;
}
}
}
const deprecationMsg =
'has been removed from `@kbn/plugin-helpers`. ' +
'During development your plugin must live in `./plugins/{pluginName}` ' +
'inside the Kibana folder or `../kibana-extra/{pluginName}` ' +
'relative to the Kibana folder to work with this package.\n';
if (config.kibanaRoot) {
throw new Error('The `kibanaRoot` config option ' + deprecationMsg);
}
if (process.env.KIBANA_ROOT) {
throw new Error('The `KIBANA_ROOT` environment variable ' + deprecationMsg);
}
// use resolve to ensure correct resolution of paths
if (Array.isArray(config.includePlugins)) {
config.includePlugins = config.includePlugins.map((path: string) => resolve(root, path));
}
configCache.set(root, config);
return config;
}

View file

@ -1,30 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Command } from 'commander';
export function enableCollectingUnknownOptions(command: Command) {
const origParse = command.parseOptions;
command.allowUnknownOption();
command.parseOptions = function (argv: string[]) {
const opts = origParse.call(this, argv);
this.unknownOptions = opts.unknown;
return opts;
};
}

View file

@ -1,23 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Stream from 'stream';
import Util from 'util';
export const pipeline = Util.promisify(Stream.pipeline);

View file

@ -1,74 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { resolve } from 'path';
import { readFileSync } from 'fs';
import { configFile } from './config_file';
export interface PluginConfig {
root: string;
kibanaRoot: string;
serverTestPatterns: string[];
buildSourcePatterns: string[];
skipInstallDependencies: boolean;
id: string;
version: string;
pkg: any;
[k: string]: unknown;
}
export function pluginConfig(root: string = process.cwd()): PluginConfig {
const pluginPackageJsonPath = resolve(root, 'package.json');
const pkg = JSON.parse(readFileSync(pluginPackageJsonPath, 'utf8'));
const buildSourcePatterns = [
'yarn.lock',
'tsconfig.json',
'package.json',
'index.{js,ts}',
'{lib,public,server,translations}/**/*',
];
const kibanaExtraDir = resolve(root, '../../kibana');
const kibanaPluginsDir = resolve(root, '../../');
const isPluginOnKibanaExtra = pluginPackageJsonPath.includes(kibanaExtraDir);
const isPluginXpack = pkg.name === 'x-pack';
if (isPluginOnKibanaExtra && !isPluginXpack) {
// eslint-disable-next-line no-console
console.warn(
`In the future we will disable ../kibana-extra/{pluginName}. You should move your plugin ${pkg.name} as soon as possible to ./plugins/{pluginName}`
);
}
const kibanaRootWhenNotXpackPlugin = isPluginOnKibanaExtra ? kibanaExtraDir : kibanaPluginsDir;
return {
root,
kibanaRoot: isPluginXpack ? resolve(root, '..') : kibanaRootWhenNotXpackPlugin,
serverTestPatterns: ['server/**/__tests__/**/*.js'],
buildSourcePatterns,
skipInstallDependencies: false,
id: pkg.name as string,
version: pkg.version as string,
pkg,
...configFile(root),
};
}

View file

@ -1,42 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { resolve } from 'path';
import { pluginConfig } from './plugin_config';
export function babelRegister() {
const plugin = pluginConfig();
try {
// add support for moved @babel/register source: https://github.com/elastic/kibana/pull/13973
require(resolve(plugin.kibanaRoot, 'src/setup_node_env/babel_register'));
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
require(resolve(plugin.kibanaRoot, 'src/optimize/babel/register'));
} else {
throw error;
}
}
}
export function resolveKibanaPath(path: string) {
const plugin = pluginConfig();
return resolve(plugin.kibanaRoot, path);
}

View file

@ -1,24 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Os from 'os';
export function winCmd(cmd: string) {
return /^win/.test(Os.platform()) ? cmd + '.cmd' : cmd;
}

View file

@ -17,32 +17,29 @@
* under the License.
*/
import { execFileSync } from 'child_process';
import globby from 'globby';
import Path from 'path';
import { TaskContext } from '../../../lib';
import {
REPO_ROOT,
parseKibanaPlatformPlugin,
KibanaPlatformPlugin,
createFailError,
} from '@kbn/dev-utils';
export function testMochaTask({ plugin, options }: TaskContext) {
options = options || {};
let testPatterns = plugin.serverTestPatterns;
export type Plugin = KibanaPlatformPlugin;
// allow server test files to be overridden
if (options.files && options.files.length) {
testPatterns = options.files;
export function loadKibanaPlatformPlugin(pluginDir: string) {
const parentDir = Path.resolve(pluginDir, '..');
const isFixture = pluginDir.includes('__fixtures__');
const isExample = Path.basename(parentDir) === 'examples';
const isRootPlugin = parentDir === Path.resolve(REPO_ROOT, 'plugins');
if (isFixture || isExample || isRootPlugin) {
return parseKibanaPlatformPlugin(Path.resolve(pluginDir, 'kibana.json'));
}
execFileSync(
process.execPath,
[
'scripts/mocha',
...globby.sync(testPatterns, {
cwd: plugin.root,
absolute: true,
}),
],
{
cwd: plugin.kibanaRoot,
stdio: ['ignore', 1, 2],
}
throw createFailError(
`Plugin located at [${pluginDir}] must be moved to the plugins directory at the root of the Kibana repo`
);
}

View file

@ -17,19 +17,24 @@
* under the License.
*/
import { buildTask } from '../tasks/build';
import { startTask } from '../tasks/start';
import { testMochaTask } from '../tasks/test/mocha';
import inquirer from 'inquirer';
// define a tasks interface that we can extend in the tests
export interface Tasks {
build: typeof buildTask;
start: typeof startTask;
testMocha: typeof testMochaTask;
import { Plugin } from './load_kibana_platform_plugin';
export async function resolveKibanaVersion(option: string | undefined, plugin: Plugin) {
const preselectedVersion = option || plugin.manifest.kibanaVersion || plugin.manifest.version;
if (preselectedVersion && preselectedVersion !== 'kibana') {
return preselectedVersion;
}
const answers = await inquirer.prompt([
{
type: 'input',
name: 'kibanaVersion',
message: 'What version of Kibana are you building for?',
},
]);
return answers.kibanaVersion;
}
export const tasks: Tasks = {
build: buildTask,
start: startTask,
testMocha: testMochaTask,
};

View file

@ -1,19 +0,0 @@
Copies files from the source into a zip archive that can be distributed for
installation into production kibana installs. The archive includes the non-
development npm dependencies and builds itself using raw files in the source
directory so make sure they are clean/up to date. The resulting archive can
be found at:
```
build/{pkg.name}-{pkg.version}.zip
```
If you use the `--build-destination` flag, the resulting build will be found
in that directory.
```
plugin-helpers build --build-destination build/some/child/path
# This will place the resulting build at:
build/some/child/path/{pkg.name}-{pkg.version}.zip
```

View file

@ -1,63 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { join, resolve } from 'path';
import inquirer from 'inquirer';
import { TaskContext } from '../../lib';
import { createBuild } from './create_build';
import { createPackage } from './create_package';
export async function buildTask({ plugin, options = {} }: TaskContext) {
let buildVersion = plugin.version;
let kibanaVersion = (plugin.pkg.kibana && plugin.pkg.kibana.version) || plugin.pkg.version;
let buildFiles = plugin.buildSourcePatterns;
let buildTarget = join(plugin.root, 'build');
// allow source files to be overridden
if (options.files && options.files.length) {
buildFiles = options.files;
}
// allow options to override plugin info
if (options.buildDestination) buildTarget = resolve(plugin.root, options.buildDestination);
if (options.buildVersion) buildVersion = options.buildVersion;
if (options.kibanaVersion) kibanaVersion = options.kibanaVersion;
const chosenKibanaVersion =
kibanaVersion === 'kibana' ? await askForKibanaVersion() : kibanaVersion;
await createBuild(plugin, buildTarget, buildVersion, chosenKibanaVersion, buildFiles);
if (!options.skipArchive) {
await createPackage(plugin, buildTarget, buildVersion);
}
}
async function askForKibanaVersion() {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'kibanaVersion',
message: 'What version of Kibana are you building for?',
},
]);
return answers.kibanaVersion;
}

View file

@ -1,179 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { relative } from 'path';
import path from 'path';
import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs';
import execa from 'execa';
import del from 'del';
import File from 'vinyl';
import vfs from 'vinyl-fs';
import rename from 'gulp-rename';
import through from 'through2';
import minimatch from 'minimatch';
// @ts-ignore
import gulpBabel from 'gulp-babel';
import { PluginConfig, winCmd, pipeline } from '../../lib';
import { rewritePackageJson } from './rewrite_package_json';
// `link:` dependencies create symlinks, but we don't want to include symlinks
// in the built zip file. Therefore we remove all symlinked dependencies, so we
// can re-create them when installing the plugin.
function removeSymlinkDependencies(root: string) {
const nodeModulesPattern = path.join(root, '**', 'node_modules', '**');
return through.obj((file: File, _, cb) => {
const isSymlink = file.symlink != null;
const isDependency = minimatch(file.path, nodeModulesPattern);
if (isSymlink && isDependency) {
unlinkSync(file.path);
}
cb();
});
}
// parse a ts config file
function parseTsconfig(pluginSourcePath: string, configPath: string) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ts = require(path.join(pluginSourcePath, 'node_modules', 'typescript'));
const { error, config } = ts.parseConfigFileTextToJson(
configPath,
readFileSync(configPath, 'utf8')
);
if (error) {
throw error;
}
return config;
}
// transpile with babel
async function transpileWithBabel(srcGlobs: string[], buildRoot: string, presets: string[]) {
await pipeline(
vfs.src(
srcGlobs.concat([
'!**/*.d.ts',
'!**/*.{test,test.mocks,mock,mocks}.{ts,tsx}',
'!**/node_modules/**',
'!**/bower_components/**',
'!**/__tests__/**',
]),
{
cwd: buildRoot,
}
),
gulpBabel({
babelrc: false,
presets,
}),
vfs.dest(buildRoot)
);
}
export async function createBuild(
plugin: PluginConfig,
buildTarget: string,
buildVersion: string,
kibanaVersion: string,
files: string[]
) {
const buildSource = plugin.root;
const buildRoot = path.join(buildTarget, 'kibana', plugin.id);
await del(buildTarget);
// copy source files and apply some transformations in the process
await pipeline(
vfs.src(files, {
cwd: buildSource,
base: buildSource,
allowEmpty: true,
}),
// modify the package.json file
rewritePackageJson(buildSource, buildVersion, kibanaVersion),
// put all files inside the correct directories
rename(function nestFileInDir(filePath) {
const nonRelativeDirname = filePath.dirname!.replace(/^(\.\.\/?)+/g, '');
filePath.dirname = path.join(relative(buildTarget, buildRoot), nonRelativeDirname);
}),
// write files back to disk
vfs.dest(buildTarget)
);
// install packages in build
if (!plugin.skipInstallDependencies) {
execa.sync(winCmd('yarn'), ['install', '--production', '--pure-lockfile'], {
cwd: buildRoot,
});
}
// transform typescript to js and clean out typescript
const tsConfigPath = path.join(buildRoot, 'tsconfig.json');
if (existsSync(tsConfigPath)) {
// attempt to patch the extends path in the tsconfig file
const buildConfig = parseTsconfig(buildSource, tsConfigPath);
if (buildConfig.extends) {
buildConfig.extends = path.join(relative(buildRoot, buildSource), buildConfig.extends);
writeFileSync(tsConfigPath, JSON.stringify(buildConfig));
}
// Transpile ts server code
//
// Include everything except content from public folders
await transpileWithBabel(['**/*.{ts,tsx}', '!**/public/**'], buildRoot, [
require.resolve('@kbn/babel-preset/node_preset'),
]);
// Transpile ts client code
//
// Include everything inside a public directory
await transpileWithBabel(['**/public/**/*.{ts,tsx}'], buildRoot, [
require.resolve('@kbn/babel-preset/webpack_preset'),
]);
del.sync([
path.join(buildRoot, '**', '*.{ts,tsx,d.ts}'),
path.join(buildRoot, 'tsconfig.json'),
]);
}
// remove symlinked dependencies
await pipeline(
vfs.src([relative(buildTarget, buildRoot) + '/**/*'], {
cwd: buildTarget,
base: buildTarget,
resolveSymlinks: false,
}),
removeSymlinkDependencies(buildRoot)
);
}

View file

@ -1,46 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { execFileSync } from 'child_process';
export function gitInfo(rootPath: string) {
try {
const LOG_SEPARATOR = '||';
const commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
cwd: rootPath,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
});
const logLine = execFileSync('git', ['log', '--pretty=%h' + LOG_SEPARATOR + '%cD', '-n', '1'], {
cwd: rootPath,
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
}).split(LOG_SEPARATOR);
return {
count: commitCount.trim(),
sha: logLine[0].trim(),
date: logLine[1].trim(),
};
} catch (e) {
return {};
}
}

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export * from './build_task';

View file

@ -1,16 +0,0 @@
{
"name": "build_action_test_plugin",
"version": "0.0.1",
"kibana": {
"version": "6.0.0"
},
"dependencies": {
},
"devDependencies": {
},
"scripts": {
"start": "node index.js"
}
}

View file

@ -1,4 +0,0 @@
{
"common.ui.welcomeMessage": "Cargando Kibana",
"common.ui.welcomeError": "Kibana no se cargó correctamente. Heck la salida del servidor para obtener más información."
}

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
module.exports = (kibana) => new kibana.Plugin({});

View file

@ -1,16 +0,0 @@
{
"name": "create_build_test_plugin",
"version": "0.0.1",
"kibana": {
"version": "6.0.0"
},
"dependencies": {
"noop3": "999.999.999"
},
"devDependencies": {
},
"scripts": {
"start": "node index.js"
}
}

View file

@ -1,4 +0,0 @@
{
"common.ui.welcomeMessage": "Cargando Kibana",
"common.ui.welcomeError": "Kibana no se cargó correctamente. Heck la salida del servidor para obtener más información."
}

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
module.exports = (kibana) => new kibana.Plugin({});

View file

@ -1,16 +0,0 @@
{
"name": "create_package_test_plugin",
"version": "0.0.1",
"kibana": {
"version": "6.0.0"
},
"dependencies": {
},
"devDependencies": {
},
"scripts": {
"start": "node index.js"
}
}

View file

@ -1,4 +0,0 @@
{
"common.ui.welcomeMessage": "Cargando Kibana",
"common.ui.welcomeError": "Kibana no se cargó correctamente. Heck la salida del servidor para obtener más información."
}

View file

@ -1,3 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`calling create_build rejects returned promise when build fails 1`] = `"foo bar"`;

View file

@ -1,117 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { resolve } from 'path';
import fs from 'fs';
import del from 'del';
import { pluginConfig } from '../../../lib';
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/build_action_test_plugin');
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build');
const plugin = pluginConfig(PLUGIN_FIXTURE);
describe('creating build zip', () => {
const { buildTask } = require('../build_task');
beforeEach(() => del(PLUGIN_BUILD_DIR));
afterEach(() => del(PLUGIN_BUILD_DIR));
it('creates a zip in the build directory', async () => {
await buildTask({ plugin });
const buildFile = resolve(PLUGIN_BUILD_DIR, plugin.id + '-' + plugin.version + '.zip');
if (!fs.existsSync(buildFile)) {
throw new Error('Build file not found: ' + buildFile);
}
});
it('skips zip creation based on flag', async () => {
await buildTask({ plugin, options: { skipArchive: true } });
const buildFile = resolve(PLUGIN_BUILD_DIR, plugin.id + '-' + plugin.version + '.zip');
if (fs.existsSync(buildFile)) {
throw new Error('Build file not found: ' + buildFile);
}
});
});
describe('calling create_build', () => {
let mockBuild;
let buildTask;
beforeEach(() => {
jest.resetModules();
jest.mock('../create_build');
({ createBuild: mockBuild } = require('../create_build'));
({ buildTask } = require('../build_task'));
});
const nameArgs = ([plugin, buildTarget, buildVersion, kibanaVersion, files]) => ({
plugin,
buildTarget,
buildVersion,
kibanaVersion,
files,
});
it('takes optional build version', async () => {
const options = {
buildVersion: '1.2.3',
kibanaVersion: '4.5.6',
};
await buildTask({ plugin, options });
expect(mockBuild.mock.calls).toHaveLength(1);
const { buildVersion, kibanaVersion } = nameArgs(mockBuild.mock.calls[0]);
expect(buildVersion).toBe('1.2.3');
expect(kibanaVersion).toBe('4.5.6');
});
it('uses default file list without files option', async () => {
await buildTask({ plugin });
expect(mockBuild.mock.calls).toHaveLength(1);
const { files } = nameArgs(mockBuild.mock.calls[0]);
plugin.buildSourcePatterns.forEach((file) => expect(files).toContain(file));
});
it('uses only files passed in', async () => {
const options = {
files: ['index.js', 'LICENSE.txt', 'plugins/**/*', '{server,public}/**/*'],
};
await buildTask({ plugin, options });
expect(mockBuild.mock.calls).toHaveLength(1);
const { files } = nameArgs(mockBuild.mock.calls[0]);
options.files.forEach((file) => expect(files).toContain(file));
});
it('rejects returned promise when build fails', async () => {
mockBuild.mockImplementation(async () => {
throw new Error('foo bar');
});
await expect(buildTask({ plugin })).rejects.toThrowErrorMatchingSnapshot();
});
});

View file

@ -1,87 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { resolve } from 'path';
import { readdirSync } from 'fs';
import del from 'del';
import { createBuild } from '../create_build';
import { pluginConfig } from '../../../lib';
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/create_build_test_plugin');
const PLUGIN = pluginConfig(PLUGIN_FIXTURE);
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build');
const PLUGIN_BUILD_TARGET = resolve(PLUGIN_BUILD_DIR, 'kibana', PLUGIN.id);
beforeEach(() => del(PLUGIN_BUILD_DIR));
afterEach(() => del(PLUGIN_BUILD_DIR));
describe('creating the build', () => {
const buildTarget = resolve(PLUGIN.root, 'build');
const buildVersion = PLUGIN.version;
const kibanaVersion = PLUGIN.version;
const buildFiles = PLUGIN.buildSourcePatterns;
it('removes development properties from package.json', async () => {
expect(PLUGIN.pkg.scripts).not.toBeUndefined();
expect(PLUGIN.pkg.devDependencies).not.toBeUndefined();
await createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles);
const pkg = require(resolve(PLUGIN_BUILD_TARGET, 'package.json')); // eslint-disable-line import/no-dynamic-require
expect(pkg).not.toHaveProperty('scripts');
expect(pkg).not.toHaveProperty('devDependencies');
});
it('adds build metadata to package.json', async () => {
expect(PLUGIN.pkg.build).toBeUndefined();
await createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles);
const pkg = require(resolve(PLUGIN_BUILD_TARGET, 'package.json')); // eslint-disable-line import/no-dynamic-require
expect(pkg).toHaveProperty('build');
expect(pkg.build.git).not.toBeUndefined();
expect(pkg.build.date).not.toBeUndefined();
});
describe('skipInstallDependencies = false', () => {
it('installs node_modules as a part of build', async () => {
expect(PLUGIN.skipInstallDependencies).toBe(false);
await createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles);
expect(readdirSync(resolve(PLUGIN_BUILD_TARGET))).toContain('node_modules');
expect(readdirSync(resolve(PLUGIN_BUILD_TARGET, 'node_modules'))).toContain('noop3');
});
});
describe('skipInstallDependencies = true', () => {
// set skipInstallDependencies to true for these tests
beforeEach(() => (PLUGIN.skipInstallDependencies = true));
// set it back to false after
afterEach(() => (PLUGIN.skipInstallDependencies = false));
it('does not install node_modules as a part of build', async () => {
expect(PLUGIN.skipInstallDependencies).toBe(true);
await createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles);
expect(readdirSync(resolve(PLUGIN_BUILD_TARGET))).not.toContain('node_modules');
});
});
});

View file

@ -1,48 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { resolve } from 'path';
import { statSync } from 'fs';
import del from 'del';
import { createBuild } from '../create_build';
import { createPackage } from '../create_package';
import { pluginConfig } from '../../../lib';
const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/create_package_test_plugin');
const PLUGIN = pluginConfig(PLUGIN_FIXTURE);
const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build-custom');
const buildVersion = PLUGIN.version;
const kibanaVersion = PLUGIN.version;
const buildFiles = PLUGIN.buildSourcePatterns;
const packageFile = `${PLUGIN.id}-${buildVersion}.zip`;
beforeAll(() => del(PLUGIN_BUILD_DIR));
afterAll(() => del(PLUGIN_BUILD_DIR));
describe('creating the package', () => {
it('creates zip file in build target path', async () => {
await createBuild(PLUGIN, PLUGIN_BUILD_DIR, buildVersion, kibanaVersion, buildFiles);
await createPackage(PLUGIN, PLUGIN_BUILD_DIR, buildVersion);
const zipFile = resolve(PLUGIN_BUILD_DIR, packageFile);
const stats = statSync(zipFile);
expect(stats.isFile()).toBe(true);
});
});

View file

@ -1,54 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Through2Map from 'through2-map';
import File from 'vinyl';
import { gitInfo } from './git_info';
export function rewritePackageJson(
buildSource: string,
buildVersion: string,
kibanaVersion: string
) {
return Through2Map.obj(function (file: File) {
if (file.basename === 'package.json' && file.dirname === buildSource) {
const pkg = JSON.parse(file.contents!.toString('utf8'));
// rewrite the target kibana version while the
// file is on it's way to the archive
if (!pkg.kibana) pkg.kibana = {};
pkg.kibana.version = kibanaVersion;
pkg.version = buildVersion;
// append build info
pkg.build = {
git: gitInfo(buildSource),
date: new Date().toString(),
};
// remove development properties from the package file
delete pkg.scripts;
delete pkg.devDependencies;
file.contents = Buffer.from(JSON.stringify(pkg, null, 2));
}
return file;
});
}

View file

@ -17,19 +17,21 @@
* under the License.
*/
import { resolve } from 'path';
import { readFileSync } from 'fs';
import Fs from 'fs';
import { promisify } from 'util';
function indent(txt: string, n: number) {
const space = new Array(n + 1).join(' ');
return space + txt.split('\n').join('\n' + space);
}
export function docs(name: string) {
const md = readFileSync(resolve(__dirname, '../../src/tasks', name, 'README.md'), 'utf8');
return function () {
/* eslint-disable-next-line no-console */
console.log(`\n Docs:\n\n${indent(md, 4)}\n\n`);
};
import del from 'del';
import { BuildContext } from '../build_context';
const asyncMkdir = promisify(Fs.mkdir);
export async function initTargets({ log, sourceDir, buildDir }: BuildContext) {
log.info('deleting the build and target directories');
await del(['build', 'target'], {
cwd: sourceDir,
});
log.debug(`creating build output dir [${buildDir}]`);
await asyncMkdir(buildDir, { recursive: true });
}

View file

@ -17,30 +17,40 @@
* under the License.
*/
import { relative, join } from 'path';
import Path from 'path';
import { pipeline } from 'stream';
import { promisify } from 'util';
import del from 'del';
import vfs from 'vinyl-fs';
import zip from 'gulp-zip';
import { pipeline, PluginConfig } from '../../lib';
import { BuildContext } from '../build_context';
export async function createPackage(
plugin: PluginConfig,
buildTarget: string,
buildVersion: string
) {
const buildId = `${plugin.id}-${buildVersion}`;
const buildRoot = join(buildTarget, 'kibana', plugin.id);
const buildFiles = [relative(buildTarget, buildRoot) + '/**/*'];
const asyncPipeline = promisify(pipeline);
// zip up the package
await pipeline(
vfs.src(buildFiles, { cwd: buildTarget, base: buildTarget, dot: true }),
zip(`${buildId}.zip`),
vfs.dest(buildTarget)
export async function createArchive({ kibanaVersion, plugin, log }: BuildContext) {
const {
manifest: { id },
directory,
} = plugin;
const zipName = `${id}-${kibanaVersion}.zip`;
log.info(`compressing plugin into [${zipName}]`);
const buildDir = Path.resolve(directory, 'build');
// zip up the build files
await asyncPipeline(
vfs.src([`kibana/${id}/**/*`], {
cwd: buildDir,
base: buildDir,
dot: true,
}),
zip(zipName),
vfs.dest(buildDir)
);
// clean up the build path
await del(join(buildTarget, 'kibana'));
// delete the files that were zipped
await del(Path.resolve(buildDir, 'kibana'));
}

View file

@ -17,8 +17,8 @@
* under the License.
*/
export * from './run';
export * from './utils';
export * from './win_cmd';
export * from './plugin_config';
export * from './pipeline';
export * from './clean';
export * from './create_archive';
export * from './optimize';
export * from './write_server_files';
export * from './yarn_install';

View file

@ -0,0 +1,53 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Fs from 'fs';
import Path from 'path';
import { promisify } from 'util';
import { REPO_ROOT } from '@kbn/dev-utils';
import { OptimizerConfig, runOptimizer, logOptimizerState } from '@kbn/optimizer';
import { BuildContext } from '../build_context';
const asyncRename = promisify(Fs.rename);
export async function optimize({ log, plugin, sourceDir, buildDir }: BuildContext) {
if (!plugin.manifest.ui) {
return;
}
log.info('running @kbn/optimizer');
log.indent(2);
// build bundles into target
const config = OptimizerConfig.create({
repoRoot: REPO_ROOT,
pluginPaths: [sourceDir],
cache: false,
dist: true,
pluginScanDirs: [],
});
await runOptimizer(config).pipe(logOptimizerState(log, config)).toPromise();
// move target into buildDir
await asyncRename(Path.resolve(sourceDir, 'target'), Path.resolve(buildDir, 'target'));
log.indent(-2);
}

View file

@ -1,6 +0,0 @@
Starts the kibana server with this plugin included. In essence this is the same as running the
following from the root of the kibana install:
```sh
./bin/kibana --dev --plugin-path=../path/to/plugin
```

View file

@ -1,51 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { execFileSync } from 'child_process';
import { join } from 'path';
// @ts-ignore
import split from 'argv-split';
import { TaskContext } from '../../lib';
export function startTask({ plugin, options }: TaskContext) {
options = options || {};
const cmd = 'node';
const script = join('scripts', 'kibana.js');
const nodeOptions = split(process.env.NODE_OPTIONS || '');
let args = nodeOptions.concat([script, '--dev', '--plugin-path', plugin.root]);
if (Array.isArray(plugin.includePlugins)) {
plugin.includePlugins.forEach((path) => {
args = args.concat(['--plugin-path', path]);
});
}
if (options.flags) {
args = args.concat(options.flags);
}
execFileSync(cmd, args, {
cwd: plugin.kibanaRoot,
stdio: ['ignore', 1, 2],
});
}

View file

@ -1,45 +0,0 @@
writing tests
=============
Server tests are written just like browser tests, they are just executed differently.
- place tests near the code they test, in `__tests__` directories throughout
the server directory
- Use the same bdd-style `describe()` and `it()` api to define the suites
and cases of your tests.
```js
describe('some portion of your code', function () {
it('should do this thing', function () {
expect(true).to.be(false);
});
});
```
running the tests
=================
Running the server tests is simple, just execute `yarn test:mocha` in your terminal
and all of the tests in your server will be run.
By default, the runner will look for tests in `server/**/__tests__/**/*.js`. If you'd prefer to
use a different collection of globs and files, you can specify them after the `yarn test:mocha`
task, like so:
`yarn test:mocha 'plugins/myplugins/server/__tests__/**/*.js'`
NOTE: quoting the glob pattern is not required, but helps to avoid issues with globbing expansion
in your shell.
focus on the task at hand
=========================
To limit the tests that run add `.only` to your `describe()` or `it()` calls:
```js
describe.only('suite name', function () {
// ...
});
```

View file

@ -1,20 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export * from './test_mocha_task';

View file

@ -0,0 +1,101 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { pipeline } from 'stream';
import { promisify } from 'util';
import vfs from 'vinyl-fs';
import { transformFileWithBabel, transformFileStream } from '@kbn/dev-utils';
import { BuildContext } from '../build_context';
const asyncPipeline = promisify(pipeline);
export async function writeServerFiles({
log,
config,
plugin,
sourceDir,
buildDir,
kibanaVersion,
}: BuildContext) {
log.info('copying source into the build and converting with babel');
// copy source files and apply some babel transformations in the process
await asyncPipeline(
vfs.src(
[
'kibana.json',
...(plugin.manifest.server
? config.serverSourcePatterns || [
'yarn.lock',
'tsconfig.json',
'package.json',
'index.{js,ts}',
'{lib,server,common,translations}/**/*',
]
: []),
],
{
cwd: sourceDir,
base: sourceDir,
buffer: true,
ignore: [
'**/*.d.ts',
'**/public/**',
'**/__tests__/**',
'**/*.{test,test.mocks,mock,mocks}.*',
],
allowEmpty: true,
}
),
// add kibanaVersion to kibana.json files
transformFileStream((file) => {
if (file.relative !== 'kibana.json') {
return;
}
const json = file.contents.toString('utf8');
const manifest = JSON.parse(json);
file.contents = Buffer.from(
JSON.stringify(
{
...manifest,
kibanaVersion,
},
null,
2
)
);
}),
transformFileStream(async (file) => {
if (file.path.includes('node_modules')) {
return;
}
if (['.js', '.ts', '.tsx'].includes(file.extname)) {
await transformFileWithBabel(file);
}
}),
vfs.dest(buildDir)
);
}

View file

@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import Fs from 'fs';
import Path from 'path';
import execa from 'execa';
import { BuildContext } from '../build_context';
const winVersion = (path: string) => (process.platform === 'win32' ? `${path}.cmd` : path);
export async function yarnInstall({ log, buildDir, config }: BuildContext) {
const pkgJson = Path.resolve(buildDir, 'package.json');
if (config?.skipInstallDependencies || !Fs.existsSync(pkgJson)) {
return;
}
log.info('running yarn to install dependencies');
await execa(winVersion('yarn'), ['install', '--production', '--pure-lockfile'], {
cwd: buildDir,
});
}

View file

@ -3,9 +3,10 @@
"compilerOptions": {
"outDir": "target",
"declaration": true,
"sourceMap": true
"sourceMap": true,
"target": "ES2018"
},
"include": [
"src/**/*.ts"
"src/**/*"
]
}

View file

@ -17,4 +17,5 @@
* under the License.
*/
module.exports = (kibana) => new kibana.Plugin({});
require('../src/setup_node_env/prebuilt_dev_only_entry');
require('@kbn/plugin-helpers').runCli();

View file

@ -23,5 +23,6 @@ require('./harden');
// The following require statements MUST be executed before any others - END
require('symbol-observable');
require('source-map-support/register');
require('./root');
require('./node_version_validator');

View file

@ -13,7 +13,7 @@
},
"devDependencies": {
"@elastic/eui": "27.4.1",
"@kbn/plugin-helpers": "9.0.2",
"@kbn/plugin-helpers": "1.0.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"typescript": "4.0.2"

View file

@ -13,7 +13,7 @@
},
"devDependencies": {
"@elastic/eui": "27.4.1",
"@kbn/plugin-helpers": "9.0.2",
"@kbn/plugin-helpers": "1.0.0",
"react": "^16.12.0",
"typescript": "4.0.2"
}

View file

@ -1,35 +0,0 @@
{
"serverTestPatterns": [
"legacy/server/**/__tests__/**/*.js",
"legacy/plugins/**/__tests__/**/*.js",
"!legacy/plugins/**/server/**/__tests__/**/*"
],
"buildSourcePatterns": [
"LICENSE.txt",
"NOTICE.txt",
"package.json",
"yarn.lock",
"tsconfig.json",
"index.js",
".i18nrc.json",
"plugins/**/*",
"plugins/reporting/.phantom/*",
"plugins/reporting/.chromium/*",
"legacy/common/**/*",
"legacy/plugins/**/*",
"legacy/server/**/*",
"typings/**/*",
"!**/README.md",
"!__tests__",
"!__tests__/**/*",
"!**/__tests__",
"!**/__tests__/**/*",
"!legacy/plugins/**/*.test.{js,ts}",
"!legacy/plugins/**/__snapshots__",
"!legacy/plugins/**/__snapshots__/*",
"!legacy/plugins/**/__mocks__/*",
"!plugins/canvas/shareable_runtime/test",
"!plugins/canvas/shareable_runtime/test/**/*"
],
"skipInstallDependencies": true
}

View file

@ -7,12 +7,10 @@
require('../src/setup_node_env');
const { buildTask } = require('./tasks/build');
const { devTask } = require('./tasks/dev');
const { downloadChromium } = require('./tasks/download_chromium');
// export the tasks that are runnable from the CLI
module.exports = {
build: buildTask,
dev: devTask,
downloadChromium,
};

View file

@ -35,7 +35,7 @@
"@kbn/dev-utils": "1.0.0",
"@kbn/es": "1.0.0",
"@kbn/expect": "1.0.0",
"@kbn/plugin-helpers": "9.0.2",
"@kbn/plugin-helpers": "1.0.0",
"@kbn/storybook": "1.0.0",
"@kbn/test": "1.0.0",
"@kbn/utility-types": "1.0.0",

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
require('@kbn/plugin-helpers').babelRegister();
require('../../src/setup_node_env');
require('../dev-tools/api_debug').apiDebug();

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
require('@kbn/plugin-helpers').babelRegister();
require('../../src/setup_node_env');
require('@kbn/test').runFtrCli();

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
require('@kbn/plugin-helpers').babelRegister();
require('../../src/setup_node_env');
require('@kbn/test').runTestsCli([
require.resolve('../test/functional/config.js'),

View file

@ -6,5 +6,5 @@
process.env.ALLOW_PERFORMANCE_HOOKS_IN_TASK_MANAGER = true;
require('@kbn/plugin-helpers').babelRegister();
require('../../src/setup_node_env');
require('@kbn/test').startServersCli(require.resolve('../test/functional/config.js'));

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
require('@kbn/plugin-helpers').babelRegister();
require('../../src/setup_node_env');
require('../dev-tools/jest').runJest();

View file

@ -7,22 +7,26 @@
import execa from 'execa';
import { resolve } from 'path';
import { writeFileSync } from 'fs';
import { promisify } from 'util';
import { pipeline } from 'stream';
import * as pluginHelpers from '@kbn/plugin-helpers';
import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils';
import { ToolingLog, REPO_ROOT, transformFileStream, transformFileWithBabel } from '@kbn/dev-utils';
import gulp from 'gulp';
import del from 'del';
import fancyLog from 'fancy-log';
import chalk from 'chalk';
import vfs from 'vinyl-fs';
import { generateNoticeFromSource } from '../../src/dev/notice';
import { gitInfo } from './helpers/git_info';
import { PKG_NAME } from './helpers/pkg';
import { BUILD_VERSION } from './helpers/build_version';
const asyncPipeline = promisify(pipeline);
const XPACK_DIR = resolve(REPO_ROOT, 'x-pack');
const BUILD_DIR = resolve(XPACK_DIR, 'build');
const PLUGIN_BUILD_DIR = resolve(BUILD_DIR, 'plugin');
const PLUGIN_BUILD_DIR = resolve(BUILD_DIR, 'plugin/kibana/x-pack');
async function cleanBuildTask() {
fancyLog('Deleting', BUILD_DIR);
@ -41,11 +45,52 @@ async function reportTask() {
fancyLog('Build SHA', chalk.yellow(info.sha));
}
async function pluginHelpersBuild() {
await pluginHelpers.run('build', {
skipArchive: true,
buildDestination: PLUGIN_BUILD_DIR,
});
async function copySourceAndBabelify() {
// copy source files and apply some babel transformations in the process
await asyncPipeline(
vfs.src(
[
'LICENSE.txt',
'NOTICE.txt',
'package.json',
'yarn.lock',
'tsconfig.json',
'index.js',
'.i18nrc.json',
'plugins/**/*',
'plugins/reporting/.phantom/*',
'plugins/reporting/.chromium/*',
'legacy/common/**/*',
'legacy/plugins/**/*',
'legacy/server/**/*',
'typings/**/*',
],
{
cwd: XPACK_DIR,
base: XPACK_DIR,
buffer: true,
nodir: true,
ignore: [
'**/README.md',
'**/*.{test,test.mocks,mock,mocks}.*',
'**/*.d.ts',
'**/node_modules/**',
'**/public/**',
'**/{__tests__,__mocks__,__snapshots__}/**',
'plugins/canvas/shareable_runtime/test/**',
],
allowEmpty: true,
}
),
transformFileStream(async (file) => {
if (['.js', '.ts', '.tsx'].includes(file.extname)) {
await transformFileWithBabel(file);
}
}),
vfs.dest(PLUGIN_BUILD_DIR)
);
}
async function buildCanvasShareableRuntime() {
@ -58,18 +103,17 @@ async function buildCanvasShareableRuntime() {
}
async function generateNoticeText() {
const buildRoot = resolve(PLUGIN_BUILD_DIR, 'kibana/x-pack');
const log = new ToolingLog({
level: 'info',
writeTo: process.stdout,
});
writeFileSync(
resolve(buildRoot, 'NOTICE.txt'),
resolve(PLUGIN_BUILD_DIR, 'NOTICE.txt'),
await generateNoticeFromSource({
productName: 'Kibana X-Pack',
log,
directory: buildRoot,
directory: PLUGIN_BUILD_DIR,
})
);
}
@ -78,6 +122,6 @@ export const buildTask = gulp.series(
cleanBuildTask,
reportTask,
buildCanvasShareableRuntime,
pluginHelpersBuild,
copySourceAndBabelify,
generateNoticeText
);

View file

@ -1,14 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as pluginHelpers from '@kbn/plugin-helpers';
import gulp from 'gulp';
export const devTask = gulp.series(async function startKibanaServer() {
await pluginHelpers.run('start', {
flags: process.argv.slice(3),
});
});

View file

@ -4,9 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { resolveKibanaPath } from '@kbn/plugin-helpers';
import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import path from 'path';
import { REPO_ROOT } from '@kbn/dev-utils';
import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import { services } from './services';
interface CreateTestConfigOptions {
@ -20,7 +22,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
return async ({ readConfigFile }: FtrConfigProviderContext) => {
const config = {
kibana: {
api: await readConfigFile(resolveKibanaPath('test/api_integration/config.js')),
api: await readConfigFile(path.resolve(REPO_ROOT, 'test/api_integration/config.js')),
functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')),
},
xpack: {

View file

@ -4,8 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { resolveKibanaPath } from '@kbn/plugin-helpers';
import path from 'path';
import { REPO_ROOT } from '@kbn/dev-utils';
import { TestInvoker } from './lib/types';
// @ts-ignore
import { LegacyEsProvider } from './services/legacy_es';
@ -21,7 +23,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
return async ({ readConfigFile }: TestInvoker) => {
const config = {
kibana: {
api: await readConfigFile(resolveKibanaPath('test/api_integration/config.js')),
api: await readConfigFile(path.resolve(REPO_ROOT, 'test/api_integration/config.js')),
functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')),
},
xpack: {

228
yarn.lock
View file

@ -3670,6 +3670,13 @@
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-3.5.43.tgz#e9b4992817e0b6c5efaa7d6e5bb2cee4d73eab58"
integrity sha512-t9ZmXOcpVxywRw86YtIC54g7M9puRh8hFedRvVfHKf5YyOP6pSxA0TvpXpfseXSCInoW4P7bggTrSDiUOs4g5w==
"@types/decompress@^4.2.3":
version "4.2.3"
resolved "https://registry.yarnpkg.com/@types/decompress/-/decompress-4.2.3.tgz#98eed48af80001038aa05690b2094915f296fe65"
integrity sha512-W24e3Ycz1UZPgr1ZEDHlK4XnvOr+CpJH3qNsFeqXwwlW/9END9gxn3oJSsp7gYdiQxrXUHwUUd3xuzVz37MrZQ==
dependencies:
"@types/node" "*"
"@types/dedent@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@types/dedent/-/dedent-0.7.0.tgz#155f339ca404e6dd90b9ce46a3f78fd69ca9b050"
@ -3836,13 +3843,6 @@
resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.13.4.tgz#55ae9c29f0fd6b85ee536f5c72b4769d5c5e06b1"
integrity sha512-B4yel4ro2nTb3v0pYO8vO6SjgvFJSrwUY+IO6TUSLdOSB+gQFslylrhRCHxvXMIhxB71mv5PEE9dAX+24S8sew==
"@types/gulp-rename@^0.0.33":
version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/gulp-rename/-/gulp-rename-0.0.33.tgz#38d146e97786569f74f5391a1b1f9b5198674b6c"
integrity sha512-FIZQvbZJj6V1gHPTzO+g/BCWpDur7fJrroae4gwV3LaoHBQ+MrR9sB+2HssK8fHv4WdY6hVNxkcft9bYatuPIA==
dependencies:
"@types/node" "*"
"@types/gulp-zip@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/gulp-zip/-/gulp-zip-4.0.1.tgz#96cd0b994219f9ae3bbbec7ec3baa043fba9d9ef"
@ -4814,21 +4814,6 @@
"@types/react-dom" "*"
"@types/testing-library__dom" "*"
"@types/through2-map@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/through2-map/-/through2-map-3.0.0.tgz#2fda6049bf0ec16fd75394fc64536d73024d3189"
integrity sha512-r2m4v3Lggg30dCt7nG9uDl93LhImYRsAutECYNU7JenHTM3MdwMHudcC3sOqk/rEUEpN9CDNOIuOxRGzJUP1pg==
dependencies:
"@types/node" "*"
"@types/through2" "*"
"@types/through2@*", "@types/through2@^2.0.35":
version "2.0.35"
resolved "https://registry.yarnpkg.com/@types/through2/-/through2-2.0.35.tgz#9add1643da9f936ecf0622311759b33e881047e8"
integrity sha512-5puhsegK8DdiZkVL71+iL67KxKd92l7kzzzeclc+idlp5L6PbjxDDQX9JCIA6jOUS9aNHgcmONyW5CRtZUvKFw==
dependencies:
"@types/node" "*"
"@types/through@*":
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
@ -6194,11 +6179,6 @@ argparse@^1.0.7, argparse@~1.0.9:
dependencies:
sprintf-js "~1.0.2"
argv-split@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argv-split/-/argv-split-2.0.1.tgz#be264117790dbd5ccd63ec3f449a1804814ac4c5"
integrity sha1-viZBF3kNvVzNY+w/RJoYBIFKxMU=
aria-hidden@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.1.1.tgz#0c356026d3f65e2bd487a3adb73f0c586be2c37e"
@ -7764,6 +7744,19 @@ btoa-lite@^1.0.0:
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc=
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
buffer-alloc@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@ -7784,6 +7777,11 @@ buffer-equal@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74=
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@ -7816,6 +7814,14 @@ buffer@^5.1.0, buffer@^5.2.0:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffer@^5.2.1:
version "5.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
builtin-modules@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@ -9054,7 +9060,7 @@ commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
commander@^2.20.0, commander@^2.7.1, commander@^2.9.0:
commander@^2.20.0, commander@^2.7.1:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@ -10374,6 +10380,59 @@ decompress-response@^5.0.0:
dependencies:
mimic-response "^2.0.0"
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
dependencies:
file-type "^5.2.0"
is-stream "^1.1.0"
tar-stream "^1.5.2"
decompress-tarbz2@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
dependencies:
decompress-tar "^4.1.0"
file-type "^6.1.0"
is-stream "^1.1.0"
seek-bzip "^1.0.5"
unbzip2-stream "^1.0.9"
decompress-targz@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
dependencies:
decompress-tar "^4.1.1"
file-type "^5.2.0"
is-stream "^1.1.0"
decompress-unzip@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k=
dependencies:
file-type "^3.8.0"
get-stream "^2.2.0"
pify "^2.3.0"
yauzl "^2.4.2"
decompress@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
dependencies:
decompress-tar "^4.0.0"
decompress-tarbz2 "^4.0.0"
decompress-targz "^4.0.0"
decompress-unzip "^4.0.1"
graceful-fs "^4.1.10"
make-dir "^1.0.0"
pify "^2.3.0"
strip-dirs "^2.0.0"
dedent@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
@ -11141,20 +11200,10 @@ duplexer@^0.1.1:
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
duplexify@^3.2.0, duplexify@^3.5.3:
version "3.5.4"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.4.tgz#4bb46c1796eabebeec4ca9a2e66b808cb7a3d8b4"
integrity sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==
dependencies:
end-of-stream "^1.0.0"
inherits "^2.0.1"
readable-stream "^2.0.0"
stream-shift "^1.0.0"
duplexify@^3.4.2, duplexify@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410"
integrity sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==
duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.5.3, duplexify@^3.6.0:
version "3.7.1"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
dependencies:
end-of-stream "^1.0.0"
inherits "^2.0.1"
@ -12950,6 +12999,21 @@ file-type@^10.9.0:
resolved "https://registry.yarnpkg.com/file-type/-/file-type-10.9.0.tgz#f6c12c7cb9e6b8aeefd6917555fd4f9eadf31891"
integrity sha512-9C5qtGR/fNibHC5gzuMmmgnjH3QDDLKMa8lYe9CiZVmAnI4aUaoMh40QyUPzzs0RYo837SOBKh7TYwle4G8E4w==
file-type@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek=
file-type@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
integrity sha1-LdvqfHP/42No365J3DOMBYwritY=
file-type@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
file-type@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-9.0.0.tgz#a68d5ad07f486414dfb2c8866f73161946714a18"
@ -13745,6 +13809,14 @@ get-stream@3.0.0, get-stream@^3.0.0:
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=
dependencies:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
get-stream@^4.0.0, get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
@ -14353,7 +14425,7 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4:
graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
@ -14780,11 +14852,6 @@ gulp-cli@^2.2.0:
v8flags "^3.0.1"
yargs "^7.1.0"
gulp-rename@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd"
integrity sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==
gulp-sourcemaps@2.6.5:
version "2.6.5"
resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz#a3f002d87346d2c0f3aec36af7eb873f23de8ae6"
@ -14802,10 +14869,10 @@ gulp-sourcemaps@2.6.5:
strip-bom-string "1.X"
through2 "2.X"
gulp-zip@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.1.tgz#0dba5094901b0d91bcc8b53b3f6ff797c0f2002d"
integrity sha512-M/IWLh9RvOpuofDZkgDirtiyz9J3yIqnDOJ3muzk2D/XnZ1ruqPlPLRIpXnl/aZU+xXwKPdOIxjRzkUcVEQyZQ==
gulp-zip@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.2.tgz#2edf797ec842e770f4dfde8bef97d139015b1972"
integrity sha512-rZd0Ppuc8Bf7J2/WzcdNaeb+lcEXf1R8mV/PJ9Kdu7PmnInWVeLSmiXIka/2QSe6uhAsGVFAMffWSaMzAPGTBg==
dependencies:
get-stream "^5.1.0"
plugin-error "^1.0.1"
@ -16465,6 +16532,11 @@ is-native@^1.0.1:
is-nil "^1.0.0"
to-source-code "^1.0.0"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
is-negated-glob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
@ -24054,7 +24126,7 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
"readable-stream@1 || 2", readable-stream@~2.3.3:
"readable-stream@1 || 2", readable-stream@^2.3.0, readable-stream@~2.3.3:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -25426,6 +25498,13 @@ seedrandom@^3.0.5:
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"
integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==
seek-bzip@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
dependencies:
commander "^2.8.1"
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@ -26797,6 +26876,13 @@ strip-bom@^4.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
strip-dirs@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
dependencies:
is-natural-number "^4.0.1"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
@ -27272,6 +27358,19 @@ tar-stream@^1.1.2:
readable-stream "^2.0.0"
xtend "^4.0.0"
tar-stream@^1.5.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
dependencies:
bl "^1.0.0"
buffer-alloc "^1.2.0"
end-of-stream "^1.0.0"
fs-constants "^1.0.0"
readable-stream "^2.3.0"
to-buffer "^1.1.1"
xtend "^4.0.0"
tar-stream@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
@ -27496,14 +27595,6 @@ through2-filter@^2.0.0:
through2 "~2.0.0"
xtend "~4.0.0"
through2-map@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/through2-map/-/through2-map-3.0.0.tgz#a6c3026ce63b4898a997d540506b66ffd970f271"
integrity sha1-psMCbOY7SJipl9VAUGtm/9lw8nE=
dependencies:
through2 "~2.0.0"
xtend "^4.0.0"
through2@2.X, through2@^2.0.0, through2@^2.0.3, through2@~2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
@ -27709,6 +27800,11 @@ to-arraybuffer@^1.0.0:
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
to-buffer@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
to-camel-case@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-camel-case/-/to-camel-case-1.0.0.tgz#1a56054b2f9d696298ce66a60897322b6f423e46"
@ -28257,6 +28353,14 @@ uid-safe@2.1.5:
dependencies:
random-bytes "~1.0.0"
unbzip2-stream@^1.0.9:
version "1.4.3"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
dependencies:
buffer "^5.2.1"
through "^2.3.8"
unc-path-regex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
@ -30523,7 +30627,7 @@ yargs@~3.10.0:
decamelize "^1.0.0"
window-size "0.1.0"
yauzl@2.10.0, yauzl@^2.10.0:
yauzl@2.10.0, yauzl@^2.10.0, yauzl@^2.4.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=