diff --git a/index.ipynb b/index.ipynb index e37a987..518b9dc 100644 --- a/index.ipynb +++ b/index.ipynb @@ -19,7 +19,8 @@ "| `markdown-it-diagrams` | [diagrams](#Diagrams) |\n", "| `markdown-it-deflist` | [definition lists](#Definition-Lists) |\n", "| `markdown-it-footnote` | [footnotes](#Footnotes) |\n", - "| `markdown-it-task-lists` | [task lists](#Task-Lists) |" + "| `markdown-it-task-lists` | [task lists](#Task-Lists) |\n", + "| `markdown-it-dollarmath` | [dollar math](#Dollar-Math) |" ] }, { @@ -175,6 +176,31 @@ "```\n", "\"\"\"}, raw=True)" ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Dollar Math\n", + "
\n", + " \n", + "This is some math: $x + y$. This is some _display_ math:\n", + " \n", + "$$\n", + "x + y = z\n", + "$$\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -193,7 +219,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.10" + "version": "3.10.5" }, "toc-autonumbering": true, "toc-showcode": false, diff --git a/package.json b/package.json index 09a6c56..5232ff5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agoose77/jupyterlab-markup", - "version": "2.0.0", + "version": "2.1.0-alpha.0", "description": "Additional markdown rendering support in JupyterLab.", "keywords": [ "jupyter", @@ -60,6 +60,7 @@ "markdown-it": "^12.2.3", "markdown-it-anchor": "^8.6.4", "markdown-it-deflist": "^2.0.3", + "markdown-it-dollarmath": "^0.4.2", "markdown-it-footnote": "^3.0.2", "markdown-it-task-lists": "^2.1.1", "react": "^17.0.1" diff --git a/pyproject.toml b/pyproject.toml index cd8834c..33027bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "hatchling.build" name = "jupyterlab-markup" description = "Extensible markdown rendering support in markdown" readme = "README.md" -license = "BSD-3-Clause" +license = { file="LICENSE" } requires-python = ">=3.7" authors = [ { name = "Angus Hollands", email = "goosey15@gmail.com" }, diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d310186 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +# setup.py shim for use with versions of JupyterLab that require +# it for extensions. +__import__("setuptools").setup() diff --git a/src/builtins/dollarmath.ts b/src/builtins/dollarmath.ts new file mode 100644 index 0000000..b5e1f6a --- /dev/null +++ b/src/builtins/dollarmath.ts @@ -0,0 +1,45 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; + +interface IRenderOptions { + displayMode: boolean; +} + +/** + * ADd support for math parsing + */ +export const dollarmath = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-dollarmath', + title: 'Dollar Math', + description: 'Parse inline and display LaTeX math using $-delimiters', + documentationUrls: { + Plugin: 'https://github.com/executablebooks/markdown-it-dollarmath' + }, + plugin: async () => { + const dollarmathPlugin = await import( + /* webpackChunkName: "markdown-it-anchor" */ 'markdown-it-dollarmath' + ); + return [ + dollarmathPlugin.default, + { + allow_space: true, + allow_digits: true, + double_inline: true, + allow_labels: true, + labelNormalizer(label: string) { + return label.replace(/[\s]+/g, '-'); + }, + renderer(content: string, opts: IRenderOptions) { + const { displayMode } = opts; + if (displayMode) { + return `$$${content}$$`; + } else { + return `$${content}$`; + } + }, + labelRenderer(label: string) { + return `ΒΆ`; + } + } + ]; + } +}); diff --git a/src/builtins/index.ts b/src/builtins/index.ts index ea7915f..b1cc784 100644 --- a/src/builtins/index.ts +++ b/src/builtins/index.ts @@ -4,8 +4,19 @@ import { svgbob } from './svgbob'; import { mermaid } from './mermaid'; import { footnote } from './footnote'; import { taskLists } from './task-lists'; +import { typesetterAdaptor } from './typesetter-adaptor'; +import { dollarmath } from './dollarmath'; /** * Builtin plugins provided by this labextension */ -export const BUILTINS = [anchor, deflist, footnote, mermaid, svgbob, taskLists]; +export const BUILTINS = [ + anchor, + deflist, + footnote, + mermaid, + svgbob, + taskLists, + typesetterAdaptor, + dollarmath +]; diff --git a/src/builtins/typesetter-adaptor.ts b/src/builtins/typesetter-adaptor.ts new file mode 100644 index 0000000..f55e7d1 --- /dev/null +++ b/src/builtins/typesetter-adaptor.ts @@ -0,0 +1,40 @@ +import { IMarkdownIt, PACKAGE_NS } from '..'; +import { ILatexTypesetter } from '@jupyterlab/rendermime'; +import { JupyterFrontEndPlugin } from '@jupyterlab/application'; + +/** + * Adds anchors to headers + */ +const plugin_id = 'typesetter-adaptor'; +export const typesetterAdaptor: JupyterFrontEndPlugin = { + id: `${PACKAGE_NS}:${plugin_id}`, + autoStart: true, + requires: [IMarkdownIt, ILatexTypesetter], + activate: (app, markdownIt: IMarkdownIt, typesetter: ILatexTypesetter) => { + const provider: IMarkdownIt.IPluginProvider = { + id: plugin_id, + title: 'ILatexTypesetter Adaptor', + description: + 'Enable math rendering using JupyterLab ILatexTypesetter interface', + documentationUrls: {}, + plugin: async () => { + // eslint-disable-next-line @typescript-eslint/no-empty-function + return [md => {}]; + }, + postRenderHook: async () => { + const math_selectors = ['.math']; + return { + async postRender(node: HTMLElement): Promise { + // Find nodes to typeset + const nodes = [ + ...node.querySelectorAll(math_selectors.join(',')) + ] as HTMLElement[]; + // Only typeset these nodes + await Promise.all(nodes.map(node => typesetter.typeset(node))); + } + }; + } + }; + markdownIt.addPluginProvider(provider); + } +}; diff --git a/src/manager.ts b/src/manager.ts index c4bd43b..994301c 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -9,6 +9,14 @@ import MarkdownIt from 'markdown-it'; import { RenderedMarkdown } from './widgets'; import { IMarkdownIt } from './tokens'; +/** + * Comparator of IRanked implementations + */ +function rankedComparator(default_rank: number) { + return (left: IMarkdownIt.IRanked, right: IMarkdownIt.IRanked) => + (left.rank ?? default_rank) - (right.rank ?? default_rank); +} + /** * An implementation of a source of markdown renderers with markdown-it and plugins */ @@ -114,27 +122,32 @@ export class MarkdownItManager implements IMarkdownIt { return new RenderedMarkdown(options); }; - /** - * Get a MarkdownIt instance - */ - async getMarkdownIt( + async getRenderer( widget: RenderedMarkdown, options: MarkdownIt.Options = {} - ): Promise { + ): Promise { + // Create MarkdownIt instance const allOptions = { ...(await this.getOptions(widget)), ...options, ...this.userMarkdownItOptions }; - let md = new MarkdownIt('default', allOptions); - for (const [id, provider] of this._pluginProviders.entries()) { - if (this.userDisabledPlugins.indexOf(id) !== -1) { + // Sort providers by rank + const rankComparator = rankedComparator(100); + const pluginProviders = [...this._pluginProviders.values()]; + pluginProviders.sort(rankComparator); + + // Lifecycle hooks + const preParseHooks: IMarkdownIt.IPreParseHook[] = []; + const postRenderHooks: IMarkdownIt.IPostRenderHook[] = []; + for (const provider of pluginProviders) { + if (this.userDisabledPlugins.indexOf(provider.id) !== -1) { continue; } try { - const userOptions = this.userPluginOptions[id] || []; + const userOptions = this.userPluginOptions[provider.id] || []; const [plugin, ...pluginOptions] = await provider.plugin(); let i = 0; const maxOptions = Math.max(pluginOptions.length, userOptions.length); @@ -145,12 +158,47 @@ export class MarkdownItManager implements IMarkdownIt { i++; } md = md.use(plugin, ...compositeOptions); + + // Build table of lifecycle hooks + if (provider?.preParseHook !== undefined) { + preParseHooks.push(await provider.preParseHook()); + } + if (provider?.postRenderHook !== undefined) { + postRenderHooks.push(await provider.postRenderHook()); + } } catch (err) { - console.warn(`Failed to load/use markdown-it plugin ${id}`, err); + console.warn( + `Failed to load/use markdown-it plugin ${provider.id}`, + err + ); } } + // Sort hooks by rank + preParseHooks.sort(rankComparator); + postRenderHooks.sort(rankComparator); + + return { + get markdownIt(): MarkdownIt { + return md; + }, + + render: content => md.render(content), - return md; + // Run hooks serially + preParse: async (content: string) => { + for (const hook of preParseHooks) { + content = await hook.preParse(content); + } + return content; + }, + + // Run hooks serially + postRender: async (node: HTMLElement) => { + for (const hook of postRenderHooks) { + await hook.postRender(node); + } + } + }; } /** diff --git a/src/renderers.ts b/src/renderers.ts index 69e3659..ac1b9f5 100644 --- a/src/renderers.ts +++ b/src/renderers.ts @@ -1,7 +1,7 @@ -import { removeMath, renderHTML, replaceMath } from '@jupyterlab/rendermime'; +import { renderHTML } from '@jupyterlab/rendermime'; import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; import { ISanitizer } from '@jupyterlab/apputils'; -import MarkdownIt from 'markdown-it'; +import { IMarkdownIt } from './tokens'; /** * Render Markdown into a host node. @@ -13,7 +13,7 @@ import MarkdownIt from 'markdown-it'; export async function renderMarkdown( options: renderMarkdown.IRenderOptions ): Promise { - const { host, source, md, ...others } = options; + const { host, source, renderer, ...others } = options; // Clear the content if there is no source. if (!source) { @@ -21,20 +21,14 @@ export async function renderMarkdown( return; } - // Separate math from normal markdown text. - const parts = removeMath(source); - - let html = md.render(parts['text']); - - // Replace math. - html = replaceMath(html, parts['math']); - // Render HTML. await renderHTML({ host, - source: html, - ...others + source: renderer.render(source), + ...others, + shouldTypeset: false }); + await renderer.postRender(host); } /** @@ -83,7 +77,7 @@ export namespace renderMarkdown { /** * MarkdownIt renderer */ - md: MarkdownIt; + renderer: IMarkdownIt.IRenderer; /** * The LaTeX typesetter for the application. diff --git a/src/tokens.ts b/src/tokens.ts index e032c22..7a2c01b 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -26,10 +26,10 @@ export interface IMarkdownIt { addPluginProvider(provider: IMarkdownIt.IPluginProvider): void; removePluginProvider(id: string): void; getPluginProvider(id: string): IMarkdownIt.IPluginProvider | null; - getMarkdownIt( + getRenderer( widget: RenderedMarkdown, options?: MarkdownIt.Options - ): Promise; + ): Promise; pluginProviderIds: string[]; } @@ -45,10 +45,16 @@ export namespace CommandIDs { * A namespace for plugin-related types and interfaces */ export namespace IMarkdownIt { + export interface IRanked { + /** + * Order (ascending), default of 100; + */ + rank?: number; + } export interface IPlugin { (md: MarkdownIt, ...params: any[]): void; } - export interface IPluginProvider { + export interface IPluginProvider extends IRanked { /** * A unique identifier for the plugin, usually the name of the upstream package */ @@ -77,5 +83,46 @@ export namespace IMarkdownIt { * Additional options to pass to the MarkdownIt constructor */ options?(widget: RenderedMarkdown): Promise<{ [key: string]: any }>; + /** + * A lazy provider of a post-render hook + */ + preParseHook?(): Promise; + /** + * A lazy provider of a post-render hook + */ + postRenderHook?(): Promise; + } + export interface IPreParseHook extends IRanked { + /** + * Pre-parsing callback + */ + preParse(content: string): Promise; + } + export interface IPostRenderHook extends IRanked { + /** + * Post-rendering callback + */ + postRender(node: HTMLElement): Promise; + } + export interface IRenderer { + markdownIt: MarkdownIt; + + /** + * Interface to render content to HTML + * @param content + */ + render(content: string): string; + + /** + * Interface to transform pre-parsed Markdown + * @param node + */ + preParse(node: string): Promise; + + /** + * Interface to transform rendered HTML + * @param node + */ + postRender(node: HTMLElement): Promise; } } diff --git a/src/widgets.ts b/src/widgets.ts index a788f82..0fe2247 100644 --- a/src/widgets.ts +++ b/src/widgets.ts @@ -2,7 +2,6 @@ import { RenderedHTMLCommon } from '@jupyterlab/rendermime'; import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; import { Message } from '@lumino/messaging'; import * as renderers from './renderers'; -import MarkdownIt from 'markdown-it'; import { IMarkdownIt } from './tokens'; /** @@ -14,7 +13,7 @@ export class RenderedMarkdown extends RenderedHTMLCommon { * * @param options - The options for initializing the widget. */ - md: MarkdownIt = null; + renderer: IMarkdownIt.IRenderer = null; /** * A static manager set by the core plugin for getting MarkdownIt instances @@ -34,8 +33,11 @@ export class RenderedMarkdown extends RenderedHTMLCommon { * @returns A promise which resolves when rendering is complete. */ async render(model: IRenderMime.IMimeModel): Promise { - if (this.md === null) { - this.md = await RenderedMarkdown.markdownItManager.getMarkdownIt(this); + if (this.renderer === null) { + this.renderer = await RenderedMarkdown.markdownItManager.getRenderer( + this, + {} + ); } return await renderers.renderMarkdown({ host: this.node, @@ -45,7 +47,7 @@ export class RenderedMarkdown extends RenderedHTMLCommon { sanitizer: this.sanitizer, linkHandler: this.linkHandler, shouldTypeset: this.isAttached, - md: this.md, + renderer: this.renderer, latexTypesetter: this.latexTypesetter }); } @@ -54,8 +56,9 @@ export class RenderedMarkdown extends RenderedHTMLCommon { * A message handler invoked on an `'after-attach'` message. */ onAfterAttach(msg: Message): void { - if (this.latexTypesetter) { - this.latexTypesetter.typeset(this.node); - } + // Don't render math automatically + // if (this.latexTypesetter ) { + // this.latexTypesetter.typeset(this.node); + // } } } diff --git a/yarn.lock b/yarn.lock index e496df3..812a675 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1466,15 +1466,14 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.14.5: - version "4.20.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477" - integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== + version "4.21.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.0.tgz#7ab19572361a140ecd1e023e2c1ed95edda0cefe" + integrity sha512-UQxE0DIhRB5z/zDz9iA03BOfxaN2+GQdBYH/2WrSIWEUrnpzTPJbhqt+umq6r3acaPRTW1FNTkrcp0PXgtFkvA== dependencies: - caniuse-lite "^1.0.30001349" - electron-to-chromium "^1.4.147" - escalade "^3.1.1" + caniuse-lite "^1.0.30001358" + electron-to-chromium "^1.4.164" node-releases "^2.0.5" - picocolors "^1.0.0" + update-browserslist-db "^1.0.0" buffer-equal-constant-time@1.0.1: version "1.0.1" @@ -1559,10 +1558,10 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001349: - version "1.0.30001355" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001355.tgz#e240b7177443ed0198c737a7f609536976701c77" - integrity sha512-Sd6pjJHF27LzCB7pT7qs+kuX2ndurzCzkpJl6Qct7LPSZ9jn0bkOA8mdgMgmqnQAWLVOOGjLpc+66V57eLtb1g== +caniuse-lite@^1.0.30001358: + version "1.0.30001358" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001358.tgz#473d35dabf5e448b463095cab7924e96ccfb8c00" + integrity sha512-hvp8PSRymk85R20bsDra7ZTCpSVGN/PAz9pSAjPSjKC+rNmnUk5vCRgJwiTT/O4feQ/yu/drvZYpKxxhbFuChw== caseless@~0.12.0: version "0.12.0" @@ -1798,9 +1797,9 @@ cookies@0.8.0: keygrip "~1.1.0" core-js-pure@^3.6.5: - version "3.23.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.1.tgz#0b27e4c3ad46178b84e790dbbb81987218ab82ad" - integrity sha512-3qNgf6TqI3U1uhuSYRzJZGfFd4T+YlbyVPl+jgRiKjdZopvG4keZQwWZDAWpu1UH9nCgTpUzIV3GFawC7cJsqg== + version "3.23.2" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.2.tgz#efe5e486469c5ed2d088d76e973eb12e74a930e7" + integrity sha512-t6u7H4Ff/yZNk+zqTr74UjCcZ3k8ApBryeLLV4rYQd9aF3gqmjjGjjR44ENfeBMH8VVvSynIjAJ0mUuFhzQtrA== core-util-is@1.0.2: version "1.0.2" @@ -2660,10 +2659,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.147: - version "1.4.160" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.160.tgz#da54e24fddeaca52f37965c7bec1bd964c97d487" - integrity sha512-O1Z12YfyeX2LXYO7MdHIPazGXzLzQnr1ADW55U2ARQsJBPgfpJz3u+g3Mo2l1wSyfOCdiGqaX9qtV4XKZ0HNRA== +electron-to-chromium@^1.4.164: + version "1.4.165" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.165.tgz#a1ae079a4412b0c2d3bf6908e8db54511fb0bbac" + integrity sha512-DKQW1lqUSAYQvn9dnpK7mWaDpWbNOXQLXhfCi7Iwx0BKxdZOxkKcCyKw1l3ihWWW5iWSxKKbhEUoNRoHvl/hbA== emoji-regex@^8.0.0: version "8.0.0" @@ -3792,7 +3791,7 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== @@ -4488,6 +4487,11 @@ markdown-it-deflist@^2.0.3: resolved "https://registry.yarnpkg.com/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz#50d7a56b9544cd81252f7623bd785e28a8dcef5c" integrity sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg== +markdown-it-dollarmath@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/markdown-it-dollarmath/-/markdown-it-dollarmath-0.4.2.tgz#66cc90b1cb97bb4c0a29eaf8adf91a578e9ceec6" + integrity sha512-2qgZEyMDgW+ysfsit41N4Xcenc71RIiwNNhhJPMUQG8KX0RO+lt8PHibkD4gDrjbU/ExRbZIN112l3nQPFlr7A== + markdown-it-footnote@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/markdown-it-footnote/-/markdown-it-footnote-3.0.3.tgz#e0e4c0d67390a4c5f0c75f73be605c7c190ca4d8" @@ -4680,9 +4684,9 @@ minipass-pipeline@^1.2.2: minipass "^3.0.0" minipass@^3.0.0, minipass@^3.1.1: - version "3.1.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" - integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== + version "3.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.3.tgz#fd1f0e6c06449c10dadda72618b59c00f3d6378d" + integrity sha512-N0BOsdFAlNRfmwMhjAsLVWOk7Ljmeb39iqFlsV1At+jqRhSUP9yeof8FyJu4imaJiSUp8vQebWD/guZwGQC8iA== dependencies: yallist "^4.0.0" @@ -5619,11 +5623,11 @@ resolve-from@^5.0.0: integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve@^1.10.0, resolve@^1.20.0, resolve@^1.9.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6429,6 +6433,14 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +update-browserslist-db@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.3.tgz#6c47cb996f34afb363e924748e2f6e4d983c6fc1" + integrity sha512-ufSazemeh9Gty0qiWtoRpJ9F5Q5W3xdIPm1UZQqYQv/q0Nyb9EMHUB2lu+O9x1re9WsorpMAUu4Y6Lxcs5n+XQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"