Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.x] [typescript] add typescript support for the server and browser (#19104) #19223

Merged
merged 6 commits into from
May 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion docs/development/plugin/development-plugin-resources.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,24 @@ The Kibana directory must be named `kibana`, and your plugin directory must be l
If you're developing a plugin that has a user interface, take a look at our https://elastic.github.io/eui[Elastic UI Framework].
It documents the CSS and React components we use to build Kibana's user interface.

You're welcome to use these components, but be aware that they are rapidly evolving and we might introduce breaking changes that will disrupt your plugin's UI.
You're welcome to use these components, but be aware that they are rapidly evolving and we might introduce breaking changes that will disrupt your plugin's UI.

[float]
==== TypeScript Support
Plugin code can be written in http://www.typescriptlang.org/[TypeScript] if desired. To enable TypeScript support create a `tsconfig.json` file at the root of your plugin that looks something like this:

["source","js"]
-----------
{
// extend Kibana's tsconfig, or use your own settings
"extends": "../../kibana/tsconfig.json",

// tell the TypeScript compiler where to find your source files
"include": [
"server/**/*",
"public/**/*"
]
}
-----------

TypeScript code is automatically converted into JavaScript during development, but not in the distributable version of Kibana. If you use the {repo}blob/{branch}/packages/kbn-plugin-helpers[@kbn/plugin-helpers] to build your plugin then your `.ts` and `.tsx` files will be permanently transpiled before your plugin is archived. If you have your own build process, make sure to run the TypeScript compiler on your source files and ship the compilation output so that your plugin will work with the distributable version of Kibana.
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@
"validate-npm-package-name": "2.2.2",
"vega-lib": "^3.3.1",
"vega-lite": "^2.4.0",
"vega-tooltip": "^0.9.14",
"vega-schema-url-parser": "1.0.0",
"vega-tooltip": "^0.9.14",
"vision": "4.1.0",
"webpack": "3.6.0",
"webpack-merge": "4.1.0",
Expand All @@ -225,6 +225,8 @@
"@kbn/eslint-plugin-license-header": "link:packages/kbn-eslint-plugin-license-header",
"@kbn/plugin-generator": "link:packages/kbn-plugin-generator",
"@kbn/test": "link:packages/kbn-test",
"@types/globby": "^6.1.0",
"@types/minimatch": "^2.0.29",
"angular-mocks": "1.4.7",
"babel-eslint": "8.1.2",
"babel-jest": "^22.4.3",
Expand Down Expand Up @@ -298,8 +300,10 @@
"supertest": "3.0.0",
"supertest-as-promised": "4.0.2",
"tree-kill": "^1.1.0",
"ts-jest": "^22.4.3",
"typescript": "^2.8.1",
"ts-jest": "^22.4.6",
"ts-loader": "^3.5.0",
"ts-node": "^6.0.3",
"typescript": "^2.8.3",
"vinyl-fs": "^3.0.2",
"xml2js": "^0.4.19",
"xmlbuilder": "9.0.4"
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-dev-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@kbn/dev-utils",
"main": "./target/index.js",
"types": "./index.d.ts",
"version": "1.0.0",
"license": "Apache-2.0",
"private": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ function tryNodeResolver(importRequest, file, config) {
return nodeResolver.resolve(
importRequest,
file,
// we use Object.assign so that this file is compatible with slightly older
// versions of node.js used by IDEs (eg. resolvers are run in the Electron
// process in Atom)
Object.assign({}, config, {
extensions: ['.js', '.json'],
extensions: ['.js', '.json', '.ts', '.tsx'],
isFile,
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ exports.getWebpackConfig = function(kibanaPath, projectRoot, config) {
return {
context: kibanaPath,
resolve: {
extensions: ['.js', '.json'],
extensions: ['.js', '.json', '.ts', '.tsx'],
mainFields: ['browser', 'main'],
modules: [
'webpackShims',
Expand Down
17 changes: 17 additions & 0 deletions packages/kbn-plugin-helpers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,20 @@ Setting | Description
`skipInstallDependencies` | Don't install dependencies defined in package.json into build output
`buildVersion` | Version for the build output
`kibanaVersion` | Kibana version for the build output (added to package.json)

## TypeScript support

Plugin code can be written in [TypeScript](http://www.typescriptlang.org/) if desired. To enable TypeScript support create a `tsconfig.json` file at the root of your plugin that looks something like this:

```js
{
// extend Kibana's tsconfig, or use your own settings
"extends": "../../kibana/tsconfig.json",

// tell the TypeScript compiler where to find your source files
"include": [
"server/**/*",
"public/**/*"
]
}
```
1 change: 1 addition & 0 deletions packages/kbn-plugin-helpers/lib/plugin_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = function (root) {

const buildSourcePatterns = [
'yarn.lock',
'tsconfig.json',
'package.json',
'index.js',
'{lib,public,server,webpackShims,translations}/**/*',
Expand Down
65 changes: 63 additions & 2 deletions packages/kbn-plugin-helpers/tasks/build/create_build.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const join = require('path').join;
const relative = require('path').relative;
const unlinkSync = require('fs').unlinkSync;
const { readFileSync, writeFileSync, unlinkSync, existsSync } = require('fs');
const execFileSync = require('child_process').execFileSync;
const del = require('del');
const vfs = require('vinyl-fs');
Expand Down Expand Up @@ -29,7 +29,29 @@ function removeSymlinkDependencies(root) {
});
}

module.exports = function createBuild(plugin, buildTarget, buildVersion, kibanaVersion, files) {
// parse a ts config file
function parseTsconfig(pluginSourcePath, configPath) {
const ts = require(join(pluginSourcePath, 'node_modules', 'typescript'));

const { error, config } = ts.parseConfigFileTextToJson(
configPath,
readFileSync(configPath, 'utf8')
);

if (error) {
throw error;
}

return config;
}

module.exports = function createBuild(
plugin,
buildTarget,
buildVersion,
kibanaVersion,
files
) {
const buildSource = plugin.root;
const buildRoot = join(buildTarget, 'kibana', plugin.id);

Expand Down Expand Up @@ -65,6 +87,45 @@ module.exports = function createBuild(plugin, buildTarget, buildVersion, kibanaV

execFileSync(winCmd('yarn'), ['install', '--production', '--pure-lockfile'], options);
})
.then(function () {
const buildConfigPath = join(buildRoot, 'tsconfig.json');

if (!existsSync(buildConfigPath)) {
return;
}

if (!plugin.pkg.devDependencies.typescript) {
throw new Error(
'Found tsconfig.json file in plugin but typescript is not a devDependency.'
);
}

// attempt to patch the extends path in the tsconfig file
const buildConfig = parseTsconfig(buildSource, buildConfigPath);

if (buildConfig.extends) {
buildConfig.extends = join(
relative(buildRoot, buildSource),
buildConfig.extends
);

writeFileSync(buildConfigPath, JSON.stringify(buildConfig));
}

execFileSync(
join(buildSource, 'node_modules', '.bin', 'tsc'),
['--pretty', 'true'],
{
cwd: buildRoot,
stdio: ['ignore', 'pipe', 'pipe'],
}
);

del.sync([
join(buildRoot, '**', '*.{ts,tsx,d.ts}'),
join(buildRoot, 'tsconfig.json'),
]);
})
.then(function () {
const buildFiles = [relative(buildTarget, buildRoot) + '/**/*'];

Expand Down
6 changes: 6 additions & 0 deletions packages/kbn-pm/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ module.exports = {
},
{
loader: 'ts-loader',
options: {
compilerOptions: {
// enable esnext modules so webpack can do its thing better
module: 'esnext',
},
},
},
],
exclude: /node_modules/,
Expand Down
2 changes: 0 additions & 2 deletions packages/kbn-system-loader/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"declaration": true,
"outDir": "./target"
},
Expand Down
10 changes: 10 additions & 0 deletions src/babel-register/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
// unless we are running a prebuilt/distributable version of
// kibana, automatically transpile typescript to js before babel
if (!global.__BUILT_WITH_BABEL__) {
const { resolve } = require('path');
require('ts-node').register({
transpileOnly: true,
cacheDirectory: resolve(__dirname, '../../optimize/.cache/ts-node')
});
}

// register and polyfill need to happen in this
// order and in separate files. Checkout each file
// for a much more detailed explaination
Expand Down
8 changes: 6 additions & 2 deletions src/dev/build/build_distributables.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CleanExtraBinScriptsTask,
CleanExtraFilesFromModulesTask,
CleanPackagesTask,
CleanTypescriptTask,
CleanTask,
CopySourceTask,
CreateArchivesSourcesTask,
Expand All @@ -21,7 +22,8 @@ import {
InstallDependenciesTask,
OptimizeBuildTask,
RemovePackageJsonDepsTask,
TranspileSourceTask,
TranspileBabelTask,
TranspileTypescriptTask,
UpdateLicenseFileTask,
VerifyEnvTask,
VerifyExistingNodeBuildsTask,
Expand Down Expand Up @@ -76,10 +78,12 @@ export async function buildDistributables(options) {
await run(CopySourceTask);
await run(CreateEmptyDirsAndFilesTask);
await run(CreateReadmeTask);
await run(TranspileSourceTask);
await run(TranspileBabelTask);
await run(TranspileTypescriptTask);
await run(BuildPackagesTask);
await run(CreatePackageJsonTask);
await run(InstallDependenciesTask);
await run(CleanTypescriptTask);
await run(CleanPackagesTask);
await run(CreateNoticeFileTask);
await run(UpdateLicenseFileTask);
Expand Down
24 changes: 24 additions & 0 deletions src/dev/build/lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import fs from 'fs';
import { createHash } from 'crypto';
import { resolve, dirname, isAbsolute } from 'path';
import { createGunzip } from 'zlib';
import { inspect } from 'util';

import vfs from 'vinyl-fs';
import { promisify } from 'bluebird';
import mkdirpCb from 'mkdirp';
import del from 'del';
import { createPromiseFromStreams, createMapStream } from '../../../utils';

import { Extract } from 'tar';
Expand All @@ -26,6 +28,12 @@ function assertAbsolute(path) {
}
}

function longInspect(value) {
return inspect(value, {
maxArrayLength: Infinity
});
}

export async function mkdirp(path) {
assertAbsolute(path);
await mkdirpAsync(path);
Expand Down Expand Up @@ -67,6 +75,22 @@ export async function copy(source, destination) {
await chmodAsync(destination, stat.mode);
}

export async function deleteAll(log, patterns) {
if (!Array.isArray(patterns)) {
throw new TypeError('Expected patterns to be an array');
}

log.debug('Deleting patterns:', longInspect(patterns));

for (const pattern of patterns) {
assertAbsolute(pattern.startsWith('!') ? pattern.slice(1) : pattern);
}

const files = await del(patterns);
log.debug('Deleted %d files/directories', files.length);
log.verbose('Deleted:', longInspect(files));
}

export async function copyAll(sourceDir, destination, options = {}) {
const {
select = ['**/*'],
Expand Down
1 change: 1 addition & 0 deletions src/dev/build/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export {
copyAll,
getFileHash,
untar,
deleteAll,
} from './fs';
38 changes: 29 additions & 9 deletions src/dev/build/tasks/clean_tasks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import del from 'del';
import { deleteAll } from '../lib';

export const CleanTask = {
global: true,
description: 'Cleaning artifacts from previous builds',

async run(config) {
await del([
async run(config, log) {
await deleteAll(log, [
config.resolveFromRepo('build'),
config.resolveFromRepo('target'),
]);
Expand All @@ -16,15 +16,29 @@ export const CleanPackagesTask = {
description: 'Cleaning source for packages that are now installed in node_modules',

async run(config, log, build) {
await del([build.resolvePath('packages'), build.resolvePath('x-pack')]);
await deleteAll(log, [
build.resolvePath('packages'),
build.resolvePath('x-pack')
]);
},
};

export const CleanTypescriptTask = {
description: 'Cleaning typescript source files that have been transpiled to JS',

async run(config, log, build) {
await deleteAll(log, [
build.resolvePath('**/*.{ts,tsx,d.ts}'),
build.resolvePath('**/tsconfig.json'),
]);
},
};

export const CleanExtraFilesFromModulesTask = {
description: 'Cleaning tests, examples, docs, etc. from node_modules',

async run(config, log, build) {
await del([
await deleteAll(log, [
build.resolvePath('node_modules/**/test/**/*'),
build.resolvePath('node_modules/**/tests/**/*'),
build.resolvePath('node_modules/**/example/**/*'),
Expand All @@ -38,10 +52,16 @@ export const CleanExtraBinScriptsTask = {

async run(config, log, build) {
for (const platform of config.getPlatforms()) {
const patterns = platform.isWindows() ? ['*', '!*.bat'] : ['*.bat'];
await del(patterns, {
cwd: build.resolvePathForPlatform(platform, 'bin')
});
if (platform.isWindows()) {
await deleteAll(log, [
build.resolvePathForPlatform(platform, 'bin', '*'),
`!${build.resolvePathForPlatform(platform, 'bin', '*.bat')}`
]);
} else {
await deleteAll(log, [
build.resolvePathForPlatform(platform, 'bin', '*.bat'),
]);
}
}
}
};
Loading