mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* [APM] Add new kibana package '@kbn/babel-plugin-apm-idx' based on 'babel-plugin-idx' to work with the APM implementation of the deep object property helper function. Configure it as a babel plugin in the common babel preset. * [APM] modified the expanded output code to handle null checking correction for nested properties and added tests. * [APM] use same versions of babel and jest as core kibana. fixes broken tests. * [APM] created namespaced package `@kbn/elastic-idx`. Made available the apm/common/idx and the corresponding babel plugin under this package. Modified all imports of apm/common/idx to @kbn/elastic-idx. * [APM] add typescript build to the @kbn/elastic-idx package * [APM] fix idx import and linting failures * [APM] make @kbn/elastic-idx/babel plugin detect idx calls as member functions from the bound scope
This commit is contained in:
parent
161c2f56c8
commit
ab4be80d4c
36 changed files with 1224 additions and 42 deletions
26
NOTICE.txt
26
NOTICE.txt
|
@ -133,6 +133,32 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---
|
||||
This product includes code that is based on facebookincubator/idx, which was
|
||||
available under a "MIT" license.
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-present, Facebook, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
This product includes code that is based on flot-charts, which was available
|
||||
under a "MIT" license.
|
||||
|
|
|
@ -18,11 +18,9 @@
|
|||
*/
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
require.resolve('@babel/preset-typescript'),
|
||||
require.resolve('@babel/preset-react')
|
||||
],
|
||||
presets: [require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
|
||||
plugins: [
|
||||
require.resolve('@kbn/elastic-idx/babel'),
|
||||
require.resolve('babel-plugin-add-module-exports'),
|
||||
|
||||
// The class properties proposal was merged with the private fields proposal
|
||||
|
@ -41,11 +39,7 @@ module.exports = {
|
|||
//
|
||||
// See https://github.com/babel/babel/issues/8244#issuecomment-466548733
|
||||
test: /x-pack[\/\\]plugins[\/\\]infra[\/\\].*[\/\\]graphql/,
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-typescript-strip-namespaces'),
|
||||
],
|
||||
]
|
||||
}
|
||||
]
|
||||
plugins: [[require.resolve('babel-plugin-typescript-strip-namespaces')]],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"@babel/preset-react":"^7.0.0",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"@babel/preset-typescript": "^7.3.3",
|
||||
"@kbn/elastic-idx": "1.0.0",
|
||||
"babel-plugin-add-module-exports": "^1.0.0",
|
||||
"babel-plugin-transform-define": "^1.3.1",
|
||||
"babel-plugin-typescript-strip-namespaces": "^1.1.1"
|
||||
|
|
3
packages/kbn-elastic-idx/.npmignore
Normal file
3
packages/kbn-elastic-idx/.npmignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
/tsconfig.json
|
||||
/src
|
||||
/babel/index.test.js
|
76
packages/kbn-elastic-idx/README.md
Normal file
76
packages/kbn-elastic-idx/README.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
Kibana elastic-idx Library
|
||||
==========================
|
||||
|
||||
The `@kbn/elastic-idx` package provides the `idx` function used for optional
|
||||
chaining. Currently, the optional chaining draft is in stage 1, making it too
|
||||
uncertain to add syntax support within Kibana. Other optional chaining
|
||||
libraries require the Proxy object to be polyfilled for browser support,
|
||||
however, this polyfill is not fully supported across all browsers that Kibana
|
||||
requires. The facebookincubator `idx` project
|
||||
(https://github.com/facebookincubator/idx) provides an answer to this with a
|
||||
specific implementation that is understood by TypeScript so that type
|
||||
information does not get lost (unlike lodash get) The `@kbn/elastic-idx`
|
||||
library makes use the `idx` idiom but differs in the way null values within the
|
||||
property chain are handled.
|
||||
|
||||
Similar to the facebookincubator `idx` project, `@kbn/elastic-idx` also
|
||||
provides the Babel plugin to transform `idx()` function calls into the expanded
|
||||
form. This Babel plugin was based off the facebookincubator `idx` Babel
|
||||
plugin, since the invocation syntax is almost identical, but the transformed
|
||||
code differs to match how the `@kbn/elastic-idx` library treats null values.
|
||||
|
||||
App Usage
|
||||
----------
|
||||
Within Kibana, `@kbn/elastic-idx` can be imported and used in any JavaScript or
|
||||
TypeScript project:
|
||||
|
||||
```
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
|
||||
const obj0 = { a: { b: { c: { d: 'iamdefined' } } } };
|
||||
const obj1 = { a: { b: null } };
|
||||
|
||||
idx(obj0, _ => _.a.b.c.d); // returns 'iamdefined'
|
||||
idx(obj1, _ => _.a.b.c.e); // returns undefined
|
||||
idx(obj1, _ => _.a.b); // returns null
|
||||
```
|
||||
|
||||
Build Optimization
|
||||
-------------------
|
||||
Similar to the facebookincubator `idx` project, it is NOT RECOMMENDED to use
|
||||
idx in shipped app code. The implementation details which make
|
||||
`@kbn/elastic-idx` possible comes at a non-negligible performance cost. This
|
||||
usually isn't noticable during development, but for production builds, it is
|
||||
recommended to transform idx calls into native, expanded form JS. Use the
|
||||
plugin `@kbn/elastic-idx/babel` within your Babel configuration:
|
||||
|
||||
```
|
||||
{ "plugins": [ "@kbn/elastic-idx/babel" ] }
|
||||
```
|
||||
|
||||
The resulting Babel transforms the following:
|
||||
|
||||
```
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
const obj = { a: { b: { c: { d: 'iamdefined' } } } };
|
||||
idx(obj, _ => _.a.b.c.d);
|
||||
```
|
||||
|
||||
into this:
|
||||
|
||||
```
|
||||
obj != null &&
|
||||
obj.a != null &&
|
||||
obj.a.b != null &&
|
||||
obj.a.b.c != null ?
|
||||
obj.a.b.c.d : undefined
|
||||
```
|
||||
|
||||
Note that this also removes the import statement from the source code, since it
|
||||
no longer needs to be bundled.
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
Tests can be run with `npm test`. This includes "functional" tests that
|
||||
transform and evaluate idx calls.
|
321
packages/kbn-elastic-idx/babel/index.js
Normal file
321
packages/kbn-elastic-idx/babel/index.js
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @notice
|
||||
* This product includes code that is based on facebookincubator/idx, which was
|
||||
* available under a "MIT" license.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* eslint strict: 0, new-cap: 0 */
|
||||
|
||||
'use strict';
|
||||
module.exports = context => {
|
||||
const t = context.types;
|
||||
|
||||
const idxRe = /\bidx\b/;
|
||||
|
||||
function checkIdxArguments(file, node) {
|
||||
const args = node.arguments;
|
||||
if (args.length !== 2) {
|
||||
throw file.buildCodeFrameError(node, 'The `idx` function takes exactly two arguments.');
|
||||
}
|
||||
const arrowFunction = args[1];
|
||||
if (!t.isArrowFunctionExpression(arrowFunction)) {
|
||||
throw file.buildCodeFrameError(
|
||||
arrowFunction,
|
||||
'The second argument supplied to `idx` must be an arrow function.'
|
||||
);
|
||||
}
|
||||
if (!t.isExpression(arrowFunction.body)) {
|
||||
throw file.buildCodeFrameError(
|
||||
arrowFunction.body,
|
||||
'The body of the arrow function supplied to `idx` must be a single ' +
|
||||
'expression (without curly braces).'
|
||||
);
|
||||
}
|
||||
if (arrowFunction.params.length !== 1) {
|
||||
throw file.buildCodeFrameError(
|
||||
arrowFunction.params[2] || arrowFunction,
|
||||
'The arrow function supplied to `idx` must take exactly one parameter.'
|
||||
);
|
||||
}
|
||||
const input = arrowFunction.params[0];
|
||||
if (!t.isIdentifier(input)) {
|
||||
throw file.buildCodeFrameError(
|
||||
arrowFunction.params[0],
|
||||
'The parameter supplied to `idx` must be an identifier.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function checkIdxBindingNode(file, node) {
|
||||
if (t.isImportDeclaration(node)) {
|
||||
// E.g. `import '...'`
|
||||
if (node.specifiers.length === 0) {
|
||||
throw file.buildCodeFrameError(node, 'The idx import must have a value.');
|
||||
}
|
||||
// E.g. `import A, {B} from '...'`
|
||||
// `import A, * as B from '...'`
|
||||
// `import {A, B} from '...'`
|
||||
if (node.specifiers.length > 1) {
|
||||
throw file.buildCodeFrameError(
|
||||
node.specifiers[1],
|
||||
'The idx import must be a single specifier.'
|
||||
);
|
||||
}
|
||||
// `importKind` is not a property unless flow syntax is enabled.
|
||||
// On specifiers, `importKind` is not "value" when it's not a type, it's
|
||||
// `null`.
|
||||
// E.g. `import type {...} from '...'`
|
||||
// `import typeof {...} from '...'`
|
||||
// `import {type ...} from '...'`.
|
||||
// `import {typeof ...} from '...'`
|
||||
if (
|
||||
node.importKind === 'type' ||
|
||||
node.importKind === 'typeof' ||
|
||||
node.specifiers[0].importKind === 'type' ||
|
||||
node.specifiers[0].importKind === 'typeof'
|
||||
) {
|
||||
throw file.buildCodeFrameError(node, 'The idx import must be a value import.');
|
||||
}
|
||||
} else if (t.isVariableDeclarator(node)) {
|
||||
// E.g. var {idx} or var [idx]
|
||||
if (!t.isIdentifier(node.id)) {
|
||||
throw file.buildCodeFrameError(
|
||||
node.specifiers[0],
|
||||
'The idx declaration must be an identifier.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UnsupportedNodeTypeError extends Error {
|
||||
constructor(node, ...params) {
|
||||
super(`Node type is not supported: ${node.type}`, ...params);
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, UnsupportedNodeTypeError);
|
||||
}
|
||||
|
||||
this.name = 'UnsupportedNodeTypeError';
|
||||
}
|
||||
}
|
||||
|
||||
function getDeepProperties(node, properties = [], computedProperties = new Set()) {
|
||||
if (t.isMemberExpression(node)) {
|
||||
if (node.computed) {
|
||||
computedProperties.add(node.property);
|
||||
}
|
||||
return getDeepProperties(node.object, [node.property, ...properties], computedProperties);
|
||||
} else if (t.isIdentifier(node)) {
|
||||
return [[node, ...properties], computedProperties];
|
||||
}
|
||||
|
||||
throw new UnsupportedNodeTypeError(node);
|
||||
}
|
||||
|
||||
function buildMemberChain(properties, computedProperties) {
|
||||
if (properties.length > 1) {
|
||||
const lead = properties.slice(0, properties.length - 1);
|
||||
const last = properties[properties.length - 1];
|
||||
return t.MemberExpression(
|
||||
buildMemberChain(lead, computedProperties),
|
||||
last,
|
||||
computedProperties.has(last)
|
||||
);
|
||||
} else if (properties.length === 1) {
|
||||
return properties[0];
|
||||
}
|
||||
return t.identifier('undefined');
|
||||
}
|
||||
|
||||
function buildExpandedMemberNullChecks(
|
||||
leadingProperties = [],
|
||||
trailingProperties = [],
|
||||
computedProperties
|
||||
) {
|
||||
const propertyChainNullCheck = t.BinaryExpression(
|
||||
'!=',
|
||||
buildMemberChain(leadingProperties, computedProperties),
|
||||
t.NullLiteral()
|
||||
);
|
||||
|
||||
if (trailingProperties.length <= 1) {
|
||||
return propertyChainNullCheck;
|
||||
}
|
||||
|
||||
const [headTrailingProperty, ...tailProperties] = trailingProperties;
|
||||
|
||||
return t.LogicalExpression(
|
||||
'&&',
|
||||
propertyChainNullCheck,
|
||||
buildExpandedMemberNullChecks(
|
||||
[...leadingProperties, headTrailingProperty],
|
||||
tailProperties,
|
||||
computedProperties
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function buildExpandedMemberAccess(node, state) {
|
||||
let baseNode;
|
||||
let properties;
|
||||
let computedProperties;
|
||||
|
||||
try {
|
||||
[[baseNode, ...properties], computedProperties] = getDeepProperties(node);
|
||||
} catch (error) {
|
||||
if (error instanceof UnsupportedNodeTypeError) {
|
||||
throw state.file.buildCodeFrameError(
|
||||
node,
|
||||
'idx callbacks may only access properties on the callback parameter.'
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (baseNode.name !== state.base.name) {
|
||||
throw state.file.buildCodeFrameError(
|
||||
node,
|
||||
'The parameter of the arrow function supplied to `idx` must match ' +
|
||||
'the base of the body expression.'
|
||||
);
|
||||
}
|
||||
return t.ConditionalExpression(
|
||||
buildExpandedMemberNullChecks([state.input], properties, computedProperties),
|
||||
buildMemberChain([state.input, ...properties], computedProperties),
|
||||
t.identifier('undefined')
|
||||
);
|
||||
}
|
||||
|
||||
function visitIdxCallExpression(path, state) {
|
||||
const node = path.node;
|
||||
checkIdxArguments(state.file, node);
|
||||
const replacement = buildExpandedMemberAccess(node.arguments[1].body, {
|
||||
file: state.file,
|
||||
input: node.arguments[0],
|
||||
base: node.arguments[1].params[0],
|
||||
});
|
||||
path.replaceWith(replacement);
|
||||
}
|
||||
|
||||
function isIdxImportOrRequire(node, name) {
|
||||
if (t.isImportDeclaration(node)) {
|
||||
if (name instanceof RegExp) {
|
||||
return name.test(node.source.value);
|
||||
} else {
|
||||
return t.isStringLiteral(node.source, { value: name });
|
||||
}
|
||||
} else if (t.isVariableDeclarator(node)) {
|
||||
return (
|
||||
t.isCallExpression(node.init) &&
|
||||
t.isIdentifier(node.init.callee, { name: 'require' }) &&
|
||||
(name instanceof RegExp
|
||||
? name.test(node.init.arguments[0].value)
|
||||
: t.isLiteral(node.init.arguments[0], { value: name }))
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const declareVisitor = {
|
||||
'ImportDeclaration|VariableDeclarator'(path, state) {
|
||||
if (!isIdxImportOrRequire(path.node, state.importName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkIdxBindingNode(state.file, path.node);
|
||||
|
||||
const bindingName = t.isImportDeclaration(path.node)
|
||||
? path.node.specifiers[0].local.name
|
||||
: path.node.id.name;
|
||||
const idxBinding = path.scope.getOwnBinding(bindingName);
|
||||
|
||||
idxBinding.constantViolations.forEach(refPath => {
|
||||
throw state.file.buildCodeFrameError(refPath.node, '`idx` cannot be redefined.');
|
||||
});
|
||||
|
||||
let didTransform = false;
|
||||
let didSkip = false;
|
||||
|
||||
// Traverse the references backwards to process inner calls before
|
||||
// outer calls.
|
||||
idxBinding.referencePaths
|
||||
.slice()
|
||||
.reverse()
|
||||
.forEach(refPath => {
|
||||
if (refPath.node === idxBinding.node) {
|
||||
// Do nothing...
|
||||
} else if (refPath.parentPath.isMemberExpression()) {
|
||||
visitIdxCallExpression(refPath.parentPath.parentPath, state);
|
||||
didTransform = true;
|
||||
} else if (refPath.parentPath.isCallExpression()) {
|
||||
visitIdxCallExpression(refPath.parentPath, state);
|
||||
didTransform = true;
|
||||
} else {
|
||||
// Should this throw?
|
||||
didSkip = true;
|
||||
}
|
||||
});
|
||||
if (didTransform && !didSkip) {
|
||||
path.remove();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
Program(path, state) {
|
||||
const importName = state.opts.importName || '@kbn/elastic-idx';
|
||||
// If there can't reasonably be an idx call, exit fast.
|
||||
if (importName !== '@kbn/elastic-idx' || idxRe.test(state.file.code)) {
|
||||
// We're very strict about the shape of idx. Some transforms, like
|
||||
// "babel-plugin-transform-async-to-generator", will convert arrow
|
||||
// functions inside async functions into regular functions. So we do
|
||||
// our transformation before any one else interferes.
|
||||
const newState = { file: state.file, importName };
|
||||
path.traverse(declareVisitor, newState);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
711
packages/kbn-elastic-idx/babel/index.test.js
Normal file
711
packages/kbn-elastic-idx/babel/index.test.js
Normal file
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @notice
|
||||
* This product includes code that is based on facebookincubator/idx, which was
|
||||
* available under a "MIT" license.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
const { transformSync: babelTransform } = require('@babel/core');
|
||||
const babelPluginIdx = require('./index');
|
||||
const transformAsyncToGenerator = require('@babel/plugin-transform-async-to-generator');
|
||||
const vm = require('vm');
|
||||
|
||||
function transform(source, plugins, options) {
|
||||
return babelTransform(source, {
|
||||
plugins: plugins || [[babelPluginIdx, options]],
|
||||
babelrc: false,
|
||||
highlightCode: false,
|
||||
}).code;
|
||||
}
|
||||
|
||||
const asyncToGeneratorHelperCode = `
|
||||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
||||
try {
|
||||
var info = gen[key](arg);
|
||||
var value = info.value;
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (info.done) {
|
||||
resolve(value);
|
||||
} else {
|
||||
Promise.resolve(value).then(_next, _throw);
|
||||
}
|
||||
}
|
||||
|
||||
function _asyncToGenerator(fn) {
|
||||
return function() {
|
||||
var self = this,
|
||||
args = arguments;
|
||||
return new Promise(function(resolve, reject) {
|
||||
var gen = fn.apply(self, args);
|
||||
|
||||
function _next(value) {
|
||||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
||||
}
|
||||
|
||||
function _throw(err) {
|
||||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
||||
}
|
||||
_next(undefined);
|
||||
});
|
||||
};
|
||||
}
|
||||
`;
|
||||
|
||||
function stringByTrimmingSpaces(string) {
|
||||
return string.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
expect.extend({
|
||||
toTransformInto: (input, expected) => {
|
||||
const plugins = typeof input === 'string' ? null : input.plugins;
|
||||
const options = typeof input === 'string' ? undefined : input.options;
|
||||
const code = typeof input === 'string' ? input : input.code;
|
||||
const actual = transform(code, plugins, options);
|
||||
const pass = stringByTrimmingSpaces(actual) === stringByTrimmingSpaces(expected);
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
'Expected input to transform into:\n' + expected + '\n' + 'Instead, got:\n' + actual,
|
||||
};
|
||||
},
|
||||
toThrowTransformError: (input, expected) => {
|
||||
try {
|
||||
const plugins = typeof input === 'string' ? null : input.plugins;
|
||||
const options = typeof input === 'string' ? undefined : input.options;
|
||||
const code = typeof input === 'string' ? input : input.code;
|
||||
transform(code, plugins, options);
|
||||
} catch (error) {
|
||||
const actual = /^.+:\s*(.*)/.exec(error.message)[1]; // Strip "undefined: " and code snippet
|
||||
return {
|
||||
pass: actual === expected,
|
||||
message: () =>
|
||||
'Expected transform to throw "' + expected + '", but instead ' + 'got "' + actual + '".',
|
||||
};
|
||||
}
|
||||
return {
|
||||
pass: false,
|
||||
message: () => 'Expected transform to throw "' + expected + '".',
|
||||
};
|
||||
},
|
||||
toReturn: (input, expected) => {
|
||||
const code = transform(input, undefined);
|
||||
const actual = vm.runInNewContext(code);
|
||||
return {
|
||||
pass: actual === expected,
|
||||
message: () => 'Expected "' + expected + '" but got "' + actual + '".',
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
describe('kbn-babel-plugin-apm-idx', () => {
|
||||
it('transforms member expressions', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b.c.d.e);
|
||||
`).toTransformInto(`
|
||||
base != null && base.b != null && base.b.c != null && base.b.c.d != null
|
||||
? base.b.c.d.e
|
||||
: undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws on call expressions', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b.c(...foo)().d(bar, null, [...baz]));
|
||||
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
|
||||
});
|
||||
|
||||
it('transforms bracket notation', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _["b"][0][c + d]);
|
||||
`).toTransformInto(`
|
||||
base != null && base["b"] != null && base["b"][0] != null ? base["b"][0][c + d] : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws on bracket notation call expressions', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _["b"](...foo)()[0][c + d](bar, null, [...baz]));
|
||||
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
|
||||
});
|
||||
|
||||
it('transforms combination of both member access notations', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.a["b"].c[d[e[f]]].g);
|
||||
`).toTransformInto(`
|
||||
base != null && base.a != null && base.a["b"] != null && base.a["b"].c != null && base.a["b"].c[d[e[f]]] != null
|
||||
? base.a["b"].c[d[e[f]]].g
|
||||
: undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms if the base is an expression', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(this.props.base[5], _ => _.property);
|
||||
`).toTransformInto(`
|
||||
this.props.base[5] != null ? this.props.base[5].property : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws if the arrow function has more than one param', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, (a, b) => _.property);
|
||||
`).toThrowTransformError(
|
||||
'The arrow function supplied to `idx` must take exactly one parameter.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if the arrow function has an invalid base', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, a => b.property)
|
||||
`).toThrowTransformError(
|
||||
'The parameter of the arrow function supplied to `idx` must match the ' +
|
||||
'base of the body expression.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if the arrow function expression has non-properties/methods', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => (_.a++).b.c);
|
||||
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
|
||||
});
|
||||
|
||||
it('throws if the body of the arrow function is not an expression', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => {})
|
||||
`).toThrowTransformError(
|
||||
'The body of the arrow function supplied to `idx` must be a single ' +
|
||||
'expression (without curly braces).'
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores non-function call idx', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
result = idx;
|
||||
`).toTransformInto(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
result = idx;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws if idx is called with zero arguments', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx();
|
||||
`).toThrowTransformError('The `idx` function takes exactly two arguments.');
|
||||
});
|
||||
|
||||
it('throws if idx is called with one argument', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(1);
|
||||
`).toThrowTransformError('The `idx` function takes exactly two arguments.');
|
||||
});
|
||||
|
||||
it('throws if idx is called with three arguments', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(1, 2, 3);
|
||||
`).toThrowTransformError('The `idx` function takes exactly two arguments.');
|
||||
});
|
||||
|
||||
it('transforms idx calls as part of another expressions', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
paddingStatement();
|
||||
a = idx(base, _ => _.b[c]);
|
||||
`).toTransformInto(`
|
||||
paddingStatement();
|
||||
a = base != null && base.b != null ? base.b[c] : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms nested idx calls', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(
|
||||
idx(
|
||||
idx(base, _ => _.a.b),
|
||||
_ => _.c.d
|
||||
),
|
||||
_ => _.e.f
|
||||
);
|
||||
`).toTransformInto(`
|
||||
(
|
||||
(base != null && base.a != null ? base.a.b : undefined) != null &&
|
||||
(base != null && base.a != null ? base.a.b : undefined).c != null ?
|
||||
(base != null && base.a != null ? base.a.b : undefined).c.d :
|
||||
undefined
|
||||
) != null
|
||||
&&
|
||||
(
|
||||
(base != null && base.a != null ? base.a.b : undefined) != null &&
|
||||
(base != null && base.a != null ? base.a.b : undefined).c != null ?
|
||||
(base != null && base.a != null ? base.a.b : undefined).c.d :
|
||||
undefined
|
||||
).e != null ?
|
||||
(
|
||||
(base != null && base.a != null ? base.a.b : undefined) != null &&
|
||||
(base != null && base.a != null ? base.a.b : undefined).c != null ?
|
||||
(base != null && base.a != null ? base.a.b : undefined).c.d :
|
||||
undefined
|
||||
).e.f :
|
||||
undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms idx calls inside async functions (plugin order #1)', () => {
|
||||
expect({
|
||||
plugins: [babelPluginIdx, transformAsyncToGenerator],
|
||||
code: `
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
async function f() {
|
||||
idx(base, _ => _.b.c.d.e);
|
||||
}
|
||||
`,
|
||||
}).toTransformInto(`
|
||||
${asyncToGeneratorHelperCode}
|
||||
function f() {
|
||||
return _f.apply(this, arguments);
|
||||
}
|
||||
|
||||
function _f() {
|
||||
_f = _asyncToGenerator(function* () {
|
||||
base != null && base.b != null && base.b.c != null && base.b.c.d != null ? base.b.c.d.e : undefined;
|
||||
});
|
||||
return _f.apply(this, arguments);
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms idx calls inside async functions (plugin order #2)', () => {
|
||||
expect({
|
||||
plugins: [transformAsyncToGenerator, babelPluginIdx],
|
||||
code: `
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
async function f() {
|
||||
idx(base, _ => _.b.c.d.e);
|
||||
}
|
||||
`,
|
||||
}).toTransformInto(`
|
||||
${asyncToGeneratorHelperCode}
|
||||
|
||||
function f() {
|
||||
return _f.apply(this, arguments);
|
||||
}
|
||||
|
||||
function _f() {
|
||||
_f = _asyncToGenerator(function* () {
|
||||
base != null && base.b != null && base.b.c != null && base.b.c.d != null ? base.b.c.d.e : undefined;
|
||||
});
|
||||
return _f.apply(this, arguments);
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms idx calls in async methods', () => {
|
||||
expect({
|
||||
plugins: [transformAsyncToGenerator, babelPluginIdx],
|
||||
code: `
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
class Foo {
|
||||
async bar() {
|
||||
idx(base, _ => _.b);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}).toTransformInto(`
|
||||
${asyncToGeneratorHelperCode}
|
||||
|
||||
class Foo {
|
||||
bar() {
|
||||
var _this = this;
|
||||
|
||||
return _asyncToGenerator(function* () {
|
||||
base != null ? base.b : undefined;
|
||||
return _this;
|
||||
})();
|
||||
}
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms idx calls when an idx import binding is in scope', () => {
|
||||
expect(`
|
||||
import idx from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b);
|
||||
`).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms idx calls when an idx const binding is in scope', () => {
|
||||
expect(`
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
idx(base, _ => _.b);
|
||||
`).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms deep idx calls when an idx import binding is in scope', () => {
|
||||
expect(`
|
||||
import idx from '@kbn/elastic-idx';
|
||||
function f() {
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
`).toTransformInto(`
|
||||
function f() {
|
||||
base != null ? base.b : undefined;
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms deep idx calls when an idx const binding is in scope', () => {
|
||||
expect(`
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
function f() {
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
`).toTransformInto(`
|
||||
function f() {
|
||||
base != null ? base.b : undefined;
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('transforms idx calls when an idx is called as a member function on the binding in scope', () => {
|
||||
expect(`
|
||||
const elastic_idx = require("@kbn/elastic-idx");
|
||||
const result = elastic_idx.idx(base, _ => _.a.b.c.d);
|
||||
`).toTransformInto(`
|
||||
const result = base != null &&
|
||||
base.a != null &&
|
||||
base.a.b != null &&
|
||||
base.a.b.c != null ?
|
||||
base.a.b.c.d :
|
||||
undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws on base call expressions', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _().b.c);
|
||||
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
|
||||
});
|
||||
|
||||
it('transforms when the idx parent is a scope creating expression', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
(() => idx(base, _ => _.b));
|
||||
`).toTransformInto(`
|
||||
() => base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws if redefined before use', () => {
|
||||
expect(`
|
||||
let idx = require('@kbn/elastic-idx');
|
||||
idx = null;
|
||||
idx(base, _ => _.b);
|
||||
`).toThrowTransformError('`idx` cannot be redefined.');
|
||||
});
|
||||
|
||||
it('throws if redefined after use', () => {
|
||||
expect(`
|
||||
let idx = require('@kbn/elastic-idx');
|
||||
idx(base, _ => _.b);
|
||||
idx = null;
|
||||
`).toThrowTransformError('`idx` cannot be redefined.');
|
||||
});
|
||||
|
||||
it('throws if there is a duplicate declaration', () => {
|
||||
expect(() =>
|
||||
transform(`
|
||||
let idx = require('@kbn/elastic-idx');
|
||||
idx(base, _ => _.b);
|
||||
function idx() {}
|
||||
`)
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('handles sibling scopes with unique idx', () => {
|
||||
expect(`
|
||||
function aaa() {
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
function bbb() {
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
`).toTransformInto(`
|
||||
function aaa() {
|
||||
base != null ? base.b : undefined;
|
||||
}
|
||||
function bbb() {
|
||||
base != null ? base.b : undefined;
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('handles sibling scopes with and without idx', () => {
|
||||
expect(`
|
||||
function aaa() {
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
function bbb() {
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
`).toTransformInto(`
|
||||
function aaa() {
|
||||
base != null ? base.b : undefined;
|
||||
}
|
||||
function bbb() {
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('handles nested scopes with shadowing', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b);
|
||||
function aaa() {
|
||||
idx(base, _ => _.b);
|
||||
function bbb(idx) {
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
}
|
||||
`).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
function aaa() {
|
||||
base != null ? base.b : undefined;
|
||||
function bbb(idx) {
|
||||
idx(base, _ => _.b);
|
||||
}
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('handles named idx import', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b);
|
||||
`).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws on default plus named import', () => {
|
||||
expect(`
|
||||
import idx, {foo} from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b);
|
||||
`).toThrowTransformError('The idx import must be a single specifier.');
|
||||
});
|
||||
|
||||
it('throws on default plus namespace import', () => {
|
||||
expect(`
|
||||
import idx, * as foo from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b);
|
||||
`).toThrowTransformError('The idx import must be a single specifier.');
|
||||
});
|
||||
|
||||
it('throws on named default plus other import', () => {
|
||||
expect(`
|
||||
import {default as idx, foo} from '@kbn/elastic-idx';
|
||||
idx(base, _ => _.b);
|
||||
`).toThrowTransformError('The idx import must be a single specifier.');
|
||||
});
|
||||
|
||||
it('unused idx import should be left alone', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
`).toTransformInto(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
`);
|
||||
});
|
||||
|
||||
it('allows configuration of the import name', () => {
|
||||
expect({
|
||||
code: `
|
||||
import { idx } from 'i_d_x';
|
||||
idx(base, _ => _.b);
|
||||
`,
|
||||
options: { importName: 'i_d_x' },
|
||||
}).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('follows configuration of the import name', () => {
|
||||
expect({
|
||||
code: `
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { idx as i_d_x } from 'i_d_x';
|
||||
i_d_x(base, _ => _.b);
|
||||
idx(base, _ => _.c);
|
||||
`,
|
||||
options: { importName: 'i_d_x' },
|
||||
}).toTransformInto(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
|
||||
base != null ? base.b : undefined;
|
||||
idx(base, _ => _.c);
|
||||
`);
|
||||
});
|
||||
|
||||
it('allows configuration of the require name as a string', () => {
|
||||
expect({
|
||||
code: `
|
||||
import { idx } from 'i_d_x';
|
||||
idx(base, _ => _.b);
|
||||
`,
|
||||
options: { importName: 'i_d_x' },
|
||||
}).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('allows configuration of the require name as a RegExp', () => {
|
||||
expect({
|
||||
code: `
|
||||
import { idx } from '../../common/idx';
|
||||
idx(base, _ => _.b);
|
||||
`,
|
||||
options: { importName: /.*idx$/ },
|
||||
}).toTransformInto(`
|
||||
base != null ? base.b : undefined;
|
||||
`);
|
||||
});
|
||||
|
||||
it('follows configuration of the require name', () => {
|
||||
expect({
|
||||
code: `
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
const i_d_x = require('i_d_x');
|
||||
i_d_x(base, _ => _.b);
|
||||
idx(base, _ => _.c);
|
||||
`,
|
||||
options: { importName: 'i_d_x' },
|
||||
}).toTransformInto(`
|
||||
const idx = require('@kbn/elastic-idx');
|
||||
|
||||
base != null ? base.b : undefined;
|
||||
idx(base, _ => _.c);
|
||||
`);
|
||||
});
|
||||
|
||||
describe('functional', () => {
|
||||
it('works with only properties', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
const base = {a: {b: {c: 2}}};
|
||||
idx(base, _ => _.a.b.c);
|
||||
`).toReturn(2);
|
||||
});
|
||||
|
||||
it('works with missing properties', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
const base = {a: {b: {}}};
|
||||
idx(base, _ => _.a.b.c);
|
||||
`).toReturn(undefined);
|
||||
});
|
||||
|
||||
it('works with null properties', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
const base = {a: {b: null}};
|
||||
idx(base, _ => _.a.b.c);
|
||||
`).toReturn(undefined);
|
||||
});
|
||||
|
||||
it('works with nested idx calls', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
const base = {a: {b: {c: {d: {e: {f: 2}}}}}};
|
||||
idx(
|
||||
idx(
|
||||
idx(base, _ => _.a.b),
|
||||
_ => _.c.d
|
||||
),
|
||||
_ => _.e.f
|
||||
);
|
||||
`).toReturn(2);
|
||||
});
|
||||
|
||||
it('works with nested idx calls with missing properties', () => {
|
||||
expect(`
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
const base = {a: {b: {c: null}}};
|
||||
idx(
|
||||
idx(
|
||||
idx(base, _ => _.a.b),
|
||||
_ => _.c.d
|
||||
),
|
||||
_ => _.e.f
|
||||
);
|
||||
`).toReturn(undefined);
|
||||
});
|
||||
});
|
||||
});
|
28
packages/kbn-elastic-idx/package.json
Normal file
28
packages/kbn-elastic-idx/package.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "@kbn/elastic-idx",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "Library for optional chaining & the Babel plugin to transpile idx calls to plain, optimized JS",
|
||||
"main": "target/index.js",
|
||||
"types": "target/index.d.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/elastic/kibana/tree/master/packages/kbn-elastic-idx"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"kbn:bootstrap": "yarn build",
|
||||
"kbn:watch": "yarn build --watch",
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.4.0",
|
||||
"jest": "^24.1.0",
|
||||
"typescript": "^3.3.3333"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -20,9 +33,9 @@ type DeepRequiredObject<T> = { [P in keyof T]-?: DeepRequired<T[P]> };
|
|||
/**
|
||||
* Function that has deeply required return type
|
||||
*/
|
||||
type FunctionWithRequiredReturnType<
|
||||
T extends (...args: any[]) => any
|
||||
> = T extends (...args: infer A) => infer R
|
||||
type FunctionWithRequiredReturnType<T extends (...args: any[]) => any> = T extends (
|
||||
...args: infer A
|
||||
) => infer R
|
||||
? (...args: A) => DeepRequired<R>
|
||||
: never;
|
||||
|
8
packages/kbn-elastic-idx/tsconfig.json
Normal file
8
packages/kbn-elastic-idx/tsconfig.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"outDir": "./target"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
|
@ -154,6 +154,7 @@
|
|||
"@elastic/node-crypto": "0.1.2",
|
||||
"@elastic/numeral": "2.3.3",
|
||||
"@kbn/babel-preset": "1.0.0",
|
||||
"@kbn/elastic-idx": "1.0.0",
|
||||
"@kbn/es-query": "1.0.0",
|
||||
"@kbn/i18n": "1.0.0",
|
||||
"@kbn/interpreter": "1.0.0",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
|
||||
|
||||
export interface ErrorTab {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Fragment } from 'react';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
ERROR_EXC_HANDLED,
|
||||
HTTP_REQUEST_METHOD,
|
||||
|
@ -14,7 +15,6 @@ import {
|
|||
USER_ID
|
||||
} from '../../../../../common/elasticsearch_fieldnames';
|
||||
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
|
||||
import { APMLink } from '../../../shared/Links/APMLink';
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Location } from 'history';
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { first } from 'lodash';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { ErrorGroupAPIResponse } from '../../../../../server/lib/errors/get_error_group';
|
||||
import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
|
||||
import { IUrlParams } from '../../../../store/urlParams';
|
||||
|
|
|
@ -18,8 +18,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { Location } from 'history';
|
||||
import React, { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { useFetcher } from '../../../hooks/useFetcher';
|
||||
import {
|
||||
loadErrorDistribution,
|
||||
|
|
|
@ -9,7 +9,7 @@ import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
|
||||
import { fontSize } from '../../../../style/variables';
|
||||
import { APMLink } from '../../../shared/Links/APMLink';
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
TRANSACTION_DURATION,
|
||||
TRANSACTION_RESULT,
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
USER_ID
|
||||
} from '../../../../../common/elasticsearch_fieldnames';
|
||||
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
|
||||
import { asPercent, asTime } from '../../../../utils/formatters';
|
||||
import {
|
||||
|
|
|
@ -9,6 +9,7 @@ import styled from 'styled-components';
|
|||
|
||||
import { EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
borderRadius,
|
||||
fontFamilyCode,
|
||||
|
@ -17,7 +18,6 @@ import {
|
|||
unit,
|
||||
units
|
||||
} from '../../../../../../../style/variables';
|
||||
import { idx } from '../../../../../../../../common/idx';
|
||||
import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
|
||||
|
||||
const ContextUrl = styled.div`
|
||||
|
|
|
@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { get, keys } from 'lodash';
|
||||
import React, { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { idx } from '../../../../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
|
||||
import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
|
||||
import { DiscoverSpanLink } from '../../../../../../shared/Links/DiscoverLinks/DiscoverSpanLink';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { EuiCallOut, EuiHorizontalRule } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { idx } from '../../../../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
|
||||
import { ElasticDocsLink } from '../../../../../../shared/Links/ElasticDocsLink';
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
uniq,
|
||||
zipObject
|
||||
} from 'lodash';
|
||||
import { idx } from '../../../../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace';
|
||||
import { StringMap } from '../../../../../../../../typings/common';
|
||||
import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { EuiBasicTable } from '@elastic/eui';
|
||||
import { sortByOrder } from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
|
||||
// TODO: this should really be imported from EUI
|
||||
export interface ITableColumn<T> {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { registerLanguage } from 'react-syntax-highlighter/dist/light';
|
|||
// @ts-ignore
|
||||
import { xcode } from 'react-syntax-highlighter/dist/styles';
|
||||
import styled from 'styled-components';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { IStackframeWithLineContext } from '../../../../typings/es_schemas/raw/fields/Stackframe';
|
||||
import { borderRadius, px, unit, units } from '../../../style/variables';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import React, { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
|
||||
import { fontFamilyCode, fontSize, px, units } from '../../../style/variables';
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
|
||||
import { DiscoverTransactionLink } from '../Links/DiscoverLinks/DiscoverTransactionLink';
|
||||
import { InfraLink } from '../Links/InfraLink';
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { ESFilter } from 'elasticsearch';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
ERROR_GROUP_ID,
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_SAMPLED
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { idx } from '../../../common/idx';
|
||||
import { PromiseReturnType } from '../../../typings/common';
|
||||
import { APMError } from '../../../typings/es_schemas/ui/APMError';
|
||||
import { rangeFilter } from '../helpers/range_filter';
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { SearchParams } from 'elasticsearch';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
ERROR_CULPRIT,
|
||||
ERROR_EXC_HANDLED,
|
||||
|
@ -14,7 +15,6 @@ import {
|
|||
PROCESSOR_EVENT,
|
||||
SERVICE_NAME
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { idx } from '../../../common/idx';
|
||||
import { PromiseReturnType } from '../../../typings/common';
|
||||
import { APMError } from '../../../typings/es_schemas/ui/APMError';
|
||||
import { rangeFilter } from '../helpers/range_filter';
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { BucketAgg, ESFilter } from 'elasticsearch';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_AGENT_NAME,
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_TYPE
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { idx } from '../../../common/idx';
|
||||
import { PromiseReturnType } from '../../../typings/common';
|
||||
import { rangeFilter } from '../helpers/range_filter';
|
||||
import { Setup } from '../helpers/setup_request';
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { BucketAgg, ESFilter } from 'elasticsearch';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_AGENT_NAME,
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_DURATION
|
||||
} from '../../../../common/elasticsearch_fieldnames';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { PromiseReturnType } from '../../../../typings/common';
|
||||
import { rangeFilter } from '../../helpers/range_filter';
|
||||
import { Setup } from '../../helpers/setup_request';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { idx } from '../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { ESResponse } from './fetcher';
|
||||
|
||||
function calculateRelativeImpacts(transactionGroups: ITransactionGroup[]) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { getMlIndex } from '../../../../../common/ml_job_constants';
|
||||
import { Setup } from '../../../helpers/setup_request';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { ESBucket, ESResponse } from './fetcher';
|
||||
import { mlAnomalyResponse } from './mock-responses/mlAnomalyResponse';
|
||||
import { anomalySeriesTransform, replaceFirstAndLastBucket } from './transform';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { first, last } from 'lodash';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { Coordinate, RectCoordinate } from '../../../../../typings/timeseries';
|
||||
import { ESBucket, ESResponse } from './fetcher';
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
import { isNumber, round, sortBy } from 'lodash';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { Coordinate } from '../../../../../typings/timeseries';
|
||||
import { ESResponse } from './fetcher';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { idx } from '../../../../../common/idx';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { ESResponse } from './fetcher';
|
||||
|
||||
function getDefaultSample(buckets: IBucket[]) {
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
*/
|
||||
|
||||
import { ESFilter } from 'elasticsearch';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import {
|
||||
PROCESSOR_EVENT,
|
||||
TRACE_ID,
|
||||
TRANSACTION_ID
|
||||
} from '../../../../common/elasticsearch_fieldnames';
|
||||
import { idx } from '../../../../common/idx';
|
||||
import { PromiseReturnType } from '../../../../typings/common';
|
||||
import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
|
||||
import { rangeFilter } from '../../helpers/range_filter';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue