From bed2bdcd0c3349f2336c00708147c91f6bcbd6c9 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Tue, 21 Jan 2020 02:51:36 +0800 Subject: [PATCH] feat: Language-Understanding LSP (#1711) * Add LSP of LG * change folder name * change folder location * seperate server and client demo * add completion and hover for builtin-functions * change file names * change dependency * refactor the package * update demo readme * make some refine * remove npm lock file * add demos to workspace and run test in 1 command * remove declaration files * change tsc outDir to dist, simplify test command * add syntax highlight in demo and new API * change naming of the project and move the demo * remove package.json in demo * change the content in readme * fix lg-lsp package publish problem * fix build command and redundent d.ts files * integrate LG LSP server to composer server * change api in demo * change the order of commands in build:prod * delete redundent files generated from build * change lg-lsp-server api to attachLSPServer * remove gitignore in lg-lsp-server demo * remove attachLSPServer in server * fix token rules and suggestion context awareness * init of LU LSP * add diagnostic in LSP * add auto suggestions * fix * remove redundent in client * add doc on type config * fix * modify doc type format * fix * change version of bf-lu * fix diagnostic * refactor getRangeAtPosition method * fix tokens * remove black line * fix tokens * add roles suggestions * add regex entity seperated line definition * add suggestions for ml entity * matching only ML entities * fix * update documents * fix * integrate lu-lsp to composer * upgrade botbuilder-expressions * clean up * update sample * add test * add syntax highlight * add token rule for {@ expr * add a case in token * fix import statement token * fix composite entity auto complete * fix entity suggestion and tokens * add labeling experience of add unlabeled entity * fix typos * fix lint * fix redundent edits * fix unnecessary space * fix naming and role suggestions * fix labeling and error postion * fix insert text in wrong line, intent usesFeature * update bf-lu version * change find a valid luisJson and suggest composite * add unit test for LU lsp basic funcionalities Co-authored-by: Hongyang Du (hond) Co-authored-by: Dong Lei Co-authored-by: Zhixiang Zhan --- .vscode/launch.json | 18 +- Composer/packages/client/setupProxy.js | 2 +- .../language-understanding/code-editor.js | 8 + .../lib/code-editor/demo/src/index.tsx | 4 +- .../lib/code-editor/demo/src/luEditor.tsx | 4 + .../packages/lib/code-editor/src/LuEditor.tsx | 135 ++++- .../lib/code-editor/src/languages/index.ts | 1 + .../lib/code-editor/src/languages/lu.ts | 77 +++ Composer/packages/server/package.json | 1 + Composer/packages/server/src/server.ts | 20 + .../language-generation/package.json | 1 + .../language-understanding/.gitignore | 3 + .../language-understanding/README.md | 27 + .../__tests__/app.test.ts | 94 +++ .../__tests__/helpers/server.ts | 77 +++ .../__tests__/mocks/greeting.lu | 7 + .../__tests__/mocks/initialize-params.json | 238 ++++++++ .../__tests__/unitTest/serverUtil.test.ts | 227 ++++++++ .../language-understanding/demo/.eslintrc.js | 7 + .../language-understanding/demo/src/attach.ts | 31 + .../language-understanding/demo/src/server.ts | 44 ++ .../language-understanding/demo/tsconfig.json | 10 + .../language-understanding/jest.config.js | 18 + .../language-understanding/package.json | 51 ++ .../language-understanding/src/LUServer.ts | 551 ++++++++++++++++++ .../language-understanding/src/entityEnum.ts | 26 + .../language-understanding/src/index.ts | 4 + .../src/matchingPattern.ts | 225 +++++++ .../tsconfig.build.json | 8 + .../language-understanding/tsconfig.json | 11 + .../tools/language-servers/package.json | 5 +- Composer/yarn.lock | 295 ++++++++-- 32 files changed, 2166 insertions(+), 64 deletions(-) create mode 100644 Composer/packages/lib/code-editor/src/languages/lu.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/.gitignore create mode 100644 Composer/packages/tools/language-servers/language-understanding/README.md create mode 100644 Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu create mode 100644 Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json create mode 100644 Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js create mode 100644 Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json create mode 100644 Composer/packages/tools/language-servers/language-understanding/jest.config.js create mode 100644 Composer/packages/tools/language-servers/language-understanding/package.json create mode 100644 Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/src/index.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts create mode 100644 Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json create mode 100644 Composer/packages/tools/language-servers/language-understanding/tsconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 669e566005..4b0516a1ef 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,23 @@ "webRoot": "${workspaceFolder}" }, { - "name": "LSP Server", + "name": "LU LSP Server", + "type": "node", + "request": "launch", + "args": [ + "${workspaceFolder}/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts" + ], + "runtimeArgs": [ + "--nolazy", + "-r", + "${workspaceFolder}/Composer/node_modules/ts-node/register" + ], + "sourceMaps": true, + "cwd": "${workspaceFolder}/Composer/packages/tools/language-servers/language-understanding/demo/src", + "protocol": "inspector", + }, + { + "name": "LG LSP Server", "type": "node", "request": "launch", "args": [ diff --git a/Composer/packages/client/setupProxy.js b/Composer/packages/client/setupProxy.js index a75cd21360..522cc762f7 100644 --- a/Composer/packages/client/setupProxy.js +++ b/Composer/packages/client/setupProxy.js @@ -4,7 +4,7 @@ const paths = require('./config/paths'); const proxySetting = require(paths.appPackageJson).proxy; module.exports = function(app) { - const wsProxy = proxy('/lg-language-server', { + const wsProxy = proxy(['/lg-language-server', '/lu-language-server'], { target: proxySetting.replace('/^http/', 'ws'), changeOrigin: true, ws: true, diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.js b/Composer/packages/client/src/pages/language-understanding/code-editor.js index 04ccd189dc..a51ba5b456 100644 --- a/Composer/packages/client/src/pages/language-understanding/code-editor.js +++ b/Composer/packages/client/src/pages/language-understanding/code-editor.js @@ -43,6 +43,14 @@ export default function CodeEditor(props) { minimap: 'on', lineDecorationsWidth: undefined, lineNumbersMinChars: false, + glyphMargin: true, + autoClosingBrackets: 'always', + wordBasedSuggestions: false, + autoIndent: true, + formatOnType: true, + lightbulb: { + enabled: true, + }, }} errorMsg={errorMsg} value={content} diff --git a/Composer/packages/lib/code-editor/demo/src/index.tsx b/Composer/packages/lib/code-editor/demo/src/index.tsx index 2be749e1f3..db9db561d6 100644 --- a/Composer/packages/lib/code-editor/demo/src/index.tsx +++ b/Composer/packages/lib/code-editor/demo/src/index.tsx @@ -4,9 +4,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import App from './lgEditor'; +// import App from './lgEditor'; // import App from './jsonEditor'; -// import App from './luEditor'; +import App from './luEditor'; // import App from './lgJsonEditor'; // import App from './inlineEditor'; // import App from './multiEditors'; diff --git a/Composer/packages/lib/code-editor/demo/src/luEditor.tsx b/Composer/packages/lib/code-editor/demo/src/luEditor.tsx index 4b333bc10b..6dedb51fac 100644 --- a/Composer/packages/lib/code-editor/demo/src/luEditor.tsx +++ b/Composer/packages/lib/code-editor/demo/src/luEditor.tsx @@ -224,6 +224,10 @@ export default function App() { const props = { value, onChange, + languageServer: { + port: 5003, + path: '/lu-language-server', + }, }; return ; } diff --git a/Composer/packages/lib/code-editor/src/LuEditor.tsx b/Composer/packages/lib/code-editor/src/LuEditor.tsx index a31b60a7ea..c3967415a6 100644 --- a/Composer/packages/lib/code-editor/src/LuEditor.tsx +++ b/Composer/packages/lib/code-editor/src/LuEditor.tsx @@ -2,12 +2,139 @@ // Licensed under the MIT License. import React from 'react'; +import { listen, MessageConnection } from 'vscode-ws-jsonrpc'; +import * as monacoEditor from '@bfcomposer/monaco-editor/esm/vs/editor/editor.api'; +import * as monacoCore from 'monaco-editor-core'; +import get from 'lodash/get'; +import { MonacoServices, MonacoLanguageClient } from 'monaco-languageclient'; +import { registerLULanguage } from './languages'; +import { createUrl, createWebSocket, createLanguageClient } from './utils/lspUtil'; import { RichEditor, RichEditorProps } from './RichEditor'; -const LU_HELP = 'https://aka.ms/lu-file-format'; -const placeholder = `> See ${LU_HELP} to learn about supported LU concepts.`; +const LU_HELP = 'https://github.com/microsoft/botframework-cli/blob/master/packages/lu/docs/lu-file-format.md'; +const placeholder = `> To learn more about the LU file format, read the documentation at +> ${LU_HELP}`; -export function LuEditor(props: RichEditorProps) { - return ; +export interface LUOption { + inline: boolean; + content: string; + template?: { + name: string; + parameters?: string[]; + body: string; + }; +} + +export interface LULSPEditorProps extends RichEditorProps { + luOption?: LUOption; + languageServer?: + | { + host?: string; + hostname?: string; + port?: number | string; + path: string; + } + | string; +} + +const defaultLUServer = { + path: '/lu-language-server', +}; +declare global { + interface Window { + monacoServiceInstance: MonacoServices; + monacoLUEditorInstance: MonacoLanguageClient; + } +} + +type ServerEdit = { + range: { start: { line: number; character: number }; end: { line: number; character: number } }; + newText: string; +}; + +/* +convert the edits results from the server to an exectable object in manoco editor +*/ +function convertEdit(serverEdit: ServerEdit) { + return { + range: { + startLineNumber: serverEdit.range.start.line, + startColumn: serverEdit.range.start.character, + endLineNumber: serverEdit.range.end.line, + endColumn: serverEdit.range.end.character, + }, + text: serverEdit.newText, + forceMoveMarkers: true, + }; +} + +export function LuEditor(props: LULSPEditorProps) { + const options = { + quickSuggestions: true, + wordBasedSuggestions: false, + formatOnType: true, + }; + + const { languageServer, ...restProps } = props; + const luServer = languageServer || defaultLUServer; + + const editorWillMount = (monaco: typeof monacoEditor) => { + registerLULanguage(monaco); + if (typeof props.editorWillMount === 'function') { + return props.editorWillMount(monaco); + } + }; + const editorDidMount = (editor: monacoEditor.editor.IStandaloneCodeEditor, monaco: typeof monacoEditor) => { + if (!window.monacoServiceInstance) { + window.monacoServiceInstance = MonacoServices.install(editor as monacoCore.editor.IStandaloneCodeEditor | any); + } + + if (!window.monacoLUEditorInstance) { + const uri = get(editor.getModel(), 'uri._formatted', ''); + const url = createUrl(luServer); + const webSocket: WebSocket = createWebSocket(url); + listen({ + webSocket, + onConnection: (connection: MessageConnection) => { + const languageClient = createLanguageClient('LU Language Client', ['lu'], connection); + if (!window.monacoLUEditorInstance) { + window.monacoLUEditorInstance = languageClient; + } + + editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, function() { + const position = editor.getPosition(); + languageClient.sendRequest('labelingExperienceRequest', { uri, position }); + }); + languageClient.onReady().then(() => + languageClient.onNotification('addUnlabelUtterance', result => { + const edits = result.edits.map(e => { + return convertEdit(e); + }); + editor.executeEdits(uri, edits); + }) + ); + const disposable = languageClient.start(); + connection.onClose(() => disposable.dispose()); + }, + }); + } + + if (typeof props.editorDidMount === 'function') { + return props.editorDidMount(editor, monaco); + } + }; + + return ( + + ); } diff --git a/Composer/packages/lib/code-editor/src/languages/index.ts b/Composer/packages/lib/code-editor/src/languages/index.ts index 7014f7824e..eff516de15 100644 --- a/Composer/packages/lib/code-editor/src/languages/index.ts +++ b/Composer/packages/lib/code-editor/src/languages/index.ts @@ -2,3 +2,4 @@ // Licensed under the MIT License. export * from './lg'; +export * from './lu'; diff --git a/Composer/packages/lib/code-editor/src/languages/lu.ts b/Composer/packages/lib/code-editor/src/languages/lu.ts new file mode 100644 index 0000000000..9102017824 --- /dev/null +++ b/Composer/packages/lib/code-editor/src/languages/lu.ts @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as monacoEditor from '@bfcomposer/monaco-editor/esm/vs/editor/editor.api'; + +export function registerLULanguage(monaco: typeof monacoEditor) { + monaco.languages.setMonarchTokensProvider('lu', { + tokenizer: { + root: [ + [/^\s*#/, { token: 'intent', next: '@intent' }], + [/^\s*@/, { token: 'entity-identifier', goBack: 1, next: '@entityMode' }], + [/^\s*>\s*[\s\S]*$/, { token: 'comments' }], + ], + + intent: [ + [/^\s*#/, { token: 'intent', next: '@intent' }], + [/^\s*-/, { token: 'utterrance-indentifier', next: '@utterrance' }], + [/^\s*>\s*[\s\S]*$/, { token: 'comments' }], + [/^\s*@/, { token: 'entity-identifier', goBack: 1, next: '@entityMode' }], + [/.*$/, 'intent'], + ], + utterrance: [ + [/^\s*#/, { token: 'intent', next: '@intent' }], + [/^\s*>\s*[\s\S]*$/, { token: 'comments' }], + [/^\s*-/, { token: 'utterrance-indentifier', next: 'utterrance' }], + [/^\s*@/, { token: 'entity-identifier', goBack: 1, next: '@entityMode' }], + [/({)(\s*[\w.@:\s]*\s*)(=)(\s*[\w.]*\s*)(})/, ['lb', 'pattern', 'equal', 'entity-name', 'rb']], + [/({\s*@)(\s*[\w.]*\s*)(})/, ['lb', 'entity-name', 'rb']], + // eslint-disable-next-line security/detect-unsafe-regex + [/\s*\[[\w\s.]+\]\(.{1,2}\/[\w.*]+(#[\w.?]+)?\)/, 'import-desc'], + [/./, 'utterance-other'], + ], + entityMode: [ + [/^\s*#/, { token: 'intent', next: '@intent' }], + [/^\s*>\s*[\s\S]*$/, { token: 'comments' }], + [/^\s*-/, { token: 'utterrance-indentifier', next: 'utterrance' }], + [ + /(@\s*)(ml|prebuilt|regex|list|composite|patternany|phraselist)(\s*\w*)/, + ['intent-indentifier', 'entity-type', 'entity-name'], + ], + [/(@\s*)(\s*\w*)/, ['intent-indentifier', 'entity-name']], + [/\s*(hasRoles|useFeature)\s*/, 'keywords'], + [/.*$/, 'entity-other', '@pop'], + ], + }, + }); + + monaco.languages.register({ + id: 'lu', + extensions: ['.lu'], + aliases: ['LU', 'language-understanding'], + mimetypes: ['application/lu'], + }); + + monaco.languages.setLanguageConfiguration('lu', { + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + ], + }); + + monaco.editor.defineTheme('lu', { + base: 'vs', + inherit: false, + colors: {}, + rules: [ + { token: 'intent', foreground: '0000FF' }, + { token: 'pattern', foreground: '00B7C3' }, + { token: 'entity-name', foreground: '038387' }, + { token: 'comments', foreground: '7A7A7A' }, + { token: 'import-desc', foreground: '00A32B' }, + { token: 'entity-type', foreground: 'DF2C2C' }, + { token: 'keywords', foreground: '0078D7' }, + ], + }); +} diff --git a/Composer/packages/server/package.json b/Composer/packages/server/package.json index 1265a41e56..3920015655 100644 --- a/Composer/packages/server/package.json +++ b/Composer/packages/server/package.json @@ -55,6 +55,7 @@ "@azure/ms-rest-js": "^1.8.7", "@bfc/indexers": "*", "@bfc/lg-languageserver": "*", + "@bfc/lu-languageserver": "*", "@bfc/shared": "*", "@bfcomposer/lubuild": "1.1.2-preview", "archiver": "^3.0.0", diff --git a/Composer/packages/server/src/server.ts b/Composer/packages/server/src/server.ts index 4a514bbc22..07492ac3c3 100644 --- a/Composer/packages/server/src/server.ts +++ b/Composer/packages/server/src/server.ts @@ -13,6 +13,7 @@ import * as ws from 'ws'; import * as rpc from 'vscode-ws-jsonrpc'; import { IConnection, createConnection } from 'vscode-languageserver'; import { LGServer } from '@bfc/lg-languageserver'; +import { LUServer } from '@bfc/lu-languageserver'; import { BotProjectService } from './services/project'; import { getAuthProvider } from './router/auth'; @@ -124,6 +125,14 @@ function launchLanguageServer(socket: rpc.IWebSocket) { server.start(); } +function launchLuLanguageServer(socket: rpc.IWebSocket) { + const reader = new rpc.WebSocketMessageReader(socket); + const writer = new rpc.WebSocketMessageWriter(socket); + const connection: IConnection = createConnection(reader, writer); + const server = new LUServer(connection); + server.start(); +} + attachLSPServer(wss, server, '/lg-language-server', webSocket => { // launch language server when the web socket is opened if (webSocket.readyState === webSocket.OPEN) { @@ -134,3 +143,14 @@ attachLSPServer(wss, server, '/lg-language-server', webSocket => { }); } }); + +attachLSPServer(wss, server, '/lu-language-server', webSocket => { + // launch language server when the web socket is opened + if (webSocket.readyState === webSocket.OPEN) { + launchLuLanguageServer(webSocket); + } else { + webSocket.on('open', () => { + launchLuLanguageServer(webSocket); + }); + } +}); diff --git a/Composer/packages/tools/language-servers/language-generation/package.json b/Composer/packages/tools/language-servers/language-generation/package.json index b05fb13a4d..3d8e889244 100644 --- a/Composer/packages/tools/language-servers/language-generation/package.json +++ b/Composer/packages/tools/language-servers/language-generation/package.json @@ -17,6 +17,7 @@ "dependencies": { "@bfc/indexers": "*", "botbuilder-lg": "4.7.0-preview.93464", + "botframework-expressions": "4.7.0-preview.93464", "request-light": "^0.2.2", "vscode-languageserver": "^5.3.0-next" }, diff --git a/Composer/packages/tools/language-servers/language-understanding/.gitignore b/Composer/packages/tools/language-servers/language-understanding/.gitignore new file mode 100644 index 0000000000..0437368d03 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/.gitignore @@ -0,0 +1,3 @@ +/lib +/demo/lib +/demo/dist \ No newline at end of file diff --git a/Composer/packages/tools/language-servers/language-understanding/README.md b/Composer/packages/tools/language-servers/language-understanding/README.md new file mode 100644 index 0000000000..2088fcec58 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/README.md @@ -0,0 +1,27 @@ +# Language-understanding language server demo + + + +## goto language-understanding directory, install packages, run +``` +npm install +``` + +## How to start the demo + +#### 1. under language-understanding directory, run +``` +yarn start +``` + +### 2. go to code-editor directory + +start luEditor demo, connect :5003/lu-language-server + +## Features + +### 1.Auto-suggestions for entity types and defined entities + +### 2.Syntax and semantic diagonostics + +### Auto-copletion for ml entities and list entities diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts b/Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts new file mode 100644 index 0000000000..09cb074566 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/app.test.ts @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import fs from 'fs'; + +import WebSocket from 'ws'; + +import { startServer } from './helpers/server'; + +const luFile = fs.readFileSync(`${__dirname}/mocks/greeting.lu`, 'utf-8'); +const initializeParams = fs.readFileSync(`${__dirname}/mocks/initialize-params.json`, 'utf-8'); + +const ws = new WebSocket('ws://localhost:50003/lu-language-server'); + +type messageResolver = (data) => void; + +const subscribers: messageResolver[] = []; + +ws.on('message', data => { + const subscriber = subscribers.shift(); + if (subscriber) { + subscriber(data); + } +}); + +function send(data, resolvers?: messageResolver[]): Promise { + ws.send(data); + return new Promise(resolve => { + if (typeof resolvers === 'undefined') { + subscribers.push(data => { + resolve(data); + }); + return; + } + if (resolvers.length === 0) { + resolve(); + return; + } + let count = 0; + const readers = resolvers.map(resolver => { + return data => { + count++; + resolver(JSON.parse(data)); + if (resolvers.length === count) { + resolve(JSON.parse(data)); + } + }; + }); + subscribers.push(...readers); + }); +} + +function jsonEscape(str) { + return str + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t'); +} + +const content = jsonEscape(luFile); + +describe('lu lsp server', () => { + const server = startServer(); + beforeAll(async () => { + await new Promise(resolve => { + ws.on('open', () => { + resolve(); + }); + }); + }); + + afterAll(async done => { + ws.close(); + server.close(); + done(); + }); + it('websocket should connect server', async () => { + await send(`{ "jsonrpc":"2.0","id":0,"method":"initialize","params": ${initializeParams} }`, [ + response => { + expect(response.method).toEqual('window/logMessage'); + expect(response.params.type).toEqual(4); + }, + response => { + expect(response.id).toEqual(0); + }, + ]); + // client initialized + await send(`{"jsonrpc":"2.0","method":"initialized","params":{}}`, []); + await send( + `{"jsonrpc":"2.0","method":"textDocument/didOpen","params": {"textDocument":{"uri":"inmemory://model/1","languageId":"lu","version":2,"text": "${content}" }}}`, + [] + ); + }); +}); diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts b/Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts new file mode 100644 index 0000000000..1f2f8aad8f --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/helpers/server.ts @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as http from 'http'; +import * as url from 'url'; +import * as net from 'net'; + +import * as ws from 'ws'; +import * as rpc from 'vscode-ws-jsonrpc'; +import { IConnection, createConnection } from 'vscode-languageserver'; +import express from 'express'; + +import { LUServer } from '../../src'; + +function createSocketHandler(webSocket: any): rpc.IWebSocket { + const socket: rpc.IWebSocket = { + send: content => + webSocket.send(content, error => { + if (error) { + throw error; + } + }), + onMessage: cb => webSocket.on('message', cb), + onError: cb => webSocket.on('error', cb), + onClose: cb => webSocket.on('close', cb), + dispose: () => webSocket.close(), + }; + return socket; +} + +function attachLSPServer(wss: ws.Server, server: http.Server, path: string, handler: (webSocket) => void) { + server.on('upgrade', (request: http.IncomingMessage, socket: net.Socket, head: Buffer) => { + const pathname = request.url ? url.parse(request.url).pathname : undefined; + if (pathname === path) { + wss.handleUpgrade(request, socket, head, webSocket => { + const socketHandler = createSocketHandler(webSocket); + handler(socketHandler); + }); + } + }); +} + +function launchLanguageServer(socket: rpc.IWebSocket) { + const reader = new rpc.WebSocketMessageReader(socket); + const writer = new rpc.WebSocketMessageWriter(socket); + const connection: IConnection = createConnection(reader, writer); + return new LUServer(connection); +} + +export function startServer() { + // create the express application + const app = express(); + // server the static content, i.e. index.html + app.use(express.static(__dirname)); + // start the server + const server = app.listen(50003); + + const wss: ws.Server = new ws.Server({ + noServer: true, + perMessageDeflate: false, + }); + attachLSPServer(wss, server, '/lu-language-server', webSocket => { + // const socketHandler = createSocketHandler(webSocket); + + // launch language server when the web socket is opened + if (webSocket.readyState === webSocket.OPEN) { + const server = launchLanguageServer(webSocket); + server.start(); + } else { + webSocket.on('open', () => { + const server = launchLanguageServer(webSocket); + server.start(); + }); + } + }); + return server; +} diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu new file mode 100644 index 0000000000..5ce8120767 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/greeting.lu @@ -0,0 +1,7 @@ +> Greeting intent and related utterances +# Greeting +- hi +- hello +- hiya +- how are you? +- how do you do? diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json new file mode 100644 index 0000000000..c9cdfd678b --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/mocks/initialize-params.json @@ -0,0 +1,238 @@ +{ + "rootPath": null, + "rootUri": null, + "capabilities": { + "workspace": { + "applyEdit": true, + "workspaceEdit": { + "documentChanges": true, + "resourceOperations": [ + "create", + "rename", + "delete" + ], + "failureHandling": "textOnlyTransactional" + }, + "didChangeConfiguration": { + "dynamicRegistration": true + }, + "didChangeWatchedFiles": { + "dynamicRegistration": true + }, + "symbol": { + "dynamicRegistration": true, + "symbolKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ] + } + }, + "executeCommand": { + "dynamicRegistration": true + }, + "configuration": true, + "workspaceFolders": true + }, + "textDocument": { + "publishDiagnostics": { + "relatedInformation": true, + "tagSupport": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + }, + "completion": { + "dynamicRegistration": true, + "contextSupport": true, + "completionItem": { + "snippetSupport": true, + "commitCharactersSupport": true, + "documentationFormat": [ + "markdown", + "plaintext" + ], + "deprecatedSupport": true, + "preselectSupport": true + }, + "completionItemKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25 + ] + } + }, + "hover": { + "dynamicRegistration": true, + "contentFormat": [ + "markdown", + "plaintext" + ] + }, + "signatureHelp": { + "dynamicRegistration": true, + "signatureInformation": { + "documentationFormat": [ + "markdown", + "plaintext" + ], + "parameterInformation": { + "labelOffsetSupport": true + } + } + }, + "definition": { + "dynamicRegistration": true, + "linkSupport": true + }, + "references": { + "dynamicRegistration": true + }, + "documentHighlight": { + "dynamicRegistration": true + }, + "documentSymbol": { + "dynamicRegistration": true, + "symbolKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26 + ] + }, + "hierarchicalDocumentSymbolSupport": true + }, + "codeAction": { + "dynamicRegistration": true, + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "", + "quickfix", + "refactor", + "refactor.extract", + "refactor.inline", + "refactor.rewrite", + "source", + "source.organizeImports" + ] + } + } + }, + "codeLens": { + "dynamicRegistration": true + }, + "formatting": { + "dynamicRegistration": true + }, + "rangeFormatting": { + "dynamicRegistration": true + }, + "onTypeFormatting": { + "dynamicRegistration": true + }, + "rename": { + "dynamicRegistration": true, + "prepareSupport": true + }, + "documentLink": { + "dynamicRegistration": true + }, + "typeDefinition": { + "dynamicRegistration": true, + "linkSupport": true + }, + "implementation": { + "dynamicRegistration": true, + "linkSupport": true + }, + "colorProvider": { + "dynamicRegistration": true + }, + "foldingRange": { + "dynamicRegistration": true, + "rangeLimit": 5000, + "lineFoldingOnly": true + }, + "declaration": { + "dynamicRegistration": true, + "linkSupport": true + } + } + }, + "trace": "off", + "workspaceFolders": null +} diff --git a/Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts b/Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts new file mode 100644 index 0000000000..77e367d057 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/__tests__/unitTest/serverUtil.test.ts @@ -0,0 +1,227 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable @typescript-eslint/camelcase */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +const assert = require('assert'); + +const util = require('../../lib/matchingPattern'); + +const text = `@ml location hasRoles homeaddress +@ composite city1 +@ regex zipcode = /[0-9]{5}/ +@ ml mail usesFeature address +#checktemperature +-it is sunny and the temperature is +- the temperature is 35 +- address is {address=[beijing,100080]} +`; + +const luisObject = { + intents: [], + entities: [{ name: 'address', roles: ['role1'] }], + composites: [{ name: 'geoInfo', children: [], roles: [] }], + closedLists: [{ name: 'city', subLists: [], roles: ['role2'] }], + regex_entities: [ + { + name: 'zipcode', + regexPattern: '[0-9]{5}', + roles: [], + }, + ], + model_features: [], + regex_features: [], + utterances: [], + patterns: [], + patternAnyEntities: [], + prebuiltEntities: [], +}; + +describe('LU LSP Server Function Unit Tests', function() { + it('Test Get ML Entities', function() { + const result = util.getMLEntities(text); + assert.deepStrictEqual(result, ['location', 'mail']); + }); + + it('Test Get Composites Entities', function() { + const result = util.getCompositesEntities(luisObject); + assert.deepStrictEqual(result, ['geoInfo']); + }); + + it('Test Get RegExp Entities', function() { + const result = util.getRegexEntities(luisObject); + assert.deepStrictEqual(result, ['zipcode']); + }); + + it('Test Get All Parsed Entities', function() { + const result = util.getSuggestionEntities(luisObject, util.suggestionAllEntityTypes); + assert.deepStrictEqual(result, ['address', 'zipcode', 'city', 'geoInfo']); + }); + + it('Test Get All Parsed Roles', function() { + const result = util.getSuggestionRoles(luisObject, util.suggestionAllEntityTypes); + assert.deepStrictEqual(result, ['role1', 'role2']); + }); + + it('Test Entity Can UsesFeature', function() { + let lineContent = '@ geoInfo usesFeature '; + let result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject); + assert.equal(result, true); + + lineContent = '@ composites newAddress2 usesFeature '; + result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject); + assert.equal(result, true); + + lineContent = '@ ml newAddress usesFeature '; + result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject); + assert.equal(result, true); + + lineContent = '@ zipcode usesFeature '; + result = util.matchedEntityCanUsesFeature(lineContent, text, luisObject); + assert.equal(result, false); + }); + + it('Test Intent Can UsesFeature', function() { + let text = '@ intent mockIntent usesFeature '; + let result = util.matchIntentUsesFeatures(text); + assert.equal(result, true); + + text = '@ intent mockIntent useFeature '; + result = util.matchIntentUsesFeatures(text); + assert.equal(result, false); + }); + + it('Test Intent In a Entity Definiton', function() { + let text = '@ intent mockIntent'; + let result = util.matchIntentInEntityDef(text); + assert.equal(result, true); + + text = '@ intents mockIntent '; + result = util.matchIntentInEntityDef(text); + assert.equal(result, false); + }); + + it('Test Entity Definition', function() { + let text = '@ '; + let result = util.isEntityType(text); + assert.equal(result, true); + + text = '@ ml '; + result = util.isEntityType(text); + assert.equal(result, false); + }); + + it('Test Prebuilt Entity Definition', function() { + let text = '@ prebuilt '; + let result = util.isPrebuiltEntity(text); + assert.equal(result, true); + + text = '@ prebuilt ml1 '; + result = util.isPrebuiltEntity(text); + assert.equal(result, false); + }); + + it('Test RegExp Entity Definition', function() { + let text = '@ regex zipcode ='; + let result = util.isRegexEntity(text); + assert.equal(result, true); + + text = '@ ml ml1 '; + result = util.isRegexEntity(text); + assert.equal(result, false); + }); + + it('Test Seperated Line Entity', function() { + let text = '@ zipcode ='; + let result = util.isSeperatedEntityDef(text); + assert.equal(result, true); + + text = '@ ml ml1 '; + result = util.isSeperatedEntityDef(text); + assert.equal(result, false); + }); + + it('Test Entity Name ', function() { + let text = '@ ml location'; + let result = util.isEntityName(text); + assert.equal(result, true); + + text = '@ location'; + result = util.isEntityName(text); + assert.equal(result, true); + + text = '@ intent '; + result = util.isEntityName(text); + assert.equal(result, false); + }); + + it('Test Composite Entity ', function() { + let text = '@ composite location = ['; + let result = util.isCompositeEntity(text); + assert.equal(result, true); + + text = '@ composite location = []'; + result = util.isCompositeEntity(text); + assert.equal(result, true); + + text = '@ intent '; + result = util.isCompositeEntity(text); + assert.equal(result, false); + }); + + it('Test Entering Pattern ', function() { + let text = '- The weather in Seattle is { '; + let result = util.matchedEnterPattern(text); + assert.equal(result, true); + + text = '- The weather in Seattle is { }'; + result = util.matchedEnterPattern(text); + assert.equal(result, true); + + text = '- The weather in Seattle is '; + result = util.matchedEnterPattern(text); + assert.equal(result, false); + }); + + it('Test Entering Roles ', function() { + let text = '- The weather in Seattle is {morning: '; + let result = util.matchedRolesPattern(text); + assert.equal(result, true); + + text = '- The weather in Seattle is {morning: }'; + result = util.matchedRolesPattern(text); + assert.equal(result, true); + + text = '- The weather in Seattle is { '; + result = util.matchedRolesPattern(text); + assert.equal(result, false); + }); + + it('Test Entering Entity ', function() { + let text = '- The weather in Seattle is {@ '; + let result = util.matchedEntityPattern(text); + assert.equal(result, true); + + text = '- The weather in Seattle is {@}'; + result = util.matchedEntityPattern(text); + assert.equal(result, true); + + text = '- The weather in Seattle is { '; + result = util.matchedEntityPattern(text); + assert.equal(result, false); + }); + + it('Test Remove Labeled Utterance', function() { + let text = '- this is a {type = Audio} message from {device = PC}'; + let result = util.removeLabelsInUtterance(text); + assert.equal(result, '- this is a Audio message from PC'); + + text = '- this is a {@ type = Audio} message from {@device = PC}'; + result = util.removeLabelsInUtterance(text); + assert.equal(result, '- this is a Audio message from PC'); + + text = '- this is a { type: role =Audio} message from {@device = PC}'; + result = util.removeLabelsInUtterance(text); + assert.equal(result, '- this is a Audio message from PC'); + }); +}); diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js b/Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js new file mode 100644 index 0000000000..328b328945 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/demo/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['../../../../../.eslintrc.react.js'], + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, +}; diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts b/Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts new file mode 100644 index 0000000000..82e3ca58c8 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/demo/src/attach.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as http from 'http'; +import * as url from 'url'; +import * as net from 'net'; + +import * as ws from 'ws'; +import * as rpc from 'vscode-ws-jsonrpc'; + +export function createSocketHandler(webSocket: any): rpc.IWebSocket { + const socket: rpc.IWebSocket = { + send: content => webSocket.send(content), + onMessage: cb => webSocket.on('message', cb), + onError: cb => webSocket.on('error', cb), + onClose: cb => webSocket.on('close', cb), + dispose: () => webSocket.close(), + }; + return socket; +} + +export function attachLSPServer(wss: ws.Server, server: http.Server, path: string, handler: (webSocket) => void) { + server.on('upgrade', (request: http.IncomingMessage, socket: net.Socket, head: Buffer) => { + const pathname = request.url ? url.parse(request.url).pathname : undefined; + if (pathname === path) { + wss.handleUpgrade(request, socket, head, webSocket => { + handler(createSocketHandler(webSocket)); + }); + } + }); +} diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts b/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts new file mode 100644 index 0000000000..64392d5b54 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/demo/src/server.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// import { attachLSPServer } from '../../src/attachWSToHTTPServer'; +import * as ws from 'ws'; +import * as rpc from 'vscode-ws-jsonrpc'; +import { IConnection, createConnection } from 'vscode-languageserver'; + +import { LUServer } from '../../src'; + +import { attachLSPServer } from './attach'; + +const express = require('express'); + +// create the express application +const app = express(); +// server the static content, i.e. index.html +app.use(express.static(__dirname)); +// start the server +const server = app.listen(5003); + +const wss: ws.Server = new ws.Server({ + noServer: true, + perMessageDeflate: false, +}); + +function launchLanguageServer(socket: rpc.IWebSocket) { + const reader = new rpc.WebSocketMessageReader(socket); + const writer = new rpc.WebSocketMessageWriter(socket); + const connection: IConnection = createConnection(reader, writer); + const server = new LUServer(connection); + server.start(); + return server; +} + +attachLSPServer(wss, server, '/lu-language-server', webSocket => { + if (webSocket.readyState === webSocket.OPEN) { + launchLanguageServer(webSocket); + } else { + webSocket.on('open', () => { + launchLanguageServer(webSocket); + }); + } +}); diff --git a/Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json b/Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json new file mode 100644 index 0000000000..715831d5b4 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/demo/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "include": [ + "./src/**/*", + "./__tests__/**/*" + ], + "exclude": [ + "node_modules" + ], +} \ No newline at end of file diff --git a/Composer/packages/tools/language-servers/language-understanding/jest.config.js b/Composer/packages/tools/language-servers/language-understanding/jest.config.js new file mode 100644 index 0000000000..20a62c0671 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/jest.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +const path = require('path'); + +module.exports = { + preset: 'ts-jest/presets/js-with-babel', + testPathIgnorePatterns: ['/node_modules/', '/helpers/', '/mocks/'], + watchPathIgnorePatterns: ['/__tests__/mocks'], + globals: { + 'ts-jest': { + tsConfig: path.resolve(__dirname, './tsconfig.json'), + diagnostics: { + warnOnly: true, + }, + }, + }, +}; diff --git a/Composer/packages/tools/language-servers/language-understanding/package.json b/Composer/packages/tools/language-servers/language-understanding/package.json new file mode 100644 index 0000000000..fa8ebaddee --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/package.json @@ -0,0 +1,51 @@ +{ + "name": "@bfc/lu-languageserver", + "license": "MIT", + "version": "0.1.0", + "main": "lib/index.js", + "scripts": { + "build": "tsc --build tsconfig.build.json", + "build:demo": "cd demo && tsc --build tsconfig.json", + "prepublishOnly": "npm run build", + "clean": "rimraf lib demo/dist", + "start": "cd demo && ts-node ./src/server.ts", + "test": "jest", + "lint": "eslint --quiet --ext .js,.jsx,.ts,.tsx ./src ./__tests__", + "lint:fix": "yarn lint --fix", + "lint:typecheck": "tsc --noEmit", + "startDemo": "concurrently --kill-others \"yarn start:server\" \"yarn start:client\"", + "start:client": "cd demo && webpack-dev-server", + "start:server": "cd demo && ts-node ./src/server.ts" + }, + "dependencies": { + "@microsoft/bf-cli-command": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-1.0.0.tgz", + "@bfcomposer/bf-lu": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/@bfcomposer/bf-lu/-/@bfcomposer/bf-lu-1.1.8.tgz", + "@types/node": "^12.0.4", + "express": "^4.15.2", + "monaco-editor-core": "^0.17.0", + "monaco-languageclient": "^0.10.0", + "normalize-url": "^2.0.1", + "reconnecting-websocket": "^3.2.2", + "request-light": "^0.2.2", + "vscode": "^1.1.34", + "vscode-json-languageservice": "^3.3.1", + "vscode-languageserver": "^5.3.0-next", + "vscode-ws-jsonrpc": "^0.1.1", + "ws": "^5.0.0" + }, + "devDependencies": { + "clean-webpack-plugin": "^3.0.0", + "css-loader": "^3.2.0", + "html-webpack-plugin": "^3.2.0", + "source-map-loader": "^0.2.4", + "style-loader": "^1.0.0", + "ts-loader": "^6.1.0", + "ts-node": "^8.3.0", + "typescript": "^3.6.3", + "uglifyjs-webpack-plugin": "^2.2.0", + "webpack": "^4.39.3", + "webpack-cli": "^3.3.8", + "webpack-dev-server": "^3.8.0", + "webpack-merge": "^4.2.2" + } +} diff --git a/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts new file mode 100644 index 0000000000..d5f6855cb6 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/src/LUServer.ts @@ -0,0 +1,551 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as fs from 'fs'; + +import { xhr, getErrorStatusDescription } from 'request-light'; +import URI from 'vscode-uri'; +import { IConnection, TextDocuments } from 'vscode-languageserver'; +import { + TextDocument, + Diagnostic, + CompletionList, + Position, + CompletionItem, + CompletionItemKind, + Range, + DiagnosticSeverity, + TextEdit, +} from 'vscode-languageserver-types'; +import { TextDocumentPositionParams, DocumentOnTypeFormattingParams } from 'vscode-languageserver-protocol'; + +import { EntityTypesObj, LineState } from './entityEnum'; +import * as util from './matchingPattern'; + +const parseFile = require('@bfcomposer/bf-lu/lib/parser/lufile/parseFileContents.js').parseFile; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const validateLUISBlob = require('@bfcomposer/bf-lu/lib/parser/luis/luisValidator'); +const LABELEXPERIENCEREQUEST = 'labelingExperienceRequest'; +export class LUServer { + protected workspaceRoot: URI | undefined; + protected readonly documents = new TextDocuments(); + protected readonly pendingValidationRequests = new Map(); + + constructor(protected readonly connection: IConnection) { + this.documents.listen(this.connection); + this.documents.onDidChangeContent(change => this.validate(change.document)); + this.documents.onDidClose(event => { + this.cleanPendingValidation(event.document); + this.cleanDiagnostics(event.document); + }); + + this.connection.onInitialize(params => { + if (params.rootPath) { + this.workspaceRoot = URI.file(params.rootPath); + } else if (params.rootUri) { + this.workspaceRoot = URI.parse(params.rootUri); + } + this.connection.console.log('The server is initialized.'); + return { + capabilities: { + textDocumentSync: this.documents.syncKind, + codeActionProvider: false, + completionProvider: { + resolveProvider: true, + triggerCharacters: ['@', ' ', '{', ':', '['], + }, + foldingRangeProvider: false, + documentOnTypeFormattingProvider: { + firstTriggerCharacter: '\n', + }, + }, + }; + }); + this.connection.onCompletion(params => this.completion(params)); + this.connection.onDocumentOnTypeFormatting(docTypingParams => this.docTypeFormat(docTypingParams)); + this.connection.onRequest((method, params) => { + if (method === LABELEXPERIENCEREQUEST) { + this.labelingExperienceHandler(params); + } + }); + } + + start() { + this.connection.listen(); + } + + protected labelingExperienceHandler(params: any): void { + const document: TextDocument | undefined = this.documents.get(params.uri); + if (!document) { + return; + } + const position = params.position; + const range = Range.create(position.lineNumber - 1, 0, position.lineNumber - 1, position.column); + const curLineContent = document.getText(range); + // eslint-disable-next-line security/detect-unsafe-regex + const labeledUtterRegex = /^\s*-([^{}]*\s*\{[\w.@:\s]+\s*=\s*[\w.]+\}[^{}]*)+$/; + + if (labeledUtterRegex.test(curLineContent)) { + const newText = util.removeLabelsInUtterance(curLineContent); + + const newPos = Position.create(position.lineNumber, 0); + const newUnlalbelText = newText + '\n'; + const editPreviousLine: TextEdit = TextEdit.insert(newPos, newUnlalbelText); + const newPos2 = Position.create(position.lineNumber, curLineContent.length + 1); + const editNextLine: TextEdit = TextEdit.insert(newPos2, '\n' + newUnlalbelText); + const edits: TextEdit[] = [editPreviousLine, editNextLine]; + this.connection.sendNotification('addUnlabelUtterance', { edits: edits }); + } + } + + protected async docTypeFormat(params: DocumentOnTypeFormattingParams): Promise { + const document = this.documents.get(params.textDocument.uri); + if (!document) { + return Promise.resolve(null); + } + + const lastLineContent = this.getLastLineContent(params); + const edits: TextEdit[] = []; + const curLineNumber = params.position.line; + const lineCount = document.lineCount; + const text = document.getText(); + const lines = text.split('\n'); + const position = params.position; + const textBeforeCurLine = lines.slice(0, curLineNumber).join('\n'); + const range = Range.create(position.line, 0, position.line, position.character); + const curLineContent = document.getText(range); + const key = params.ch; + const inputState = this.getInputLineState(params); + + const pos = params.position; + if ( + key === '\n' && + inputState === 'utterance' && + lastLineContent.trim() !== '-' && + curLineNumber === lineCount - 1 + ) { + const newPos = Position.create(pos.line + 1, 0); + const item: TextEdit = TextEdit.insert(newPos, '- '); + edits.push(item); + } + + if (key === '\n' && inputState === 'mlEntity' && lastLineContent.trim().endsWith('=')) { + const mlEntities = util.getMLEntities(textBeforeCurLine); + const entityNameRegExp = /^\s*@\s*([0-9a-zA-Z_.-]+)\s*.*/; + let entityName = ''; + if (entityNameRegExp.test(lastLineContent)) { + const entityGroup = lastLineContent.match(entityNameRegExp); + if (entityGroup && entityGroup.length >= 2) { + entityName = entityGroup[1]; + } + if (mlEntities.includes(entityName)) { + const newPos = Position.create(pos.line, 0); + const item: TextEdit = TextEdit.insert(newPos, '\t-@'); + edits.push(item); + } + } + } + + if ( + key === '\n' && + inputState === 'listEntity' && + lastLineContent.trim() !== '-' && + curLineNumber === lineCount - 1 + ) { + const newPos = Position.create(pos.line + 1, 0); + let insertStr = ''; + if (lastLineContent.trim().endsWith(':') || lastLineContent.trim().endsWith('=')) { + insertStr = '\t-'; + } else { + insertStr = '-'; + } + const item: TextEdit = TextEdit.insert(newPos, insertStr); + edits.push(item); + } + + if (lastLineContent.trim() === '-') { + const range = Range.create(pos.line - 1, 0, pos.line, curLineContent.length + 1); + const item: TextEdit = TextEdit.del(range); + edits.push(item); + } + + return Promise.resolve(edits); + } + + private getLastLineContent(params: TextDocumentPositionParams): string { + const document = this.documents.get(params.textDocument.uri); + if (!document) { + return ''; + } + const content = document.getText(); + const position = params.position; + if (position.line === 0) { + return ''; + } else { + return content.split('\n')[position.line - 1]; + } + } + + private getInputLineState(params: DocumentOnTypeFormattingParams): LineState { + const document = this.documents.get(params.textDocument.uri); + const position = params.position; + const regListEnity = /^\s*@\s*list\s*.*$/; + const regUtterance = /^\s*#.*$/; + const regDashLine = /^\s*-.*$/; + const mlEntity = /^\s*@\s*ml\s*.*$/; + const regEntityDefLine = /^\s*@.*$/; + let state: LineState = 'other'; + if (!document) { + return 'other'; + } + const lineContentList = document.getText().split('\n'); + for (let i = 0; i < position.line; i++) { + const line = lineContentList[i]; + if (regListEnity.test(line)) { + state = 'listEntity'; + } else if (regUtterance.test(line)) { + state = 'utterance'; + } else if (mlEntity.test(line)) { + state = 'mlEntity'; + } else if (regDashLine.test(line) || regEntityDefLine.test(line) || line.trim().startsWith('>')) { + continue; + } else { + state = 'other'; + } + } + + return state; + } + + protected async resovleSchema(url: string): Promise { + const uri = URI.parse(url); + if (uri.scheme === 'file') { + return new Promise((resolve, reject) => { + // eslint-disable-next-line security/detect-non-literal-fs-filename + fs.readFile(uri.fsPath, 'UTF-8', (err, result) => { + err ? reject('') : resolve(result.toString()); + }); + }); + } + try { + const response = await xhr({ url, followRedirects: 5 }); + return response.responseText; + } catch (error) { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + } + } + + private async validateLuBody(content: string): Promise<{ parsedContent: any; errors: any }> { + const errors: Diagnostic[] = []; + let parsedContent: any; + const log = false; + const locale = 'en-us'; + try { + parsedContent = await parseFile(content, log, locale); + if (parsedContent !== undefined) { + try { + validateLUISBlob(parsedContent.LUISJsonStructure); + } catch (e) { + e.diagnostics.forEach(diag => { + const range = Range.create(0, 0, 0, 1); + const message = diag.Message; + const severity = DiagnosticSeverity.Error; + errors.push(Diagnostic.create(range, message, severity)); + }); + } + } + } catch (e) { + e.diagnostics.forEach(diag => { + const range = Range.create( + diag.Range.Start.Line - 1, + diag.Range.Start.Character, + diag.Range.End.Line - 1, + diag.Range.End.Character + ); + const message = diag.Message; + const severity = DiagnosticSeverity.Error; + errors.push(Diagnostic.create(range, message, severity)); + }); + } + + return Promise.resolve({ parsedContent, errors }); + } + + private async extractLUISContent(text: string): Promise { + let parsedContent: any; + const log = false; + const locale = 'en-us'; + try { + parsedContent = await parseFile(text, log, locale); + } catch (e) { + // nothing to do in catch block + } + + if (parsedContent !== undefined) { + return Promise.resolve(parsedContent.LUISJsonStructure); + } else { + return undefined; + } + } + + protected async completion(params: TextDocumentPositionParams): Promise { + const document = this.documents.get(params.textDocument.uri); + if (!document) { + return Promise.resolve(null); + } + + const position = params.position; + const range = Range.create(position.line, 0, position.line, position.character); + const curLineContent = document.getText(range); + const text = document.getText(); + const lines = text.split('\n'); + const curLineNumber = params.position.line; + //const textBeforeCurLine = lines.slice(0, curLineNumber).join('\n'); + const textExceptCurLine = lines + .slice(0, curLineNumber) + .concat(lines.slice(curLineNumber + 1)) + .join('\n'); + const completionList: CompletionItem[] = []; + if (util.isEntityType(curLineContent)) { + const entityTypes: string[] = EntityTypesObj.EntityType; + entityTypes.forEach(entity => { + const item = { + label: entity, + kind: CompletionItemKind.Keyword, + insertText: `${entity}`, + documentation: `Enitity type: ${entity}`, + }; + + completionList.push(item); + }); + } + + if (util.isPrebuiltEntity(curLineContent)) { + const prebuiltTypes: string[] = EntityTypesObj.Prebuilt; + prebuiltTypes.forEach(entity => { + const item = { + label: entity, + kind: CompletionItemKind.Keyword, + insertText: `${entity}`, + documentation: `Prebuilt enitity: ${entity}`, + }; + + completionList.push(item); + }); + } + + if (util.isRegexEntity(curLineContent)) { + const item = { + label: 'RegExp Entity', + kind: CompletionItemKind.Keyword, + insertText: `//`, + documentation: `regex enitity`, + }; + + completionList.push(item); + } + + if (util.isEntityName(curLineContent)) { + const item = { + label: 'hasRoles?', + kind: CompletionItemKind.Keyword, + insertText: `hasRoles`, + documentation: `Entity name hasRole?`, + }; + + completionList.push(item); + const item2 = { + label: 'usesFeature?', + kind: CompletionItemKind.Keyword, + insertText: `usesFeature`, + documentation: `Entity name usesFeature?`, + }; + + completionList.push(item2); + } + + // completion for entities and patterns, use the text without current line due to usually it will cause parser errors, the luisjson will be undefined + + let luisJson = await this.extractLUISContent(text); + if (!luisJson) { + luisJson = await this.extractLUISContent(textExceptCurLine); + } + + const suggestionEntityList = util.getSuggestionEntities(luisJson, util.suggestionAllEntityTypes); + const regexEntityList = util.getRegexEntities(luisJson); + + //suggest a regex pattern for seperated line definition + if (util.isSeperatedEntityDef(curLineContent)) { + const seperatedEntityDef = /^\s*@\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/; + let entityName = ''; + const matchGroup = curLineContent.match(seperatedEntityDef); + if (matchGroup && matchGroup.length >= 2) { + entityName = matchGroup[1].trim(); + } + + if (regexEntityList.includes(entityName)) { + const item = { + label: 'RegExp Entity', + kind: CompletionItemKind.Keyword, + insertText: `//`, + documentation: `regex enitity`, + }; + + completionList.push(item); + } + } + + // auto suggest pattern + if (util.matchedEnterPattern(curLineContent)) { + suggestionEntityList.forEach(name => { + const item = { + label: `Entity: ${name}`, + kind: CompletionItemKind.Property, + insertText: `${name}`, + documentation: `pattern suggestion for entity: ${name}`, + }; + + completionList.push(item); + }); + } + + // suggestions for entities in a seperated line + if (util.isEntityType(curLineContent)) { + suggestionEntityList.forEach(entity => { + const item = { + label: entity, + kind: CompletionItemKind.Property, + insertText: `${entity}`, + documentation: `Enitity type: ${entity}`, + }; + + completionList.push(item); + }); + } + + if (util.isCompositeEntity(curLineContent)) { + util.getSuggestionEntities(luisJson, util.suggestionNoCompositeEntityTypes).forEach(entity => { + const item = { + label: entity, + kind: CompletionItemKind.Property, + insertText: `${entity}`, + documentation: `Enitity type: ${entity}`, + }; + + completionList.push(item); + }); + } + + const suggestionRolesList = util.getSuggestionRoles(luisJson, util.suggestionAllEntityTypes); + // auto suggest roles + if (util.matchedRolesPattern(curLineContent)) { + suggestionRolesList.forEach(name => { + const item = { + label: `Role: ${name}`, + kind: CompletionItemKind.Property, + insertText: `${name}`, + documentation: `roles suggestion for entity name: ${name}`, + }; + + completionList.push(item); + }); + } + + if (util.matchedEntityPattern(curLineContent)) { + suggestionEntityList.forEach(name => { + const item = { + label: `Entity: ${name}`, + kind: CompletionItemKind.Property, + insertText: ` ${name}`, + documentation: `pattern suggestion for entity: ${name}`, + }; + completionList.push(item); + }); + } + + if (util.matchedEntityCanUsesFeature(curLineContent, text, luisJson)) { + const enitityName = util.extractEntityNameInUseFeature(curLineContent); + const suggestionFeatureList = util.getSuggestionEntities(luisJson, util.suggestionNoPatternAnyEntityTypes); + suggestionFeatureList.forEach(name => { + if (name !== enitityName) { + const item = { + label: `Entity: ${name}`, + kind: CompletionItemKind.Method, + insertText: `${name}`, + documentation: `Feature suggestion for current entity: ${name}`, + }; + + completionList.push(item); + } + }); + } + + if (util.matchIntentInEntityDef(curLineContent)) { + const item = { + label: 'usesFeature?', + kind: CompletionItemKind.Keyword, + insertText: `usesFeature`, + documentation: `Does this intent usesFeature?`, + }; + + completionList.push(item); + } + + if (util.matchIntentUsesFeatures(curLineContent)) { + const suggestionFeatureList = util.getSuggestionEntities(luisJson, util.suggestionNoPatternAnyEntityTypes); + suggestionFeatureList.forEach(name => { + const item = { + label: `Entity: ${name}`, + kind: CompletionItemKind.Method, + insertText: `${name}`, + documentation: `Feature suggestion for current entity: ${name}`, + }; + + completionList.push(item); + }); + } + + return Promise.resolve({ isIncomplete: false, items: completionList }); + } + + protected validate(document: TextDocument): void { + this.cleanPendingValidation(document); + document.uri, + setTimeout(() => { + this.pendingValidationRequests.delete(document.uri); + this.doValidate(document); + }); + } + + protected cleanPendingValidation(document: TextDocument): void { + const request = this.pendingValidationRequests.get(document.uri); + if (request !== undefined) { + clearTimeout(request); + this.pendingValidationRequests.delete(document.uri); + } + } + + protected doValidate(document: TextDocument): void { + if (document.getText().length === 0) { + this.cleanDiagnostics(document); + return; + } + + const text = document.getText(); + this.validateLuBody(text).then(result => { + const diagnostics: Diagnostic[] = result.errors; + this.sendDiagnostics(document, diagnostics); + }); + } + + protected cleanDiagnostics(document: TextDocument): void { + this.sendDiagnostics(document, []); + } + + protected sendDiagnostics(document: TextDocument, diagnostics: Diagnostic[]): void { + this.connection.sendDiagnostics({ + uri: document.uri, + diagnostics, + }); + } +} diff --git a/Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts b/Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts new file mode 100644 index 0000000000..c30b5c1039 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/src/entityEnum.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export const EntityTypesObj = { + EntityType: ['ml', 'prebuilt', 'regex', 'list', 'composite', 'patternany', 'phraselist'], + Prebuilt: [ + 'age', + 'datetimeV2', + 'dimension', + 'email', + 'geographyV2', + 'keyPhrase', + 'money', + 'number', + 'ordinal', + 'ordinalV2', + 'percentage', + 'personName', + 'phonenumber', + 'temperature', + 'url', + 'datetime', + ], +}; + +export type LineState = 'listEntity' | 'utterance' | 'mlEntity' | 'other'; diff --git a/Composer/packages/tools/language-servers/language-understanding/src/index.ts b/Composer/packages/tools/language-servers/language-understanding/src/index.ts new file mode 100644 index 0000000000..0b5ae34d3c --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/src/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export * from './LUServer'; diff --git a/Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts b/Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts new file mode 100644 index 0000000000..6f1cc9fec2 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/src/matchingPattern.ts @@ -0,0 +1,225 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export function getMLEntities(text: string): string[] { + const lines = text.split('\n'); + + const mlEntityRegExp = /^\s*@\s*ml\s*([0-9a-zA-Z_.-]+)\s*.*$/; + const mlEntities: string[] = []; + for (const line of lines) { + if (mlEntityRegExp.test(line)) { + const entityGroup = line.match(mlEntityRegExp); + if (entityGroup && entityGroup.length >= 2) { + mlEntities.push(entityGroup[1]); + } + } + } + + return mlEntities; +} + +export function getCompositesEntities(luisJson: any): string[] { + const suggestionCompositesList: string[] = []; + if (luisJson !== undefined) { + if (luisJson.composites !== undefined && luisJson.composites.length > 0) { + luisJson.composites.forEach(entity => { + suggestionCompositesList.push(entity.name); + }); + } + } + + return suggestionCompositesList; +} + +export function matchedEntityCanUsesFeature(lineContent: string, text: string, luisJson: any): boolean { + const mlTypedEntityusesFeature = /^\s*@\s*ml\s*\w+\s*usesFeature\s*$/; + const compositesTypedEntityusesFeature = /^\s*@\s*composites\s*\w+\s*usesFeature\s*$/; + const notTypedEntityusesFeature = /^\s*@\s*(\w*)\s*usesFeature\s*$/; + if (mlTypedEntityusesFeature.test(lineContent) || compositesTypedEntityusesFeature.test(lineContent)) { + return true; + } else if (notTypedEntityusesFeature.test(lineContent)) { + const matchedGroups = lineContent.match(notTypedEntityusesFeature); + if (matchedGroups && matchedGroups.length >= 2) { + const entityName = matchedGroups[1]; + const validEntitiesCanusesFeature = getMLEntities(text).concat(getCompositesEntities(luisJson)); + if (validEntitiesCanusesFeature.includes(entityName)) { + return true; + } + } + } + + return false; +} + +export function matchIntentUsesFeatures(content: string): boolean { + const intentUsesFeaturesRegEx = /^\s*@\s*intent\s*\w*\s*usesFeature\s*$/; + return intentUsesFeaturesRegEx.test(content); +} + +export function matchIntentInEntityDef(content: string): boolean { + const intentInEntityDefRegEx = /^\s*@\s*intent\s*\w+\s*$/; + return intentInEntityDefRegEx.test(content); +} + +export function isEntityType(content: string): boolean { + const regexEntifyDef = /^\s*@\s*$/; + return regexEntifyDef.test(content); +} + +export function isPrebuiltEntity(content: string): boolean { + const regexPrebuiltEntifyDef = /^\s*@\s*prebuilt\s*$/; + return regexPrebuiltEntifyDef.test(content); +} + +export function isRegexEntity(content: string): boolean { + const regexPrebuiltEntifyDef = /^\s*@\s*regex\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/; + return regexPrebuiltEntifyDef.test(content); +} + +export function isSeperatedEntityDef(content: string): boolean { + const regexPrebuiltEntifyDef = /^\s*@\s*([\w._]+|"[\w._\s]+")+\s*=\s*$/; + return regexPrebuiltEntifyDef.test(content); +} + +export function isEntityName(content: string): boolean { + const hasNameEntifyDef = /^\s*@\s*(ml|list|regex|prebuilt|composite|patternany|phraselist)\s*([\w._]+|"[\w._\s]+")\s*$/; + const hasTypeEntityDef = /^\s*@\s*(ml|list|regex|prebuilt|composite|patternany|phraselist|intent)\s*$/; + const hasNameEntifyDef2 = /^\s*@\s*([\w._]+|"[\w._\s]+")\s*$/; + return hasNameEntifyDef.test(content) || (!hasTypeEntityDef.test(content) && hasNameEntifyDef2.test(content)); +} + +export function isCompositeEntity(content: string): boolean { + const compositePatternDef = /^\s*@\s*composite\s*[\w]*\s*=\s*\[\s*.*\s*$/; + const compositePatternDef2 = /^\s*@\s*composite\s*[\w]*\s*=\s*\[\s*.*\s*\]\s*$/; + return compositePatternDef.test(content) || compositePatternDef2.test(content); +} +export function matchedEnterPattern(content: string): boolean { + const regexPatternDef = /^\s*-.*{\s*$/; + const regexPatternDef2 = /^\s*-.*{\s*}$/; + return regexPatternDef.test(content) || regexPatternDef2.test(content); +} + +export function matchedRolesPattern(content: string): boolean { + const regexRolesPatternDef = /^\s*-.*{\s*.*:/; + const regexRolesPatternDef2 = /^\s*-.*{\s*.*:}/; + return regexRolesPatternDef.test(content) || regexRolesPatternDef2.test(content); +} + +export function matchedEntityPattern(content: string): boolean { + const regexRolesEntityPatternDef = /^\s*-.*{\s*@\s*$/; + const regexRolesEntityPatternDef2 = /^\s*-.*{\s*@\s*}\s*$/; + return regexRolesEntityPatternDef.test(content) || regexRolesEntityPatternDef2.test(content); +} + +export function getRegexEntities(luisJson: any): string[] { + const suggestionRegexList: string[] = []; + if (luisJson !== undefined) { + if (luisJson.regex_entities !== undefined && luisJson.regex_entities.length > 0) { + luisJson.regex_entities.forEach(entity => { + suggestionRegexList.push(entity.name); + }); + } + } + + return suggestionRegexList; +} + +export function getSuggestionEntities(luisJson: any, suggestionEntityTypes: string[]): string[] { + const suggestionEntityList: string[] = []; + if (luisJson !== undefined) { + suggestionEntityTypes.forEach(entityType => { + if (luisJson[entityType] !== undefined && luisJson[entityType].length > 0) { + luisJson[entityType].forEach(entity => { + if (entity) { + suggestionEntityList.push(entity.name); + } + }); + } + }); + } + + return suggestionEntityList; +} + +export const suggestionAllEntityTypes = [ + 'entities', + 'regex_entities', + 'patternAnyEntities', + 'preBuiltEntities', + 'closedLists', + 'phraselists', + 'composites', +]; + +export const suggestionNoPatternAnyEntityTypes = [ + 'entities', + 'regex_entities', + 'preBuiltEntities', + 'closedLists', + 'phraselists', + 'composites', +]; + +export const suggestionNoCompositeEntityTypes = [ + 'entities', + 'regex_entities', + 'patternAnyEntities', + 'preBuiltEntities', + 'closedLists', + 'phraselists', +]; + +export function getSuggestionRoles(luisJson: any, suggestionEntityTypes: string[]): string[] { + const suggestionRolesList: string[] = []; + if (luisJson !== undefined) { + suggestionEntityTypes.forEach(entityType => { + if (luisJson[entityType] !== undefined && luisJson[entityType].length > 0) { + luisJson[entityType].forEach(entity => { + if (entity.roles !== undefined && entity.roles.length > 0) { + entity.roles.forEach(role => { + suggestionRolesList.push(role); + }); + } + }); + } + }); + } + + return suggestionRolesList; +} + +export function extractEntityNameInUseFeature(lineContent: string): string { + const notTypedEntityusesFeature = /^\s*@\s*(\w*)\s*usesFeature\s*/; + if (notTypedEntityusesFeature.test(lineContent)) { + const matchedGroups = lineContent.match(notTypedEntityusesFeature); + if (matchedGroups && matchedGroups.length === 2) { + const entityName = matchedGroups[1]; + return entityName; + } + } + + return ''; +} + +export function removeLabelsInUtterance(lineContent: string): string { + const entityLabelRegex = /\{\s*[\w.@:\s]+\s*=\s*[\w.]+\s*\}/g; + let match: RegExpMatchArray | null; + let resultStr = ''; + let startIdx = 0; + while ((match = entityLabelRegex.exec(lineContent))) { + const leftBoundIdx = match.index; + const rightBoundIdx = entityLabelRegex.lastIndex; + resultStr += lineContent.slice(startIdx, leftBoundIdx); + if (leftBoundIdx && rightBoundIdx) { + const entityStr = lineContent.slice(leftBoundIdx + 1, rightBoundIdx - 1); + if (entityStr.split('=').length == 2) { + const enitity = entityStr.split('=')[1].trim(); + resultStr += enitity; + } + + startIdx = rightBoundIdx; + } + } + + return resultStr; +} diff --git a/Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json b/Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json new file mode 100644 index 0000000000..7573b46ddf --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + /* Options used for building production code (tests excluded) */ + "extends": "./tsconfig.json", + "include": [ + "./src/**/*" + ] + } + \ No newline at end of file diff --git a/Composer/packages/tools/language-servers/language-understanding/tsconfig.json b/Composer/packages/tools/language-servers/language-understanding/tsconfig.json new file mode 100644 index 0000000000..960cdb7e22 --- /dev/null +++ b/Composer/packages/tools/language-servers/language-understanding/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "./src/**/*", + "./__tests__/**/*" + ] + } + \ No newline at end of file diff --git a/Composer/packages/tools/language-servers/package.json b/Composer/packages/tools/language-servers/package.json index 81f9f1ffd7..0567b63dd1 100644 --- a/Composer/packages/tools/language-servers/package.json +++ b/Composer/packages/tools/language-servers/package.json @@ -5,8 +5,9 @@ "description": "", "main": "index.js", "scripts": { - "build": "yarn build:lg", - "build:lg": "cd language-generation && yarn build" + "build": "yarn build:lg && yarn build:lu", + "build:lg": "cd language-generation && yarn build", + "build:lu": "cd language-understanding && yarn build" }, "author": "" } diff --git a/Composer/yarn.lock b/Composer/yarn.lock index f3f43aa876..3bb2acdd96 100644 --- a/Composer/yarn.lock +++ b/Composer/yarn.lock @@ -2658,7 +2658,7 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@microsoft/bf-cli-command@1.0.0": +"@microsoft/bf-cli-command@1.0.0", "@microsoft/bf-cli-command@https://botbuilder.myget.org/F/botbuilder-declarative/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-1.0.0.tgz": version "1.0.0" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-1.0.0.tgz#0d681e93690c4e872bf1181a3ff02f094830929e" integrity sha1-DWgek2kMTocr8RgaP/AvCUgwkp4= @@ -3415,7 +3415,7 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@types/webpack@^4.4.19": +"@types/webpack@^4.4.19", "@types/webpack@^4.4.31": version "4.39.1" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.39.1.tgz#d76cd551cc851198f67f75ff3e26551d204530e9" integrity sha512-rgO9ihNu/l72Sjx3shqwc9r6gi+tOMsqxhMEZhOEVIZt82GFOeUyEdpTk1BO2HqEHLS/XJW8ldUTIIfIMMyYFQ== @@ -5520,6 +5520,14 @@ clean-stack@^2.0.0: resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha1-7oRy27Ep5yezHooQpCfe6d/kAIs= +clean-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz#a99d8ec34c1c628a4541567aa7b457446460c62b" + integrity sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A== + dependencies: + "@types/webpack" "^4.4.31" + del "^4.1.1" + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -6246,7 +6254,7 @@ css-has-pseudo@^0.10.0: postcss "^7.0.6" postcss-selector-parser "^5.0.0-rc.4" -css-loader@3.2.0: +css-loader@3.2.0, css-loader@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2" integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ== @@ -8097,75 +8105,75 @@ expect@^24.5.0: jest-message-util "^24.5.0" jest-regex-util "^24.3.0" -express@^4.16.4: - version "4.16.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" - integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== +express@^4.15.2, express@^4.17.1: + version "4.17.1" + resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ= dependencies: - accepts "~1.3.5" + accepts "~1.3.7" array-flatten "1.1.1" - body-parser "1.18.3" - content-disposition "0.5.2" + body-parser "1.19.0" + content-disposition "0.5.3" content-type "~1.0.4" - cookie "0.3.1" + cookie "0.4.0" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.1.1" + finalhandler "~1.1.2" fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.2" + parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.4" - qs "6.5.2" - range-parser "~1.2.0" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" safe-buffer "5.1.2" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" -express@^4.17.1: - version "4.17.1" - resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ= +express@^4.16.4: + version "4.16.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== dependencies: - accepts "~1.3.7" + accepts "~1.3.5" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.18.3" + content-disposition "0.5.2" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.3.1" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.1.1" fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.3" + parseurl "~1.3.2" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" + proxy-addr "~2.0.4" + qs "6.5.2" + range-parser "~1.2.0" safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" utils-merge "1.0.1" vary "~1.1.2" @@ -10874,6 +10882,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonc-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" + integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -11523,6 +11536,11 @@ loglevel@^1.6.4: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g== +loglevel@^1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" + integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== + lookup-closest-locale@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz#57f665e604fd26f77142d48152015402b607bcf3" @@ -12036,7 +12054,7 @@ mocha-multi-reporters@^1.1.7: debug "^3.1.0" lodash "^4.16.4" -mocha@5.2.0: +mocha@5.2.0, mocha@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== @@ -12070,7 +12088,7 @@ moment@2.24.0, "moment@>= 2.9.0", moment@>=2.14.0: resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== -monaco-editor-core@0.17.0: +monaco-editor-core@0.17.0, monaco-editor-core@^0.17.0: version "0.17.0" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/monaco-editor-core/-/monaco-editor-core-0.17.0.tgz#8d2b7f7e02ac68f68eecc881d7a26457cbc09b49" integrity sha1-jSt/fgKsaPaO7MiB16JkV8vAm0k= @@ -14246,7 +14264,7 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -querystringify@^2.0.0: +querystringify@^2.0.0, querystringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== @@ -15497,6 +15515,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.5.0, source-map-support@~0.5.12: + version "0.5.16" + resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha1-CuBp5/47p1OMZMmFFeNTOerFoEI= + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6: version "0.5.12" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" @@ -15513,14 +15539,6 @@ source-map-support@~0.5.10: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.12: - version "0.5.16" - resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha1-CuBp5/47p1OMZMmFFeNTOerFoEI= - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -15906,7 +15924,7 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -style-loader@1.0.0: +style-loader@1.0.0, style-loader@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82" integrity sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw== @@ -16422,7 +16440,7 @@ ts-loader@^6.0.3: micromatch "^4.0.0" semver "^6.0.0" -ts-loader@^6.2.1: +ts-loader@^6.1.0, ts-loader@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.1.tgz#67939d5772e8a8c6bdaf6277ca023a4812da02ef" integrity sha512-Dd9FekWuABGgjE1g0TlQJ+4dFUfYGbYcs52/HQObE0ZmUNjQlmLAS7xXsSzy23AMaMwipsx5sNHvoEpT2CZq1g== @@ -16433,6 +16451,17 @@ ts-loader@^6.2.1: micromatch "^4.0.0" semver "^6.0.0" +ts-node@^8.3.0: + version "8.5.4" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.5.4.tgz#a152add11fa19c221d0b48962c210cf467262ab2" + integrity sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "^3.0.0" + ts-node@^8.4.1: version "8.4.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.4.1.tgz#270b0dba16e8723c9fa4f9b4775d3810fd994b4f" @@ -16546,6 +16575,11 @@ typescript@3.7.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" + integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== + ua-parser-js@^0.7.18: version "0.7.19" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b" @@ -16575,6 +16609,29 @@ uglify-js@^3.5.1: commander "~2.20.3" source-map "~0.6.1" +uglify-js@^3.6.0: + version "3.7.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a" + integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg== + dependencies: + commander "~2.20.3" + source-map "~0.6.1" + +uglifyjs-webpack-plugin@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.2.0.tgz#e75bc80e7f1937f725954c9b4c5a1e967ea9d0d7" + integrity sha512-mHSkufBmBuJ+KHQhv5H0MXijtsoA1lynJt1lXOaotja8/I0pR4L9oGaPIZw+bQBOFittXZg9OC1sXSGO9D9ZYg== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^1.7.0" + source-map "^0.6.1" + uglify-js "^3.6.0" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + undefsafe@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" @@ -16781,6 +16838,14 @@ url-parse@^1.4.3: querystringify "^2.0.0" requires-port "^1.0.0" +url-parse@^1.4.4: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" @@ -16935,6 +17000,17 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +vscode-json-languageservice@^3.3.1: + version "3.4.11" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.11.tgz#7c0632bccc4b2b955f99f99f43d96d3eece1de42" + integrity sha512-26Qv1SFp6x3XmCqU1BRceRsSKRO3xkQa6/K8ziSRt52/LQPiw5ipSxlGVSlzIoi5LCmQVEqUajhiVEMNlFXhNw== + dependencies: + jsonc-parser "^2.2.0" + vscode-languageserver-textdocument "^1.0.0-next.5" + vscode-languageserver-types "^3.15.0-next.9" + vscode-nls "^4.1.1" + vscode-uri "^2.1.1" + vscode-jsonrpc@^4.1.0-next, vscode-jsonrpc@^4.1.0-next.3: version "4.1.0-next.3" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.3.tgz#05fe742959a2726020d4d0bfbc3d3c97873c7fde" @@ -16956,11 +17032,21 @@ vscode-languageserver-protocol@^3.15.0-next.8: vscode-jsonrpc "^4.1.0-next.3" vscode-languageserver-types "^3.15.0-next.4" +vscode-languageserver-textdocument@^1.0.0-next.5: + version "1.0.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.0-next.5.tgz#dbb7a45dd973a19261a7c57ab9a439c40f3799ee" + integrity sha512-1jp/zAidN/bF/sqPimhBX1orH5G4rzRw63k75TesukJDuxm8yW79ECStWbDSy41BHGOwSGN4M69QFvhancSr5A== + vscode-languageserver-types@^3.15.0-next.4: version "3.15.0-next.5" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" integrity sha1-hj1xG/R7M4/15jrhn7INT81NcTs= +vscode-languageserver-types@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz#957a9d1d5998a02edf62298fb7e37d9efcc6c157" + integrity sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw== + vscode-languageserver@^5.3.0-next: version "5.3.0-next.10" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-languageserver/-/vscode-languageserver-5.3.0-next.10.tgz#995fe8b57fc4eb9fea0d11762d3a803de4278995" @@ -16969,11 +17055,19 @@ vscode-languageserver@^5.3.0-next: vscode-languageserver-protocol "^3.15.0-next.8" vscode-textbuffer "^1.0.0" -vscode-nls@^4.0.0: +vscode-nls@^4.0.0, vscode-nls@^4.1.1: version "4.1.1" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha1-+ZFrZOSUeyAyLe+x5naklYYfEzw= +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + vscode-textbuffer@^1.0.0: version "1.0.0" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086" @@ -16984,6 +17078,11 @@ vscode-uri@^1.0.5: resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-uri/-/vscode-uri-1.0.8.tgz#9769aaececae4026fb6e22359cb38946580ded59" integrity sha1-l2mq7OyuQCb7biI1nLOJRlgN7Vk= +vscode-uri@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90" + integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A== + vscode-ws-jsonrpc@^0.1.1: version "0.1.1" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-ws-jsonrpc/-/vscode-ws-jsonrpc-0.1.1.tgz#163ff05662635b4fd161ed132e112cec4d83f126" @@ -16991,6 +17090,19 @@ vscode-ws-jsonrpc@^0.1.1: dependencies: vscode-jsonrpc "^4.1.0-next" +vscode@^1.1.34: + version "1.1.36" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" + integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + dependencies: + glob "^7.1.2" + mocha "^5.2.0" + request "^2.88.0" + semver "^5.4.1" + source-map-support "^0.5.0" + url-parse "^1.4.4" + vscode-test "^0.4.1" + w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" @@ -17065,7 +17177,7 @@ webpack-cli@^3.3.0: v8-compile-cache "^2.0.2" yargs "^12.0.5" -webpack-cli@^3.3.2: +webpack-cli@^3.3.2, webpack-cli@^3.3.8: version "3.3.10" resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13" integrity sha1-F7J5Jn6bT7VJAj+uFw2o5udm2hM= @@ -17132,6 +17244,45 @@ webpack-dev-server@3.9.0, webpack-dev-server@^3.9.0: ws "^6.2.1" yargs "12.0.5" +webpack-dev-server@^3.8.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.1.tgz#1ff3e5cccf8e0897aa3f5909c654e623f69b1c0e" + integrity sha512-AGG4+XrrXn4rbZUueyNrQgO4KGnol+0wm3MPdqGLmmA+NofZl3blZQKxZ9BND6RDNuvAK9OMYClhjOSnxpWRoA== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.2.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.6" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.25" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.19" + sockjs-client "1.4.0" + spdy "^4.0.1" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "12.0.5" + webpack-dev-server@^3.8.2: version "3.8.2" resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz#3292427bf6510da9a3ac2d500b924a4197667ff9" @@ -17188,6 +17339,13 @@ webpack-manifest-plugin@2.1.0: lodash ">=3.5 <5" tapable "^1.0.0" +webpack-merge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + webpack-sources@^1.1.0, webpack-sources@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" @@ -17263,6 +17421,35 @@ webpack@^4.30.0: watchpack "^1.5.0" webpack-sources "^1.3.0" +webpack@^4.39.3: + version "4.41.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" + integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.1" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + websocket-driver@>=0.5.1: version "0.7.0" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"