mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
Merge remote-tracking branch 'origin/master' into feature/merge-code
This commit is contained in:
commit
c68c2d74c2
439 changed files with 7744 additions and 3751 deletions
86
.sass-lint.yml
Normal file
86
.sass-lint.yml
Normal file
|
@ -0,0 +1,86 @@
|
|||
files:
|
||||
include:
|
||||
- '{src,x-pack}/**/*.s+(a|c)ss'
|
||||
ignore:
|
||||
# _only include_ rollup and security files
|
||||
- '**/x-pack/plugins/!(rollup|security)/**'
|
||||
# ignore all of src
|
||||
- '**/src/**/*'
|
||||
# ignore all node_modules
|
||||
- '**/node_modules/**'
|
||||
rules:
|
||||
quotes:
|
||||
- 2
|
||||
-
|
||||
style: 'single'
|
||||
# } else { style on one line, like our JS
|
||||
brace-style:
|
||||
- 2
|
||||
-
|
||||
style: '1tbs'
|
||||
variable-name-format:
|
||||
- 2
|
||||
-
|
||||
convention: 'camelcase'
|
||||
# Needs regex, right now we ignore
|
||||
class-name-format: 0
|
||||
# Order how you please
|
||||
property-sort-order: 0
|
||||
hex-notation:
|
||||
- 2
|
||||
-
|
||||
style: 'uppercase'
|
||||
mixin-name-format:
|
||||
- 2
|
||||
-
|
||||
allow-leading-underscore: false
|
||||
convention: 'camelcase'
|
||||
# Use none instead of 0 for no border
|
||||
border-zero:
|
||||
- 2
|
||||
- convention: 'none'
|
||||
force-element-nesting: 0
|
||||
# something { not something{
|
||||
space-before-brace:
|
||||
- 2
|
||||
force-pseudo-nesting: 0
|
||||
# 2 spaces for indentation
|
||||
indentation: 2
|
||||
function-name-format:
|
||||
- 2
|
||||
-
|
||||
allow-leading-underscore: false
|
||||
convention: 'camelcase'
|
||||
# This removes the need for ::hover
|
||||
pseudo-element: 0
|
||||
# ($var / 2) rather than ($var/2)
|
||||
space-around-operator: 2
|
||||
# We minify css, so this doesn't apply
|
||||
no-css-comments: 0
|
||||
# We use _ (underscores) for import path that don't directly compile
|
||||
clean-import-paths: 0
|
||||
# Allows input[type=search]
|
||||
force-attribute-nesting: 0
|
||||
no-qualifying-elements:
|
||||
- 2
|
||||
-
|
||||
# Allows input[type=search]
|
||||
allow-element-with-attribute: 1
|
||||
# Files can end without a newline
|
||||
final-newline: 0
|
||||
# We use some rare duplicate property values for browser variance
|
||||
no-duplicate-properties:
|
||||
- 2
|
||||
-
|
||||
exclude:
|
||||
- 'font-size'
|
||||
- 'word-break'
|
||||
# Put a line-break between sections of CSS, but allow quicky one-liners for legibility
|
||||
empty-line-between-blocks:
|
||||
- 2
|
||||
-
|
||||
allow-single-line-rulesets: 1
|
||||
# Warns are nice for deprecations and development
|
||||
no-warn: 0
|
||||
# Transition all is useful in certain situations and there's no recent info to suggest slowdown
|
||||
no-transition-all: 0
|
|
@ -1,5 +1,5 @@
|
|||
Kibana source code with Kibana X-Pack source code
|
||||
Copyright 2012-2018 Elasticsearch B.V.
|
||||
Copyright 2012-2019 Elasticsearch B.V.
|
||||
|
||||
---
|
||||
This product has relied on ASTExplorer that is licensed under MIT.
|
||||
|
|
|
@ -52,19 +52,11 @@ triangle that appears next to the URL line of the request. Notice that as you mo
|
|||
.The Action Menu
|
||||
image::dev-tools/console/images/introduction_action_menu.png["The Action Menu",width=400,align="center"]
|
||||
|
||||
When the response come back, you should see it in the left hand panel:
|
||||
When the response come back, you should see it in the right hand panel:
|
||||
|
||||
.The Output Pane
|
||||
image::dev-tools/console/images/introduction_output.png[Screenshot]
|
||||
|
||||
[float]
|
||||
[[console-ui]]
|
||||
=== The Console UI
|
||||
|
||||
In this section you will find a more detailed description of UI of Console. The basic aspects of the UI are explained
|
||||
in the <<console-kibana>> section.
|
||||
|
||||
|
||||
include::multi-requests.asciidoc[]
|
||||
|
||||
include::auto-formatting.asciidoc[]
|
||||
|
|
12
package.json
12
package.json
|
@ -42,6 +42,8 @@
|
|||
"test:dev": "grunt test:dev",
|
||||
"test:quick": "grunt test:quick",
|
||||
"test:browser": "grunt test:browser",
|
||||
"test:jest": "node scripts/jest",
|
||||
"test:mocha": "grunt test:mocha",
|
||||
"test:ui": "echo 'use `node scripts/functional_tests`' && false",
|
||||
"test:ui:server": "echo 'use `node scripts/functional_tests_server`' && false",
|
||||
"test:ui:runner": "echo 'use `node scripts/functional_test_runner`' && false",
|
||||
|
@ -93,7 +95,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@elastic/datemath": "5.0.2",
|
||||
"@elastic/eui": "5.8.1",
|
||||
"@elastic/eui": "6.0.1",
|
||||
"@elastic/filesaver": "1.1.2",
|
||||
"@elastic/good": "8.1.1-kibana2",
|
||||
"@elastic/numeral": "2.3.2",
|
||||
|
@ -107,6 +109,7 @@
|
|||
"@kbn/pm": "1.0.0",
|
||||
"@kbn/test-subj-selector": "0.2.1",
|
||||
"@kbn/ui-framework": "1.0.0",
|
||||
"JSONStream": "1.1.1",
|
||||
"abortcontroller-polyfill": "^1.1.9",
|
||||
"angular": "1.6.9",
|
||||
"angular-aria": "1.6.6",
|
||||
|
@ -238,7 +241,7 @@
|
|||
"vision": "^5.3.3",
|
||||
"webpack": "4.23.1",
|
||||
"webpack-merge": "4.1.4",
|
||||
"whatwg-fetch": "^2.0.3",
|
||||
"whatwg-fetch": "^3.0.0",
|
||||
"wreck": "^14.0.2",
|
||||
"x-pack": "7.0.0",
|
||||
"yauzl": "2.7.0"
|
||||
|
@ -270,7 +273,7 @@
|
|||
"@types/enzyme": "^3.1.12",
|
||||
"@types/eslint": "^4.16.2",
|
||||
"@types/execa": "^0.9.0",
|
||||
"@types/fetch-mock": "^5.12.2",
|
||||
"@types/fetch-mock": "7.2.1",
|
||||
"@types/getopts": "^2.0.0",
|
||||
"@types/glob": "^5.0.35",
|
||||
"@types/globby": "^8.0.0",
|
||||
|
@ -337,7 +340,7 @@
|
|||
"eslint-plugin-react": "^7.11.1",
|
||||
"expect.js": "0.3.1",
|
||||
"faker": "1.1.0",
|
||||
"fetch-mock": "^5.13.1",
|
||||
"fetch-mock": "7.3.0",
|
||||
"geckodriver": "1.12.2",
|
||||
"getopts": "2.0.0",
|
||||
"grunt": "1.0.1",
|
||||
|
@ -382,6 +385,7 @@
|
|||
"prettier": "^1.14.3",
|
||||
"proxyquire": "1.7.11",
|
||||
"regenerate": "^1.4.0",
|
||||
"sass-lint": "^1.12.1",
|
||||
"simple-git": "1.37.0",
|
||||
"sinon": "^7.2.2",
|
||||
"strip-ansi": "^3.0.1",
|
||||
|
|
|
@ -172,6 +172,7 @@ module.exports = {
|
|||
'import/no-named-as-default': 'error',
|
||||
'import/no-named-as-default-member': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'import/no-dynamic-require': 'error',
|
||||
|
||||
'prefer-object-spread/prefer-object-spread': 'error',
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ exports.getPlugins = function(config, kibanaPath, projectRoot) {
|
|||
return pluginsFromMap.concat(
|
||||
glob.sync(globPatterns).map(pkgJsonPath => {
|
||||
const path = dirname(pkgJsonPath);
|
||||
const pkg = require(pkgJsonPath);
|
||||
const pkg = require(pkgJsonPath); // eslint-disable-line import/no-dynamic-require
|
||||
return {
|
||||
name: pkg.name,
|
||||
directory: path,
|
||||
|
|
|
@ -393,29 +393,29 @@ For example, there is a component that is wrapped by `injectI18n`, like in the `
|
|||
|
||||
```js
|
||||
// ...
|
||||
class AddFilterUi extends Component {
|
||||
export const AddFilter = injectI18n(
|
||||
class AddFilterUi extends Component {
|
||||
// ...
|
||||
render() {
|
||||
const { filter } = this.state;
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={10}>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
value={filter}
|
||||
onChange={e => this.setState({ filter: e.target.value.trim() })}
|
||||
placeholder={this.props.intl.formatMessage({
|
||||
id: 'kbn.management.indexPattern.edit.source.placeholder',
|
||||
defaultMessage: 'source filter, accepts wildcards (e.g., `user*` to filter fields starting with \'user\')'
|
||||
})}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
render() {
|
||||
const { filter } = this.state;
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={10}>
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
value={filter}
|
||||
onChange={e => this.setState({ filter: e.target.value.trim() })}
|
||||
placeholder={this.props.intl.formatMessage({
|
||||
id: 'kbn.management.indexPattern.edit.source.placeholder',
|
||||
defaultMessage: 'source filter, accepts wildcards (e.g., `user*` to filter fields starting with \'user\')'
|
||||
})}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const AddFilter = injectI18n(AddFilterUi);
|
||||
);
|
||||
```
|
||||
|
||||
To test the `AddFilter` component it is needed to render its `WrappedComponent` property using `shallowWithIntl` function to pass `intl` object into the `props`.
|
||||
|
|
|
@ -298,7 +298,7 @@ React component as a pure function:
|
|||
import React from 'react';
|
||||
import { injectI18n, intlShape } from '@kbn/i18n/react';
|
||||
|
||||
const MyComponentContent = ({ intl }) => (
|
||||
export const MyComponent = injectI18n({ intl }) => (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={intl.formatMessage(
|
||||
|
@ -311,13 +311,11 @@ const MyComponentContent = ({ intl }) => (
|
|||
{ name, unreadCount }
|
||||
)}
|
||||
/>
|
||||
);
|
||||
));
|
||||
|
||||
MyComponentContent.propTypes = {
|
||||
MyComponent.WrappedComponent.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const MyComponent = injectI18n(MyComponentContent);
|
||||
```
|
||||
|
||||
React component as a class:
|
||||
|
@ -326,27 +324,27 @@ React component as a class:
|
|||
import React from 'react';
|
||||
import { injectI18n, intlShape } from '@kbn/i18n/react';
|
||||
|
||||
class MyComponentContent extends React.Component {
|
||||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
export const MyComponent = injectI18n(
|
||||
class MyComponent extends React.Component {
|
||||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'kbn.management.objects.searchPlaceholder',
|
||||
defaultMessage: 'Search',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'kbn.management.objects.searchPlaceholder',
|
||||
defaultMessage: 'Search',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const MyComponent = injectI18n(MyComponentContent);
|
||||
);
|
||||
```
|
||||
|
||||
## AngularJS
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"kbn:watch": "node scripts/build --dev --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kbn/i18n": "1.0.0",
|
||||
"lodash": "npm:@elastic/lodash@3.10.1-kibana1",
|
||||
"lodash.clone": "^4.5.0",
|
||||
"scriptjs": "^2.5.8",
|
||||
|
@ -24,6 +25,7 @@
|
|||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "6.20.0",
|
||||
"css-loader": "1.0.0",
|
||||
"copy-webpack-plugin": "^4.6.0",
|
||||
"del": "^3.0.0",
|
||||
"getopts": "^2.2.3",
|
||||
"pegjs": "0.9.0",
|
||||
|
@ -34,4 +36,4 @@
|
|||
"webpack": "4.23.1",
|
||||
"webpack-cli": "^3.1.2"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 '../common/register';
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import $script from 'scriptjs';
|
||||
|
||||
let resolvePromise = null;
|
||||
|
@ -43,6 +44,7 @@ const loadBrowserRegistries = (registries, basePath) => {
|
|||
const type = remainingTypes.pop();
|
||||
window.canvas = window.canvas || {};
|
||||
window.canvas.register = d => registries[type].register(d);
|
||||
window.canvas.i18n = i18n;
|
||||
|
||||
// Load plugins one at a time because each needs a different loader function
|
||||
// $script will only load each of these once, we so can call this as many times as we need?
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { typesRegistry } from '../common/lib/types_registry';
|
||||
import { functionsRegistry as serverFunctions } from '../common/lib/functions_registry';
|
||||
import { getPluginPaths } from './get_plugin_paths';
|
||||
|
@ -48,23 +49,18 @@ export const populateServerRegistries = types => {
|
|||
const remainingTypes = types;
|
||||
const populatedTypes = {};
|
||||
|
||||
const globalKeys = Object.keys(global);
|
||||
|
||||
const loadType = () => {
|
||||
const type = remainingTypes.pop();
|
||||
getPluginPaths(type).then(paths => {
|
||||
global.canvas = global.canvas || {};
|
||||
global.canvas.register = d => registries[type].register(d);
|
||||
global.canvas.i18n = i18n;
|
||||
|
||||
paths.forEach(path => {
|
||||
require(path);
|
||||
require(path); // eslint-disable-line import/no-dynamic-require
|
||||
});
|
||||
|
||||
Object.keys(global).forEach(key => {
|
||||
if (!globalKeys.includes(key)) {
|
||||
delete global[key];
|
||||
}
|
||||
});
|
||||
delete global.canvas;
|
||||
|
||||
populatedTypes[type] = registries[type];
|
||||
if (remainingTypes.length) loadType();
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/* eslint-disable */
|
||||
import util from 'util';
|
||||
console.log(util.format('hello world'));
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const { extname } = require('path');
|
||||
|
||||
const { transform } = require('babel-core');
|
||||
|
||||
exports.createServerCodeTransformer = (sourceMaps) => {
|
||||
return (content, path) => {
|
||||
switch (extname(path)) {
|
||||
case '.js':
|
||||
const { code = '' } = transform(content.toString('utf8'), {
|
||||
filename: path,
|
||||
ast: false,
|
||||
code: true,
|
||||
sourceMaps: sourceMaps ? 'inline' : false,
|
||||
babelrc: false,
|
||||
presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
|
||||
});
|
||||
|
||||
return code;
|
||||
|
||||
default:
|
||||
return content.toString('utf8');
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { readFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { createServerCodeTransformer } from './server_code_transformer';
|
||||
|
||||
const JS_FIXTURE_PATH = resolve(__dirname, '__fixtures__/sample.js');
|
||||
const JS_FIXTURE = readFileSync(JS_FIXTURE_PATH);
|
||||
|
||||
describe('js support', () => {
|
||||
it('transpiles js file', () => {
|
||||
const transformer = createServerCodeTransformer();
|
||||
expect(transformer(JS_FIXTURE, JS_FIXTURE_PATH)).toMatchInlineSnapshot(`
|
||||
"'use strict';
|
||||
|
||||
var _util = require('util');
|
||||
|
||||
var _util2 = _interopRequireDefault(_util);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
console.log(_util2.default.format('hello world')); /* eslint-disable */"
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws errors for js syntax errors', () => {
|
||||
const transformer = createServerCodeTransformer();
|
||||
expect(() => transformer(Buffer.from(`export default 'foo`), JS_FIXTURE_PATH)).toThrowError(
|
||||
/Unterminated string constant/
|
||||
);
|
||||
});
|
||||
});
|
|
@ -18,6 +18,10 @@
|
|||
*/
|
||||
|
||||
const { resolve } = require('path');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const { createServerCodeTransformer } = require('./server_code_transformer');
|
||||
|
||||
const {
|
||||
PLUGIN_SOURCE_DIR,
|
||||
PLUGIN_BUILD_DIR,
|
||||
|
@ -31,7 +35,7 @@ module.exports = function ({ sourceMaps }, { watch }) {
|
|||
mode: 'none',
|
||||
entry: {
|
||||
'types/all': resolve(PLUGIN_SOURCE_DIR, 'types/register.js'),
|
||||
'functions/common/all': resolve(PLUGIN_SOURCE_DIR, 'functions/common/register.js'),
|
||||
'functions/browser/all': resolve(PLUGIN_SOURCE_DIR, 'functions/browser/register.js'),
|
||||
},
|
||||
|
||||
// there were problems with the node and web targets since this code is actually
|
||||
|
@ -95,6 +99,15 @@ module.exports = function ({ sourceMaps }, { watch }) {
|
|||
stats: 'errors-only',
|
||||
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: resolve(PLUGIN_SOURCE_DIR, 'functions/common'),
|
||||
to: resolve(PLUGIN_BUILD_DIR, 'functions/common'),
|
||||
ignore: '**/__tests__/**',
|
||||
transform: createServerCodeTransformer(sourceMaps)
|
||||
},
|
||||
]),
|
||||
|
||||
function LoaderFailHandlerPlugin() {
|
||||
if (!watch) {
|
||||
return;
|
||||
|
|
|
@ -27,7 +27,7 @@ const chalk = require('chalk');
|
|||
|
||||
const pkg = require('../package.json');
|
||||
const kibanaPkgPath = require.resolve('../../../package.json');
|
||||
const kibanaPkg = require(kibanaPkgPath);
|
||||
const kibanaPkg = require(kibanaPkgPath); // eslint-disable-line import/no-dynamic-require
|
||||
|
||||
const KBN_DIR = dirname(kibanaPkgPath);
|
||||
|
||||
|
@ -72,6 +72,7 @@ module.exports = function({ name }) {
|
|||
filters: {
|
||||
'public/**/*': 'generateApp',
|
||||
'translations/**/*': 'generateTranslations',
|
||||
'.i18nrc.json': 'generateTranslations',
|
||||
'public/hack.js': 'generateHack',
|
||||
'server/**/*': 'generateApi',
|
||||
'public/app.scss': 'generateScss',
|
||||
|
@ -80,6 +81,7 @@ module.exports = function({ name }) {
|
|||
move: {
|
||||
gitignore: '.gitignore',
|
||||
eslintrc: '.eslintrc',
|
||||
'package_template.json': 'package.json',
|
||||
},
|
||||
data: answers =>
|
||||
Object.assign(
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"paths": {
|
||||
"<%= camelCase(name) %>": "./"
|
||||
}
|
||||
}
|
|
@ -17,6 +17,11 @@
|
|||
"test:browser": "plugin-helpers test:browser",
|
||||
"build": "plugin-helpers build"
|
||||
},
|
||||
<%_ if (generateTranslations) { _%>
|
||||
"dependencies": {
|
||||
"@kbn/i18n": "link:../../kibana/packages/kbn-i18n"
|
||||
},
|
||||
<%_ } _%>
|
||||
"devDependencies": {
|
||||
"@elastic/eslint-config-kibana": "link:../../kibana/packages/eslint-config-kibana",
|
||||
"@elastic/eslint-import-resolver-kibana": "link:../../kibana/packages/kbn-eslint-import-resolver-kibana",
|
|
@ -2,6 +2,9 @@ import React from 'react';
|
|||
import { uiModules } from 'ui/modules';
|
||||
import chrome from 'ui/chrome';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
<%_ if (generateTranslations) { _%>
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
<%_ } _%>
|
||||
|
||||
import 'ui/autoload/styles';
|
||||
import './less/main.less';
|
||||
|
@ -24,7 +27,16 @@ function RootController($scope, $element, $http) {
|
|||
const domNode = $element[0];
|
||||
|
||||
// render react to DOM
|
||||
<%_ if (generateTranslations) { _%>
|
||||
render(
|
||||
<I18nProvider>
|
||||
<Main title="<%= name %>" httpClient={$http} />
|
||||
</I18nProvider>,
|
||||
domNode
|
||||
);
|
||||
<%_ } else { _%>
|
||||
render(<Main title="<%= name %>" httpClient={$http} />, domNode);
|
||||
<%_ } _%>
|
||||
|
||||
// unmount react on controller destroy
|
||||
$scope.$on('$destroy', () => {
|
||||
|
|
|
@ -9,6 +9,9 @@ import {
|
|||
EuiPageContentBody,
|
||||
EuiText
|
||||
} from '@elastic/eui';
|
||||
<%_ if (generateTranslations) { _%>
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
<%_ } _%>
|
||||
|
||||
export class Main extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -33,19 +36,57 @@ export class Main extends React.Component {
|
|||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiTitle size="l">
|
||||
<h1>{title} Hello World!</h1>
|
||||
<h1>
|
||||
<%_ if (generateTranslations) { _%>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.helloWorldText"
|
||||
defaultMessage="{title} Hello World!"
|
||||
values={{ title }}
|
||||
/>
|
||||
<%_ } else { _%>
|
||||
{title} Hello World!
|
||||
<%_ } _%>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader>
|
||||
<EuiTitle>
|
||||
<h2>Congratulations</h2>
|
||||
<h2>
|
||||
<%_ if (generateTranslations) { _%>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.congratulationsTitle"
|
||||
defaultMessage="Congratulations"
|
||||
/>
|
||||
<%_ } else { _%>
|
||||
Congratulations
|
||||
<%_ } _%>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
<EuiText>
|
||||
<h3>You have successfully created your first Kibana Plugin!</h3>
|
||||
<p>The server time (via API call) is {this.state.time || 'NO API CALL YET'}</p>
|
||||
<h3>
|
||||
<%_ if (generateTranslations) { _%>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.congratulationsText"
|
||||
defaultMessage="You have successfully created your first Kibana Plugin!"
|
||||
/>
|
||||
<%_ } else { _%>
|
||||
You have successfully created your first Kibana Plugin!
|
||||
<%_ } _%>
|
||||
</h3>
|
||||
<p>
|
||||
<%_ if (generateTranslations) { _%>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.serverTimeText"
|
||||
defaultMessage="The server time (via API call) is {time}"
|
||||
values={{ time: this.state.time || 'NO API CALL YET' }}
|
||||
/>
|
||||
<%_ } else { _%>
|
||||
The server time (via API call) is {this.state.time || 'NO API CALL YET'}
|
||||
<%_ } _%>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"formats": {
|
||||
"number": {
|
||||
"currency": {
|
||||
"style": "currency"
|
||||
},
|
||||
"percent": {
|
||||
"style": "percent"
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"short": {
|
||||
"month": "numeric",
|
||||
"day": "numeric",
|
||||
"year": "2-digit"
|
||||
},
|
||||
"medium": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"year": "numeric"
|
||||
},
|
||||
"full": {
|
||||
"weekday": "long",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"year": "numeric"
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"short": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
},
|
||||
"medium": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short"
|
||||
},
|
||||
"full": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short"
|
||||
}
|
||||
},
|
||||
"relative": {
|
||||
"years": {
|
||||
"units": "year"
|
||||
},
|
||||
"months": {
|
||||
"units": "month"
|
||||
},
|
||||
"days": {
|
||||
"units": "day"
|
||||
},
|
||||
"hours": {
|
||||
"units": "hour"
|
||||
},
|
||||
"minutes": {
|
||||
"units": "minute"
|
||||
},
|
||||
"seconds": {
|
||||
"units": "second"
|
||||
}
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"<%= camelCase(name) %>.congratulationsText": "您已经成功创建第一个 Kibana 插件。",
|
||||
"<%= camelCase(name) %>.congratulationsTitle": "恭喜!",
|
||||
"<%= camelCase(name) %>.helloWorldText": "{title} 您好,世界!",
|
||||
"<%= camelCase(name) %>.serverTimeText": "服务器时间(通过 API 调用)为 {time}"
|
||||
}
|
||||
}
|
|
@ -26,10 +26,10 @@ function babelRegister() {
|
|||
|
||||
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'));
|
||||
require(resolve(plugin.kibanaRoot, 'src/setup_node_env/babel_register')); // eslint-disable-line import/no-dynamic-require
|
||||
} catch (error) {
|
||||
if (error.code === 'MODULE_NOT_FOUND') {
|
||||
require(resolve(plugin.kibanaRoot, 'src/optimize/babel/register'));
|
||||
require(resolve(plugin.kibanaRoot, 'src/optimize/babel/register')); // eslint-disable-line import/no-dynamic-require
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
@ -42,11 +42,8 @@ function resolveKibanaPath(path) {
|
|||
}
|
||||
|
||||
function readFtrConfigFile(log, path, settingOverrides) {
|
||||
return require(resolveKibanaPath('src/functional_test_runner')).readConfigFile(
|
||||
log,
|
||||
path,
|
||||
settingOverrides
|
||||
);
|
||||
return require(resolveKibanaPath('src/functional_test_runner')) // eslint-disable-line import/no-dynamic-require
|
||||
.readConfigFile(log, path, settingOverrides);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -51,7 +51,7 @@ function removeSymlinkDependencies(root) {
|
|||
|
||||
// parse a ts config file
|
||||
function parseTsconfig(pluginSourcePath, configPath) {
|
||||
const ts = require(path.join(pluginSourcePath, 'node_modules', 'typescript'));
|
||||
const ts = require(path.join(pluginSourcePath, 'node_modules', 'typescript')); // eslint-disable-line import/no-dynamic-require
|
||||
|
||||
const { error, config } = ts.parseConfigFileTextToJson(
|
||||
configPath,
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('creating the build', () => {
|
|||
|
||||
await createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles);
|
||||
|
||||
const pkg = require(resolve(PLUGIN_BUILD_TARGET, 'package.json'));
|
||||
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');
|
||||
});
|
||||
|
@ -52,7 +52,7 @@ describe('creating the build', () => {
|
|||
|
||||
await createBuild(PLUGIN, buildTarget, buildVersion, kibanaVersion, buildFiles);
|
||||
|
||||
const pkg = require(resolve(PLUGIN_BUILD_TARGET, 'package.json'));
|
||||
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();
|
||||
|
|
21
scripts/i18n_integrate.js
Normal file
21
scripts/i18n_integrate.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../src/setup_node_env');
|
||||
require('../src/dev/run_i18n_integrate');
|
21
scripts/sasslint.js
Normal file
21
scripts/sasslint.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../src/setup_node_env');
|
||||
require('../src/dev/run_sasslint');
|
|
@ -1,126 +1,114 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#batchSet Buffers are always clear of previously buffered changes: two requests, second only sends bar, not foo 1`] = `
|
||||
Object {
|
||||
"matched": Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
],
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"bar\\":\\"box\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"unmatched": Array [],
|
||||
}
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"bar\\":\\"box\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`#batchSet Overwrites previously buffered values with new values for the same key: two requests, foo=d in final 1`] = `
|
||||
Object {
|
||||
"matched": Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"a\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"a\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
],
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"d\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"unmatched": Array [],
|
||||
}
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"d\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`#batchSet buffers changes while first request is in progress, sends buffered changes after first request completes: final, includes both requests 1`] = `
|
||||
Object {
|
||||
"matched": Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
],
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"box\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"unmatched": Array [],
|
||||
}
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"box\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`#batchSet buffers changes while first request is in progress, sends buffered changes after first request completes: initial, only one request 1`] = `
|
||||
Object {
|
||||
"matched": Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
],
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"unmatched": Array [],
|
||||
}
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`#batchSet rejects all promises for batched requests that fail: promise rejections 1`] = `
|
||||
|
@ -147,22 +135,19 @@ exports[`#batchSet rejects on 404 response 1`] = `"Request failed with status co
|
|||
exports[`#batchSet rejects on 500 1`] = `"Request failed with status code: 500"`;
|
||||
|
||||
exports[`#batchSet sends a single change immediately: synchronous fetch 1`] = `
|
||||
Object {
|
||||
"matched": Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
"method": "POST",
|
||||
Array [
|
||||
Array [
|
||||
"/foo/bar/api/kibana/settings",
|
||||
Object {
|
||||
"body": "{\\"changes\\":{\\"foo\\":\\"bar\\"}}",
|
||||
"credentials": "same-origin",
|
||||
"headers": Object {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"kbn-version": "v9.9.9",
|
||||
},
|
||||
],
|
||||
"method": "POST",
|
||||
},
|
||||
],
|
||||
"unmatched": Array [],
|
||||
}
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import fetchMock from 'fetch-mock';
|
||||
// @ts-ignore
|
||||
import fetchMock from 'fetch-mock/es5/client';
|
||||
import * as Rx from 'rxjs';
|
||||
import { takeUntil, toArray } from 'rxjs/operators';
|
||||
|
||||
|
@ -142,10 +143,16 @@ describe('#batchSet', () => {
|
|||
fetchMock.once('*', {
|
||||
body: { settings: {} },
|
||||
});
|
||||
fetchMock.once('*', {
|
||||
status: 400,
|
||||
body: 'invalid',
|
||||
});
|
||||
fetchMock.once(
|
||||
'*',
|
||||
{
|
||||
status: 400,
|
||||
body: 'invalid',
|
||||
},
|
||||
{
|
||||
overwriteRoutes: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { uiSettingsApi } = setup();
|
||||
// trigger the initial sync request, which enabled buffering
|
||||
|
@ -161,7 +168,7 @@ describe('#batchSet', () => {
|
|||
).resolves.toMatchSnapshot('promise rejections');
|
||||
|
||||
// ensure only two requests were sent
|
||||
expect(fetchMock.calls().matched).toHaveLength(2);
|
||||
expect(fetchMock.calls()).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -191,10 +198,16 @@ describe('#getLoadingCount$()', () => {
|
|||
fetchMock.once('*', {
|
||||
body: { settings: {} },
|
||||
});
|
||||
fetchMock.once('*', {
|
||||
status: 400,
|
||||
body: 'invalid',
|
||||
});
|
||||
fetchMock.once(
|
||||
'*',
|
||||
{
|
||||
status: 400,
|
||||
body: 'invalid',
|
||||
},
|
||||
{
|
||||
overwriteRoutes: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { uiSettingsApi } = setup();
|
||||
const done$ = new Rx.Subject();
|
||||
|
|
|
@ -25,7 +25,7 @@ import { createPlatform } from './platform';
|
|||
|
||||
export async function getConfig({ isRelease, targetAllPlatforms, versionQualifier }) {
|
||||
const pkgPath = resolve(__dirname, '../../../../package.json');
|
||||
const pkg = require(pkgPath);
|
||||
const pkg = require(pkgPath); // eslint-disable-line import/no-dynamic-require
|
||||
const repoRoot = dirname(pkgPath);
|
||||
const nodeVersion = pkg.engines.node;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ export const TranspileScssTask = {
|
|||
const uiExports = collectUiExports(enabledPlugins);
|
||||
|
||||
try {
|
||||
const bundles = await buildAll(uiExports.styleSheetPaths);
|
||||
const bundles = await buildAll(uiExports.styleSheetPaths, log);
|
||||
bundles.forEach(bundle => log.info(`Compiled SCSS: ${bundle.source}`));
|
||||
} catch (error) {
|
||||
const { message, line, file } = error;
|
||||
|
|
63
src/dev/i18n/__fixtures__/integrate_locale_files/fr.json
Normal file
63
src/dev/i18n/__fixtures__/integrate_locale_files/fr.json
Normal file
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"formats": {
|
||||
"number": {
|
||||
"currency": {
|
||||
"style": "currency"
|
||||
},
|
||||
"percent": {
|
||||
"style": "percent"
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"short": {
|
||||
"month": "numeric",
|
||||
"day": "numeric",
|
||||
"year": "2-digit"
|
||||
},
|
||||
"medium": {
|
||||
"month": "short",
|
||||
"day": "numeric",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"year": "numeric"
|
||||
},
|
||||
"full": {
|
||||
"weekday": "long",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"year": "numeric"
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"short": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric"
|
||||
},
|
||||
"medium": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short"
|
||||
},
|
||||
"full": {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short"
|
||||
}
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"plugin-1.message-id-1": "Translated text 1",
|
||||
"plugin-1.message-id-2": "Translated text 2",
|
||||
"plugin-2.message-id": "Translated text"
|
||||
}
|
||||
}
|
|
@ -55,9 +55,17 @@ Array [
|
|||
`;
|
||||
|
||||
exports[`dev/i18n/extract_default_translations throws on id collision 1`] = `
|
||||
"[37m[41m I18N ERROR [49m[39m Error in src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx
|
||||
Array [
|
||||
"[37m[41m I18N ERROR [49m[39m Error in src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx
|
||||
Error: There is more than one default message for the same id \\"plugin_3.duplicate_id\\":
|
||||
\\"Message 1\\" and \\"Message 2\\""
|
||||
\\"Message 1\\" and \\"Message 2\\"",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extract_default_translations throws on wrong message namespace 1`] = `"Expected \\"wrong_plugin_namespace.message-id\\" id to have \\"plugin_2\\" namespace. See .i18nrc.json for the list of supported namespaces."`;
|
||||
exports[`dev/i18n/extract_default_translations throws on wrong message namespace 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Expected "wrong_plugin_namespace.message-id" id to have "plugin_2" namespace. See .i18nrc.json for the list of supported namespaces.],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
163
src/dev/i18n/__snapshots__/integrate_locale_files.test.js.snap
Normal file
163
src/dev/i18n/__snapshots__/integrate_locale_files.test.js.snap
Normal file
|
@ -0,0 +1,163 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`dev/i18n/integrate_locale_files integrateLocaleFiles splits locale file by plugins and writes them into the right folders 1`] = `
|
||||
Array [
|
||||
"src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_1/translations/fr.json",
|
||||
"{
|
||||
\\"formats\\": {
|
||||
\\"number\\": {
|
||||
\\"currency\\": {
|
||||
\\"style\\": \\"currency\\"
|
||||
},
|
||||
\\"percent\\": {
|
||||
\\"style\\": \\"percent\\"
|
||||
}
|
||||
},
|
||||
\\"date\\": {
|
||||
\\"short\\": {
|
||||
\\"month\\": \\"numeric\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"2-digit\\"
|
||||
},
|
||||
\\"medium\\": {
|
||||
\\"month\\": \\"short\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"numeric\\"
|
||||
},
|
||||
\\"long\\": {
|
||||
\\"month\\": \\"long\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"numeric\\"
|
||||
},
|
||||
\\"full\\": {
|
||||
\\"weekday\\": \\"long\\",
|
||||
\\"month\\": \\"long\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"numeric\\"
|
||||
}
|
||||
},
|
||||
\\"time\\": {
|
||||
\\"short\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\"
|
||||
},
|
||||
\\"medium\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\",
|
||||
\\"second\\": \\"numeric\\"
|
||||
},
|
||||
\\"long\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\",
|
||||
\\"second\\": \\"numeric\\",
|
||||
\\"timeZoneName\\": \\"short\\"
|
||||
},
|
||||
\\"full\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\",
|
||||
\\"second\\": \\"numeric\\",
|
||||
\\"timeZoneName\\": \\"short\\"
|
||||
}
|
||||
}
|
||||
},
|
||||
\\"messages\\": {
|
||||
\\"plugin-1.message-id-1\\": \\"Translated text 1\\",
|
||||
\\"plugin-1.message-id-2\\": \\"Translated text 2\\"
|
||||
}
|
||||
}",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/integrate_locale_files integrateLocaleFiles splits locale file by plugins and writes them into the right folders 2`] = `
|
||||
Array [
|
||||
"src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_2/translations/fr.json",
|
||||
"{
|
||||
\\"formats\\": {
|
||||
\\"number\\": {
|
||||
\\"currency\\": {
|
||||
\\"style\\": \\"currency\\"
|
||||
},
|
||||
\\"percent\\": {
|
||||
\\"style\\": \\"percent\\"
|
||||
}
|
||||
},
|
||||
\\"date\\": {
|
||||
\\"short\\": {
|
||||
\\"month\\": \\"numeric\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"2-digit\\"
|
||||
},
|
||||
\\"medium\\": {
|
||||
\\"month\\": \\"short\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"numeric\\"
|
||||
},
|
||||
\\"long\\": {
|
||||
\\"month\\": \\"long\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"numeric\\"
|
||||
},
|
||||
\\"full\\": {
|
||||
\\"weekday\\": \\"long\\",
|
||||
\\"month\\": \\"long\\",
|
||||
\\"day\\": \\"numeric\\",
|
||||
\\"year\\": \\"numeric\\"
|
||||
}
|
||||
},
|
||||
\\"time\\": {
|
||||
\\"short\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\"
|
||||
},
|
||||
\\"medium\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\",
|
||||
\\"second\\": \\"numeric\\"
|
||||
},
|
||||
\\"long\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\",
|
||||
\\"second\\": \\"numeric\\",
|
||||
\\"timeZoneName\\": \\"short\\"
|
||||
},
|
||||
\\"full\\": {
|
||||
\\"hour\\": \\"numeric\\",
|
||||
\\"minute\\": \\"numeric\\",
|
||||
\\"second\\": \\"numeric\\",
|
||||
\\"timeZoneName\\": \\"short\\"
|
||||
}
|
||||
}
|
||||
},
|
||||
\\"messages\\": {
|
||||
\\"plugin-2.message-id\\": \\"Translated text\\"
|
||||
}
|
||||
}",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/integrate_locale_files integrateLocaleFiles splits locale file by plugins and writes them into the right folders 3`] = `
|
||||
Array [
|
||||
"src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_1/translations",
|
||||
"src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_2/translations",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/integrate_locale_files verifyMessages throws an error for unused id and missing id 1`] = `
|
||||
"
|
||||
Missing translations:
|
||||
plugin-1.message-id-2"
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/integrate_locale_files verifyMessages throws an error for unused id and missing id 2`] = `
|
||||
"
|
||||
Unused translations:
|
||||
plugin-1.message-id-3"
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/integrate_locale_files verifyMessages throws an error for unused id and missing id 3`] = `
|
||||
"
|
||||
Unused translations:
|
||||
plugin-2.message
|
||||
Missing translations:
|
||||
plugin-2.message-id"
|
||||
`;
|
|
@ -9,6 +9,8 @@ exports[`i18n utils should create verbose parser error message 1`] = `
|
|||
"
|
||||
`;
|
||||
|
||||
exports[`i18n utils should normalizePath 1`] = `"src/dev/i18n"`;
|
||||
|
||||
exports[`i18n utils should not escape linebreaks 1`] = `
|
||||
"Text
|
||||
with
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
import normalize from 'normalize-path';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import {
|
||||
extractHtmlMessages,
|
||||
|
@ -27,26 +25,24 @@ import {
|
|||
extractPugMessages,
|
||||
extractHandlebarsMessages,
|
||||
} from './extractors';
|
||||
import { globAsync, readFileAsync } from './utils';
|
||||
import { paths, exclude } from '../../../.i18nrc.json';
|
||||
import { globAsync, readFileAsync, normalizePath } from './utils';
|
||||
|
||||
import { createFailError, isFailError } from '../run';
|
||||
|
||||
function addMessageToMap(targetMap, key, value) {
|
||||
function addMessageToMap(targetMap, key, value, reporter) {
|
||||
const existingValue = targetMap.get(key);
|
||||
|
||||
if (targetMap.has(key) && existingValue.message !== value.message) {
|
||||
throw createFailError(`There is more than one default message for the same id "${key}":
|
||||
"${existingValue.message}" and "${value.message}"`);
|
||||
reporter.report(
|
||||
createFailError(`There is more than one default message for the same id "${key}":
|
||||
"${existingValue.message}" and "${value.message}"`)
|
||||
);
|
||||
} else {
|
||||
targetMap.set(key, value);
|
||||
}
|
||||
|
||||
targetMap.set(key, value);
|
||||
}
|
||||
|
||||
function normalizePath(inputPath) {
|
||||
return normalize(path.relative('.', inputPath));
|
||||
}
|
||||
|
||||
export function filterPaths(inputPaths) {
|
||||
export function filterPaths(inputPaths, paths) {
|
||||
const availablePaths = Object.values(paths);
|
||||
const pathsForExtraction = new Set();
|
||||
|
||||
|
@ -70,26 +66,28 @@ export function filterPaths(inputPaths) {
|
|||
return [...pathsForExtraction];
|
||||
}
|
||||
|
||||
function filterEntries(entries) {
|
||||
function filterEntries(entries, exclude) {
|
||||
return entries.filter(entry =>
|
||||
exclude.every(excludedPath => !normalizePath(entry).startsWith(excludedPath))
|
||||
);
|
||||
}
|
||||
|
||||
export function validateMessageNamespace(id, filePath) {
|
||||
export function validateMessageNamespace(id, filePath, allowedPaths, reporter) {
|
||||
const normalizedPath = normalizePath(filePath);
|
||||
|
||||
const [expectedNamespace] = Object.entries(paths).find(([, pluginPath]) =>
|
||||
const [expectedNamespace] = Object.entries(allowedPaths).find(([, pluginPath]) =>
|
||||
normalizedPath.startsWith(`${pluginPath}/`)
|
||||
);
|
||||
|
||||
if (!id.startsWith(`${expectedNamespace}.`)) {
|
||||
throw createFailError(`Expected "${id}" id to have "${expectedNamespace}" namespace. \
|
||||
See .i18nrc.json for the list of supported namespaces.`);
|
||||
reporter.report(
|
||||
createFailError(`Expected "${id}" id to have "${expectedNamespace}" namespace. \
|
||||
See .i18nrc.json for the list of supported namespaces.`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function extractMessagesFromPathToMap(inputPath, targetMap) {
|
||||
export async function extractMessagesFromPathToMap(inputPath, targetMap, config, reporter) {
|
||||
const entries = await globAsync('*.{js,jsx,pug,ts,tsx,html,hbs,handlebars}', {
|
||||
cwd: inputPath,
|
||||
matchBase: true,
|
||||
|
@ -123,7 +121,7 @@ export async function extractMessagesFromPathToMap(inputPath, targetMap) {
|
|||
[hbsEntries, extractHandlebarsMessages],
|
||||
].map(async ([entries, extractFunction]) => {
|
||||
const files = await Promise.all(
|
||||
filterEntries(entries).map(async entry => {
|
||||
filterEntries(entries, config.exclude).map(async entry => {
|
||||
return {
|
||||
name: entry,
|
||||
content: await readFileAsync(entry),
|
||||
|
@ -132,21 +130,31 @@ export async function extractMessagesFromPathToMap(inputPath, targetMap) {
|
|||
);
|
||||
|
||||
for (const { name, content } of files) {
|
||||
const reporterWithContext = reporter.withContext({ name });
|
||||
|
||||
try {
|
||||
for (const [id, value] of extractFunction(content)) {
|
||||
validateMessageNamespace(id, name);
|
||||
addMessageToMap(targetMap, id, value);
|
||||
for (const [id, value] of extractFunction(content, reporterWithContext)) {
|
||||
validateMessageNamespace(id, name, config.paths, reporterWithContext);
|
||||
addMessageToMap(targetMap, id, value, reporterWithContext);
|
||||
}
|
||||
} catch (error) {
|
||||
if (isFailError(error)) {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} Error in ${normalizePath(name)}\n${error}`
|
||||
);
|
||||
if (!isFailError(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
reporterWithContext.report(error);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export async function getDefaultMessagesMap(inputPaths, config, reporter) {
|
||||
const defaultMessagesMap = new Map();
|
||||
|
||||
for (const inputPath of filterPaths(inputPaths, config.paths)) {
|
||||
await extractMessagesFromPathToMap(inputPath, defaultMessagesMap, config, reporter);
|
||||
}
|
||||
|
||||
return defaultMessagesMap;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
extractMessagesFromPathToMap,
|
||||
validateMessageNamespace,
|
||||
} from './extract_default_translations';
|
||||
import { ErrorReporter } from './utils';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, '__fixtures__', 'extract_default_translations');
|
||||
const pluginsPaths = [
|
||||
|
@ -31,31 +32,32 @@ const pluginsPaths = [
|
|||
path.join(fixturesPath, 'test_plugin_3'),
|
||||
];
|
||||
|
||||
jest.mock('../../../.i18nrc.json', () => ({
|
||||
const config = {
|
||||
paths: {
|
||||
plugin_1: 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1',
|
||||
plugin_2: 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2',
|
||||
plugin_3: 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3',
|
||||
},
|
||||
exclude: [],
|
||||
}));
|
||||
};
|
||||
|
||||
describe('dev/i18n/extract_default_translations', () => {
|
||||
test('extracts messages from path to map', async () => {
|
||||
const [pluginPath] = pluginsPaths;
|
||||
const resultMap = new Map();
|
||||
|
||||
await extractMessagesFromPathToMap(pluginPath, resultMap);
|
||||
|
||||
await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter());
|
||||
expect([...resultMap].sort()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on id collision', async () => {
|
||||
const [, , pluginPath] = pluginsPaths;
|
||||
const reporter = new ErrorReporter();
|
||||
|
||||
await expect(
|
||||
extractMessagesFromPathToMap(pluginPath, new Map())
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
extractMessagesFromPathToMap(pluginPath, new Map(), config, reporter)
|
||||
).resolves.not.toThrow();
|
||||
expect(reporter.errors).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('validates message namespace', () => {
|
||||
|
@ -64,15 +66,18 @@ describe('dev/i18n/extract_default_translations', () => {
|
|||
__dirname,
|
||||
'__fixtures__/extract_default_translations/test_plugin_2/test_file.html'
|
||||
);
|
||||
expect(() => validateMessageNamespace(id, filePath)).not.toThrow();
|
||||
expect(() => validateMessageNamespace(id, filePath, config.paths)).not.toThrow();
|
||||
});
|
||||
|
||||
test('throws on wrong message namespace', () => {
|
||||
const report = jest.fn();
|
||||
const id = 'wrong_plugin_namespace.message-id';
|
||||
const filePath = path.resolve(
|
||||
__dirname,
|
||||
'__fixtures__/extract_default_translations/test_plugin_2/test_file.html'
|
||||
);
|
||||
expect(() => validateMessageNamespace(id, filePath)).toThrowErrorMatchingSnapshot();
|
||||
|
||||
expect(() => validateMessageNamespace(id, filePath, config.paths, { report })).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,18 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/code throws on empty id 1`] = `"Empty \\"id\\" value in i18n() or i18n.translate() is not allowed."`;
|
||||
exports[`dev/i18n/extractors/code throws on empty id 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty "id" value in i18n() or i18n.translate() is not allowed.],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/code throws on missing defaultMessage 1`] = `"Empty defaultMessage in intl.formatMessage() is not allowed (\\"message-id\\")."`;
|
||||
exports[`dev/i18n/extractors/code throws on missing defaultMessage 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty defaultMessage in intl.formatMessage() is not allowed ("message-id").],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -12,10 +12,34 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/handlebars throws on empty id 1`] = `"Empty id argument in Handlebars i18n is not allowed."`;
|
||||
exports[`dev/i18n/extractors/handlebars throws on empty id 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty id argument in Handlebars i18n is not allowed.],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/handlebars throws on missing defaultMessage property 1`] = `"defaultMessage value in Handlebars i18n should be a string (\\"message-id\\")."`;
|
||||
exports[`dev/i18n/extractors/handlebars throws on missing defaultMessage property 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: defaultMessage value in Handlebars i18n should be a string ("message-id").],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/handlebars throws on wrong number of arguments 1`] = `"Wrong number of arguments for handlebars i18n call."`;
|
||||
exports[`dev/i18n/extractors/handlebars throws on wrong number of arguments 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Wrong number of arguments for handlebars i18n call.],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/handlebars throws on wrong properties argument type 1`] = `"Properties string in Handlebars i18n should be a string literal (\\"ui.id-1\\")."`;
|
||||
exports[`dev/i18n/extractors/handlebars throws on wrong properties argument type 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Properties string in Handlebars i18n should be a string literal ("ui.id-1").],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -50,12 +50,28 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/html throws on empty i18n-id 1`] = `"Empty \\"i18n-id\\" value in angular directive is not allowed."`;
|
||||
|
||||
exports[`dev/i18n/extractors/html throws on i18n filter usage in complex angular expression 1`] = `
|
||||
"Couldn't parse angular i18n expression:
|
||||
Unexpected token, expected \\";\\" (1:6):
|
||||
mode [37m[41ma[49m[39ms ('metricVis.colorModes.' + mode"
|
||||
exports[`dev/i18n/extractors/html throws on empty i18n-id 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty "i18n-id" value in angular directive is not allowed.],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/html throws on missing i18n-default-message attribute 1`] = `"Empty defaultMessage in angular directive is not allowed (\\"message-id\\")."`;
|
||||
exports[`dev/i18n/extractors/html throws on i18n filter usage in complex angular expression 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Couldn't parse angular i18n expression:
|
||||
Unexpected token, expected ";" (1:6):
|
||||
mode [37m[41ma[49m[39ms ('metricVis.colorModes.' + mode],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/html throws on missing i18n-default-message attribute 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty defaultMessage in angular directive is not allowed ("message-id").],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -20,6 +20,18 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/pug throws on empty id 1`] = `"Empty \\"id\\" value in i18n() or i18n.translate() is not allowed."`;
|
||||
exports[`dev/i18n/extractors/pug throws on empty id 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty "id" value in i18n() or i18n.translate() is not allowed.],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`dev/i18n/extractors/pug throws on missing default message 1`] = `"Empty defaultMessage in i18n() or i18n.translate() is not allowed (\\"message-id\\")."`;
|
||||
exports[`dev/i18n/extractors/pug throws on missing default message 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
[Error: Empty defaultMessage in i18n() or i18n.translate() is not allowed ("message-id").],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
import { extractI18nCallMessages } from './i18n_call';
|
||||
import { createParserErrorMessage, isI18nTranslateFunction, traverseNodes } from '../utils';
|
||||
import { extractIntlMessages, extractFormattedMessages } from './react';
|
||||
import { createFailError } from '../../run';
|
||||
import { createFailError, isFailError } from '../../run';
|
||||
|
||||
/**
|
||||
* Detect Intl.formatMessage() function call (React).
|
||||
|
@ -61,7 +61,7 @@ export function isFormattedMessageElement(node) {
|
|||
return isJSXOpeningElement(node) && isJSXIdentifier(node.name, { name: 'FormattedMessage' });
|
||||
}
|
||||
|
||||
export function* extractCodeMessages(buffer) {
|
||||
export function* extractCodeMessages(buffer, reporter) {
|
||||
let ast;
|
||||
|
||||
try {
|
||||
|
@ -72,19 +72,26 @@ export function* extractCodeMessages(buffer) {
|
|||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
const errorWithContext = createParserErrorMessage(buffer.toString(), error);
|
||||
throw createFailError(errorWithContext);
|
||||
reporter.report(createFailError(errorWithContext));
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const node of traverseNodes(ast.program.body)) {
|
||||
if (isI18nTranslateFunction(node)) {
|
||||
yield extractI18nCallMessages(node);
|
||||
} else if (isIntlFormatMessageFunction(node)) {
|
||||
yield extractIntlMessages(node);
|
||||
} else if (isFormattedMessageElement(node)) {
|
||||
yield extractFormattedMessages(node);
|
||||
try {
|
||||
if (isI18nTranslateFunction(node)) {
|
||||
yield extractI18nCallMessages(node);
|
||||
} else if (isIntlFormatMessageFunction(node)) {
|
||||
yield extractIntlMessages(node);
|
||||
} else if (isFormattedMessageElement(node)) {
|
||||
yield extractFormattedMessages(node);
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isFailError(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
reporter.report(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,13 @@ function f() {
|
|||
}
|
||||
`;
|
||||
|
||||
const report = jest.fn();
|
||||
|
||||
describe('dev/i18n/extractors/code', () => {
|
||||
beforeEach(() => {
|
||||
report.mockClear();
|
||||
});
|
||||
|
||||
test('extracts React, server-side and angular service default messages', () => {
|
||||
const actual = Array.from(extractCodeMessages(extractCodeMessagesSource));
|
||||
expect(actual.sort()).toMatchSnapshot();
|
||||
|
@ -73,12 +79,14 @@ describe('dev/i18n/extractors/code', () => {
|
|||
|
||||
test('throws on empty id', () => {
|
||||
const source = Buffer.from(`i18n.translate('', { defaultMessage: 'Default message' });`);
|
||||
expect(() => extractCodeMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractCodeMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on missing defaultMessage', () => {
|
||||
const source = Buffer.from(`intl.formatMessage({ id: 'message-id' });`);
|
||||
expect(() => extractCodeMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractCodeMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { formatJSString, checkValuesProperty } from '../utils';
|
||||
import { createFailError } from '../../run';
|
||||
import { createFailError, isFailError } from '../../run';
|
||||
import { DEFAULT_MESSAGE_KEY, DESCRIPTION_KEY } from '../constants';
|
||||
|
||||
const HBS_REGEX = /(?<=\{\{)([\s\S]*?)(?=\}\})/g;
|
||||
|
@ -27,7 +27,7 @@ const TOKENS_REGEX = /[^'\s]+|(?:'([^'\\]|\\[\s\S])*')/g;
|
|||
/**
|
||||
* Example: `'{{i18n 'message-id' '{"defaultMessage": "Message text"}'}}'`
|
||||
*/
|
||||
export function* extractHandlebarsMessages(buffer) {
|
||||
export function* extractHandlebarsMessages(buffer, reporter) {
|
||||
for (const expression of buffer.toString().match(HBS_REGEX) || []) {
|
||||
const tokens = expression.match(TOKENS_REGEX);
|
||||
|
||||
|
@ -38,58 +38,78 @@ export function* extractHandlebarsMessages(buffer) {
|
|||
}
|
||||
|
||||
if (tokens.length !== 3) {
|
||||
throw createFailError(`Wrong number of arguments for handlebars i18n call.`);
|
||||
reporter.report(createFailError(`Wrong number of arguments for handlebars i18n call.`));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!idString.startsWith(`'`) || !idString.endsWith(`'`)) {
|
||||
throw createFailError(`Message id should be a string literal.`);
|
||||
reporter.report(createFailError(`Message id should be a string literal.`));
|
||||
continue;
|
||||
}
|
||||
|
||||
const messageId = formatJSString(idString.slice(1, -1));
|
||||
|
||||
if (!messageId) {
|
||||
throw createFailError(`Empty id argument in Handlebars i18n is not allowed.`);
|
||||
reporter.report(createFailError(`Empty id argument in Handlebars i18n is not allowed.`));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!propertiesString.startsWith(`'`) || !propertiesString.endsWith(`'`)) {
|
||||
throw createFailError(
|
||||
`Properties string in Handlebars i18n should be a string literal ("${messageId}").`
|
||||
reporter.report(
|
||||
createFailError(
|
||||
`Properties string in Handlebars i18n should be a string literal ("${messageId}").`
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const properties = JSON.parse(propertiesString.slice(1, -1));
|
||||
|
||||
if (typeof properties.defaultMessage !== 'string') {
|
||||
throw createFailError(
|
||||
`defaultMessage value in Handlebars i18n should be a string ("${messageId}").`
|
||||
reporter.report(
|
||||
createFailError(
|
||||
`defaultMessage value in Handlebars i18n should be a string ("${messageId}").`
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (properties[DESCRIPTION_KEY] != null && typeof properties[DESCRIPTION_KEY] !== 'string') {
|
||||
throw createFailError(
|
||||
`Description value in Handlebars i18n should be a string ("${messageId}").`
|
||||
reporter.report(
|
||||
createFailError(`Description value in Handlebars i18n should be a string ("${messageId}").`)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const message = formatJSString(properties[DEFAULT_MESSAGE_KEY]);
|
||||
const description = formatJSString(properties[DESCRIPTION_KEY]);
|
||||
|
||||
if (!message) {
|
||||
throw createFailError(
|
||||
`Empty defaultMessage in Handlebars i18n is not allowed ("${messageId}").`
|
||||
reporter.report(
|
||||
createFailError(`Empty defaultMessage in Handlebars i18n is not allowed ("${messageId}").`)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const valuesObject = properties.values;
|
||||
|
||||
if (valuesObject != null && typeof valuesObject !== 'object') {
|
||||
throw createFailError(
|
||||
`"values" value should be an object in Handlebars i18n ("${messageId}").`
|
||||
reporter.report(
|
||||
createFailError(`"values" value should be an object in Handlebars i18n ("${messageId}").`)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
checkValuesProperty(Object.keys(valuesObject || {}), message, messageId);
|
||||
try {
|
||||
checkValuesProperty(Object.keys(valuesObject || {}), message, messageId);
|
||||
|
||||
yield [messageId, { message, description }];
|
||||
yield [messageId, { message, description }];
|
||||
} catch (error) {
|
||||
if (!isFailError(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
reporter.report(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,13 @@
|
|||
|
||||
import { extractHandlebarsMessages } from './handlebars';
|
||||
|
||||
const report = jest.fn();
|
||||
|
||||
describe('dev/i18n/extractors/handlebars', () => {
|
||||
beforeEach(() => {
|
||||
report.mockClear();
|
||||
});
|
||||
|
||||
test('extracts handlebars default messages', () => {
|
||||
const source = Buffer.from(`\
|
||||
window.onload = function () {
|
||||
|
@ -49,7 +55,8 @@ window.onload = function () {
|
|||
};
|
||||
`);
|
||||
|
||||
expect(() => extractHandlebarsMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHandlebarsMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on wrong properties argument type', () => {
|
||||
|
@ -59,7 +66,8 @@ window.onload = function () {
|
|||
};
|
||||
`);
|
||||
|
||||
expect(() => extractHandlebarsMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHandlebarsMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on empty id', () => {
|
||||
|
@ -69,7 +77,8 @@ window.onload = function () {
|
|||
};
|
||||
`);
|
||||
|
||||
expect(() => extractHandlebarsMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHandlebarsMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on missing defaultMessage property', () => {
|
||||
|
@ -79,6 +88,7 @@ window.onload = function () {
|
|||
};
|
||||
`);
|
||||
|
||||
expect(() => extractHandlebarsMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHandlebarsMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,7 +33,7 @@ import {
|
|||
extractDescriptionValueFromNode,
|
||||
} from '../utils';
|
||||
import { DEFAULT_MESSAGE_KEY, DESCRIPTION_KEY, VALUES_KEY } from '../constants';
|
||||
import { createFailError } from '../../run';
|
||||
import { createFailError, isFailError } from '../../run';
|
||||
|
||||
/**
|
||||
* Find all substrings of "{{ any text }}" pattern allowing '{' and '}' chars in single quote strings
|
||||
|
@ -152,41 +152,49 @@ function* extractExpressions(htmlContent) {
|
|||
}
|
||||
}
|
||||
|
||||
function* getFilterMessages(htmlContent) {
|
||||
function* getFilterMessages(htmlContent, reporter) {
|
||||
for (const expression of extractExpressions(htmlContent)) {
|
||||
const filterStart = expression.indexOf(I18N_FILTER_MARKER);
|
||||
|
||||
const idExpression = trimOneTimeBindingOperator(expression.slice(0, filterStart).trim());
|
||||
const filterObjectExpression = expression.slice(filterStart + I18N_FILTER_MARKER.length).trim();
|
||||
|
||||
if (!filterObjectExpression || !idExpression) {
|
||||
throw createFailError(`Cannot parse i18n filter expression: ${expression}`);
|
||||
}
|
||||
try {
|
||||
if (!filterObjectExpression || !idExpression) {
|
||||
throw createFailError(`Cannot parse i18n filter expression: ${expression}`);
|
||||
}
|
||||
|
||||
const messageId = parseIdExpression(idExpression);
|
||||
const messageId = parseIdExpression(idExpression);
|
||||
|
||||
if (!messageId) {
|
||||
throw createFailError(`Empty "id" value in angular filter expression is not allowed.`);
|
||||
}
|
||||
if (!messageId) {
|
||||
throw createFailError('Empty "id" value in angular filter expression is not allowed.');
|
||||
}
|
||||
|
||||
const { message, description, valuesKeys } = parseFilterObjectExpression(
|
||||
filterObjectExpression,
|
||||
messageId
|
||||
);
|
||||
|
||||
if (!message) {
|
||||
throw createFailError(
|
||||
`Empty defaultMessage in angular filter expression is not allowed ("${messageId}").`
|
||||
const { message, description, valuesKeys } = parseFilterObjectExpression(
|
||||
filterObjectExpression,
|
||||
messageId
|
||||
);
|
||||
|
||||
if (!message) {
|
||||
throw createFailError(
|
||||
`Empty defaultMessage in angular filter expression is not allowed ("${messageId}").`
|
||||
);
|
||||
}
|
||||
|
||||
checkValuesProperty(valuesKeys, message, messageId);
|
||||
|
||||
yield [messageId, { message, description }];
|
||||
} catch (error) {
|
||||
if (!isFailError(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
reporter.report(error);
|
||||
}
|
||||
|
||||
checkValuesProperty(valuesKeys, message, messageId);
|
||||
|
||||
yield [messageId, { message, description }];
|
||||
}
|
||||
}
|
||||
|
||||
function* getDirectiveMessages(htmlContent) {
|
||||
function* getDirectiveMessages(htmlContent, reporter) {
|
||||
const $ = cheerio.load(htmlContent);
|
||||
|
||||
const elements = $('[i18n-id]')
|
||||
|
@ -205,34 +213,51 @@ function* getDirectiveMessages(htmlContent) {
|
|||
for (const element of elements) {
|
||||
const messageId = formatHTMLString(element.id);
|
||||
if (!messageId) {
|
||||
throw createFailError(`Empty "i18n-id" value in angular directive is not allowed.`);
|
||||
reporter.report(
|
||||
createFailError('Empty "i18n-id" value in angular directive is not allowed.')
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const message = formatHTMLString(element.defaultMessage);
|
||||
if (!message) {
|
||||
throw createFailError(
|
||||
`Empty defaultMessage in angular directive is not allowed ("${messageId}").`
|
||||
reporter.report(
|
||||
createFailError(
|
||||
`Empty defaultMessage in angular directive is not allowed ("${messageId}").`
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.values) {
|
||||
const ast = parseExpression(element.values);
|
||||
const valuesObjectNode = [...traverseNodes(ast.program.body)].find(node =>
|
||||
isObjectExpression(node)
|
||||
);
|
||||
const valuesKeys = extractValuesKeysFromNode(valuesObjectNode);
|
||||
try {
|
||||
if (element.values) {
|
||||
const ast = parseExpression(element.values);
|
||||
const valuesObjectNode = [...traverseNodes(ast.program.body)].find(node =>
|
||||
isObjectExpression(node)
|
||||
);
|
||||
const valuesKeys = extractValuesKeysFromNode(valuesObjectNode);
|
||||
|
||||
checkValuesProperty(valuesKeys, message, messageId);
|
||||
} else {
|
||||
checkValuesProperty([], message, messageId);
|
||||
checkValuesProperty(valuesKeys, message, messageId);
|
||||
} else {
|
||||
checkValuesProperty([], message, messageId);
|
||||
}
|
||||
|
||||
yield [
|
||||
messageId,
|
||||
{ message, description: formatHTMLString(element.description) || undefined },
|
||||
];
|
||||
} catch (error) {
|
||||
if (!isFailError(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
reporter.report(error);
|
||||
}
|
||||
|
||||
yield [messageId, { message, description: formatHTMLString(element.description) || undefined }];
|
||||
}
|
||||
}
|
||||
|
||||
export function* extractHtmlMessages(buffer) {
|
||||
export function* extractHtmlMessages(buffer, reporter) {
|
||||
const content = buffer.toString();
|
||||
yield* getDirectiveMessages(content);
|
||||
yield* getFilterMessages(content);
|
||||
yield* getDirectiveMessages(content, reporter);
|
||||
yield* getFilterMessages(content, reporter);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,13 @@ const htmlSourceBuffer = Buffer.from(`
|
|||
</div>
|
||||
`);
|
||||
|
||||
const report = jest.fn();
|
||||
|
||||
describe('dev/i18n/extractors/html', () => {
|
||||
beforeEach(() => {
|
||||
report.mockClear();
|
||||
});
|
||||
|
||||
test('extracts default messages from HTML', () => {
|
||||
const actual = Array.from(extractHtmlMessages(htmlSourceBuffer));
|
||||
expect(actual.sort()).toMatchSnapshot();
|
||||
|
@ -67,7 +73,8 @@ describe('dev/i18n/extractors/html', () => {
|
|||
></p>
|
||||
`);
|
||||
|
||||
expect(() => extractHtmlMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHtmlMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on missing i18n-default-message attribute', () => {
|
||||
|
@ -77,7 +84,8 @@ describe('dev/i18n/extractors/html', () => {
|
|||
></p>
|
||||
`);
|
||||
|
||||
expect(() => extractHtmlMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHtmlMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on i18n filter usage in complex angular expression', () => {
|
||||
|
@ -87,7 +95,8 @@ describe('dev/i18n/extractors/html', () => {
|
|||
></div>
|
||||
`);
|
||||
|
||||
expect(() => extractHtmlMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractHtmlMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('extracts message from i18n filter in interpolating directive', () => {
|
||||
|
|
|
@ -21,40 +21,52 @@ import { parse } from '@babel/parser';
|
|||
|
||||
import { extractI18nCallMessages } from './i18n_call';
|
||||
import { isI18nTranslateFunction, traverseNodes, createParserErrorMessage } from '../utils';
|
||||
import { createFailError } from '../../run';
|
||||
import { createFailError, isFailError } from '../../run';
|
||||
|
||||
/**
|
||||
* Matches `i18n(...)` in `#{i18n('id', { defaultMessage: 'Message text' })}`
|
||||
*/
|
||||
const PUG_I18N_REGEX = /i18n\((([^)']|'([^'\\]|\\.)*')*)\)/g;
|
||||
|
||||
function parsePugExpression(expression) {
|
||||
let ast;
|
||||
|
||||
try {
|
||||
ast = parse(expression);
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
const errorWithContext = createParserErrorMessage(expression, error);
|
||||
throw createFailError(
|
||||
`Couldn't parse Pug expression with i18n(...) call:\n${errorWithContext}`
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: `#{i18n('message-id', { defaultMessage: 'Message text' })}`
|
||||
*/
|
||||
export function* extractPugMessages(buffer) {
|
||||
export function* extractPugMessages(buffer, reporter) {
|
||||
const expressions = buffer.toString().match(PUG_I18N_REGEX) || [];
|
||||
|
||||
for (const expression of expressions) {
|
||||
let ast;
|
||||
|
||||
try {
|
||||
ast = parse(expression);
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
const errorWithContext = createParserErrorMessage(expression, error);
|
||||
throw createFailError(
|
||||
`Couldn't parse Pug expression with i18n(...) call:\n${errorWithContext}`
|
||||
);
|
||||
}
|
||||
const ast = parsePugExpression(expression);
|
||||
const node = [...traverseNodes(ast.program.body)].find(node => isI18nTranslateFunction(node));
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const node of traverseNodes(ast.program.body)) {
|
||||
if (isI18nTranslateFunction(node)) {
|
||||
if (node) {
|
||||
yield extractI18nCallMessages(node);
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isFailError(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
reporter.report(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,13 @@
|
|||
|
||||
import { extractPugMessages } from './pug';
|
||||
|
||||
const report = jest.fn();
|
||||
|
||||
describe('dev/i18n/extractors/pug', () => {
|
||||
beforeEach(() => {
|
||||
report.mockClear();
|
||||
});
|
||||
|
||||
test('extracts messages from pug template with interpolation', () => {
|
||||
const source = Buffer.from(`\
|
||||
#{i18n('message-id', { defaultMessage: 'Default message', description: 'Message description' })}
|
||||
|
@ -43,7 +49,8 @@ describe('dev/i18n/extractors/pug', () => {
|
|||
h1= i18n('', { defaultMessage: 'Default message', description: 'Message description' })
|
||||
`);
|
||||
|
||||
expect(() => extractPugMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractPugMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('throws on missing default message', () => {
|
||||
|
@ -51,6 +58,7 @@ h1= i18n('', { defaultMessage: 'Default message', description: 'Message descript
|
|||
#{i18n('message-id', { description: 'Message description' })}
|
||||
`);
|
||||
|
||||
expect(() => extractPugMessages(source).next()).toThrowErrorMatchingSnapshot();
|
||||
expect(() => extractPugMessages(source, { report }).next()).not.toThrow();
|
||||
expect(report.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
*/
|
||||
|
||||
export { filterPaths, extractMessagesFromPathToMap } from './extract_default_translations';
|
||||
export { writeFileAsync } from './utils';
|
||||
export { writeFileAsync, readFileAsync, normalizePath, ErrorReporter } from './utils';
|
||||
export { serializeToJson, serializeToJson5 } from './serializers';
|
||||
|
|
112
src/dev/i18n/integrate_locale_files.js
Normal file
112
src/dev/i18n/integrate_locale_files.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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 {
|
||||
difference,
|
||||
readFileAsync,
|
||||
writeFileAsync,
|
||||
accessAsync,
|
||||
makeDirAsync,
|
||||
normalizePath,
|
||||
ErrorReporter,
|
||||
} from './utils';
|
||||
import { paths, exclude } from '../../../.i18nrc.json';
|
||||
import { getDefaultMessagesMap } from './extract_default_translations';
|
||||
import { createFailError } from '../run';
|
||||
import { serializeToJson } from './serializers/json';
|
||||
|
||||
export function verifyMessages(localizedMessagesMap, defaultMessagesMap) {
|
||||
let errorMessage = '';
|
||||
|
||||
const defaultMessagesIds = [...defaultMessagesMap.keys()];
|
||||
const localizedMessagesIds = [...localizedMessagesMap.keys()];
|
||||
|
||||
const unusedTranslations = difference(localizedMessagesIds, defaultMessagesIds);
|
||||
if (unusedTranslations.length > 0) {
|
||||
errorMessage += `\nUnused translations:\n${unusedTranslations.join(', ')}`;
|
||||
}
|
||||
|
||||
const missingTranslations = difference(defaultMessagesIds, localizedMessagesIds);
|
||||
if (missingTranslations.length > 0) {
|
||||
errorMessage += `\nMissing translations:\n${missingTranslations.join(', ')}`;
|
||||
}
|
||||
|
||||
if (errorMessage) {
|
||||
throw createFailError(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function groupMessagesByNamespace(localizedMessagesMap) {
|
||||
const localizedMessagesByNamespace = new Map();
|
||||
const knownNamespaces = Object.keys(paths);
|
||||
|
||||
for (const [messageId, messageValue] of localizedMessagesMap) {
|
||||
const namespace = knownNamespaces.find(key => messageId.startsWith(`${key}.`));
|
||||
|
||||
if (!namespace) {
|
||||
throw createFailError(`Unknown namespace in id ${messageId}.`);
|
||||
}
|
||||
|
||||
if (!localizedMessagesByNamespace.has(namespace)) {
|
||||
localizedMessagesByNamespace.set(namespace, []);
|
||||
}
|
||||
|
||||
localizedMessagesByNamespace
|
||||
.get(namespace)
|
||||
.push([messageId, { message: messageValue.text || messageValue }]);
|
||||
}
|
||||
|
||||
return localizedMessagesByNamespace;
|
||||
}
|
||||
|
||||
async function writeMessages(localizedMessagesByNamespace, fileName, formats, log) {
|
||||
for (const [namespace, messages] of localizedMessagesByNamespace) {
|
||||
const destPath = path.resolve(paths[namespace], 'translations');
|
||||
|
||||
try {
|
||||
await accessAsync(destPath);
|
||||
} catch (_) {
|
||||
await makeDirAsync(destPath);
|
||||
}
|
||||
|
||||
const writePath = path.resolve(destPath, fileName);
|
||||
await writeFileAsync(writePath, serializeToJson(messages, formats));
|
||||
log.success(`Translations have been integrated to ${normalizePath(writePath)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function integrateLocaleFiles(filePath, log) {
|
||||
const reporter = new ErrorReporter();
|
||||
const defaultMessagesMap = await getDefaultMessagesMap(['.'], { paths, exclude }, reporter);
|
||||
const localizedMessages = JSON.parse((await readFileAsync(filePath)).toString());
|
||||
|
||||
if (!localizedMessages.formats) {
|
||||
throw createFailError(`Locale file should contain formats object.`);
|
||||
}
|
||||
|
||||
const localizedMessagesMap = new Map(Object.entries(localizedMessages.messages));
|
||||
verifyMessages(localizedMessagesMap, defaultMessagesMap);
|
||||
|
||||
// use basename of filePath to write the same locale name as the source file has
|
||||
const fileName = path.basename(filePath);
|
||||
const localizedMessagesByNamespace = groupMessagesByNamespace(localizedMessagesMap);
|
||||
await writeMessages(localizedMessagesByNamespace, fileName, localizedMessages.formats, log);
|
||||
}
|
105
src/dev/i18n/integrate_locale_files.test.js
Normal file
105
src/dev/i18n/integrate_locale_files.test.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 { verifyMessages, integrateLocaleFiles } from './integrate_locale_files';
|
||||
import { normalizePath } from './utils';
|
||||
|
||||
const localePath = path.resolve(__dirname, '__fixtures__', 'integrate_locale_files', 'fr.json');
|
||||
|
||||
const mockDefaultMessagesMap = new Map([
|
||||
['plugin-1.message-id-1', 'Message text 1'],
|
||||
['plugin-1.message-id-2', 'Message text 2'],
|
||||
['plugin-2.message-id', 'Message text'],
|
||||
]);
|
||||
|
||||
jest.mock('./extract_default_translations.js', () => ({
|
||||
getDefaultMessagesMap: () => mockDefaultMessagesMap,
|
||||
}));
|
||||
|
||||
jest.mock('../../../.i18nrc.json', () => ({
|
||||
paths: {
|
||||
'plugin-1': 'src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_1',
|
||||
'plugin-2': 'src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_2',
|
||||
},
|
||||
exclude: [],
|
||||
}));
|
||||
|
||||
const utils = require('./utils');
|
||||
utils.writeFileAsync = jest.fn();
|
||||
utils.makeDirAsync = jest.fn();
|
||||
|
||||
describe('dev/i18n/integrate_locale_files', () => {
|
||||
describe('verifyMessages', () => {
|
||||
test('validates localized messages', () => {
|
||||
const localizedMessagesMap = new Map([
|
||||
['plugin-1.message-id-1', 'Translated text 1'],
|
||||
['plugin-1.message-id-2', 'Translated text 2'],
|
||||
['plugin-2.message-id', 'Translated text'],
|
||||
]);
|
||||
|
||||
expect(() => verifyMessages(localizedMessagesMap, mockDefaultMessagesMap)).not.toThrow();
|
||||
});
|
||||
|
||||
test('throws an error for unused id and missing id', () => {
|
||||
const localizedMessagesMapWithMissingMessage = new Map([
|
||||
['plugin-1.message-id-1', 'Translated text 1'],
|
||||
['plugin-2.message-id', 'Translated text'],
|
||||
]);
|
||||
|
||||
const localizedMessagesMapWithUnusedMessage = new Map([
|
||||
['plugin-1.message-id-1', 'Translated text 1'],
|
||||
['plugin-1.message-id-2', 'Translated text 2'],
|
||||
['plugin-1.message-id-3', 'Translated text 3'],
|
||||
['plugin-2.message-id', 'Translated text'],
|
||||
]);
|
||||
|
||||
const localizedMessagesMapWithIdTypo = new Map([
|
||||
['plugin-1.message-id-1', 'Message text 1'],
|
||||
['plugin-1.message-id-2', 'Message text 2'],
|
||||
['plugin-2.message', 'Message text'],
|
||||
]);
|
||||
|
||||
expect(() =>
|
||||
verifyMessages(localizedMessagesMapWithMissingMessage, mockDefaultMessagesMap)
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
expect(() =>
|
||||
verifyMessages(localizedMessagesMapWithUnusedMessage, mockDefaultMessagesMap)
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
expect(() =>
|
||||
verifyMessages(localizedMessagesMapWithIdTypo, mockDefaultMessagesMap)
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('integrateLocaleFiles', () => {
|
||||
test('splits locale file by plugins and writes them into the right folders', async () => {
|
||||
const success = jest.fn();
|
||||
await integrateLocaleFiles(localePath, { success });
|
||||
|
||||
const [[path1, json1], [path2, json2]] = utils.writeFileAsync.mock.calls;
|
||||
const [[dirPath1], [dirPath2]] = utils.makeDirAsync.mock.calls;
|
||||
|
||||
expect([normalizePath(path1), json1]).toMatchSnapshot();
|
||||
expect([normalizePath(path2), json2]).toMatchSnapshot();
|
||||
expect([normalizePath(dirPath1), normalizePath(dirPath2)]).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export function serializeToJson(defaultMessages) {
|
||||
const resultJsonObject = { formats: i18n.formats, messages: {} };
|
||||
export function serializeToJson(messages, formats = i18n.formats) {
|
||||
const resultJsonObject = { formats, messages: {} };
|
||||
|
||||
for (const [mapKey, mapValue] of defaultMessages) {
|
||||
for (const [mapKey, mapValue] of messages) {
|
||||
if (mapValue.description) {
|
||||
resultJsonObject.messages[mapKey] = { text: mapValue.message, comment: mapValue.description };
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,7 @@ import { serializeToJson } from './json';
|
|||
|
||||
describe('dev/i18n/serializers/json', () => {
|
||||
test('should serialize default messages to JSON', () => {
|
||||
const messages = new Map([
|
||||
const messages = [
|
||||
['plugin1.message.id-1', { message: 'Message text 1 ' }],
|
||||
[
|
||||
'plugin2.message.id-2',
|
||||
|
@ -30,7 +30,7 @@ describe('dev/i18n/serializers/json', () => {
|
|||
description: 'Message description',
|
||||
},
|
||||
],
|
||||
]);
|
||||
];
|
||||
|
||||
expect(serializeToJson(messages)).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -22,15 +22,15 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
const ESCAPE_SINGLE_QUOTE_REGEX = /\\([\s\S])|(')/g;
|
||||
|
||||
export function serializeToJson5(defaultMessages) {
|
||||
export function serializeToJson5(messages, formats = i18n.formats) {
|
||||
// .slice(0, -4): remove closing curly braces from json to append messages
|
||||
let jsonBuffer = Buffer.from(
|
||||
JSON5.stringify({ formats: i18n.formats, messages: {} }, { quote: `'`, space: 2 })
|
||||
JSON5.stringify({ formats, messages: {} }, { quote: `'`, space: 2 })
|
||||
.slice(0, -4)
|
||||
.concat('\n')
|
||||
);
|
||||
|
||||
for (const [mapKey, mapValue] of defaultMessages) {
|
||||
for (const [mapKey, mapValue] of messages) {
|
||||
const formattedMessage = mapValue.message.replace(ESCAPE_SINGLE_QUOTE_REGEX, '\\$1$2');
|
||||
const formattedDescription = mapValue.description
|
||||
? mapValue.description.replace(ESCAPE_SINGLE_QUOTE_REGEX, '\\$1$2')
|
||||
|
|
|
@ -21,7 +21,7 @@ import { serializeToJson5 } from './json5';
|
|||
|
||||
describe('dev/i18n/serializers/json5', () => {
|
||||
test('should serialize default messages to JSON5', () => {
|
||||
const messages = new Map([
|
||||
const messages = [
|
||||
[
|
||||
'plugin1.message.id-1',
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ describe('dev/i18n/serializers/json5', () => {
|
|||
description: 'Message description',
|
||||
},
|
||||
],
|
||||
]);
|
||||
];
|
||||
|
||||
expect(serializeToJson5(messages).toString()).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -31,6 +31,8 @@ import {
|
|||
import fs from 'fs';
|
||||
import glob from 'glob';
|
||||
import { promisify } from 'util';
|
||||
import normalize from 'normalize-path';
|
||||
import path from 'path';
|
||||
import chalk from 'chalk';
|
||||
import parser from 'intl-messageformat-parser';
|
||||
|
||||
|
@ -44,8 +46,14 @@ const HTML_KEY_PREFIX = 'html_';
|
|||
|
||||
export const readFileAsync = promisify(fs.readFile);
|
||||
export const writeFileAsync = promisify(fs.writeFile);
|
||||
export const makeDirAsync = promisify(fs.mkdir);
|
||||
export const accessAsync = promisify(fs.access);
|
||||
export const globAsync = promisify(glob);
|
||||
|
||||
export function normalizePath(inputPath) {
|
||||
return normalize(path.relative('.', inputPath));
|
||||
}
|
||||
|
||||
export function difference(left = [], right = []) {
|
||||
return left.filter(value => !right.includes(value));
|
||||
}
|
||||
|
@ -174,8 +182,8 @@ export function checkValuesProperty(prefixedValuesKeys, defaultMessage, messageI
|
|||
return;
|
||||
}
|
||||
|
||||
const valuesKeys = prefixedValuesKeys.map(
|
||||
key => (key.startsWith(HTML_KEY_PREFIX) ? key.slice(HTML_KEY_PREFIX.length) : key)
|
||||
const valuesKeys = prefixedValuesKeys.map(key =>
|
||||
key.startsWith(HTML_KEY_PREFIX) ? key.slice(HTML_KEY_PREFIX.length) : key
|
||||
);
|
||||
|
||||
let defaultMessageAst;
|
||||
|
@ -284,7 +292,21 @@ export function extractValuesKeysFromNode(node, messageId) {
|
|||
throw createFailError(`"values" value should be an object expression ("${messageId}").`);
|
||||
}
|
||||
|
||||
return node.properties.map(
|
||||
property => (isStringLiteral(property.key) ? property.key.value : property.key.name)
|
||||
return node.properties.map(property =>
|
||||
isStringLiteral(property.key) ? property.key.value : property.key.name
|
||||
);
|
||||
}
|
||||
|
||||
export class ErrorReporter {
|
||||
errors = [];
|
||||
|
||||
withContext(context) {
|
||||
return { report: error => this.report(error, context) };
|
||||
}
|
||||
|
||||
report(error, context) {
|
||||
this.errors.push(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} Error in ${normalizePath(context.name)}\n${error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
formatJSString,
|
||||
checkValuesProperty,
|
||||
createParserErrorMessage,
|
||||
normalizePath,
|
||||
extractMessageValueFromNode,
|
||||
} from './utils';
|
||||
|
||||
|
@ -107,6 +108,10 @@ describe('i18n utils', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('should normalizePath', () => {
|
||||
expect(normalizePath(__dirname)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should validate conformity of "values" and "defaultMessage"', () => {
|
||||
const valuesKeys = ['url', 'username', 'password'];
|
||||
const defaultMessage = 'Test message with {username}, {password} and [markdown link]({url}).';
|
||||
|
|
|
@ -24,3 +24,5 @@ bluebird.Promise.setScheduler(function (fn) { global.setImmediate.call(global, f
|
|||
|
||||
const MutationObserver = require('mutation-observer');
|
||||
Object.defineProperty(window, 'MutationObserver', { value: MutationObserver });
|
||||
|
||||
require('whatwg-fetch');
|
||||
|
|
|
@ -42,6 +42,8 @@ export function runMochaCli() {
|
|||
|
||||
// check that we aren't leaking any globals
|
||||
process.argv.push('--check-leaks');
|
||||
// prevent globals injected from canvas plugins from triggering leak check
|
||||
process.argv.push('--globals', 'core,regeneratorRuntime,_');
|
||||
|
||||
// ensure that mocha requires the setup_node_env script
|
||||
process.argv.push('--require', require.resolve('../../setup_node_env'));
|
||||
|
|
|
@ -22,17 +22,36 @@ import Listr from 'listr';
|
|||
import { resolve } from 'path';
|
||||
|
||||
import { run, createFailError } from './run';
|
||||
import config from '../../.i18nrc.json';
|
||||
import {
|
||||
filterPaths,
|
||||
extractMessagesFromPathToMap,
|
||||
writeFileAsync,
|
||||
readFileAsync,
|
||||
serializeToJson,
|
||||
serializeToJson5,
|
||||
ErrorReporter,
|
||||
normalizePath,
|
||||
} from './i18n/';
|
||||
|
||||
run(async ({ flags: { path, output, 'output-format': outputFormat } }) => {
|
||||
run(async ({ flags: { path, output, 'output-format': outputFormat, include = [] } }) => {
|
||||
const paths = Array.isArray(path) ? path : [path || './'];
|
||||
const filteredPaths = filterPaths(paths);
|
||||
const additionalI18nConfigPaths = Array.isArray(include) ? include : [include];
|
||||
const mergedConfig = { exclude: [], ...config };
|
||||
|
||||
for (const configPath of additionalI18nConfigPaths) {
|
||||
const additionalConfig = JSON.parse(await readFileAsync(resolve(configPath)));
|
||||
|
||||
for (const [pathNamespace, pathValue] of Object.entries(additionalConfig.paths)) {
|
||||
mergedConfig.paths[pathNamespace] = normalizePath(resolve(configPath, '..', pathValue));
|
||||
}
|
||||
|
||||
for (const exclude of additionalConfig.exclude || []) {
|
||||
mergedConfig.exclude.push(normalizePath(resolve(configPath, '..', exclude)));
|
||||
}
|
||||
}
|
||||
|
||||
const filteredPaths = filterPaths(paths, mergedConfig.paths);
|
||||
|
||||
if (filteredPaths.length === 0) {
|
||||
throw createFailError(
|
||||
|
@ -41,22 +60,51 @@ None of input paths is available for extraction or validation. See .i18nrc.json.
|
|||
);
|
||||
}
|
||||
|
||||
const reporter = new ErrorReporter();
|
||||
|
||||
const list = new Listr(
|
||||
filteredPaths.map(filteredPath => ({
|
||||
task: messages => extractMessagesFromPathToMap(filteredPath, messages),
|
||||
task: async messages => {
|
||||
const initialErrorsNumber = reporter.errors.length;
|
||||
|
||||
// Return result if no new errors were reported for this path.
|
||||
const result = await extractMessagesFromPathToMap(
|
||||
filteredPath,
|
||||
messages,
|
||||
mergedConfig,
|
||||
reporter
|
||||
);
|
||||
if (reporter.errors.length === initialErrorsNumber) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// throw an empty error to make listr mark the task as failed without any message
|
||||
throw new Error('');
|
||||
},
|
||||
title: filteredPath,
|
||||
}))
|
||||
})),
|
||||
{
|
||||
exitOnError: false,
|
||||
}
|
||||
);
|
||||
|
||||
// messages shouldn't be extracted to a file if output is not supplied
|
||||
const messages = await list.run(new Map());
|
||||
if (!output || !messages.size) {
|
||||
return;
|
||||
try {
|
||||
// messages shouldn't be extracted to a file if output is not supplied
|
||||
const messages = await list.run(new Map());
|
||||
if (!output || !messages.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedMessages = [...messages].sort(([key1], [key2]) => key1.localeCompare(key2));
|
||||
await writeFileAsync(
|
||||
resolve(output, 'en.json'),
|
||||
outputFormat === 'json5' ? serializeToJson5(sortedMessages) : serializeToJson(sortedMessages)
|
||||
);
|
||||
} catch (error) {
|
||||
if (error.name === 'ListrError' && reporter.errors.length) {
|
||||
throw createFailError(reporter.errors.join('\n\n'));
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const sortedMessages = [...messages].sort(([key1], [key2]) => key1.localeCompare(key2));
|
||||
await writeFileAsync(
|
||||
resolve(output, 'en.json'),
|
||||
outputFormat === 'json5' ? serializeToJson5(sortedMessages) : serializeToJson(sortedMessages)
|
||||
);
|
||||
});
|
||||
|
|
37
src/dev/run_i18n_integrate.js
Normal file
37
src/dev/run_i18n_integrate.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 chalk from 'chalk';
|
||||
|
||||
import { createFailError, run } from './run';
|
||||
import { integrateLocaleFiles } from './i18n/integrate_locale_files';
|
||||
|
||||
run(async ({ flags: { path }, log }) => {
|
||||
if (!path || typeof path === 'boolean') {
|
||||
throw createFailError(`${chalk.white.bgRed(' I18N ERROR ')} --path option isn't provided.`);
|
||||
}
|
||||
|
||||
if (Array.isArray(path)) {
|
||||
throw createFailError(
|
||||
`${chalk.white.bgRed(' I18N ERROR ')} --path should be specified only once`
|
||||
);
|
||||
}
|
||||
|
||||
await integrateLocaleFiles(path, log);
|
||||
});
|
28
src/dev/run_sasslint.js
Normal file
28
src/dev/run_sasslint.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
process.argv.push('--no-exit'); // don't exit after encountering a rule error
|
||||
process.argv.push('--verbose'); // print results
|
||||
process.argv.push('--max-warnings', '0'); // return nonzero exit code on any warnings
|
||||
process.argv.push('--config', resolve(__dirname, '..', '..', '.sass-lint.yml')); // configuration file
|
||||
|
||||
// common-js is required so that logic before this executes before loading sass-lint
|
||||
require('sass-lint/bin/sass-lint');
|
|
@ -17,8 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import {
|
||||
resolve,
|
||||
dirname,
|
||||
relative
|
||||
} from 'path';
|
||||
|
||||
import {
|
||||
stat,
|
||||
rename,
|
||||
createReadStream,
|
||||
createWriteStream
|
||||
|
@ -38,28 +44,38 @@ import {
|
|||
createFormatArchiveStreams,
|
||||
} from '../lib';
|
||||
|
||||
export async function rebuildAllAction({ dataDir, log }) {
|
||||
const archiveNames = await readDirectory(dataDir);
|
||||
async function isDirectory(path) {
|
||||
const stats = await fromNode(cb => stat(path, cb));
|
||||
return stats.isDirectory();
|
||||
}
|
||||
|
||||
for (const name of archiveNames) {
|
||||
const inputDir = resolve(dataDir, name);
|
||||
const files = prioritizeMappings(await readDirectory(inputDir));
|
||||
for (const filename of files) {
|
||||
log.info('[%s] Rebuilding %j', name, filename);
|
||||
export async function rebuildAllAction({ dataDir, log, rootDir = dataDir }) {
|
||||
const childNames = prioritizeMappings(await readDirectory(dataDir));
|
||||
for (const childName of childNames) {
|
||||
const childPath = resolve(dataDir, childName);
|
||||
|
||||
const path = resolve(inputDir, filename);
|
||||
const gzip = isGzip(path);
|
||||
const tempFile = path + (gzip ? '.rebuilding.gz' : '.rebuilding');
|
||||
|
||||
await createPromiseFromStreams([
|
||||
createReadStream(path),
|
||||
...createParseArchiveStreams({ gzip }),
|
||||
...createFormatArchiveStreams({ gzip }),
|
||||
createWriteStream(tempFile),
|
||||
]);
|
||||
|
||||
await fromNode(cb => rename(tempFile, path, cb));
|
||||
log.info('[%s] Rebuilt %j', name, filename);
|
||||
if (await isDirectory(childPath)) {
|
||||
await rebuildAllAction({
|
||||
dataDir: childPath,
|
||||
log,
|
||||
rootDir,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const archiveName = dirname(relative(rootDir, childPath));
|
||||
log.info(`${archiveName} Rebuilding ${childName}`);
|
||||
const gzip = isGzip(childPath);
|
||||
const tempFile = childPath + (gzip ? '.rebuilding.gz' : '.rebuilding');
|
||||
|
||||
await createPromiseFromStreams([
|
||||
createReadStream(childPath),
|
||||
...createParseArchiveStreams({ gzip }),
|
||||
...createFormatArchiveStreams({ gzip }),
|
||||
createWriteStream(tempFile),
|
||||
]);
|
||||
|
||||
await fromNode(cb => rename(tempFile, childPath, cb));
|
||||
log.info(`${archiveName} Rebuilt ${childName}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import { transformDeprecations } from './transform_deprecations';
|
|||
const cache = new WeakMap();
|
||||
|
||||
async function getSettingsFromFile(log, path, settingOverrides) {
|
||||
const configModule = require(path);
|
||||
const configModule = require(path); // eslint-disable-line import/no-dynamic-require
|
||||
const configProvider = configModule.__esModule
|
||||
? configModule.default
|
||||
: configModule;
|
||||
|
|
|
@ -48,7 +48,7 @@ export const loadTestFiles = ({ mocha, log, lifecycle, providers, paths, exclude
|
|||
loadTracer(path, `testFile[${path}]`, () => {
|
||||
log.verbose('Loading test file %s', path);
|
||||
|
||||
const testModule = require(path);
|
||||
const testModule = require(path); // eslint-disable-line import/no-dynamic-require
|
||||
const testProvider = testModule.__esModule
|
||||
? testModule.default
|
||||
: testModule;
|
||||
|
|
|
@ -27,7 +27,7 @@ export function resolveApi(senseVersion, apis, h) {
|
|||
{
|
||||
if (KNOWN_APIS.includes(name)) {
|
||||
// for now we ignore sense_version. might add it in the api name later
|
||||
const api = require('./' + name);
|
||||
const api = require('./' + name); // eslint-disable-line import/no-dynamic-require
|
||||
result[name] = api.asJson();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
role="option"
|
||||
ng-repeat="req in history.reqs"
|
||||
id="historyReq{{$index}}"
|
||||
ng-class="{ conHistory__req--selected: history.viewingReq === req }"
|
||||
ng-class="{ 'conHistory__req-selected': history.viewingReq === req }"
|
||||
ng-click="history.selectedReq = req; history.viewingReq = req; history.selectedIndex = $index"
|
||||
ng-mouseenter="history.viewingReq = req"
|
||||
ng-mouseleave="history.viewingReq = history.selectedReq"
|
||||
|
|
|
@ -47,7 +47,7 @@ export const vega = () => ({
|
|||
|
||||
const response = await vegaRequestHandler({
|
||||
timeRange: get(context, 'timeRange', null),
|
||||
query: get(context, 'q', null),
|
||||
query: get(context, 'query', null),
|
||||
filters: get(context, 'filters', null),
|
||||
visParams: { spec: args.spec },
|
||||
forceFetch: true
|
||||
|
|
|
@ -25,7 +25,6 @@ import { functions } from './functions';
|
|||
const basePath = chrome.getBasePath();
|
||||
|
||||
const types = {
|
||||
commonFunctions: functionsRegistry,
|
||||
browserFunctions: functionsRegistry,
|
||||
types: typesRegistry
|
||||
};
|
||||
|
|
|
@ -83,7 +83,7 @@ exports[`after fetch initialFilter 1`] = `
|
|||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="newDashboardLink"
|
||||
fill={false}
|
||||
fill={true}
|
||||
href="#/dashboard"
|
||||
iconSide="left"
|
||||
type="button"
|
||||
|
@ -333,7 +333,7 @@ exports[`after fetch renders table rows 1`] = `
|
|||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="newDashboardLink"
|
||||
fill={false}
|
||||
fill={true}
|
||||
href="#/dashboard"
|
||||
iconSide="left"
|
||||
type="button"
|
||||
|
@ -501,7 +501,7 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = `
|
|||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="newDashboardLink"
|
||||
fill={false}
|
||||
fill={true}
|
||||
href="#/dashboard"
|
||||
iconSide="left"
|
||||
type="button"
|
||||
|
|
|
@ -522,6 +522,7 @@ class DashboardListingUi extends React.Component {
|
|||
<EuiButton
|
||||
href={`#${DashboardConstants.CREATE_NEW_DASHBOARD_URL}`}
|
||||
data-test-subj="newDashboardLink"
|
||||
fill
|
||||
>
|
||||
<FormattedMessage
|
||||
id="kbn.dashboard.listing.createNewDashboardButtonLabel"
|
||||
|
|
|
@ -564,6 +564,7 @@ exports[`bulkCreate should display success message when bulkCreate is successful
|
|||
>
|
||||
<path
|
||||
d="M6.5 12a.502.502 0 0 1-.354-.146l-4-4a.502.502 0 0 1 .708-.708L6.5 10.793l6.646-6.647a.502.502 0 0 1 .708.708l-7 7A.502.502 0 0 1 6.5 12"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</check>
|
||||
|
|
|
@ -287,7 +287,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
|
@ -298,17 +297,9 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
d="M7.293 8l-4.147 4.146a.5.5 0 0 0 .708.708L8 8.707l4.146 4.147a.5.5 0 0 0 .708-.708L8.707 8l4.147-4.146a.5.5 0 0 0-.708-.708L8 7.293 3.854 3.146a.5.5 0 1 0-.708.708L7.293 8z"
|
||||
id="cross-a"
|
||||
/>
|
||||
</defs>
|
||||
<use
|
||||
fillRule="nonzero"
|
||||
xlinkHref="#cross-a"
|
||||
<path
|
||||
d="M7.293 8L3.146 3.854a.5.5 0 1 1 .708-.708L8 7.293l4.146-4.147a.5.5 0 0 1 .708.708L8.707 8l4.147 4.146a.5.5 0 0 1-.708.708L8 8.707l-4.146 4.147a.5.5 0 0 1-.708-.708L7.293 8z"
|
||||
/>
|
||||
</svg>
|
||||
</cross>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,7 +7,7 @@
|
|||
"title": "Services [APM]",
|
||||
"uiStateJSON": "{\"vis\": {\"params\": {\"sort\": {\"direction\": null, \"columnIndex\": null}}}}",
|
||||
"version": 1,
|
||||
"visState": "{\"type\": \"table\", \"title\": \"Services [APM]\", \"aggs\": [{\"id\": \"1\", \"type\": \"avg\", \"enabled\": true, \"params\": {\"customLabel\": \"Avg. Trans. Time\", \"field\": \"transaction.duration.us\"}, \"schema\": \"metric\"}, {\"id\": \"3\", \"type\": \"percentiles\", \"enabled\": true, \"params\": {\"customLabel\": \"Trans. Time\", \"field\": \"transaction.duration.us\", \"percents\": [95]}, \"schema\": \"metric\"}, {\"id\": \"4\", \"type\": \"cardinality\", \"enabled\": true, \"params\": {\"customLabel\": \"Total Transactions\", \"field\": \"transaction.id\"}, \"schema\": \"metric\"}, {\"id\": \"6\", \"type\": \"cardinality\", \"enabled\": true, \"params\": {\"customLabel\": \"Errors\", \"field\": \"error.id\"}, \"schema\": \"metric\"}, {\"id\": \"5\", \"type\": \"top_hits\", \"enabled\": true, \"params\": {\"sortOrder\": \"desc\", \"customLabel\": \"-\", \"size\": 1, \"field\": \"view errors\", \"aggregate\": \"concat\", \"sortField\": \"@timestamp\"}, \"schema\": \"metric\"}, {\"id\": \"2\", \"type\": \"terms\", \"enabled\": true, \"params\": {\"field\": \"context.service.name\", \"orderBy\": \"1\", \"size\": 1000, \"order\": \"desc\"}, \"schema\": \"bucket\"}], \"params\": {\"sort\": {\"direction\": null, \"columnIndex\": null}, \"showPartialRows\": false, \"showTotal\": false, \"totalFunc\": \"sum\", \"perPage\": 10, \"showMeticsAtAllLevels\": false}}"
|
||||
"visState": "{\"type\": \"table\", \"title\": \"Services [APM]\", \"aggs\": [{\"id\": \"1\", \"type\": \"avg\", \"enabled\": true, \"params\": {\"customLabel\": \"Avg. Trans. Time (us)\", \"field\": \"transaction.duration.us\"}, \"schema\": \"metric\"}, {\"id\": \"3\", \"type\": \"percentiles\", \"enabled\": true, \"params\": {\"customLabel\": \"Trans. Time (us)\", \"field\": \"transaction.duration.us\", \"percents\": [95]}, \"schema\": \"metric\"}, {\"id\": \"4\", \"type\": \"cardinality\", \"enabled\": true, \"params\": {\"customLabel\": \"Total Transactions\", \"field\": \"transaction.id\"}, \"schema\": \"metric\"}, {\"id\": \"6\", \"type\": \"cardinality\", \"enabled\": true, \"params\": {\"customLabel\": \"Errors\", \"field\": \"error.id\"}, \"schema\": \"metric\"}, {\"id\": \"5\", \"type\": \"top_hits\", \"enabled\": true, \"params\": {\"sortOrder\": \"desc\", \"customLabel\": \"-\", \"size\": 1, \"field\": \"view errors\", \"aggregate\": \"concat\", \"sortField\": \"@timestamp\"}, \"schema\": \"metric\"}, {\"id\": \"2\", \"type\": \"terms\", \"enabled\": true, \"params\": {\"field\": \"context.service.name\", \"orderBy\": \"1\", \"size\": 1000, \"order\": \"desc\"}, \"schema\": \"bucket\"}], \"params\": {\"sort\": {\"direction\": null, \"columnIndex\": null}, \"showPartialRows\": false, \"showTotal\": false, \"totalFunc\": \"sum\", \"perPage\": 10, \"showMeticsAtAllLevels\": false}}"
|
||||
},
|
||||
"id": "1ffc5e20-7827-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
|
@ -162,7 +162,7 @@
|
|||
"title": "Top Transactions for Time Period [APM]",
|
||||
"uiStateJSON": "{\"vis\": {\"params\": {\"sort\": {\"direction\": null, \"columnIndex\": null}}}}",
|
||||
"version": 1,
|
||||
"visState": "{\"type\": \"table\", \"title\": \"Top Transactions for Time Period [APM]\", \"aggs\": [{\"id\": \"2\", \"type\": \"terms\", \"enabled\": true, \"schema\": \"bucket\", \"params\": {\"customLabel\": \"Transaction\", \"field\": \"transaction.name.keyword\", \"orderBy\": \"1\", \"size\": 1000, \"order\": \"desc\"}}, {\"id\": \"5\", \"type\": \"top_hits\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"Type\", \"sortOrder\": \"desc\", \"size\": 1, \"field\": \"transaction.type\", \"aggregate\": \"concat\", \"sortField\": \"@timestamp\"}}, {\"id\": \"1\", \"type\": \"avg\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"Avg. Resp Time (ms)\", \"field\": \"transaction.duration.us\"}}, {\"id\": \"3\", \"type\": \"percentiles\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"Resp Time (ms)\", \"field\": \"transaction.duration.us\", \"percents\": [95]}}, {\"id\": \"4\", \"type\": \"top_hits\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"View Spans\", \"sortOrder\": \"desc\", \"size\": 1, \"field\": \"transaction.id\", \"aggregate\": \"concat\", \"sortField\": \"@timestamp\"}}], \"params\": {\"sort\": {\"direction\": null, \"columnIndex\": null}, \"showPartialRows\": false, \"showTotal\": false, \"totalFunc\": \"sum\", \"perPage\": 25, \"showMeticsAtAllLevels\": false}}"
|
||||
"visState": "{\"type\": \"table\", \"title\": \"Top Transactions for Time Period [APM]\", \"aggs\": [{\"id\": \"2\", \"type\": \"terms\", \"enabled\": true, \"schema\": \"bucket\", \"params\": {\"customLabel\": \"Transaction\", \"field\": \"transaction.name.keyword\", \"orderBy\": \"1\", \"size\": 1000, \"order\": \"desc\"}}, {\"id\": \"5\", \"type\": \"top_hits\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"Type\", \"sortOrder\": \"desc\", \"size\": 1, \"field\": \"transaction.type\", \"aggregate\": \"concat\", \"sortField\": \"@timestamp\"}}, {\"id\": \"1\", \"type\": \"avg\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"Avg. Trans. Time (us)\", \"field\": \"transaction.duration.us\"}}, {\"id\": \"3\", \"type\": \"percentiles\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"Trans. Time (us)\", \"field\": \"transaction.duration.us\", \"percents\": [95]}}, {\"id\": \"4\", \"type\": \"top_hits\", \"enabled\": true, \"schema\": \"metric\", \"params\": {\"customLabel\": \"View Spans\", \"sortOrder\": \"desc\", \"size\": 1, \"field\": \"transaction.id\", \"aggregate\": \"concat\", \"sortField\": \"@timestamp\"}}], \"params\": {\"sort\": {\"direction\": null, \"columnIndex\": null}, \"showPartialRows\": false, \"showTotal\": false, \"totalFunc\": \"sum\", \"perPage\": 25, \"showMeticsAtAllLevels\": false}}"
|
||||
},
|
||||
"id": "a2e199b0-7820-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
|
|
|
@ -42,7 +42,7 @@ const MetricsRequestHandlerProvider = function (Private, Notifier, config, $http
|
|||
if (panel && panel.id) {
|
||||
const params = {
|
||||
timerange: { timezone, ...parsedTimeRange },
|
||||
filters: [buildEsQuery(undefined, [query], filters, esQueryConfigs)],
|
||||
filters: [buildEsQuery(undefined, query, filters, esQueryConfigs)],
|
||||
panels: [panel],
|
||||
state: uiStateObj
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colorma
|
|||
import { mapToLayerWithId } from './util';
|
||||
import { RegionMapsVisualizationProvider } from './region_map_visualization';
|
||||
import { Status } from 'ui/vis/update_status';
|
||||
import { ORIGIN } from '../../../../core_plugins/ems_util/common/origin';
|
||||
import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin';
|
||||
|
||||
VisTypesRegistryProvider.register(function RegionMapProvider(Private, regionmapsConfig, config, i18n) {
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import { toastNotifications } from 'ui/notify';
|
|||
import regionMapVisParamsTemplate from './region_map_vis_params.html';
|
||||
import { mapToLayerWithId } from './util';
|
||||
import '../../tile_map/public/editors/wms_options';
|
||||
import { ORIGIN } from '../../../../core_plugins/ems_util/common/origin';
|
||||
import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin';
|
||||
|
||||
uiModules.get('kibana/region_map')
|
||||
.directive('regionMapVisParams', function (serviceSettings, regionmapsConfig) {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { ORIGIN } from '../../../../core_plugins/ems_util/common/origin';
|
||||
import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin';
|
||||
|
||||
export function mapToLayerWithId(prefix, layer) {
|
||||
const clonedLayer = _.cloneDeep(layer);
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import _ from 'lodash';
|
||||
import { modifyUrl } from '../../../core/public/utils';
|
||||
import { TMSService } from './tms_service';
|
||||
import { FileLayer } from './file_layer';
|
||||
import fetch from 'node-fetch';
|
||||
import { format as formatUrl, parse as parseUrl } from 'url';
|
||||
|
||||
const extendUrl = (url, props) => (
|
||||
modifyUrl(url, parsed => _.merge(parsed, props))
|
||||
modifyUrlLocal(url, parsed => _.merge(parsed, props))
|
||||
);
|
||||
|
||||
const markdownIt = new MarkdownIt({
|
||||
|
@ -32,6 +33,49 @@ const markdownIt = new MarkdownIt({
|
|||
linkify: true
|
||||
});
|
||||
|
||||
/**
|
||||
* plugins cannot have upstream dependencies on core/*-kibana.
|
||||
* Work-around by copy-pasting modifyUrl routine here.
|
||||
* @param url
|
||||
* @param block
|
||||
*/
|
||||
function modifyUrlLocal(url, block) {
|
||||
|
||||
const parsed = parseUrl(url, true);
|
||||
|
||||
// copy over the most specific version of each
|
||||
// property. By default, the parsed url includes
|
||||
// several conflicting properties (like path and
|
||||
// pathname + search, or search and query) and keeping
|
||||
// track of which property is actually used when they
|
||||
// are formatted is harder than necessary
|
||||
const meaningfulParts = {
|
||||
protocol: parsed.protocol,
|
||||
slashes: parsed.slashes,
|
||||
auth: parsed.auth,
|
||||
hostname: parsed.hostname,
|
||||
port: parsed.port,
|
||||
pathname: parsed.pathname,
|
||||
query: parsed.query || {},
|
||||
hash: parsed.hash,
|
||||
};
|
||||
|
||||
// the block modifies the meaningfulParts object, or returns a new one
|
||||
const modifiedParts = block(meaningfulParts) || meaningfulParts;
|
||||
|
||||
// format the modified/replaced meaningfulParts back into a url
|
||||
return formatUrl({
|
||||
protocol: modifiedParts.protocol,
|
||||
slashes: modifiedParts.slashes,
|
||||
auth: modifiedParts.auth,
|
||||
hostname: modifiedParts.hostname,
|
||||
port: modifiedParts.port,
|
||||
pathname: modifiedParts.pathname,
|
||||
query: modifiedParts.query,
|
||||
hash: modifiedParts.hash,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescape a url template that was escaped by encodeURI() so leaflet
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import * as emsClient from './common/ems_client';
|
||||
|
||||
export default function (kibana) {
|
||||
|
||||
|
@ -25,6 +26,11 @@ export default function (kibana) {
|
|||
uiExports: {
|
||||
visTypes: ['plugins/tile_map/tile_map_vis'],
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
},
|
||||
init(server) {
|
||||
server.expose({
|
||||
ems_client: emsClient
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ const TimelionRequestHandlerProvider = function (Private, Notifier, $http, confi
|
|||
sheet: [expression],
|
||||
extended: {
|
||||
es: {
|
||||
filter: buildEsQuery(undefined, [query], filters, esQueryConfigs)
|
||||
filter: buildEsQuery(undefined, query, filters, esQueryConfigs)
|
||||
}
|
||||
},
|
||||
time: _.extend(timeRange, {
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../average`);
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import _ from 'lodash';
|
||||
|
||||
describe(filename, function () {
|
||||
describe('average.js', function () {
|
||||
|
||||
describe('average', function () {
|
||||
it('fills holes in the data', function () {
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../carry`);
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import _ from 'lodash';
|
||||
|
||||
describe(filename, function () {
|
||||
describe('carry.js', function () {
|
||||
it('fills holes in the data', function () {
|
||||
const data = [
|
||||
[moment.utc('1980', 'YYYY').valueOf(), 10],
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../load_functions`);
|
||||
|
||||
const expect = require('chai').expect;
|
||||
|
||||
describe(filename, () => {
|
||||
describe('load_functions.js', () => {
|
||||
it('exports a function', () => {
|
||||
expect(fn).to.be.a('function');
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ import processFunctionDefinition from './process_function_definition';
|
|||
export default function (directory) {
|
||||
|
||||
function getTuple(directory, name) {
|
||||
return [name, require('../' + directory + '/' + name)];
|
||||
return [name, require('../' + directory + '/' + name)]; // eslint-disable-line import/no-dynamic-require
|
||||
}
|
||||
|
||||
// Get a list of all files and use the filename as the object key
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../abs`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
const seriesList = require('./fixtures/seriesList.js')();
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, function () {
|
||||
describe('abs.js', function () {
|
||||
it('should return the positive value of every value', function () {
|
||||
|
||||
return invoke(fn, [seriesList]).then(function (result) {
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../bars`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('bars.js', () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../color`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('color.js', () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../condition`);
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
import getSeriesList from './helpers/get_single_series_list';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe(filename, function () {
|
||||
describe('condition.js', function () {
|
||||
|
||||
let comparable;
|
||||
let seriesList;
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../cusum`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('cusum.js', () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../derivative`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('derivative.js', () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../divide`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('divide.js', () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../first`);
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const seriesList = require('./fixtures/seriesList.js')();
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, function () {
|
||||
describe('first.js', function () {
|
||||
it('should return exactly the data input', function () {
|
||||
return invoke(fn, [seriesList]).then(function (result) {
|
||||
expect(result.input[0]).to.eql(result.output);
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../fit`);
|
||||
import moment from 'moment';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
import getSeriesList from './helpers/get_single_series_list';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe(filename, function () {
|
||||
describe('fit.js', function () {
|
||||
|
||||
describe('should not filter out zeros', function () {
|
||||
it('all zeros', function () {
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const filename = require('path').basename(__filename);
|
||||
const fn = require(`../${filename}`);
|
||||
const fn = require(`../hide`);
|
||||
|
||||
import _ from 'lodash';
|
||||
const expect = require('chai').expect;
|
||||
import invoke from './helpers/invoke_series_fn.js';
|
||||
|
||||
describe(filename, () => {
|
||||
describe('hide.js', () => {
|
||||
|
||||
let seriesList;
|
||||
beforeEach(() => {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue