Skip to content

Commit

Permalink
Better notebook markup renderer api
Browse files Browse the repository at this point in the history
For #121256

- Use js modules for notebook output renderers
- Rename apis from `markdown` to `markup`
- Use imports and exports for apis instead of globals for apis
- Use esbuild instead of webpack so we can emit js modules
- Clearly split top level markup renderes from renderers that extend other renderers
  • Loading branch information
mjbvz committed Apr 26, 2021
1 parent 5c8ab73 commit 652e812
Show file tree
Hide file tree
Showing 21 changed files with 293 additions and 199 deletions.
68 changes: 60 additions & 8 deletions build/gulpfile.extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require('events').EventEmitter.defaultMaxListeners = 100;

const gulp = require('gulp');
const path = require('path');
const child_process = require('child_process');
const nodeUtil = require('util');
const es = require('event-stream');
const filter = require('gulp-filter');
Expand Down Expand Up @@ -203,30 +204,38 @@ gulp.task(compileExtensionsBuildLegacyTask);
// Additional projects to webpack. These typically build code for webviews
const webpackMediaConfigFiles = [
'markdown-language-features/webpack.config.js',
'markdown-language-features/webpack.notebook.js',
'notebook-markdown-extensions/webpack.notebook.js',
'simple-browser/webpack.config.js',
];

const compileExtensionMediaTask = task.define('compile-extension-media', () => webpackExtensionMedia(false));
// Additional projects to run esbuild on. These typically build code for webviews
const esbuildMediaScripts = [
'markdown-language-features/esbuild.js',
'notebook-markdown-extensions/esbuild.js',
];

const compileExtensionMediaTask = task.define('compile-extension-media', () => buildExtensionMedia(false));
gulp.task(compileExtensionMediaTask);
exports.compileExtensionMediaTask = compileExtensionMediaTask;

const watchExtensionMedia = task.define('watch-extension-media', () => webpackExtensionMedia(true));
const watchExtensionMedia = task.define('watch-extension-media', () => buildExtensionMedia(true));
gulp.task(watchExtensionMedia);
exports.watchExtensionMedia = watchExtensionMedia;

function webpackExtensionMedia(isWatch, outputRoot) {
const compileExtensionMediaBuildTask = task.define('compile-extension-media-build', () => buildExtensionMedia(false, '.build/extensions'));
gulp.task(compileExtensionMediaBuildTask);

async function buildExtensionMedia(isWatch, outputRoot) {
const webpackConfigLocations = webpackMediaConfigFiles.map(p => {
return {
configPath: path.join(extensionsPath, p),
outputRoot: outputRoot ? path.join(root, outputRoot, path.dirname(p)) : undefined
};
});
return webpackExtensions('packaging extension media', isWatch, webpackConfigLocations);
return Promise.all([
webpackExtensions('webpacking extension media', isWatch, webpackConfigLocations),
esbuildExtensions('esbuilding extension media', isWatch, outputRoot, esbuildMediaScripts.map(p => path.join(extensionsPath, p))),
]);
}
const compileExtensionMediaBuildTask = task.define('compile-extension-media-build', () => webpackExtensionMedia(false, '.build/extensions'));
gulp.task(compileExtensionMediaBuildTask);

//#endregion

Expand Down Expand Up @@ -336,4 +345,47 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
});
}

/**
* @param {string} taskName
* @param {boolean} isWatch
* @param {string | undefined} outputRoot
* @param {string} esbuildScripts
*/
async function esbuildExtensions(taskName, isWatch, outputRoot, esbuildScripts) {
function reporter(/** @type {string} */ stdError, /** @type {string} */script) {
const matches = (stdError || '').match(/\> (.+): error: (.+)?/g);
fancyLog(`Finished ${ansiColors.green(taskName)} ${script} with ${matches ? matches.length : 0} errors.`);
for (const match of matches || []) {
fancyLog.error(match);
}
}

const scripts = esbuildScripts.map(script => {
return new Promise((resolve, reject) => {
const args = [script];
if (isWatch) {
args.push('--watch');
}
if (outputRoot) {
args.push('--outputRoot', outputRoot);
}
const proc = child_process.execFile(process.argv[0], args, {}, (error, _stdout, stderr) => {
if (error) {
return reject(error);
}
reporter(stderr, script);
if (stderr) {
return reject();
}
return resolve();
});

proc.stdout.on('data', (data) => {
fancyLog(`${ansiColors.green(taskName)}: ${data.toString('utf8')}`);
});
});
});
return Promise.all(scripts);
}


31 changes: 31 additions & 0 deletions extensions/markdown-language-features/esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const esbuild = require('esbuild');

const args = process.argv.slice(2);

const isWatch = args.indexOf('--watch') >= 0;

let outputRoot = __dirname;
const outputRootIndex = args.indexOf('--outputRoot');
if (outputRootIndex >= 0) {
outputRoot = args[outputRootIndex + 1];
}

const outDir = path.join(outputRoot, 'notebook-out');
esbuild.build({
entryPoints: [
path.join(__dirname, 'notebook', 'index.ts'),
],
bundle: true,
minify: true,
sourcemap: false,
format: 'esm',
outdir: outDir,
platform: 'browser',
target: ['es2020'],
incremental: isWatch,
}).catch(() => process.exit(1));
48 changes: 27 additions & 21 deletions extensions/markdown-language-features/notebook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,37 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as MarkdownIt from 'markdown-it';
const MarkdownIt = require('markdown-it');

declare const acquireNotebookRendererApi: any;
type extendMarkdownItFnType = (
(f: (md: MarkdownIt.MarkdownIt) => void) => void
);

(function () {
const markdownIt = new MarkdownIt({
export async function activate(ctx: {
dependencies: ReadonlyArray<{ entrypoint: string }>
}) {
let markdownIt = new MarkdownIt({
html: true
});

(globalThis as any).extendMarkdownIt = ((f: (md: MarkdownIt.MarkdownIt) => void) => {
f(markdownIt);
}) as extendMarkdownItFnType;

const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer');
// Should we load the deps before this point?
// Also could we await inside `renderMarkup`?
await Promise.all(ctx.dependencies.map(async (dep) => {
try {
const api = await import(dep.entrypoint);
if (api?.extendMarkdownIt) {
markdownIt = api.extendMarkdownIt(markdownIt);
}
} catch (e) {
console.error('Could not load markdown entryPoint', e);
}
}));

notebook.onDidCreateMarkdown(({ element, content }: any) => {
const rendered = markdownIt.render(content);
element.innerHTML = rendered;
return {
renderMarkup: (context: { element: HTMLElement, content: string }) => {
const rendered = markdownIt.render(context.content);
context.element.innerHTML = rendered;

// Insert styles into markdown preview shadow dom so that they are applied
for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) {
element.appendChild(markdownStyleNode.cloneNode(true));
// Insert styles into markdown preview shadow dom so that they are applied
for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) {
context.element.appendChild(markdownStyleNode.cloneNode(true));
}
}
});
}());
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"compilerOptions": {
"outDir": "./dist/",
"jsx": "react",
"module": "es2020",
"lib": [
"es2018",
"DOM",
Expand Down
9 changes: 6 additions & 3 deletions extensions/markdown-language-features/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@
}
},
"contributes": {
"notebookMarkdownRenderer": [
"notebookMarkupRenderers": [
{
"id": "markdownItRenderer",
"displayName": "Markdown it renderer",
"entrypoint": "./notebook-out/index.js"
"entrypoint": "./notebook-out/index.js",
"mimeTypes": [
"text/markdown"
]
}
],
"commands": [
Expand Down Expand Up @@ -343,7 +346,7 @@
"vscode:prepublish": "npm run build-ext && npm run build-preview",
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json",
"build-preview": "npx webpack-cli --mode production",
"build-notebook": "npx webpack-cli --config webpack.notebook --mode production",
"build-notebook": "node ./esbuild",
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
},
Expand Down
28 changes: 0 additions & 28 deletions extensions/markdown-language-features/webpack.notebook.js

This file was deleted.

41 changes: 41 additions & 0 deletions extensions/notebook-markdown-extensions/esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const fse = require('fs-extra');
const esbuild = require('esbuild');

const args = process.argv.slice(2);

const isWatch = args.indexOf('--watch') >= 0;

let outputRoot = __dirname;
const outputRootIndex = args.indexOf('--outputRoot');
if (outputRootIndex >= 0) {
outputRoot = args[outputRootIndex + 1];
}

const outDir = path.join(outputRoot, 'notebook-out');
esbuild.build({
entryPoints: [
path.join(__dirname, 'notebook', 'katex.ts'),
path.join(__dirname, 'notebook', 'emoji.ts')
],
bundle: true,
minify: true,
sourcemap: false,
format: 'esm',
outdir: outDir,
platform: 'browser',
target: ['es2020'],
incremental: isWatch,
}).catch(() => process.exit(1));

fse.copySync(
path.join(__dirname, 'node_modules/katex/dist/katex.min.css'),
path.join(outDir, 'katex.min.css'));

fse.copySync(
path.join(__dirname, 'node_modules/katex/dist/fonts'),
path.join(outDir, 'fonts/'));
17 changes: 4 additions & 13 deletions extensions/notebook-markdown-extensions/notebook/emoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import type * as markdownIt from 'markdown-it';

declare const extendMarkdownIt: undefined | (
(f: (md: markdownIt.MarkdownIt) => void) => void
);

(function () {
if (typeof extendMarkdownIt !== 'undefined') {
const emoji = require('markdown-it-emoji');

extendMarkdownIt((md: markdownIt.MarkdownIt) => {
md.use(emoji);
});
}
}());
const emoji = require('markdown-it-emoji');

export function extendMarkdownIt(md: markdownIt.MarkdownIt) {
return md.use(emoji);
}
18 changes: 5 additions & 13 deletions extensions/notebook-markdown-extensions/notebook/katex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import type * as markdownIt from 'markdown-it';

declare const extendMarkdownIt: undefined | (
(f: (md: markdownIt.MarkdownIt) => void) => void
);

const styleHref = (document.currentScript as any).src.replace(/katex.js$/, 'katex.min.css');
const styleHref = import.meta.url.replace(/katex.js$/, 'katex.min.css');

const link = document.createElement('link');
link.rel = 'stylesheet';
Expand All @@ -17,12 +13,8 @@ link.href = styleHref;

document.head.append(link);

(function () {
const katex = require('@iktakahiro/markdown-it-katex');
if (typeof extendMarkdownIt !== 'undefined') {
const katex = require('@iktakahiro/markdown-it-katex');

extendMarkdownIt((md: markdownIt.MarkdownIt) => {
md.use(katex);
});
}
}());
export function extendMarkdownIt(md: markdownIt.MarkdownIt) {
return md.use(katex);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"compilerOptions": {
"outDir": "./dist/",
"jsx": "react",
"module": "es2020",
"lib": [
"es2018",
"DOM",
Expand Down
10 changes: 6 additions & 4 deletions extensions/notebook-markdown-extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,25 @@
"virtualWorkspaces": false
},
"contributes": {
"notebookMarkdownRenderer": [
"notebookMarkupRenderers": [
{
"id": "markdownItRenderer-katex",
"displayName": "Markdown it katex renderer",
"entrypoint": "./notebook-out/katex.js"
"entrypoint": "./notebook-out/katex.js",
"dependsOn": "markdownItRenderer"
},
{
"id": "markdownItRenderer-emoji",
"displayName": "Markdown it emoji renderer",
"entrypoint": "./notebook-out/emoji.js"
"entrypoint": "./notebook-out/emoji.js",
"dependsOn": "markdownItRenderer"
}
]
},
"scripts": {
"compile": "npm run build-notebook",
"watch": "npm run build-notebook",
"build-notebook": "npx webpack-cli --config webpack.notebook.js --mode production"
"build-notebook": "node ./esbuild"
},
"devDependencies": {
"@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git",
Expand Down
Loading

0 comments on commit 652e812

Please sign in to comment.