diff --git a/.gitignore b/.gitignore
index ab75a6c8..20cd4cfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,11 @@ spring-javaformat/spring-javaformat-formatter-eclipse-rewriter/bin
build.log
pid
.factorypath
+
+# npm
+node_modules/
+
+# vscode
+spring-javaformat-vscode/spring-javaformat/out/
+spring-javaformat-vscode/spring-javaformat/runtime/
+*.vsix
diff --git a/README.adoc b/README.adoc
index 5ffd15a1..bce16e52 100644
--- a/README.adoc
+++ b/README.adoc
@@ -263,6 +263,9 @@ Once the configuration file is created, configure your IDE to use it:
* Specify the appropriate `Checkstyle version`
* Add your Checkstyle configuration file
+=== Visual Studio Code
+The vscode extension provides custom formatter support for Visual Studio Code.
+The extension is automatically activated whenever a `.java` file is opened. And it requires a few seconds to warm-up while you start with the first workspace.
=== About the Conventions
diff --git a/spring-javaformat-vscode/README.md b/spring-javaformat-vscode/README.md
new file mode 100644
index 00000000..e6dea6c9
--- /dev/null
+++ b/spring-javaformat-vscode/README.md
@@ -0,0 +1,19 @@
+# spring-javaformat-vscode
+
+`spring-javaformat` extension for visual studio code.
+
+![](./format.gif)
+
+## Prerequisites
+
+* Install [node.js](https://nodejs.org/en/download/)
+* Install [yarn](https://yarnpkg.com/en/docs/install)
+* Install [vsce](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#vsce)
+
+## Generate extension
+
+Just `mvn clean package`
+
+
+> `spring-javaformat-1.0.0.vsix` will be generated there
+
diff --git a/spring-javaformat-vscode/format.gif b/spring-javaformat-vscode/format.gif
new file mode 100644
index 00000000..fd1fdacd
Binary files /dev/null and b/spring-javaformat-vscode/format.gif differ
diff --git a/spring-javaformat-vscode/pom.xml b/spring-javaformat-vscode/pom.xml
new file mode 100644
index 00000000..0ffbcbae
--- /dev/null
+++ b/spring-javaformat-vscode/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ io.spring.javaformat
+ spring-javaformat-build
+ 0.0.16-SNAPSHOT
+
+ spring-javaformat-vscode
+ pom
+ Spring JavaFormat Visual Studio Code
+
+ ${basedir}/..
+
+
+
+ spring-javaformat-format-service
+
+
+
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/.vscode/launch.json b/spring-javaformat-vscode/spring-javaformat-format-service/.vscode/launch.json
new file mode 100644
index 00000000..40ef0758
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/.vscode/launch.json
@@ -0,0 +1,11 @@
+{
+ "configurations": [
+ {
+ "type": "java",
+ "name": "CodeLens (Launch) - FormatterWebApplication",
+ "request": "launch",
+ "mainClass": "io.spring.format.FormatterWebApplication",
+ "projectName": "spring-javaformat-format-service"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/.vscode/settings.json b/spring-javaformat-vscode/spring-javaformat-format-service/.vscode/settings.json
new file mode 100644
index 00000000..0e0c2a50
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "editor.formatOnSave": true,
+ "java.format.enabled": false
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/pom.xml b/spring-javaformat-vscode/spring-javaformat-format-service/pom.xml
new file mode 100644
index 00000000..8b09b585
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/pom.xml
@@ -0,0 +1,129 @@
+
+
+ 4.0.0
+
+ io.spring.javaformat
+ spring-javaformat-vscode
+ 0.0.16-SNAPSHOT
+
+ spring-javaformat-format-service
+ jar
+ Spring JavaFormat format-service
+
+ ${basedir}/../..
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.1.8.RELEASE
+
+ ${main.basedir}/spring-javaformat-vscode/spring-javaformat/runtime/
+
+
+
+
+ repackage
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ ${main.basedir}/spring-javaformat-vscode/spring-javaformat
+
+
+
+ exec-yarn-install
+ compile
+
+ yarn
+
+
+ exec
+
+
+
+ exec-package
+ package
+
+ vsce
+
+ package
+
+
+
+ exec
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ checkstyle-validation
+ validate
+
+ **/*/FormatterWebApplication.java
+
+
+
+
+
+
+ io.spring.javaformat
+ spring-javaformat-maven-plugin
+ 0.0.16-SNAPSHOT
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 2.1.6.RELEASE
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ 2.1.6.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.1.6.RELEASE
+
+
+ io.spring.javaformat
+ spring-javaformat-formatter
+ ${project.version}
+
+
+
+ io.spring.javaformat
+ spring-javaformat-formatter-eclipse-runtime
+ ${project.version}
+ provided
+
+
+
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/FormatterWebApplication.java b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/FormatterWebApplication.java
new file mode 100644
index 00000000..58f9980f
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/FormatterWebApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-2019 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package io.spring.format;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@EnableScheduling
+@SpringBootApplication
+public class FormatterWebApplication {
+
+ public FormatterWebApplication() {
+
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(FormatterWebApplication.class, args);
+ }
+
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/controllers/FormatController.java b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/controllers/FormatController.java
new file mode 100644
index 00000000..2ea27463
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/controllers/FormatController.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-2019 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package io.spring.format.controllers;
+
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.spring.format.request.FormatRequest;
+import io.spring.format.tools.FormatContent;
+
+/**
+ * .
+ *
+ * @author Howard Zuo
+ */
+@RestController
+public class FormatController {
+
+ @RequestMapping(method = RequestMethod.POST, value = "/format/code")
+ String formatSource(@RequestBody FormatRequest req) {
+
+ return FormatContent.formatContent(req.getSource());
+
+ }
+
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/controllers/HealthController.java b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/controllers/HealthController.java
new file mode 100644
index 00000000..43ccceda
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/controllers/HealthController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-2019 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package io.spring.format.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.spring.format.services.HealthService;
+
+/**
+ * .
+ *
+ * @author Howard Zuo
+ */
+@RestController
+public class HealthController {
+
+ @Autowired
+ private HealthService service;
+
+ @RequestMapping(method = RequestMethod.GET, value = "/health")
+ String heartbeat() {
+ this.service.setLastHeartbeat(System.currentTimeMillis());
+ return "";
+ }
+
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/request/FormatRequest.java b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/request/FormatRequest.java
new file mode 100644
index 00000000..d74555f9
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/request/FormatRequest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017-2019 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package io.spring.format.request;
+
+/**
+ * .
+ *
+ * @author Howard Zuo
+ */
+public class FormatRequest {
+
+ private String source = "";
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public String getSource() {
+ return this.source;
+ }
+
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/services/HealthService.java b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/services/HealthService.java
new file mode 100644
index 00000000..574652a8
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/services/HealthService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2019 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package io.spring.format.services;
+
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+/**
+ * .
+ *
+ * @author Howard Zuo
+ */
+@Service
+public class HealthService {
+
+ private Long lastHeartbeat;
+
+ public Long getLastHeartbeat() {
+ return this.lastHeartbeat;
+ }
+
+ public void setLastHeartbeat(Long lastHeartbeat) {
+ this.lastHeartbeat = lastHeartbeat;
+ }
+
+ @Scheduled(fixedRate = 1000 * 60 * 5)
+ public void liveCheck() {
+ if (this.lastHeartbeat == null) {
+ return;
+ }
+ if ((System.currentTimeMillis() - this.lastHeartbeat) > (1000 * 60 * 5)) {
+ System.exit(0);
+ }
+ }
+
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/tools/FormatContent.java b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/tools/FormatContent.java
new file mode 100644
index 00000000..4079c720
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/java/io/spring/format/tools/FormatContent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-2019 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package io.spring.format.tools;
+
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.text.edits.TextEdit;
+
+import io.spring.javaformat.formatter.Formatter;
+
+/**
+ * . Format specified java content
+ *
+ * @author Howard Zuo
+ */
+final public class FormatContent {
+
+ private static final Pattern TRAILING_WHITESPACE = Pattern.compile(" +$", Pattern.MULTILINE);
+
+ private FormatContent() {
+
+ }
+
+ public static String formatContent(String source) {
+ if (source == null || "".equals(source)) {
+ return "";
+ }
+
+ try {
+ final Formatter formatter = new Formatter();
+ TextEdit textEdit = formatter.format(source);
+ IDocument document = new Document(source);
+ textEdit.apply(document);
+ String formattedContent = document.get();
+ return trimTrailingWhitespace(formattedContent);
+ }
+ catch (Exception e) {
+ return source;
+ }
+ }
+
+ private static String trimTrailingWhitespace(String content) {
+ return TRAILING_WHITESPACE.matcher(content).replaceAll("");
+ }
+
+}
diff --git a/spring-javaformat-vscode/spring-javaformat-format-service/src/main/resources/config/application.yaml b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/resources/config/application.yaml
new file mode 100644
index 00000000..8a9d523c
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat-format-service/src/main/resources/config/application.yaml
@@ -0,0 +1,5 @@
+# 基础应用配置
+spring:
+ application.name: Spring-Java-Formatter-Web
+server:
+ port: ${port:9987}
diff --git a/spring-javaformat-vscode/spring-javaformat/.prettierrc b/spring-javaformat-vscode/spring-javaformat/.prettierrc
new file mode 100644
index 00000000..133b8992
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "tabWidth": 2,
+ "semi": false,
+ "singleQuote": true,
+ "printWidth": 110
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/.vscode/extensions.json b/spring-javaformat-vscode/spring-javaformat/.vscode/extensions.json
new file mode 100644
index 00000000..0a18b9c4
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ // See http://go.microsoft.com/fwlink/?LinkId=827846
+ // for the documentation about the extensions.json format
+ "recommendations": [
+ "ms-vscode.vscode-typescript-tslint-plugin"
+ ]
+}
\ No newline at end of file
diff --git a/spring-javaformat-vscode/spring-javaformat/.vscode/launch.json b/spring-javaformat-vscode/spring-javaformat/.vscode/launch.json
new file mode 100644
index 00000000..f5197783
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/.vscode/launch.json
@@ -0,0 +1,36 @@
+// A launch configuration that compiles the extension and then opens it inside a new window
+// Use IntelliSense to learn about possible attributes.
+// Hover to view descriptions of existing attributes.
+// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Run Extension",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": [
+ "--extensionDevelopmentPath=${workspaceFolder}"
+ ],
+ "outFiles": [
+ "${workspaceFolder}/out/**/*.js"
+ ],
+ "preLaunchTask": "npm: watch"
+ },
+ {
+ "name": "Extension Tests",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": [
+ "--extensionDevelopmentPath=${workspaceFolder}",
+ "--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
+ ],
+ "outFiles": [
+ "${workspaceFolder}/out/test/**/*.js"
+ ],
+ "preLaunchTask": "npm: watch"
+ }
+ ]
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/.vscode/settings.json b/spring-javaformat-vscode/spring-javaformat/.vscode/settings.json
new file mode 100644
index 00000000..baa30361
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/.vscode/settings.json
@@ -0,0 +1,15 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.exclude": {
+ "out": false // set this to true to hide the "out" folder with the compiled JS files
+ },
+ "search.exclude": {
+ "out": true // set this to false to include "out" folder in search results
+ },
+ // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
+ "typescript.tsc.autoDetect": "off",
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "editor.formatOnSave": true
+}
\ No newline at end of file
diff --git a/spring-javaformat-vscode/spring-javaformat/.vscode/tasks.json b/spring-javaformat-vscode/spring-javaformat/.vscode/tasks.json
new file mode 100644
index 00000000..3b17e53b
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/.vscode/tasks.json
@@ -0,0 +1,20 @@
+// See https://go.microsoft.com/fwlink/?LinkId=733558
+// for the documentation about the tasks.json format
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "npm",
+ "script": "watch",
+ "problemMatcher": "$tsc-watch",
+ "isBackground": true,
+ "presentation": {
+ "reveal": "never"
+ },
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ }
+ ]
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/.vscodeignore b/spring-javaformat-vscode/spring-javaformat/.vscodeignore
new file mode 100644
index 00000000..ed3f9d37
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/.vscodeignore
@@ -0,0 +1,10 @@
+.vscode/**
+.vscode-test/**
+out/test/**
+src/**
+.gitignore
+vsc-extension-quickstart.md
+**/tsconfig.json
+**/tslint.json
+**/*.map
+**/*.ts
\ No newline at end of file
diff --git a/spring-javaformat-vscode/spring-javaformat/README.md b/spring-javaformat-vscode/spring-javaformat/README.md
new file mode 100644
index 00000000..5f38bf4f
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/README.md
@@ -0,0 +1,6 @@
+# spring-javaformat
+
+> Format .java files in spring-javaformat way
+
+
+
diff --git a/spring-javaformat-vscode/spring-javaformat/package.json b/spring-javaformat-vscode/spring-javaformat/package.json
new file mode 100644
index 00000000..0cc76709
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "spring-javaformat",
+ "displayName": "spring-javaformat-vscode-extension",
+ "description": "Format .java files in spring-javaformat way",
+ "version": "1.0.0",
+ "publisher": "howardzuo",
+ "engines": {
+ "vscode": "^1.36.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/spring-io/spring-javaformat.git"
+ },
+ "scripts": {
+ "vscode:prepublish": "yarn run compile",
+ "compile": "tsc -p ./",
+ "watch": "tsc -watch -p ./"
+ },
+ "categories": [
+ "Formatters"
+ ],
+ "activationEvents": [
+ "onLanguage:java",
+ "onLanguage:markdown"
+ ],
+ "main": "./out/extension.js",
+ "devDependencies": {
+ "@types/glob": "^7.1.1",
+ "@types/markdown-it": "^0.0.8",
+ "@types/node": "^10.12.21",
+ "@types/vscode": "^1.36.0",
+ "glob": "^7.1.4",
+ "tslint": "^5.18.0",
+ "typescript": "^3.5.3"
+ },
+ "dependencies": {
+ "axios": "^0.19.0",
+ "markdown-it": "^10.0.0",
+ "portfinder": "^1.0.21",
+ "ps-list": "^6.3.0"
+ }
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/src/SpringJavaFormatter.ts b/spring-javaformat-vscode/spring-javaformat/src/SpringJavaFormatter.ts
new file mode 100644
index 00000000..d382b919
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/src/SpringJavaFormatter.ts
@@ -0,0 +1,28 @@
+import {
+ DocumentFormattingEditProvider,
+ TextDocument,
+ FormattingOptions,
+ CancellationToken,
+ ProviderResult,
+ TextEdit,
+ Range
+} from 'vscode'
+
+import { formatMarkdown } from './formatters/MarkdownFormatter'
+import { formatJava } from './formatters/JavaFormatter'
+
+export default class SpringJavaFormatter implements DocumentFormattingEditProvider {
+ provideDocumentFormattingEdits(
+ document: TextDocument,
+ options: FormattingOptions,
+ token: CancellationToken
+ ): ProviderResult {
+ if (document.languageId === 'java') {
+ return formatJava(document)
+ }
+ if (document.languageId === 'markdown') {
+ return formatMarkdown(document)
+ }
+ return []
+ }
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/src/extension.ts b/spring-javaformat-vscode/spring-javaformat/src/extension.ts
new file mode 100644
index 00000000..6b848bd1
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/src/extension.ts
@@ -0,0 +1,31 @@
+// The module 'vscode' contains the VS Code extensibility API
+// Import the module and reference it with the alias vscode in your code below
+import * as vscode from 'vscode'
+import SpringJavaFormatter from './SpringJavaFormatter'
+
+// this method is called when your extension is activated
+// your extension is activated the very first time the command is executed
+export function activate(context: vscode.ExtensionContext) {
+ // Use the console to output diagnostic information (console.log) and errors (console.error)
+ // This line of code will only be executed once when your extension is activated
+ console.log('Congratulations, your extension "spring-javaformat" is now active!')
+
+ context.subscriptions.push(
+ vscode.languages.registerDocumentFormattingEditProvider(
+ [
+ {
+ language: 'java',
+ scheme: 'file'
+ },
+ {
+ language: 'markdown',
+ scheme: 'file'
+ }
+ ],
+ new SpringJavaFormatter()
+ )
+ )
+}
+
+// this method is called when your extension is deactivated
+export function deactivate() {}
diff --git a/spring-javaformat-vscode/spring-javaformat/src/formatters/JavaFormatter.ts b/spring-javaformat-vscode/spring-javaformat/src/formatters/JavaFormatter.ts
new file mode 100644
index 00000000..e66942c6
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/src/formatters/JavaFormatter.ts
@@ -0,0 +1,10 @@
+import { TextDocument, Range, TextEdit } from 'vscode'
+
+import FormatService from '../services/FormatService'
+
+export async function formatJava(document: TextDocument): Promise> {
+ const code = await FormatService.getInstance().formatCode(document.getText())
+
+ const range = new Range(document.positionAt(0), document.positionAt(document.getText().length))
+ return [TextEdit.replace(range, code)]
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/src/formatters/MarkdownFormatter.ts b/spring-javaformat-vscode/spring-javaformat/src/formatters/MarkdownFormatter.ts
new file mode 100644
index 00000000..e8c334f7
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/src/formatters/MarkdownFormatter.ts
@@ -0,0 +1,28 @@
+import { TextDocument, Range, TextEdit } from 'vscode'
+
+import FormatService from '../services/FormatService'
+import MarkdownIt from 'markdown-it'
+import Token from 'markdown-it/lib/token'
+
+const md = new MarkdownIt()
+
+export async function formatMarkdown(document: TextDocument): Promise> {
+ const source = document.getText()
+ const tokens = md.parse(source, {})
+
+ const editsPromise = tokens
+ .filter((t): t is Token => t.type === 'fence' && t.tag === 'code' && t.info === 'java')
+ .map(async token => {
+ const startIndex = source.indexOf(token.content)
+ const code = await FormatService.getInstance().formatCode(token.content)
+
+ const range = new Range(
+ document.positionAt(startIndex),
+ document.positionAt(startIndex + token.content.length)
+ )
+
+ return TextEdit.replace(range, code)
+ })
+
+ return Promise.all(editsPromise)
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/src/services/FormatService.ts b/spring-javaformat-vscode/spring-javaformat/src/services/FormatService.ts
new file mode 100644
index 00000000..062ba02e
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/src/services/FormatService.ts
@@ -0,0 +1,103 @@
+import * as child_process from 'child_process'
+import * as portfinder from 'portfinder'
+import psList from 'ps-list'
+import axios from 'axios'
+import { resolve } from 'path'
+import { window } from 'vscode'
+
+const JAR_NAME = 'spring-javaformat-format-service-0.0.16-SNAPSHOT.jar'
+
+const RUNTIME_JAR_PATH = resolve(__dirname, '..', '..', 'runtime', JAR_NAME)
+
+export default class FormatService {
+ private static instance: FormatService = new FormatService()
+ private port: number = 9987
+
+ constructor() {
+ const hideFunc = window.setStatusBarMessage('spring-javaformat service initializing....')
+ this.init()
+ .catch(err => {
+ console.error('ERROR:', err)
+ })
+ .finally(() => {
+ hideFunc.dispose()
+ })
+ }
+
+ private async init() {
+ this.startNextScheduledJob()
+ const proc = await this.getJavaFormatServiceProcessInfo()
+ if (proc) {
+ const matched = (proc.cmd || '').match(/-Dport=([0-9]+)\s/)
+ if (matched) {
+ this.port = +matched[1]
+ }
+ console.log('spring-javaformat service is running with other workspace, no need run again')
+ return
+ }
+ const port = await this.findAvailablePort()
+ this.port = port
+ await this.run(port)
+ console.log('spring-javaformat service running')
+ }
+
+ private startNextScheduledJob() {
+ setTimeout(async () => {
+ const proc = await this.getJavaFormatServiceProcessInfo()
+ if (!proc) {
+ const port = await this.findAvailablePort()
+ this.port = port
+ await this.run(port)
+ }
+ await this.sendHeartbeat()
+
+ this.startNextScheduledJob()
+ }, 1000 * 60)
+ }
+
+ private async getJavaFormatServiceProcessInfo() {
+ const list = await psList()
+ return list.find(l => (l.cmd || '').includes(JAR_NAME))
+ }
+
+ private async findAvailablePort() {
+ const port = await portfinder.getPortPromise({
+ port: 20000,
+ stopPort: 60000
+ })
+ return port
+ }
+
+ private async run(port: number) {
+ return new Promise((resolve, reject) => {
+ const childProcess = child_process.exec(`java -Dport=${port} -jar ${RUNTIME_JAR_PATH} `, {})
+
+ childProcess.stdout.on('data', data => {
+ if (data.includes('Started FormatterWebApplication')) {
+ resolve()
+ }
+ })
+
+ childProcess.on('error', reject)
+ })
+ }
+
+ async formatCode(source: string): Promise {
+ try {
+ const result = await axios.post(`http://localhost:${this.port}/format/code`, {
+ source
+ })
+ return result.data
+ } catch (error) {
+ throw new Error('spring-javaformat service is not ready, please hold for few seconds')
+ }
+ }
+
+ async sendHeartbeat() {
+ return axios.get(`http://localhost:${this.port}/health`)
+ }
+
+ static getInstance(): FormatService {
+ return this.instance
+ }
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/tsconfig.json b/spring-javaformat-vscode/spring-javaformat/tsconfig.json
new file mode 100644
index 00000000..7efb850e
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "outDir": "out",
+ "esModuleInterop": true,
+ "lib": ["es6"],
+ "sourceMap": true,
+ "rootDir": "src",
+ "strict": true /* enable all strict type-checking options */
+ /* Additional Checks */
+ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+ // "noUnusedParameters": true, /* Report errors on unused parameters. */
+ },
+ "exclude": ["node_modules", ".vscode-test"]
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/tslint.json b/spring-javaformat-vscode/spring-javaformat/tslint.json
new file mode 100644
index 00000000..8b382dcf
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/tslint.json
@@ -0,0 +1,15 @@
+{
+ "rules": {
+ "no-string-throw": true,
+ "no-unused-expression": true,
+ "no-duplicate-variable": true,
+ "curly": true,
+ "class-name": true,
+ "semicolon": [
+ true,
+ "never"
+ ],
+ "triple-equals": true
+ },
+ "defaultSeverity": "warning"
+}
diff --git a/spring-javaformat-vscode/spring-javaformat/yarn.lock b/spring-javaformat-vscode/spring-javaformat/yarn.lock
new file mode 100644
index 00000000..976bbb83
--- /dev/null
+++ b/spring-javaformat-vscode/spring-javaformat/yarn.lock
@@ -0,0 +1,388 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
+ integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
+ dependencies:
+ "@babel/highlight" "^7.0.0"
+
+"@babel/highlight@^7.0.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
+ integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
+ dependencies:
+ chalk "^2.0.0"
+ esutils "^2.0.2"
+ js-tokens "^4.0.0"
+
+"@types/events@*":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
+ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+
+"@types/glob@^7.1.1":
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
+ integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+ dependencies:
+ "@types/events" "*"
+ "@types/minimatch" "*"
+ "@types/node" "*"
+
+"@types/linkify-it@*":
+ version "2.1.0"
+ resolved "https://registry.npm.taobao.org/@types/linkify-it/download/@types/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
+ integrity sha1-6j3WTEgFWXMReQth6HLL0e0s2AY=
+
+"@types/markdown-it@^0.0.8":
+ version "0.0.8"
+ resolved "https://registry.npm.taobao.org/@types/markdown-it/download/@types/markdown-it-0.0.8.tgz#9af8704acde87fec70475369ba0413d50717bd8d"
+ integrity sha1-mvhwSs3of+xwR1NpugQT1QcXvY0=
+ dependencies:
+ "@types/linkify-it" "*"
+
+"@types/minimatch@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/node@*":
+ version "12.6.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c"
+ integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==
+
+"@types/node@^10.12.21":
+ version "10.14.13"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.13.tgz#ac786d623860adf39a3f51d629480aacd6a6eec7"
+ integrity sha512-yN/FNNW1UYsRR1wwAoyOwqvDuLDtVXnaJTZ898XIw/Q5cCaeVAlVwvsmXLX5PuiScBYwZsZU4JYSHB3TvfdwvQ==
+
+"@types/vscode@^1.36.0":
+ version "1.36.0"
+ resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.36.0.tgz#ae60242e893d9eda9a0d96d51ef56f1a3fae14ed"
+ integrity sha512-SbHR3Q5g/C3N+Ila3KrRf1rSZiyHxWdOZ7X3yFHXzw6HrvRLuVZrxnwEX0lTBMRpH9LkwZdqRTgXW+D075jxkg==
+
+ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+async@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+ integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
+
+axios@^0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
+ integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==
+ dependencies:
+ follow-redirects "1.5.10"
+ is-buffer "^2.0.2"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+builtin-modules@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+ integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
+
+chalk@^2.0.0, chalk@^2.3.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+commander@^2.12.1:
+ version "2.20.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
+ integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+debug@=3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+ dependencies:
+ ms "2.0.0"
+
+debug@^2.2.0:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+diff@^3.2.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+ integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
+
+entities@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npm.taobao.org/entities/download/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+ integrity sha1-aNYITKsbB5dnVA2A5Wo5tCPkq/Q=
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esutils@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+ integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
+
+follow-redirects@1.5.10:
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+ integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
+ dependencies:
+ debug "=3.1.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+glob@^7.1.1, glob@^7.1.4:
+ version "7.1.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
+ integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-buffer@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
+ integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
+
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.13.1:
+ version "3.13.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+ integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+linkify-it@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.npm.taobao.org/linkify-it/download/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
+ integrity sha1-47VGl+eL+RXHCjis14/QngBYsc8=
+ dependencies:
+ uc.micro "^1.0.1"
+
+markdown-it@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.npm.taobao.org/markdown-it/download/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
+ integrity sha1-q/xk8UGxci1mNAIETkOSfx9QqNw=
+ dependencies:
+ argparse "^1.0.7"
+ entities "~2.0.0"
+ linkify-it "^2.0.0"
+ mdurl "^1.0.1"
+ uc.micro "^1.0.5"
+
+mdurl@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npm.taobao.org/mdurl/download/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+ integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+mkdirp@0.5.x, mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+ dependencies:
+ minimist "0.0.8"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-parse@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+portfinder@^1.0.21:
+ version "1.0.21"
+ resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.21.tgz#60e1397b95ac170749db70034ece306b9a27e324"
+ integrity sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA==
+ dependencies:
+ async "^1.5.2"
+ debug "^2.2.0"
+ mkdirp "0.5.x"
+
+ps-list@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/ps-list/-/ps-list-6.3.0.tgz#a2b775c2db7d547a28fbaa3a05e4c281771259be"
+ integrity sha512-qau0czUSB0fzSlBOQt0bo+I2v6R+xiQdj78e1BR/Qjfl5OHWJ/urXi8+ilw1eHe+5hSeDI1wrwVTgDp2wst4oA==
+
+resolve@^1.3.2:
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e"
+ integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==
+ dependencies:
+ path-parse "^1.0.6"
+
+semver@^5.3.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
+ integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+tslib@^1.8.0, tslib@^1.8.1:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
+ integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
+
+tslint@^5.18.0:
+ version "5.18.0"
+ resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.18.0.tgz#f61a6ddcf372344ac5e41708095bbf043a147ac6"
+ integrity sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ builtin-modules "^1.1.1"
+ chalk "^2.3.0"
+ commander "^2.12.1"
+ diff "^3.2.0"
+ glob "^7.1.1"
+ js-yaml "^3.13.1"
+ minimatch "^3.0.4"
+ mkdirp "^0.5.1"
+ resolve "^1.3.2"
+ semver "^5.3.0"
+ tslib "^1.8.0"
+ tsutils "^2.29.0"
+
+tsutils@^2.29.0:
+ version "2.29.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
+ integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
+ dependencies:
+ tslib "^1.8.1"
+
+typescript@^3.5.3:
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
+ integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
+
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.npm.taobao.org/uc.micro/download/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
+ integrity sha1-nEEagCpAmpH8bPdAgbq6NLJEmaw=
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=