diff --git a/utils/doclint/xmlDocumentation.js b/utils/doclint/xmlDocumentation.js
new file mode 100644
index 0000000000..f480b51449
--- /dev/null
+++ b/utils/doclint/xmlDocumentation.js
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// @ts-check
+const Documentation = require('./documentation');
+const { visitAll } = require('../markdown');
+/**
+ * @param {Documentation.MarkdownNode[]} nodes
+ * @param {number} maxColumns
+ */
+function renderXmlDoc(nodes, maxColumns = 80, prefix = '/// ') {
+ if (!nodes)
+ return [];
+
+ const renderResult = _innerRenderNodes(nodes, maxColumns);
+
+ const doc = [];
+ _wrapInNode('summary', renderResult.summary, doc);
+ _wrapInNode('remarks', renderResult.remarks, doc);
+ return doc.map(x => `${prefix}${x}`);
+}
+
+function _innerRenderNodes(nodes, maxColumns = 80, wrapParagraphs = true) {
+ const summary = [];
+ const remarks = [];
+ const handleListItem = (lastNode, node) => {
+ if (node && node.type === 'li' && (!lastNode || lastNode.type !== 'li'))
+ summary.push(``);
+ else if (lastNode && lastNode.type === 'li' && (!node || node.type !== 'li'))
+ summary.push('
');
+
+ };
+
+ let lastNode;
+ visitAll(nodes, node => {
+ // handle special cases first
+ if (_nodeShouldBeIgnored(node))
+ return;
+ if (node.text && node.text.startsWith('extends: ')) {
+ remarks.push('Inherits from ' + node.text.replace('extends: ', ''));
+ return;
+ }
+ handleListItem(lastNode, node);
+ if (node.type === 'text') {
+ if (wrapParagraphs)
+ _wrapInNode('para', _wrapAndEscape(node, maxColumns), summary);
+ else
+ summary.push(..._wrapAndEscape(node, maxColumns));
+ } else if (node.type === 'code' && node.codeLang === 'csharp') {
+ _wrapInNode('code', node.lines, summary);
+ } else if (node.type === 'li') {
+ _wrapInNode('item>${nodes[0]}<${closingTag}>`);
+ return;
+ }
+
+ target.push(`<${tag}>`);
+ target.push(...nodes);
+ target.push(`<${closingTag}>`);
+}
+
+/**
+ *
+ * @param {Documentation.MarkdownNode} node
+ */
+function _wrapAndEscape(node, maxColumns = 0) {
+ const lines = [];
+ const pushLine = text => {
+ if (text === '')
+ return;
+ text = text.trim();
+ lines.push(text);
+ };
+
+ const text = node.text.replace(/[^\[]`([^\]]*[^\[])`[^\]]/g, (m, g1) => ` ${g1} `);
+ const words = text.split(' ');
+ let line = '';
+ for (let i = 0; i < words.length; i++) {
+ line = line + ' ' + words[i];
+ if (line.length >= maxColumns) {
+ pushLine(line);
+ line = '';
+ }
+ }
+
+ pushLine(line);
+ return lines;
+}
+
+/**
+ *
+ * @param {Documentation.MarkdownNode} node
+ */
+function _nodeShouldBeIgnored(node) {
+ if (!node
+ || (node.text === 'extends: [EventEmitter]'))
+ return true;
+
+ return false;
+}
+
+/**
+ * @param {Documentation.MarkdownNode[]} nodes
+ */
+function renderTextOnly(nodes, maxColumns = 80) {
+ const result = _innerRenderNodes(nodes, maxColumns, false);
+ return result.summary;
+}
+
+module.exports = { renderXmlDoc, renderTextOnly }
\ No newline at end of file