mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Update plugin generator to generate NP plugins (#55281)
* Generate NP plugin * Added tsconfig * tsconfig * Adjust sao test * Add server side to plugin gen * Added navigation * add empty element * eslint * platform team CR * design CR improvements * text updates * temp disable plugin gen tests * eslint * Code review fixes * Add scss support - requires #53976 to be merged to work * CR fixes * comment fixes * Don't generate eslint for internal plugins by default * Update tests * reenable jest test for sao * Fix regex * review comments * code review Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
4f4d3d753c
commit
479223b0a1
33 changed files with 412 additions and 615 deletions
|
@ -29,6 +29,7 @@ exports.run = function run(argv) {
|
|||
const options = getopts(argv, {
|
||||
alias: {
|
||||
h: 'help',
|
||||
i: 'internal',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -40,17 +41,22 @@ exports.run = function run(argv) {
|
|||
if (options.help) {
|
||||
console.log(
|
||||
dedent(chalk`
|
||||
{dim usage:} node scripts/generate-plugin {bold [name]}
|
||||
|
||||
generate a fresh Kibana plugin in the plugins/ directory
|
||||
# {dim Usage:}
|
||||
node scripts/generate-plugin {bold [name]}
|
||||
Generate a fresh Kibana plugin in the plugins/ directory
|
||||
|
||||
# {dim Core Kibana plugins:}
|
||||
node scripts/generate-plugin {bold [name]} -i
|
||||
To generate a core Kibana plugin inside the src/plugins/ directory, add the -i flag.
|
||||
`) + '\n'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const name = options._[0];
|
||||
const isKibanaPlugin = options.internal;
|
||||
const template = resolve(__dirname, './sao_template');
|
||||
const kibanaPlugins = resolve(__dirname, '../../plugins');
|
||||
const kibanaPlugins = resolve(__dirname, isKibanaPlugin ? '../../src/plugins' : '../../plugins');
|
||||
const targetPath = resolve(kibanaPlugins, snakeCase(name));
|
||||
|
||||
sao({
|
||||
|
@ -58,6 +64,8 @@ exports.run = function run(argv) {
|
|||
targetPath: targetPath,
|
||||
configOptions: {
|
||||
name,
|
||||
isKibanaPlugin,
|
||||
targetPath,
|
||||
},
|
||||
}).catch(error => {
|
||||
console.error(chalk`{red fatal error}!`);
|
||||
|
|
24
packages/kbn-plugin-generator/index.js.d.ts
vendored
Normal file
24
packages/kbn-plugin-generator/index.js.d.ts
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
interface PluginGenerator {
|
||||
/**
|
||||
* Run plugin generator.
|
||||
*/
|
||||
run: (...args: any[]) => any;
|
||||
}
|
|
@ -61,7 +61,8 @@ describe(`running the plugin-generator via 'node scripts/generate_plugin.js plug
|
|||
expect(stats.isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
it(`should create an internationalization config file with a blank line appended to satisfy the parser`, async () => {
|
||||
// skipped until internationalization is re-introduced
|
||||
it.skip(`should create an internationalization config file with a blank line appended to satisfy the parser`, async () => {
|
||||
// Link to the error that happens when the blank line is not there:
|
||||
// https://github.com/elastic/kibana/pull/45044#issuecomment-530092627
|
||||
const intlFile = `${generatedPath}/.i18nrc.json`;
|
||||
|
@ -78,16 +79,7 @@ describe(`running the plugin-generator via 'node scripts/generate_plugin.js plug
|
|||
});
|
||||
});
|
||||
|
||||
it(`'yarn test:server' should exit 0`, async () => {
|
||||
await execa('yarn', ['test:server'], {
|
||||
cwd: generatedPath,
|
||||
env: {
|
||||
DISABLE_JUNIT_REPORTER: '1',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`'yarn build' should exit 0`, async () => {
|
||||
it.skip(`'yarn build' should exit 0`, async () => {
|
||||
await execa('yarn', ['build'], { cwd: generatedPath });
|
||||
});
|
||||
|
||||
|
@ -109,7 +101,7 @@ describe(`running the plugin-generator via 'node scripts/generate_plugin.js plug
|
|||
'--migrations.skip=true',
|
||||
],
|
||||
cwd: generatedPath,
|
||||
wait: /ispec_plugin.+Status changed from uninitialized to green - Ready/,
|
||||
wait: new RegExp('\\[ispecPlugin\\]\\[plugins\\] Setting up plugin'),
|
||||
});
|
||||
await proc.stop('kibana');
|
||||
});
|
||||
|
@ -120,7 +112,7 @@ describe(`running the plugin-generator via 'node scripts/generate_plugin.js plug
|
|||
await execa('yarn', ['preinstall'], { cwd: generatedPath });
|
||||
});
|
||||
|
||||
it(`'yarn lint' should exit 0`, async () => {
|
||||
it.skip(`'yarn lint' should exit 0`, async () => {
|
||||
await execa('yarn', ['lint'], { cwd: generatedPath });
|
||||
});
|
||||
|
||||
|
|
|
@ -17,21 +17,19 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
const { resolve, relative, dirname } = require('path');
|
||||
const { relative } = require('path');
|
||||
|
||||
const startCase = require('lodash.startcase');
|
||||
const camelCase = require('lodash.camelcase');
|
||||
const snakeCase = require('lodash.snakecase');
|
||||
const execa = require('execa');
|
||||
const chalk = require('chalk');
|
||||
const execa = require('execa');
|
||||
|
||||
const pkg = require('../package.json');
|
||||
const kibanaPkgPath = require.resolve('../../../package.json');
|
||||
const kibanaPkg = require(kibanaPkgPath); // eslint-disable-line import/no-dynamic-require
|
||||
|
||||
const KBN_DIR = dirname(kibanaPkgPath);
|
||||
|
||||
module.exports = function({ name }) {
|
||||
module.exports = function({ name, targetPath, isKibanaPlugin }) {
|
||||
return {
|
||||
prompts: {
|
||||
description: {
|
||||
|
@ -47,41 +45,38 @@ module.exports = function({ name }) {
|
|||
message: 'Should an app component be generated?',
|
||||
default: true,
|
||||
},
|
||||
generateTranslations: {
|
||||
type: 'confirm',
|
||||
message: 'Should translation files be generated?',
|
||||
default: true,
|
||||
},
|
||||
generateHack: {
|
||||
type: 'confirm',
|
||||
message: 'Should a hack component be generated?',
|
||||
default: true,
|
||||
},
|
||||
generateApi: {
|
||||
type: 'confirm',
|
||||
message: 'Should a server API be generated?',
|
||||
default: true,
|
||||
},
|
||||
// generateTranslations: {
|
||||
// type: 'confirm',
|
||||
// message: 'Should translation files be generated?',
|
||||
// default: true,
|
||||
// },
|
||||
generateScss: {
|
||||
type: 'confirm',
|
||||
message: 'Should SCSS be used?',
|
||||
when: answers => answers.generateApp,
|
||||
default: true,
|
||||
},
|
||||
generateEslint: {
|
||||
type: 'confirm',
|
||||
message: 'Would you like to use a custom eslint file?',
|
||||
default: !isKibanaPlugin,
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
'public/**/index.scss': 'generateScss',
|
||||
'public/**/*': 'generateApp',
|
||||
'translations/**/*': 'generateTranslations',
|
||||
'.i18nrc.json': 'generateTranslations',
|
||||
'public/hack.js': 'generateHack',
|
||||
'server/**/*': 'generateApi',
|
||||
'public/app.scss': 'generateScss',
|
||||
'.kibana-plugin-helpers.json': 'generateScss',
|
||||
// 'translations/**/*': 'generateTranslations',
|
||||
// '.i18nrc.json': 'generateTranslations',
|
||||
'eslintrc.js': 'generateEslint',
|
||||
},
|
||||
move: {
|
||||
gitignore: '.gitignore',
|
||||
'eslintrc.js': '.eslintrc.js',
|
||||
'package_template.json': 'package.json',
|
||||
},
|
||||
data: answers =>
|
||||
Object.assign(
|
||||
|
@ -91,34 +86,36 @@ module.exports = function({ name }) {
|
|||
camelCase,
|
||||
snakeCase,
|
||||
name,
|
||||
isKibanaPlugin,
|
||||
kbnVersion: answers.kbnVersion,
|
||||
upperCamelCaseName: name.charAt(0).toUpperCase() + camelCase(name).slice(1),
|
||||
hasUi: !!answers.generateApp,
|
||||
hasServer: !!answers.generateApi,
|
||||
hasScss: !!answers.generateScss,
|
||||
relRoot: isKibanaPlugin ? '../../../..' : '../../..',
|
||||
},
|
||||
answers
|
||||
),
|
||||
enforceNewFolder: true,
|
||||
installDependencies: false,
|
||||
gitInit: true,
|
||||
gitInit: !isKibanaPlugin,
|
||||
async post({ log }) {
|
||||
await execa('yarn', ['kbn', 'bootstrap'], {
|
||||
cwd: KBN_DIR,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
const dir = relative(process.cwd(), resolve(KBN_DIR, 'plugins', snakeCase(name)));
|
||||
const dir = relative(process.cwd(), targetPath);
|
||||
|
||||
// Apply eslint to the generated plugin
|
||||
try {
|
||||
await execa('yarn', ['lint', '--fix'], {
|
||||
cwd: dir,
|
||||
all: true,
|
||||
});
|
||||
await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']);
|
||||
} catch (error) {
|
||||
throw new Error(`Failure when running prettier on the generated output: ${error.all}`);
|
||||
console.error(error);
|
||||
throw new Error(
|
||||
`Failure when running prettier on the generated output: ${error.all || error}`
|
||||
);
|
||||
}
|
||||
|
||||
log.success(chalk`🎉
|
||||
|
||||
Your plugin has been created in {bold ${dir}}. Move into that directory to run it:
|
||||
Your plugin has been created in {bold ${dir}}.
|
||||
|
||||
{bold cd "${dir}"}
|
||||
{bold yarn start}
|
||||
`);
|
||||
},
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
const sao = require('sao');
|
||||
|
||||
const templatePkg = require('../package.json');
|
||||
|
||||
const template = {
|
||||
fromPath: __dirname,
|
||||
configOptions: {
|
||||
|
@ -32,121 +30,57 @@ function getFileContents(file) {
|
|||
return file.contents.toString();
|
||||
}
|
||||
|
||||
function getConfig(file) {
|
||||
const contents = getFileContents(file).replace(/\r?\n/gm, '');
|
||||
return contents.split('kibana.Plugin(')[1];
|
||||
}
|
||||
|
||||
describe('plugin generator sao integration', () => {
|
||||
test('skips files when answering no', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: false,
|
||||
generateHack: false,
|
||||
generateApi: false,
|
||||
});
|
||||
|
||||
expect(res.fileList).not.toContain('public/app.js');
|
||||
expect(res.fileList).not.toContain('public/__tests__/index.js');
|
||||
expect(res.fileList).not.toContain('public/hack.js');
|
||||
expect(res.fileList).not.toContain('server/routes/example.js');
|
||||
expect(res.fileList).not.toContain('server/__tests__/index.js');
|
||||
|
||||
const uiExports = getConfig(res.files['index.js']);
|
||||
expect(uiExports).not.toContain('app:');
|
||||
expect(uiExports).not.toContain('hacks:');
|
||||
expect(uiExports).not.toContain('init(server, options)');
|
||||
expect(uiExports).not.toContain('registerFeature(');
|
||||
expect(res.fileList).toContain('common/index.ts');
|
||||
expect(res.fileList).not.toContain('public/index.ts');
|
||||
expect(res.fileList).not.toContain('server/index.ts');
|
||||
});
|
||||
|
||||
it('includes app when answering yes', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: true,
|
||||
generateHack: false,
|
||||
generateApi: false,
|
||||
});
|
||||
|
||||
// check output files
|
||||
expect(res.fileList).toContain('public/app.js');
|
||||
expect(res.fileList).toContain('public/__tests__/index.js');
|
||||
expect(res.fileList).not.toContain('public/hack.js');
|
||||
expect(res.fileList).not.toContain('server/routes/example.js');
|
||||
expect(res.fileList).not.toContain('server/__tests__/index.js');
|
||||
|
||||
const uiExports = getConfig(res.files['index.js']);
|
||||
expect(uiExports).toContain('app:');
|
||||
expect(uiExports).toContain('init(server, options)');
|
||||
expect(uiExports).toContain('registerFeature(');
|
||||
expect(uiExports).not.toContain('hacks:');
|
||||
});
|
||||
|
||||
it('includes hack when answering yes', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: true,
|
||||
generateHack: true,
|
||||
generateApi: false,
|
||||
});
|
||||
|
||||
// check output files
|
||||
expect(res.fileList).toContain('public/app.js');
|
||||
expect(res.fileList).toContain('public/__tests__/index.js');
|
||||
expect(res.fileList).toContain('public/hack.js');
|
||||
expect(res.fileList).not.toContain('server/routes/example.js');
|
||||
expect(res.fileList).not.toContain('server/__tests__/index.js');
|
||||
|
||||
const uiExports = getConfig(res.files['index.js']);
|
||||
expect(uiExports).toContain('app:');
|
||||
expect(uiExports).toContain('hacks:');
|
||||
expect(uiExports).toContain('init(server, options)');
|
||||
expect(uiExports).toContain('registerFeature(');
|
||||
expect(res.fileList).toContain('common/index.ts');
|
||||
expect(res.fileList).toContain('public/index.ts');
|
||||
expect(res.fileList).toContain('public/plugin.ts');
|
||||
expect(res.fileList).toContain('public/types.ts');
|
||||
expect(res.fileList).toContain('public/components/app.tsx');
|
||||
expect(res.fileList).not.toContain('server/index.ts');
|
||||
});
|
||||
|
||||
it('includes server api when answering yes', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: true,
|
||||
generateHack: true,
|
||||
generateApi: true,
|
||||
});
|
||||
|
||||
// check output files
|
||||
expect(res.fileList).toContain('public/app.js');
|
||||
expect(res.fileList).toContain('public/__tests__/index.js');
|
||||
expect(res.fileList).toContain('public/hack.js');
|
||||
expect(res.fileList).toContain('server/routes/example.js');
|
||||
expect(res.fileList).toContain('server/__tests__/index.js');
|
||||
|
||||
const uiExports = getConfig(res.files['index.js']);
|
||||
expect(uiExports).toContain('app:');
|
||||
expect(uiExports).toContain('hacks:');
|
||||
expect(uiExports).toContain('init(server, options)');
|
||||
expect(uiExports).toContain('registerFeature(');
|
||||
expect(res.fileList).toContain('public/plugin.ts');
|
||||
expect(res.fileList).toContain('server/plugin.ts');
|
||||
expect(res.fileList).toContain('server/index.ts');
|
||||
expect(res.fileList).toContain('server/types.ts');
|
||||
expect(res.fileList).toContain('server/routes/index.ts');
|
||||
});
|
||||
|
||||
it('plugin config has correct name and main path', async () => {
|
||||
it('plugin package has correct title', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: true,
|
||||
generateHack: true,
|
||||
generateApi: true,
|
||||
});
|
||||
|
||||
const indexContents = getFileContents(res.files['index.js']);
|
||||
const nameLine = indexContents.match('name: (.*)')[1];
|
||||
const mainLine = indexContents.match('main: (.*)')[1];
|
||||
const contents = getFileContents(res.files['common/index.ts']);
|
||||
const controllerLine = contents.match("PLUGIN_NAME = '(.*)'")[1];
|
||||
|
||||
expect(nameLine).toContain('some_fancy_plugin');
|
||||
expect(mainLine).toContain('plugins/some_fancy_plugin/app');
|
||||
});
|
||||
|
||||
it('plugin package has correct name', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: true,
|
||||
generateHack: true,
|
||||
generateApi: true,
|
||||
});
|
||||
|
||||
const packageContents = getFileContents(res.files['package.json']);
|
||||
const pkg = JSON.parse(packageContents);
|
||||
|
||||
expect(pkg.name).toBe('some_fancy_plugin');
|
||||
expect(controllerLine).toContain('Some fancy plugin');
|
||||
});
|
||||
|
||||
it('package has version "kibana" with master', async () => {
|
||||
|
@ -154,10 +88,10 @@ describe('plugin generator sao integration', () => {
|
|||
kbnVersion: 'master',
|
||||
});
|
||||
|
||||
const packageContents = getFileContents(res.files['package.json']);
|
||||
const packageContents = getFileContents(res.files['kibana.json']);
|
||||
const pkg = JSON.parse(packageContents);
|
||||
|
||||
expect(pkg.kibana.version).toBe('kibana');
|
||||
expect(pkg.version).toBe('master');
|
||||
});
|
||||
|
||||
it('package has correct version', async () => {
|
||||
|
@ -165,39 +99,26 @@ describe('plugin generator sao integration', () => {
|
|||
kbnVersion: 'v6.0.0',
|
||||
});
|
||||
|
||||
const packageContents = getFileContents(res.files['package.json']);
|
||||
const packageContents = getFileContents(res.files['kibana.json']);
|
||||
const pkg = JSON.parse(packageContents);
|
||||
|
||||
expect(pkg.kibana.version).toBe('v6.0.0');
|
||||
});
|
||||
|
||||
it('package has correct templateVersion', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
kbnVersion: 'master',
|
||||
});
|
||||
|
||||
const packageContents = getFileContents(res.files['package.json']);
|
||||
const pkg = JSON.parse(packageContents);
|
||||
|
||||
expect(pkg.kibana.templateVersion).toBe(templatePkg.version);
|
||||
expect(pkg.version).toBe('v6.0.0');
|
||||
});
|
||||
|
||||
it('sample app has correct values', async () => {
|
||||
const res = await sao.mockPrompt(template, {
|
||||
generateApp: true,
|
||||
generateHack: true,
|
||||
generateApi: true,
|
||||
});
|
||||
|
||||
const contents = getFileContents(res.files['public/app.js']);
|
||||
const controllerLine = contents.match('setRootController(.*)')[1];
|
||||
const contents = getFileContents(res.files['common/index.ts']);
|
||||
const controllerLine = contents.match("PLUGIN_ID = '(.*)'")[1];
|
||||
|
||||
expect(controllerLine).toContain('someFancyPlugin');
|
||||
});
|
||||
|
||||
it('includes dotfiles', async () => {
|
||||
const res = await sao.mockPrompt(template);
|
||||
expect(res.files['.gitignore']).toBeTruthy();
|
||||
expect(res.files['.eslintrc.js']).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"paths": {
|
||||
"<%= camelCase(name) %>": "./"
|
||||
},
|
||||
"translations": [
|
||||
"translations/zh-CN.json"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"styleSheetToCompile": "public/app.scss"
|
||||
}
|
|
@ -6,34 +6,7 @@
|
|||
|
||||
---
|
||||
|
||||
## development
|
||||
## Development
|
||||
|
||||
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. Once you have completed that, use the following yarn scripts.
|
||||
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.
|
||||
|
||||
- `yarn kbn bootstrap`
|
||||
|
||||
Install dependencies and crosslink Kibana and all projects/plugins.
|
||||
|
||||
> ***IMPORTANT:*** Use this script instead of `yarn` to install dependencies when switching branches, and re-run it whenever your dependencies change.
|
||||
|
||||
- `yarn start`
|
||||
|
||||
Start kibana and have it include this plugin. You can pass any arguments that you would normally send to `bin/kibana`
|
||||
|
||||
```
|
||||
yarn start --elasticsearch.hosts http://localhost:9220
|
||||
```
|
||||
|
||||
- `yarn build`
|
||||
|
||||
Build a distributable archive of your plugin.
|
||||
|
||||
- `yarn test:browser`
|
||||
|
||||
Run the browser tests in a real web browser.
|
||||
|
||||
- `yarn test:mocha`
|
||||
|
||||
Run the server tests using mocha.
|
||||
|
||||
For more information about any of these commands run `yarn ${task} --help`. For a full list of tasks checkout the `package.json` file, or run `yarn run`.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export const PLUGIN_ID = '<%= camelCase(name) %>';
|
||||
export const PLUGIN_NAME = '<%= name %>';
|
31
packages/kbn-plugin-generator/sao_template/template/eslintrc.js
Executable file → Normal file
31
packages/kbn-plugin-generator/sao_template/template/eslintrc.js
Executable file → Normal file
|
@ -1,24 +1,9 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'],
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
'@kbn/eslint-import-resolver-kibana': {
|
||||
rootPackageName: '<%= snakeCase(name) %>',
|
||||
},
|
||||
},
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/public/**/*'],
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
'@kbn/eslint-import-resolver-kibana': {
|
||||
forceNode: false,
|
||||
rootPackageName: '<%= snakeCase(name) %>',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
<%_ if (!isKibanaPlugin) { -%>
|
||||
rules: {
|
||||
"@kbn/eslint/require-license-header": "off"
|
||||
}
|
||||
<%_ } -%>
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
npm-debug.log*
|
||||
node_modules
|
||||
/build/
|
||||
<%_ if (generateScss) { -%>
|
||||
/public/app.css
|
||||
<%_ } -%>
|
|
@ -1,89 +0,0 @@
|
|||
<% if (generateScss) { -%>
|
||||
import { resolve } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
<% } -%>
|
||||
|
||||
<% if (generateApp) { -%>
|
||||
import { i18n } from '@kbn/i18n';
|
||||
<% } -%>
|
||||
|
||||
<% if (generateApi) { -%>
|
||||
import exampleRoute from './server/routes/example';
|
||||
|
||||
<% } -%>
|
||||
export default function (kibana) {
|
||||
return new kibana.Plugin({
|
||||
require: ['elasticsearch'],
|
||||
name: '<%= snakeCase(name) %>',
|
||||
uiExports: {
|
||||
<%_ if (generateApp) { -%>
|
||||
app: {
|
||||
title: '<%= startCase(name) %>',
|
||||
description: '<%= description %>',
|
||||
main: 'plugins/<%= snakeCase(name) %>/app',
|
||||
},
|
||||
<%_ } -%>
|
||||
<%_ if (generateHack) { -%>
|
||||
hacks: [
|
||||
'plugins/<%= snakeCase(name) %>/hack'
|
||||
],
|
||||
<%_ } -%>
|
||||
<%_ if (generateScss) { -%>
|
||||
styleSheetPaths: [resolve(__dirname, 'public/app.scss'), resolve(__dirname, 'public/app.css')].find(p => existsSync(p)),
|
||||
<%_ } -%>
|
||||
},
|
||||
|
||||
config(Joi) {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
}).default();
|
||||
},
|
||||
<%_ if (generateApi || generateApp) { -%>
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
init(server, options) {
|
||||
<%_ if (generateApp) { -%>
|
||||
const xpackMainPlugin = server.plugins.xpack_main;
|
||||
if (xpackMainPlugin) {
|
||||
const featureId = '<%= snakeCase(name) %>';
|
||||
|
||||
xpackMainPlugin.registerFeature({
|
||||
id: featureId,
|
||||
name: i18n.translate('<%= camelCase(name) %>.featureRegistry.featureName', {
|
||||
defaultMessage: '<%= name %>',
|
||||
}),
|
||||
navLinkId: featureId,
|
||||
icon: 'questionInCircle',
|
||||
app: [featureId, 'kibana'],
|
||||
catalogue: [],
|
||||
privileges: {
|
||||
all: {
|
||||
api: [],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['show'],
|
||||
},
|
||||
read: {
|
||||
api: [],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['show'],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
<%_ } -%>
|
||||
|
||||
<%_ if (generateApi) { -%>
|
||||
// Add server routes and initialize the plugin here
|
||||
exampleRoute(server);
|
||||
<%_ } -%>
|
||||
}
|
||||
<%_ } -%>
|
||||
});
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"id": "<%= camelCase(name) %>",
|
||||
"version": "<%= kbnVersion %>",
|
||||
"server": <%= hasServer %>,
|
||||
"ui": <%= hasUi %>,
|
||||
"requiredPlugins": ["navigation"],
|
||||
"optionalPlugins": []
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"name": "<%= snakeCase(name) %>",
|
||||
"version": "0.0.0",
|
||||
"description": "<%= description %>",
|
||||
"main": "index.js",
|
||||
"kibana": {
|
||||
"version": "<%= (kbnVersion === 'master') ? 'kibana' : kbnVersion %>",
|
||||
"templateVersion": "<%= templateVersion %>"
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "node ../../preinstall_check",
|
||||
"kbn": "node ../../scripts/kbn",
|
||||
"es": "node ../../scripts/es",
|
||||
"lint": "eslint .",
|
||||
"start": "plugin-helpers start",
|
||||
"test:server": "plugin-helpers test:server",
|
||||
"test:browser": "plugin-helpers test:browser",
|
||||
"build": "plugin-helpers build"
|
||||
},
|
||||
<%_ if (generateTranslations) { _%>
|
||||
"dependencies": {
|
||||
"@kbn/i18n": "link:../../packages/kbn-i18n"
|
||||
},
|
||||
<%_ } _%>
|
||||
"devDependencies": {
|
||||
"@elastic/eslint-config-kibana": "link:../../packages/eslint-config-kibana",
|
||||
"@elastic/eslint-import-resolver-kibana": "link:../../packages/kbn-eslint-import-resolver-kibana",
|
||||
"@kbn/expect": "link:../../packages/kbn-expect",
|
||||
"@kbn/plugin-helpers": "link:../../packages/kbn-plugin-helpers",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-babel": "^5.3.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-jest": "^22.4.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.1",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint-plugin-no-unsanitized": "^3.0.2",
|
||||
"eslint-plugin-prefer-object-spread": "^1.2.1",
|
||||
"eslint-plugin-react": "^7.12.4"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import expect from '@kbn/expect';
|
||||
|
||||
describe('suite', () => {
|
||||
it('is a test', () => {
|
||||
expect(true).to.equal(true);
|
||||
});
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
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 { Main } from './components/main';
|
||||
|
||||
const app = uiModules.get('apps/<%= camelCase(name) %>');
|
||||
|
||||
app.config($locationProvider => {
|
||||
$locationProvider.html5Mode({
|
||||
enabled: false,
|
||||
requireBase: false,
|
||||
rewriteLinks: false,
|
||||
});
|
||||
});
|
||||
app.config(stateManagementConfigProvider =>
|
||||
stateManagementConfigProvider.disable()
|
||||
);
|
||||
|
||||
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', () => {
|
||||
unmountComponentAtNode(domNode);
|
||||
});
|
||||
}
|
||||
|
||||
chrome.setRootController('<%= camelCase(name) %>', RootController);
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppMountParameters, CoreStart } from '<%= relRoot %>/src/core/public';
|
||||
import { AppPluginStartDependencies } from './types';
|
||||
import { <%= upperCamelCaseName %>App } from './components/app';
|
||||
|
||||
|
||||
export const renderApp = (
|
||||
{ notifications, http }: CoreStart,
|
||||
{ navigation }: AppPluginStartDependencies,
|
||||
{ appBasePath, element }: AppMountParameters
|
||||
) => {
|
||||
ReactDOM.render(
|
||||
<<%= upperCamelCaseName %>App
|
||||
basename={appBasePath}
|
||||
notifications={notifications}
|
||||
http={http}
|
||||
navigation={navigation}
|
||||
/>,
|
||||
element
|
||||
);
|
||||
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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 React, { useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiHorizontalRule,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageContentHeader,
|
||||
EuiPageHeader,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { CoreStart } from '<%= relRoot %>/../src/core/public';
|
||||
import { NavigationPublicPluginStart } from '<%= relRoot %>/../src/plugins/navigation/public';
|
||||
|
||||
import { PLUGIN_ID, PLUGIN_NAME } from '../../common';
|
||||
|
||||
interface <%= upperCamelCaseName %>AppDeps {
|
||||
basename: string;
|
||||
notifications: CoreStart['notifications'];
|
||||
http: CoreStart['http'];
|
||||
navigation: NavigationPublicPluginStart;
|
||||
}
|
||||
|
||||
export const <%= upperCamelCaseName %>App = ({ basename, notifications, http, navigation }: <%= upperCamelCaseName %>AppDeps) => {
|
||||
// Use React hooks to manage state.
|
||||
const [timestamp, setTimestamp] = useState<string | undefined>();
|
||||
|
||||
const onClickHandler = () => {
|
||||
<%_ if (generateApi) { -%>
|
||||
// Use the core http service to make a response to the server API.
|
||||
http.get('/api/<%= snakeCase(name) %>/example').then(res => {
|
||||
setTimestamp(res.time);
|
||||
// Use the core notifications service to display a success message.
|
||||
notifications.toasts.addSuccess(i18n.translate('<%= camelCase(name) %>.dataUpdated', {
|
||||
defaultMessage: 'Data updated',
|
||||
}));
|
||||
});
|
||||
<%_ } else { -%>
|
||||
setTimestamp(new Date().toISOString());
|
||||
notifications.toasts.addSuccess(PLUGIN_NAME);
|
||||
<%_ } -%>
|
||||
};
|
||||
|
||||
// Render the application DOM.
|
||||
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
|
||||
return (
|
||||
<Router basename={basename}>
|
||||
<I18nProvider>
|
||||
<>
|
||||
<navigation.ui.TopNavMenu appName={ PLUGIN_ID } showSearchBar={true} />
|
||||
<EuiPage restrictWidth="1000px">
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiTitle size="l">
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.helloWorldText"
|
||||
defaultMessage="{name}"
|
||||
values={{ name: PLUGIN_NAME }}
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.congratulationsTitle"
|
||||
defaultMessage="Congratulations, you have successfully created a new Kibana Plugin!"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.content"
|
||||
defaultMessage="Look through the generated code and check out the plugin development documentation."
|
||||
/>
|
||||
</p>
|
||||
<EuiHorizontalRule/>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.timestampText"
|
||||
defaultMessage="Last timestamp: {time}"
|
||||
values={{ time: timestamp ? timestamp : 'Unknown' }}
|
||||
/>
|
||||
</p>
|
||||
<EuiButton type="primary" size="s" onClick={onClickHandler}>
|
||||
<FormattedMessage id="<%= camelCase(name) %>.buttonText" defaultMessage="<%_ if (generateApi) { -%>Get data<%_ } else { -%>Click me<%_ } -%>" />
|
||||
</EuiButton>
|
||||
</EuiText>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</>
|
||||
</I18nProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export { Main } from './main';
|
|
@ -1,97 +0,0 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageHeader,
|
||||
EuiTitle,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentHeader,
|
||||
EuiPageContentBody,
|
||||
EuiText
|
||||
} from '@elastic/eui';
|
||||
<%_ if (generateTranslations) { _%>
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
<%_ } _%>
|
||||
|
||||
export class Main extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
/*
|
||||
FOR EXAMPLE PURPOSES ONLY. There are much better ways to
|
||||
manage state and update your UI than this.
|
||||
*/
|
||||
const { httpClient } = this.props;
|
||||
httpClient.get('../api/<%= name %>/example').then((resp) => {
|
||||
this.setState({ time: resp.data.time });
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const { title } = this.props;
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiTitle size="l">
|
||||
<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>
|
||||
<%_ if (generateTranslations) { _%>
|
||||
<FormattedMessage
|
||||
id="<%= camelCase(name) %>.congratulationsTitle"
|
||||
defaultMessage="Congratulations"
|
||||
/>
|
||||
<%_ } else { _%>
|
||||
Congratulations
|
||||
<%_ } _%>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
<EuiText>
|
||||
<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>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
$(document.body).on('keypress', function (event) {
|
||||
if (event.which === 58) {
|
||||
alert('boo!');
|
||||
}
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
<%_ if (hasScss) { -%>
|
||||
import './index.scss';
|
||||
<%_ } -%>
|
||||
|
||||
import { <%= upperCamelCaseName %>Plugin } from './plugin';
|
||||
|
||||
// This exports static code and TypeScript types,
|
||||
// as well as, Kibana Platform `plugin()` initializer.
|
||||
export function plugin() {
|
||||
return new <%= upperCamelCaseName %>Plugin();
|
||||
}
|
||||
export {
|
||||
<%= upperCamelCaseName %>PluginSetup,
|
||||
<%= upperCamelCaseName %>PluginStart,
|
||||
} from './types';
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '<%= relRoot %>/src/core/public';
|
||||
import { <%= upperCamelCaseName %>PluginSetup, <%= upperCamelCaseName %>PluginStart, AppPluginStartDependencies } from './types';
|
||||
import { PLUGIN_NAME } from '../common';
|
||||
|
||||
export class <%= upperCamelCaseName %>Plugin
|
||||
implements Plugin<<%= upperCamelCaseName %>PluginSetup, <%= upperCamelCaseName %>PluginStart> {
|
||||
|
||||
public setup(core: CoreSetup): <%= upperCamelCaseName %>PluginSetup {
|
||||
// Register an application into the side navigation menu
|
||||
core.application.register({
|
||||
id: '<%= camelCase(name) %>',
|
||||
title: PLUGIN_NAME,
|
||||
async mount(params: AppMountParameters) {
|
||||
// Load application bundle
|
||||
const { renderApp } = await import('./application');
|
||||
// Get start services as specified in kibana.json
|
||||
const [coreStart, depsStart] = await core.getStartServices();
|
||||
// Render the application
|
||||
return renderApp(coreStart, depsStart as AppPluginStartDependencies, params);
|
||||
},
|
||||
});
|
||||
|
||||
// Return methods that should be available to other plugins
|
||||
return {
|
||||
getGreeting() {
|
||||
return i18n.translate('<%= camelCase(name) %>.greetingText', {
|
||||
defaultMessage: 'Hello from {name}!',
|
||||
values: {
|
||||
name: PLUGIN_NAME,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public start(core: CoreStart): <%= upperCamelCaseName %>PluginStart {
|
||||
return {};
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import { NavigationPublicPluginStart } from '<%= relRoot %>/src/plugins/navigation/public';
|
||||
|
||||
export interface <%= upperCamelCaseName %>PluginSetup {
|
||||
getGreeting: () => string;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface <%= upperCamelCaseName %>PluginStart {}
|
||||
|
||||
export interface AppPluginStartDependencies {
|
||||
navigation: NavigationPublicPluginStart
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
import expect from '@kbn/expect';
|
||||
|
||||
describe('suite', () => {
|
||||
it('is a test', () => {
|
||||
expect(true).to.equal(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { PluginInitializerContext } from '<%= relRoot %>/src/core/server';
|
||||
import { <%= upperCamelCaseName %>Plugin } from './plugin';
|
||||
|
||||
|
||||
// This exports static code and TypeScript types,
|
||||
// as well as, Kibana Platform `plugin()` initializer.
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new <%= upperCamelCaseName %>Plugin(initializerContext);
|
||||
}
|
||||
|
||||
export {
|
||||
<%= upperCamelCaseName %>PluginSetup,
|
||||
<%= upperCamelCaseName %>PluginStart,
|
||||
} from './types';
|
|
@ -0,0 +1,30 @@
|
|||
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '<%= relRoot %>/src/core/server';
|
||||
|
||||
import { <%= upperCamelCaseName %>PluginSetup, <%= upperCamelCaseName %>PluginStart } from './types';
|
||||
import { defineRoutes } from './routes';
|
||||
|
||||
export class <%= upperCamelCaseName %>Plugin
|
||||
implements Plugin<<%= upperCamelCaseName %>PluginSetup, <%= upperCamelCaseName %>PluginStart> {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
this.logger = initializerContext.logger.get();
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup) {
|
||||
this.logger.debug('<%= name %>: Setup');
|
||||
const router = core.http.createRouter();
|
||||
|
||||
// Register server side APIs
|
||||
defineRoutes(router);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
this.logger.debug('<%= name %>: Started');
|
||||
return {};
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
export default function (server) {
|
||||
|
||||
server.route({
|
||||
path: '/api/<%= name %>/example',
|
||||
method: 'GET',
|
||||
handler() {
|
||||
return { time: (new Date()).toISOString() };
|
||||
}
|
||||
});
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { IRouter } from '<%= relRoot %>/../src/core/server';
|
||||
|
||||
export function defineRoutes(router: IRouter) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/<%= snakeCase(name) %>/example',
|
||||
validate: false,
|
||||
},
|
||||
async (context, request, response) => {
|
||||
return response.ok({
|
||||
body: {
|
||||
time: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface <%= upperCamelCaseName %>PluginSetup {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface <%= upperCamelCaseName %>PluginStart {}
|
|
@ -1,84 +0,0 @@
|
|||
{
|
||||
"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}"
|
||||
}
|
||||
}
|
5
packages/kbn-plugin-generator/tsconfig.json
Normal file
5
packages/kbn-plugin-generator/tsconfig.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["**/*", "index.js.d.ts"],
|
||||
"exclude": ["sao_template/template/*"]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue