Monaco Editor: Load via ESM (#78261)

* chore(monaco): bump monaco-editor to latest version

* feat(codeeditor): use esm to load monaco editor

* revert(monaco): put back previous version

* feat(monaco): setup MonacoEnvironment when bootstrapping app

* feat(monaco): load monaco languages from registry as workers

* feat(webpack): clean up warnings, remove need to copy monaco into lib

* fix(plugins): wip - remove amd loader workaround in systemjs hooks

* chore(azure): clean up so QueryField passes typecheck

* test(jest): update config to fix failing tests due to missing monaco-editor

* test(jest): update config to work with monaco-editor and kusto

* test(jest): prevent message eventlistener in nodeGraph/layout.worker tripping up monaco tests

* test(plugins): wip - remove amd related tests from systemjs hooks

* test(alerting): prefer clearAllMocks to prevent monaco editor failing due to missing matchMedia

* test(parca): fix failing test due to undefined backendSrv

* chore: move monacoEnv to app/core

* test: increase testing-lib timeout to 2secs, fix parca test to assert dom element

* feat(plugins): share kusto via systemjs

* test(e2e): increase timeout for checking monaco editor in exemplars spec

* test(e2e): assert monaco has loaded by checking the spinner is gone and window.monaco exists

* test(e2e): check for monaco editor textarea

* test(e2e): check monaco editor is loaded before assertions

* test(e2e): add waitForMonacoToLoad util to reduce duplication

* test(e2e): fix failing mysql spec

* chore(jest): add comment to setupTests explaining need to incresae default timeout

* chore(nodegraph): improve comment in layout.worker.utils to better explain the need for file
This commit is contained in:
Jack Westbrook 2024-02-22 12:31:40 +01:00 committed by GitHub
parent 0dbf2da254
commit 0dcdfc261b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 703 additions and 574 deletions

View File

@ -4581,10 +4581,6 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/QueryField.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],

View File

@ -0,0 +1,7 @@
import { e2e } from '../index';
export function waitForMonacoToLoad() {
e2e.components.QueryField.container().children('[data-testid="Spinner"]').should('not.exist');
cy.window().its('monaco').should('exist');
cy.get('.monaco-editor textarea:first').should('exist');
}

View File

@ -1,4 +1,5 @@
import { e2e } from '../utils';
import { waitForMonacoToLoad } from '../utils/support/monaco';
const dataSourceName = 'PromExemplar';
const addDataSource = () => {
@ -57,12 +58,8 @@ describe('Exemplars', () => {
// Switch to code editor
e2e.components.RadioButton.container().filter(':contains("Code")').click();
// we need to wait for the query-field being lazy-loaded, in two steps:
// 1. first we wait for the text 'Loading...' to appear
// 1. then we wait for the text 'Loading...' to disappear
const monacoLoadingText = 'Loading...';
e2e.components.QueryField.container().should('be.visible').should('have.text', monacoLoadingText);
e2e.components.QueryField.container().should('be.visible').should('not.have.text', monacoLoadingText);
// Wait for lazy loading Monaco
waitForMonacoToLoad();
e2e.components.TimePicker.openButton().click();
e2e.components.TimePicker.fromField().clear().type('2021-07-10 17:10:00');

View File

@ -1,4 +1,5 @@
import { e2e } from '../utils';
import { waitForMonacoToLoad } from '../utils/support/monaco';
const dataSourceName = 'LokiEditor';
const addDataSource = () => {
@ -39,11 +40,7 @@ describe('Loki Query Editor', () => {
e2e.components.RadioButton.container().filter(':contains("Code")').click();
// Wait for lazy loading
const monacoLoadingText = 'Loading...';
e2e.components.QueryField.container().should('be.visible').should('have.text', monacoLoadingText);
e2e.components.QueryField.container().should('be.visible').should('not.have.text', monacoLoadingText);
waitForMonacoToLoad();
// adds closing braces around empty value
e2e.components.QueryField.container().type('time(');

View File

@ -37,6 +37,9 @@ describe('MySQL datasource', () => {
it.skip('code editor autocomplete should handle table name escaping/quoting', () => {
e2e.components.RadioButton.container().filter(':contains("Code")').click();
e2e.components.CodeEditor.container().children('[data-testid="Spinner"]').should('not.exist');
cy.window().its('monaco').should('exist');
cy.get('textarea').type('S{downArrow}{enter}');
cy.wait('@tables');
cy.get('.suggest-widget').contains(tableNameWithSpecialCharacter).should('be.visible');
@ -88,8 +91,10 @@ describe('MySQL datasource', () => {
cy.get("[aria-label='Macros value selector']").should('be.visible').click();
selectOption('timeFilter');
// Validate that the timeFilter macro was added
e2e.components.CodeEditor.container().children('[data-testid="Spinner"]').should('not.exist');
cy.window().its('monaco').should('exist');
// Validate that the timeFilter macro was added
e2e.components.CodeEditor.container()
.get('textarea')
.should(

View File

@ -1,4 +1,5 @@
import { e2e } from '../utils';
import { waitForMonacoToLoad } from '../utils/support/monaco';
describe('Query editor', () => {
beforeEach(() => {
@ -14,13 +15,8 @@ describe('Query editor', () => {
e2e.components.RadioButton.container().filter(':contains("Code")').click();
// we need to wait for the query-field being lazy-loaded, in two steps:
// it is a two-step process:
// 1. first we wait for the text 'Loading...' to appear
// 1. then we wait for the text 'Loading...' to disappear
const monacoLoadingText = 'Loading...';
e2e.components.QueryField.container().should('be.visible').should('have.text', monacoLoadingText);
e2e.components.QueryField.container().should('be.visible').should('not.have.text', monacoLoadingText);
waitForMonacoToLoad();
e2e.components.QueryField.container().type(queryText, { parseSpecialCharSequences: false }).type('{backspace}');
cy.contains(queryText.slice(0, -1)).should('be.visible');

View File

@ -15,6 +15,9 @@ const esModules = [
'leven',
'nanoid',
'monaco-promql',
'@kusto/monaco-kusto',
'monaco-editor',
'lodash-es',
].join('|');
module.exports = {
@ -41,7 +44,9 @@ module.exports = {
'\\.svg': '<rootDir>/public/test/mocks/svg.ts',
'\\.css': '<rootDir>/public/test/mocks/style.ts',
'react-inlinesvg': '<rootDir>/public/test/mocks/react-inlinesvg.tsx',
'monaco-editor/esm/vs/editor/editor.api': '<rootDir>/public/test/mocks/monaco.ts',
// resolve directly as monaco and kusto don't have main property in package.json which jest needs
'^monaco-editor$': 'monaco-editor/esm/vs/editor/editor.api.js',
'@kusto/monaco-kusto': '@kusto/monaco-kusto/release/esm/monaco.contribution.js',
// near-membrane-dom won't work in a nodejs environment.
'@locker/near-membrane-dom': '<rootDir>/public/test/mocks/nearMembraneDom.ts',
'^@grafana/schema/dist/esm/(.*)$': '<rootDir>/packages/grafana-schema/src/$1',

View File

@ -4,7 +4,7 @@ import { Registry, RegistryItem } from '../utils/Registry';
* @alpha
*/
export interface MonacoLanguageRegistryItem extends RegistryItem {
init: () => Promise<void>;
init: () => Worker;
}
/**

View File

@ -62,11 +62,3 @@ export function getPluginImportUtils(): PluginImportUtils {
return pluginImportUtils;
}
// Grafana relies on RequireJS for Monaco Editor to load.
// The SystemJS AMD extra creates a global define which causes RequireJS to silently bail.
// Here we move and reset global define so Monaco Editor loader script continues to work.
// @ts-ignore
window.__grafana_amd_define = window.define;
// @ts-ignore
window.define = undefined;

View File

@ -119,14 +119,12 @@ class UnthemedCodeEditor extends PureComponent<Props> {
}
});
const languagePromise = this.loadCustomLanguage();
if (onChange) {
editor.getModel()?.onDidChangeContent(() => onChange(editor.getValue()));
}
if (onEditorDidMount) {
languagePromise.then(() => onEditorDidMount(editor, monaco));
onEditorDidMount(editor, monaco);
}
};

View File

@ -1,4 +1,5 @@
import MonacoEditor, { loader as monacoEditorLoader, Monaco } from '@monaco-editor/react';
import Editor, { loader as monacoEditorLoader, Monaco } from '@monaco-editor/react';
import * as monaco from 'monaco-editor';
import React, { useCallback } from 'react';
import { useTheme2 } from '../../themes';
@ -6,11 +7,8 @@ import { useTheme2 } from '../../themes';
import defineThemes from './theme';
import type { ReactMonacoEditorProps } from './types';
monacoEditorLoader.config({
paths: {
vs: (window.__grafana_public_path__ ?? 'public/') + 'lib/monaco/min/vs',
},
});
// pass the monaco editor to the loader to bypass requirejs
monacoEditorLoader.config({ monaco });
export const ReactMonacoEditor = (props: ReactMonacoEditorProps) => {
const { beforeMount } = props;
@ -25,10 +23,6 @@ export const ReactMonacoEditor = (props: ReactMonacoEditorProps) => {
);
return (
<MonacoEditor
{...props}
theme={theme.isDark ? 'grafana-dark' : 'grafana-light'}
beforeMount={onMonacoBeforeMount}
/>
<Editor {...props} theme={theme.isDark ? 'grafana-dark' : 'grafana-light'} beforeMount={onMonacoBeforeMount} />
);
};

View File

@ -56,6 +56,7 @@ import { PluginPage } from './core/components/Page/PluginPage';
import { GrafanaContextType, useReturnToPreviousInternal } from './core/context/GrafanaContext';
import { initIconCache } from './core/icons/iconBundle';
import { initializeI18n } from './core/internationalization';
import { setMonacoEnv } from './core/monacoEnv';
import { interceptLinkClicks } from './core/navigation/patch/interceptLinkClicks';
import { ModalManager } from './core/services/ModalManager';
import { NewFrontendAssetsChecker } from './core/services/NewFrontendAssetsChecker';
@ -170,7 +171,9 @@ export class GrafanaApp {
createAdHocVariableAdapter(),
createSystemVariableAdapter(),
]);
monacoLanguageRegistry.setInit(getDefaultMonacoLanguages);
setMonacoEnv();
setQueryRunnerFactory(() => new QueryRunner());
setVariableQueryRunner(new VariableQueryRunner());

View File

@ -0,0 +1,32 @@
import { monacoLanguageRegistry } from '@grafana/data';
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
export function setMonacoEnv() {
self.MonacoEnvironment = {
getWorker(_moduleId, label) {
const language = monacoLanguageRegistry.getIfExists(label);
if (language) {
return language.init();
}
if (label === 'json') {
return new Worker(new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url));
}
if (label === 'css' || label === 'scss' || label === 'less') {
return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url));
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return new Worker(new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url));
}
if (label === 'typescript' || label === 'javascript') {
return new Worker(new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url));
}
return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url));
},
};
}

View File

@ -89,7 +89,7 @@ const ui = {
describe('Admin config', () => {
beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
// FIXME: scope down
grantUserPermissions(Object.values(AccessControlAction));
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));

View File

@ -1,5 +1,3 @@
export const SHARED_DEPENDENCY_PREFIX = 'package';
export const LOAD_PLUGIN_CSS_REGEX = /^plugins.+\.css$/i;
export const JS_CONTENT_TYPE_REGEX = /^(text|application)\/(x-)?javascript(;|$)/;
export const AMD_MODULE_REGEX =
/(?:^\uFEFF?|[^$_a-zA-Z\xA0-\uFFFF.])define\s*\(\s*("[^"]+"\s*,\s*|'[^']+'\s*,\s*)?\s*(\[(\s*(("[^"]+"|'[^']+')\s*,|\/\/.*\r?\n))*(\s*("[^"]+"|'[^']+')\s*,?)?(\s*(\/\/.*\r?\n|\/\*\/))*\s*\]|function\s*|{|[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*\))/;

View File

@ -23,68 +23,6 @@ export const mockAmdModule = `define([], function() {
}
});`;
export const mockAmdModuleNamedNoDeps = `define("named", function() {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockAmdModuleNamedWithDeps = `define("named", ["dep"], function(dep) {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockAmdModuleNamedWithDeps2 = `define("named", ["dep", "dep2"], function(dep, dep2) {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockAmdModuleNamedWithDeps3 = `define("named", ["dep",
"dep2"
], function(dep, dep2) {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockAmdModuleOnlyFunction = `define(function() {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockAmdModuleWithComments = `/*! For license information please see module.js.LICENSE.txt */
define(function(react) {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockAmdModuleWithComments2 = `/*! This is a commment */
define(["dep"],
/*! This is a commment */
function(dep) {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
export const mockModuleWithDefineMethod = `ace.define(function() {
return function() {
console.log('AMD module loaded');
var pluginPath = "/public/plugins/";
}
});`;
const server = setupServer(
http.get(
'/public/plugins/mockAmdModule/module.js',
@ -112,78 +50,6 @@ const server = setupServer(
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleNamedNoDeps/module.js',
() =>
new HttpResponse(mockAmdModuleNamedNoDeps, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleNamedWithDeps/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleNamedWithDeps2/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps2, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleNamedWithDeps3/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps3, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleOnlyFunction/module.js',
() =>
new HttpResponse(mockAmdModuleOnlyFunction, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleWithComments/module.js',
() =>
new HttpResponse(mockAmdModuleWithComments, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockAmdModuleWithComments2/module.js',
() =>
new HttpResponse(mockAmdModuleWithComments2, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
http.get(
'/public/plugins/mockModuleWithDefineMethod/module.js',
() =>
new HttpResponse(mockModuleWithDefineMethod, {
headers: {
'Content-Type': 'text/javascript',
},
})
)
);

View File

@ -1,5 +1,6 @@
import * as emotion from '@emotion/css';
import * as emotionReact from '@emotion/react';
import * as kusto from '@kusto/monaco-kusto';
import * as d3 from 'd3';
import * as i18next from 'i18next';
import jquery from 'jquery';
@ -71,6 +72,7 @@ export const sharedDependenciesMap: Record<string, System.Module> = {
'@grafana/runtime': grafanaRuntime,
'@grafana/slate-react': slateReact, // for backwards compatibility with older plugins
'@grafana/ui': grafanaUI,
'@kusto/monaco-kusto': kusto,
'app/core/app_events': {
default: appEvents,
__useDefault: true,

View File

@ -7,19 +7,7 @@ jest.mock('./cache', () => ({
resolveWithCache: (url: string) => `${url}?_cache=1234`,
}));
import {
server,
mockAmdModule,
mockSystemModule,
mockAmdModuleNamedNoDeps,
mockAmdModuleNamedWithDeps,
mockAmdModuleNamedWithDeps2,
mockAmdModuleNamedWithDeps3,
mockAmdModuleOnlyFunction,
mockAmdModuleWithComments,
mockModuleWithDefineMethod,
mockAmdModuleWithComments2,
} from './pluginLoader.mock';
import { server } from './pluginLoader.mock';
import { decorateSystemJSFetch, decorateSystemJSResolve } from './systemjsHooks';
import { SystemJSWithLoaderHooks } from './types';
@ -44,116 +32,6 @@ describe('SystemJS Loader Hooks', () => {
});
describe('decorateSystemJSFetch', () => {
it('wraps AMD modules in an AMD iife', async () => {
const basicResult = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModule/module.js',
{}
);
const basicSource = await basicResult.text();
const basicExpected = `(function(define) {
${mockAmdModule}
})(window.__grafana_amd_define);`;
expect(basicSource).toBe(basicExpected);
const mockAmdModuleNamedNoDepsResult = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedNoDeps/module.js',
{}
);
const mockAmdModuleNamedNoDepsSource = await mockAmdModuleNamedNoDepsResult.text();
const mockAmdModuleNamedNoDepsExpected = `(function(define) {
${mockAmdModuleNamedNoDeps}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleNamedNoDepsSource).toBe(mockAmdModuleNamedNoDepsExpected);
const mockAmdModuleNamedWithDepsResult = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps/module.js',
{}
);
const mockAmdModuleNamedWithDepsSource = await mockAmdModuleNamedWithDepsResult.text();
const mockAmdModuleNamedWithDepsExpected = `(function(define) {
${mockAmdModuleNamedWithDeps}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleNamedWithDepsSource).toBe(mockAmdModuleNamedWithDepsExpected);
const mockAmdModuleNamedWithDeps2Result = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps2/module.js',
{}
);
const mockAmdModuleNamedWithDeps2Source = await mockAmdModuleNamedWithDeps2Result.text();
const mockAmdModuleNamedWithDeps2Expected = `(function(define) {
${mockAmdModuleNamedWithDeps2}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleNamedWithDeps2Source).toBe(mockAmdModuleNamedWithDeps2Expected);
const mockAmdModuleNamedWithDeps3Result = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps3/module.js',
{}
);
const mockAmdModuleNamedWithDeps3Source = await mockAmdModuleNamedWithDeps3Result.text();
const mockAmdModuleNamedWithDeps3Expected = `(function(define) {
${mockAmdModuleNamedWithDeps3}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleNamedWithDeps3Source).toBe(mockAmdModuleNamedWithDeps3Expected);
const mockAmdModuleOnlyFunctionResult = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleOnlyFunction/module.js',
{}
);
const mockAmdModuleOnlyFunctionSource = await mockAmdModuleOnlyFunctionResult.text();
const mockAmdModuleOnlyFunctionExpected = `(function(define) {
${mockAmdModuleOnlyFunction}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleOnlyFunctionSource).toBe(mockAmdModuleOnlyFunctionExpected);
const mockAmdModuleWithCommentsResult = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleWithComments/module.js',
{}
);
const mockAmdModuleWithCommentsSource = await mockAmdModuleWithCommentsResult.text();
const mockAmdModuleWithCommentsExpected = `(function(define) {
${mockAmdModuleWithComments}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleWithCommentsSource).toBe(mockAmdModuleWithCommentsExpected);
const mockAmdModuleWithComments2Result = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleWithComments2/module.js',
{}
);
const mockAmdModuleWithComments2Source = await mockAmdModuleWithComments2Result.text();
const mockAmdModuleWithComments2Expected = `(function(define) {
${mockAmdModuleWithComments2}
})(window.__grafana_amd_define);`;
expect(mockAmdModuleWithComments2Source).toBe(mockAmdModuleWithComments2Expected);
});
it("doesn't wrap system modules in an AMD iife", async () => {
const url = '/public/plugins/mockSystemModule/module.js';
const result = await decorateSystemJSFetch(systemJSPrototype.fetch, url, {});
const source = await result.text();
expect(source).toBe(mockSystemModule);
});
it("doesn't wrap modules with a define method in an AMD iife", async () => {
const url = '/public/plugins/mockModuleWithDefineMethod/module.js';
const result = await decorateSystemJSFetch(systemJSPrototype.fetch, url, {});
const source = await result.text();
expect(source).toBe(mockModuleWithDefineMethod);
});
it('only transforms plugin source code hosted on cdn with cdn paths', async () => {
config.pluginsCDNBaseURL = 'http://my-cdn.com/plugins';
const cdnUrl = 'http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/my-plugin/module.js';

View File

@ -3,7 +3,7 @@ import { config, SystemJS } from '@grafana/runtime';
import { transformPluginSourceForCDN } from '../cdn/utils';
import { resolveWithCache } from './cache';
import { LOAD_PLUGIN_CSS_REGEX, JS_CONTENT_TYPE_REGEX, AMD_MODULE_REGEX, SHARED_DEPENDENCY_PREFIX } from './constants';
import { LOAD_PLUGIN_CSS_REGEX, JS_CONTENT_TYPE_REGEX, SHARED_DEPENDENCY_PREFIX } from './constants';
import { SystemJSWithLoaderHooks } from './types';
import { isHostedOnCDN } from './utils';
@ -19,10 +19,6 @@ export async function decorateSystemJSFetch(
const source = await res.text();
let transformedSrc = source;
if (AMD_MODULE_REGEX.test(transformedSrc)) {
transformedSrc = preventAMDLoaderCollision(source);
}
// JS files on the CDN need their asset paths transformed in the source
if (isHostedOnCDN(res.url)) {
const cdnTransformedSrc = transformPluginSourceForCDN({ url: res.url, source: transformedSrc });
@ -84,11 +80,3 @@ function getBackWardsCompatibleUrl(url: string) {
return hasValidFileExtension ? url : url + '.js';
}
// This transform prevents a conflict between systemjs and requirejs which Monaco Editor
// depends on. See packages/grafana-runtime/src/utils/plugin.ts for more.
function preventAMDLoaderCollision(source: string) {
return `(function(define) {
${source}
})(window.__grafana_amd_define);`;
}

View File

@ -1,5 +1,4 @@
import { EngineSchema, Schema } from '@kusto/monaco-kusto';
import { Uri } from 'monaco-editor';
import { EngineSchema, getKustoWorker } from '@kusto/monaco-kusto';
import React, { useCallback, useEffect, useState } from 'react';
import { CodeEditor, Monaco, MonacoEditor } from '@grafana/ui';
@ -13,16 +12,6 @@ interface MonacoEditorValues {
monaco: Monaco;
}
interface MonacoLanguages {
kusto: {
getKustoWorker: () => Promise<
(url: Uri) => Promise<{
setSchema: (schema: Schema) => void;
}>
>;
};
}
const QueryField = ({ query, onQueryChange, schema }: AzureQueryEditorFieldProps) => {
const [monaco, setMonaco] = useState<MonacoEditorValues | undefined>();
@ -33,10 +22,9 @@ const QueryField = ({ query, onQueryChange, schema }: AzureQueryEditorFieldProps
const setupEditor = async ({ monaco, editor }: MonacoEditorValues, schema: EngineSchema) => {
try {
const languages = monaco.languages as unknown as MonacoLanguages;
const model = editor.getModel();
if (model) {
const kustoWorker = await languages.kusto.getKustoWorker();
const kustoWorker = await getKustoWorker();
const kustoMode = await kustoWorker(model?.uri);
await kustoMode.setSchema(schema);
}

View File

@ -1,8 +1,10 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { of } from 'rxjs';
import { CoreApp, DataSourcePluginMeta, PluginType } from '@grafana/data';
import { BackendSrv, getBackendSrv, setBackendSrv } from '@grafana/runtime';
import { ParcaDataSource } from '../datasource';
import { ProfileTypeMessage } from '../types';
@ -10,6 +12,17 @@ import { ProfileTypeMessage } from '../types';
import { Props, QueryEditor } from './QueryEditor';
describe('QueryEditor', () => {
let origBackendSrv: BackendSrv;
const fetchMock = jest.fn().mockReturnValue(of({ data: [] }));
beforeEach(() => {
origBackendSrv = getBackendSrv();
});
afterEach(() => {
setBackendSrv(origBackendSrv);
});
it('should render without error', async () => {
setup();
@ -17,6 +30,7 @@ describe('QueryEditor', () => {
});
it('should render options', async () => {
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
setup();
await openOptions();
expect(screen.getByText(/Metric/)).toBeDefined();
@ -25,6 +39,7 @@ describe('QueryEditor', () => {
});
it('should render correct options outside of explore', async () => {
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
setup({ props: { app: CoreApp.Dashboard } });
await openOptions();
expect(screen.getByText(/Metric/)).toBeDefined();

View File

@ -1,176 +1,7 @@
import { forceSimulation, forceLink, forceCollide, forceX } from 'd3-force';
import { layout } from './layout.worker.utils';
addEventListener('message', (event) => {
const { nodes, edges, config } = event.data;
layout(nodes, edges, config);
postMessage({ nodes, edges });
});
/**
* Use d3 force layout to lay the nodes in a sensible way. This function modifies the nodes adding the x,y positions
* and also fills in node references in edges instead of node ids.
*/
export function layout(nodes, edges, config) {
// Start with some hardcoded positions so it starts laid out from left to right
let { roots, secondLevelRoots } = initializePositions(nodes, edges);
// There always seems to be one or more root nodes each with single edge and we want to have them static on the
// left neatly in something like grid layout
[...roots, ...secondLevelRoots].forEach((n, index) => {
n.fx = n.x;
});
const simulation = forceSimulation(nodes)
.force(
'link',
forceLink(edges)
.id((d) => d.id)
.distance(config.linkDistance)
.strength(config.linkStrength)
)
// to keep the left to right layout we add force that pulls all nodes to right but because roots are fixed it will
// apply only to non root nodes
.force('x', forceX(config.forceX).strength(config.forceXStrength))
// Make sure nodes don't overlap
.force('collide', forceCollide(config.forceCollide));
// 300 ticks for the simulation are recommended but less would probably work too, most movement is done in first
// few iterations and then all the forces gets smaller https://github.com/d3/d3-force#simulation_alphaDecay
simulation.tick(config.tick);
simulation.stop();
// We do centering here instead of using centering force to keep this more stable
centerNodes(nodes);
}
/**
* This initializes positions of the graph by going from the root to its children and laying it out in a grid from left
* to right. This works only so, so because service map graphs can have cycles and children levels are not ordered in a
* way to minimize the edge lengths. Nevertheless this seems to make the graph easier to nudge with the forces later on
* than with the d3 default initial positioning. Also we can fix the root positions later on for a bit more neat
* organisation.
*
* This function directly modifies the nodes given and only returns references to root nodes so they do not have to be
* found again later on.
*
* How the spacing could look like approximately:
* 0 - 0 - 0 - 0
* \- 0 - 0 |
* \- 0 -/
* 0 - 0 -/
*/
function initializePositions(nodes, edges) {
// To prevent going in cycles
const alreadyPositioned = {};
const nodesMap = nodes.reduce((acc, node) => {
acc[node.id] = node;
return acc;
}, {});
const edgesMap = edges.reduce((acc, edge) => {
const sourceId = edge.source;
acc[sourceId] = [...(acc[sourceId] || []), edge];
return acc;
}, {});
let roots = nodes.filter((n) => n.incoming === 0);
// For things like service maps we assume there is some root (client) node but if there is none then selecting
// any node as a starting point should work the same.
if (!roots.length) {
roots = [nodes[0]];
}
let secondLevelRoots = roots.reduce((acc, r) => {
acc.push(...(edgesMap[r.id] ? edgesMap[r.id].map((e) => nodesMap[e.target]) : []));
return acc;
}, []);
const rootYSpacing = 300;
const nodeYSpacing = 200;
const nodeXSpacing = 200;
let rootY = 0;
for (const root of roots) {
let graphLevel = [root];
let x = 0;
while (graphLevel.length > 0) {
const nextGraphLevel = [];
let y = rootY;
for (const node of graphLevel) {
if (alreadyPositioned[node.id]) {
continue;
}
// Initialize positions based on the spacing in the grid
node.x = x;
node.y = y;
alreadyPositioned[node.id] = true;
// Move to next Y position for next node
y += nodeYSpacing;
if (edgesMap[node.id]) {
nextGraphLevel.push(...edgesMap[node.id].map((edge) => nodesMap[edge.target]));
}
}
graphLevel = nextGraphLevel;
// Move to next X position for next level
x += nodeXSpacing;
// Reset Y back to baseline for this root
y = rootY;
}
rootY += rootYSpacing;
}
return { roots, secondLevelRoots };
}
/**
* Makes sure that the center of the graph based on its bound is in 0, 0 coordinates.
* Modifies the nodes directly.
*/
function centerNodes(nodes) {
const bounds = graphBounds(nodes);
for (let node of nodes) {
node.x = node.x - bounds.center.x;
node.y = node.y - bounds.center.y;
}
}
/**
* Get bounds of the graph meaning the extent of the nodes in all directions.
*/
function graphBounds(nodes) {
if (nodes.length === 0) {
return { top: 0, right: 0, bottom: 0, left: 0, center: { x: 0, y: 0 } };
}
const bounds = nodes.reduce(
(acc, node) => {
if (node.x > acc.right) {
acc.right = node.x;
}
if (node.x < acc.left) {
acc.left = node.x;
}
if (node.y > acc.bottom) {
acc.bottom = node.y;
}
if (node.y < acc.top) {
acc.top = node.y;
}
return acc;
},
{ top: Infinity, right: -Infinity, bottom: -Infinity, left: Infinity }
);
const y = bounds.top + (bounds.bottom - bounds.top) / 2;
const x = bounds.left + (bounds.right - bounds.left) / 2;
return {
...bounds,
center: {
x,
y,
},
};
}

View File

@ -0,0 +1,174 @@
// This file is a workaround so the layout function can be imported in Jest mocks. If the jest mock imports the
// layout.worker.js file it will attach the eventlistener and then call the layout function with undefined data
// which causes tests to fail.
import { forceSimulation, forceLink, forceCollide, forceX } from 'd3-force';
/**
* Use d3 force layout to lay the nodes in a sensible way. This function modifies the nodes adding the x,y positions
* and also fills in node references in edges instead of node ids.
*/
export function layout(nodes, edges, config) {
// Start with some hardcoded positions so it starts laid out from left to right
let { roots, secondLevelRoots } = initializePositions(nodes, edges);
// There always seems to be one or more root nodes each with single edge and we want to have them static on the
// left neatly in something like grid layout
[...roots, ...secondLevelRoots].forEach((n, index) => {
n.fx = n.x;
});
const simulation = forceSimulation(nodes)
.force(
'link',
forceLink(edges)
.id((d) => d.id)
.distance(config.linkDistance)
.strength(config.linkStrength)
)
// to keep the left to right layout we add force that pulls all nodes to right but because roots are fixed it will
// apply only to non root nodes
.force('x', forceX(config.forceX).strength(config.forceXStrength))
// Make sure nodes don't overlap
.force('collide', forceCollide(config.forceCollide));
// 300 ticks for the simulation are recommended but less would probably work too, most movement is done in first
// few iterations and then all the forces gets smaller https://github.com/d3/d3-force#simulation_alphaDecay
simulation.tick(config.tick);
simulation.stop();
// We do centering here instead of using centering force to keep this more stable
centerNodes(nodes);
}
/**
* This initializes positions of the graph by going from the root to its children and laying it out in a grid from left
* to right. This works only so, so because service map graphs can have cycles and children levels are not ordered in a
* way to minimize the edge lengths. Nevertheless this seems to make the graph easier to nudge with the forces later on
* than with the d3 default initial positioning. Also we can fix the root positions later on for a bit more neat
* organisation.
*
* This function directly modifies the nodes given and only returns references to root nodes so they do not have to be
* found again later on.
*
* How the spacing could look like approximately:
* 0 - 0 - 0 - 0
* \- 0 - 0 |
* \- 0 -/
* 0 - 0 -/
*/
function initializePositions(nodes, edges) {
// To prevent going in cycles
const alreadyPositioned = {};
const nodesMap = nodes.reduce((acc, node) => {
acc[node.id] = node;
return acc;
}, {});
const edgesMap = edges.reduce((acc, edge) => {
const sourceId = edge.source;
acc[sourceId] = [...(acc[sourceId] || []), edge];
return acc;
}, {});
let roots = nodes.filter((n) => n.incoming === 0);
// For things like service maps we assume there is some root (client) node but if there is none then selecting
// any node as a starting point should work the same.
if (!roots.length) {
roots = [nodes[0]];
}
let secondLevelRoots = roots.reduce((acc, r) => {
acc.push(...(edgesMap[r.id] ? edgesMap[r.id].map((e) => nodesMap[e.target]) : []));
return acc;
}, []);
const rootYSpacing = 300;
const nodeYSpacing = 200;
const nodeXSpacing = 200;
let rootY = 0;
for (const root of roots) {
let graphLevel = [root];
let x = 0;
while (graphLevel.length > 0) {
const nextGraphLevel = [];
let y = rootY;
for (const node of graphLevel) {
if (alreadyPositioned[node.id]) {
continue;
}
// Initialize positions based on the spacing in the grid
node.x = x;
node.y = y;
alreadyPositioned[node.id] = true;
// Move to next Y position for next node
y += nodeYSpacing;
if (edgesMap[node.id]) {
nextGraphLevel.push(...edgesMap[node.id].map((edge) => nodesMap[edge.target]));
}
}
graphLevel = nextGraphLevel;
// Move to next X position for next level
x += nodeXSpacing;
// Reset Y back to baseline for this root
y = rootY;
}
rootY += rootYSpacing;
}
return { roots, secondLevelRoots };
}
/**
* Makes sure that the center of the graph based on its bound is in 0, 0 coordinates.
* Modifies the nodes directly.
*/
function centerNodes(nodes) {
const bounds = graphBounds(nodes);
for (let node of nodes) {
node.x = node.x - bounds.center.x;
node.y = node.y - bounds.center.y;
}
}
/**
* Get bounds of the graph meaning the extent of the nodes in all directions.
*/
function graphBounds(nodes) {
if (nodes.length === 0) {
return { top: 0, right: 0, bottom: 0, left: 0, center: { x: 0, y: 0 } };
}
const bounds = nodes.reduce(
(acc, node) => {
if (node.x > acc.right) {
acc.right = node.x;
}
if (node.x < acc.left) {
acc.left = node.x;
}
if (node.y > acc.bottom) {
acc.bottom = node.y;
}
if (node.y < acc.top) {
acc.top = node.y;
}
return acc;
},
{ top: Infinity, right: -Infinity, bottom: -Infinity, left: Infinity }
);
const y = bounds.top + (bounds.bottom - bounds.top) / 2;
const x = bounds.left + (bounds.right - bounds.left) / 2;
return {
...bounds,
center: {
x,
y,
},
};
}

View File

@ -1,5 +1,5 @@
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
export default function loadKusto() {
return new Promise<void>((resolve) =>
__non_webpack_require__(['vs/language/kusto/monaco.contribution'], () => resolve())
);
return new Worker(new URL('@kusto/monaco-kusto/release/esm/kusto.worker', import.meta.url));
}

View File

@ -1 +0,0 @@
export const monaco = 'monaco';

View File

@ -1,7 +1,7 @@
import { Config } from 'app/plugins/panel/nodeGraph/layout';
import { EdgeDatum, NodeDatum } from 'app/plugins/panel/nodeGraph/types';
const { layout } = jest.requireActual('../../app/plugins/panel/nodeGraph/layout.worker.js');
const { layout } = jest.requireActual('../../app/plugins/panel/nodeGraph/layout.worker.utils.js');
class LayoutMockWorker {
timeout: number | undefined;

View File

@ -1,4 +1,5 @@
import '@testing-library/jest-dom';
import { configure } from '@testing-library/react';
import i18next from 'i18next';
import failOnConsole from 'jest-fail-on-console';
import { initReactI18next } from 'react-i18next';
@ -20,3 +21,7 @@ i18next.use(initReactI18next).init({
returnEmptyString: false,
lng: 'en-US', // this should be the locale of the phrases in our source JSX
});
// our tests are heavy in CI due to parallelisation and monaco and kusto
// so we increase the default timeout to 2secs to avoid flakiness
configure({ asyncUtilTimeout: 2000 });

View File

@ -1,4 +1,3 @@
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
@ -52,7 +51,13 @@ module.exports = {
string_decoder: false,
},
},
ignoreWarnings: [/export .* was not found in/],
ignoreWarnings: [
/export .* was not found in/,
{
module: /@kusto\/language-service\/bridge\.min\.js$/,
message: /^Critical dependency: the request of a dependency is an expression$/,
},
],
stats: {
children: false,
source: false,
@ -65,25 +70,6 @@ module.exports = {
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
new CopyWebpackPlugin({
patterns: [
{
context: path.join(require.resolve('monaco-editor/package.json'), '../min/vs/'),
from: '**/*',
to: '../lib/monaco/min/vs/', // inside the public/build folder
globOptions: {
ignore: [
'**/*.map', // debug files
],
},
},
{
context: path.join(require.resolve('@kusto/monaco-kusto/package.json'), '../release/min'),
from: '**/*',
to: '../lib/monaco/min/vs/language/kusto/',
},
],
}),
],
module: {
rules: [

465
yarn.lock
View File

@ -399,6 +399,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/parser@npm:^7.22.15":
version: 7.23.6
resolution: "@babel/parser@npm:7.23.6"
bin:
parser: ./bin/babel-parser.js
checksum: 10/6be3a63d3c9d07b035b5a79c022327cb7e16cbd530140ecb731f19a650c794c315a72c699a22413ebeafaff14aa8f53435111898d59e01a393d741b85629fa7d
languageName: node
linkType: hard
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15, @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3"
@ -1679,7 +1688,18 @@ __metadata:
languageName: node
linkType: hard
"@babel/template@npm:^7.22.15, @babel/template@npm:^7.22.5, @babel/template@npm:^7.23.9, @babel/template@npm:^7.3.3":
"@babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3":
version: 7.22.15
resolution: "@babel/template@npm:7.22.15"
dependencies:
"@babel/code-frame": "npm:^7.22.13"
"@babel/parser": "npm:^7.22.15"
"@babel/types": "npm:^7.22.15"
checksum: 10/21e768e4eed4d1da2ce5d30aa51db0f4d6d8700bc1821fec6292587df7bba2fe1a96451230de8c64b989740731888ebf1141138bfffb14cacccf4d05c66ad93f
languageName: node
linkType: hard
"@babel/template@npm:^7.22.5, @babel/template@npm:^7.23.9":
version: 7.23.9
resolution: "@babel/template@npm:7.23.9"
dependencies:
@ -1708,7 +1728,18 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
version: 7.23.6
resolution: "@babel/types@npm:7.23.6"
dependencies:
"@babel/helper-string-parser": "npm:^7.23.4"
"@babel/helper-validator-identifier": "npm:^7.22.20"
to-fast-properties: "npm:^2.0.0"
checksum: 10/07e70bb94d30b0231396b5e9a7726e6d9227a0a62e0a6830c0bd3232f33b024092e3d5a7d1b096a65bbf2bb43a9ab4c721bf618e115bfbb87b454fa060f88cbf
languageName: node
linkType: hard
"@babel/types@npm:^7.23.9":
version: 7.23.9
resolution: "@babel/types@npm:7.23.9"
dependencies:
@ -3749,17 +3780,7 @@ __metadata:
languageName: node
linkType: hard
"@grafana/faro-core@npm:^1.3.6, @grafana/faro-core@npm:^1.3.7":
version: 1.3.7
resolution: "@grafana/faro-core@npm:1.3.7"
dependencies:
"@opentelemetry/api": "npm:^1.7.0"
"@opentelemetry/otlp-transformer": "npm:^0.45.1"
checksum: 10/fa3ff8dce1e6fe5ad91a4d42bb9bdb13f36d594074566a100645ffb4bc509265d18c78c5cda1ef8f39a3f043c1901baee620d3e044b3a0a6e9d1c516bf71f74f
languageName: node
linkType: hard
"@grafana/faro-core@npm:^1.3.8":
"@grafana/faro-core@npm:^1.3.6, @grafana/faro-core@npm:^1.3.8":
version: 1.3.8
resolution: "@grafana/faro-core@npm:1.3.8"
dependencies:
@ -3769,6 +3790,16 @@ __metadata:
languageName: node
linkType: hard
"@grafana/faro-core@npm:^1.3.7":
version: 1.3.7
resolution: "@grafana/faro-core@npm:1.3.7"
dependencies:
"@opentelemetry/api": "npm:^1.7.0"
"@opentelemetry/otlp-transformer": "npm:^0.45.1"
checksum: 10/fa3ff8dce1e6fe5ad91a4d42bb9bdb13f36d594074566a100645ffb4bc509265d18c78c5cda1ef8f39a3f043c1901baee620d3e044b3a0a6e9d1c516bf71f74f
languageName: node
linkType: hard
"@grafana/faro-web-sdk@npm:1.3.8":
version: 1.3.8
resolution: "@grafana/faro-web-sdk@npm:1.3.8"
@ -4737,6 +4768,13 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/resolve-uri@npm:3.1.0":
version: 3.1.0
resolution: "@jridgewell/resolve-uri@npm:3.1.0"
checksum: 10/320ceb37af56953757b28e5b90c34556157676d41e3d0a3ff88769274d62373582bb0f0276a4f2d29c3f4fdd55b82b8be5731f52d391ad2ecae9b321ee1c742d
languageName: node
linkType: hard
"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0":
version: 3.1.1
resolution: "@jridgewell/resolve-uri@npm:3.1.1"
@ -4751,6 +4789,16 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/source-map@npm:^0.3.2":
version: 0.3.2
resolution: "@jridgewell/source-map@npm:0.3.2"
dependencies:
"@jridgewell/gen-mapping": "npm:^0.3.0"
"@jridgewell/trace-mapping": "npm:^0.3.9"
checksum: 10/1aaa42075bac32a551708025da0c07b11c11fb05ccd10fb70df2cb0db88773338ab0f33f175d9865379cb855bb3b1cda478367747a1087309fda40a7b9214bfa
languageName: node
linkType: hard
"@jridgewell/source-map@npm:^0.3.3":
version: 0.3.5
resolution: "@jridgewell/source-map@npm:0.3.5"
@ -4761,6 +4809,13 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/sourcemap-codec@npm:1.4.14":
version: 1.4.14
resolution: "@jridgewell/sourcemap-codec@npm:1.4.14"
checksum: 10/26e768fae6045481a983e48aa23d8fcd23af5da70ebd74b0649000e815e7fbb01ea2bc088c9176b3fffeb9bec02184e58f46125ef3320b30eaa1f4094cfefa38
languageName: node
linkType: hard
"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15":
version: 1.4.15
resolution: "@jridgewell/sourcemap-codec@npm:1.4.15"
@ -4778,7 +4833,27 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.21, @jridgewell/trace-mapping@npm:^0.3.9":
"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9":
version: 0.3.18
resolution: "@jridgewell/trace-mapping@npm:0.3.18"
dependencies:
"@jridgewell/resolve-uri": "npm:3.1.0"
"@jridgewell/sourcemap-codec": "npm:1.4.14"
checksum: 10/f4fabdddf82398a797bcdbb51c574cd69b383db041a6cae1a6a91478681d6aab340c01af655cfd8c6e01cde97f63436a1445f08297cdd33587621cf05ffa0d55
languageName: node
linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.20":
version: 0.3.21
resolution: "@jridgewell/trace-mapping@npm:0.3.21"
dependencies:
"@jridgewell/resolve-uri": "npm:^3.1.0"
"@jridgewell/sourcemap-codec": "npm:^1.4.14"
checksum: 10/925dda0620887e5a24f11b5a3a106f4e8b1a66155b49be6ceee61432174df33a17c243d8a89b2cd79ccebd281d817878759236a2fc42c47325ae9f73dfbfb90d
languageName: node
linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.21":
version: 0.3.22
resolution: "@jridgewell/trace-mapping@npm:0.3.22"
dependencies:
@ -7096,13 +7171,20 @@ __metadata:
languageName: node
linkType: hard
"@remix-run/router@npm:1.14.2, @remix-run/router@npm:^1.5.0":
"@remix-run/router@npm:1.14.2":
version: 1.14.2
resolution: "@remix-run/router@npm:1.14.2"
checksum: 10/422844e88b985f1e287301b302c6cf8169c9eea792f80d40464f97b25393bb2e697228ebd7a7b61444d5a51c5873c4a637aad20acde5886a5caf62e833c5ceee
languageName: node
linkType: hard
"@remix-run/router@npm:^1.5.0":
version: 1.11.0
resolution: "@remix-run/router@npm:1.11.0"
checksum: 10/629ec578b9dfd3c5cb5de64a0798dd7846ec5ba0351aa66f42b1c65efb43da8f30366be59b825303648965b0df55b638c110949b24ef94fd62e98117fdfb0c0f
languageName: node
linkType: hard
"@rollup/plugin-commonjs@npm:25.0.7":
version: 25.0.7
resolution: "@rollup/plugin-commonjs@npm:25.0.7"
@ -8410,6 +8492,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-darwin-arm64@npm:1.3.90"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-darwin-arm64@npm:1.4.0"
@ -8424,6 +8513,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-darwin-x64@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-darwin-x64@npm:1.3.90"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@swc/core-darwin-x64@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-darwin-x64@npm:1.4.0"
@ -8438,6 +8534,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-linux-arm-gnueabihf@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.90"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@swc/core-linux-arm-gnueabihf@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.0"
@ -8452,6 +8555,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-linux-arm64-gnu@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.90"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-arm64-gnu@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-linux-arm64-gnu@npm:1.4.0"
@ -8466,6 +8576,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-linux-arm64-musl@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-linux-arm64-musl@npm:1.3.90"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@swc/core-linux-arm64-musl@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-linux-arm64-musl@npm:1.4.0"
@ -8480,6 +8597,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-linux-x64-gnu@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-linux-x64-gnu@npm:1.3.90"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-x64-gnu@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-linux-x64-gnu@npm:1.4.0"
@ -8494,6 +8618,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-linux-x64-musl@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-linux-x64-musl@npm:1.3.90"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@swc/core-linux-x64-musl@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-linux-x64-musl@npm:1.4.0"
@ -8508,6 +8639,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-win32-arm64-msvc@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.90"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@swc/core-win32-arm64-msvc@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-win32-arm64-msvc@npm:1.4.0"
@ -8522,6 +8660,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-win32-ia32-msvc@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.90"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@swc/core-win32-ia32-msvc@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-win32-ia32-msvc@npm:1.4.0"
@ -8536,6 +8681,13 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-win32-x64-msvc@npm:1.3.90":
version: 1.3.90
resolution: "@swc/core-win32-x64-msvc@npm:1.3.90"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core-win32-x64-msvc@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core-win32-x64-msvc@npm:1.4.0"
@ -8550,7 +8702,7 @@ __metadata:
languageName: node
linkType: hard
"@swc/core@npm:1.4.0, @swc/core@npm:^1.3.49":
"@swc/core@npm:1.4.0":
version: 1.4.0
resolution: "@swc/core@npm:1.4.0"
dependencies:
@ -8642,6 +8794,52 @@ __metadata:
languageName: node
linkType: hard
"@swc/core@npm:^1.3.49":
version: 1.3.90
resolution: "@swc/core@npm:1.3.90"
dependencies:
"@swc/core-darwin-arm64": "npm:1.3.90"
"@swc/core-darwin-x64": "npm:1.3.90"
"@swc/core-linux-arm-gnueabihf": "npm:1.3.90"
"@swc/core-linux-arm64-gnu": "npm:1.3.90"
"@swc/core-linux-arm64-musl": "npm:1.3.90"
"@swc/core-linux-x64-gnu": "npm:1.3.90"
"@swc/core-linux-x64-musl": "npm:1.3.90"
"@swc/core-win32-arm64-msvc": "npm:1.3.90"
"@swc/core-win32-ia32-msvc": "npm:1.3.90"
"@swc/core-win32-x64-msvc": "npm:1.3.90"
"@swc/counter": "npm:^0.1.1"
"@swc/types": "npm:^0.1.5"
peerDependencies:
"@swc/helpers": ^0.5.0
dependenciesMeta:
"@swc/core-darwin-arm64":
optional: true
"@swc/core-darwin-x64":
optional: true
"@swc/core-linux-arm-gnueabihf":
optional: true
"@swc/core-linux-arm64-gnu":
optional: true
"@swc/core-linux-arm64-musl":
optional: true
"@swc/core-linux-x64-gnu":
optional: true
"@swc/core-linux-x64-musl":
optional: true
"@swc/core-win32-arm64-msvc":
optional: true
"@swc/core-win32-ia32-msvc":
optional: true
"@swc/core-win32-x64-msvc":
optional: true
peerDependenciesMeta:
"@swc/helpers":
optional: true
checksum: 10/214af37af77b968203d495745a86db985734527f4696243bda5fb9ce868830d70e7a2cdbb268da2ee994d9fcedded25073d7b709fa09b75e96f9ba7d13a63da0
languageName: node
linkType: hard
"@swc/counter@npm:^0.1.1, @swc/counter@npm:^0.1.2, @swc/counter@npm:^0.1.3":
version: 0.1.3
resolution: "@swc/counter@npm:0.1.3"
@ -8649,7 +8847,7 @@ __metadata:
languageName: node
linkType: hard
"@swc/helpers@npm:0.5.6, @swc/helpers@npm:^0.5.0":
"@swc/helpers@npm:0.5.6":
version: 0.5.6
resolution: "@swc/helpers@npm:0.5.6"
dependencies:
@ -8658,6 +8856,15 @@ __metadata:
languageName: node
linkType: hard
"@swc/helpers@npm:^0.5.0":
version: 0.5.1
resolution: "@swc/helpers@npm:0.5.1"
dependencies:
tslib: "npm:^2.4.0"
checksum: 10/4954c4d2dd97bf965e863a10ffa44c3fdaf7653f2fa9ef1a6cf7ffffd67f3f832216588f9751afd75fdeaea60c4688c75c01e2405119c448f1a109c9a7958c54
languageName: node
linkType: hard
"@swc/types@npm:^0.1.5":
version: 0.1.5
resolution: "@swc/types@npm:0.1.5"
@ -8681,7 +8888,7 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/jest-dom@npm:6.4.2, @testing-library/jest-dom@npm:^6.1.2":
"@testing-library/jest-dom@npm:6.4.2":
version: 6.4.2
resolution: "@testing-library/jest-dom@npm:6.4.2"
dependencies:
@ -8714,6 +8921,39 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/jest-dom@npm:^6.1.2":
version: 6.2.1
resolution: "@testing-library/jest-dom@npm:6.2.1"
dependencies:
"@adobe/css-tools": "npm:^4.3.2"
"@babel/runtime": "npm:^7.9.2"
aria-query: "npm:^5.0.0"
chalk: "npm:^3.0.0"
css.escape: "npm:^1.5.1"
dom-accessibility-api: "npm:^0.6.3"
lodash: "npm:^4.17.15"
redent: "npm:^3.0.0"
peerDependencies:
"@jest/globals": ">= 28"
"@types/bun": "*"
"@types/jest": ">= 28"
jest: ">= 28"
vitest: ">= 0.32"
peerDependenciesMeta:
"@jest/globals":
optional: true
"@types/bun":
optional: true
"@types/jest":
optional: true
jest:
optional: true
vitest:
optional: true
checksum: 10/e522314c4623d2030146570ed5907c71126477272760fa6bce7a2db4ee5dd1edf492ebc9a5f442e99f69d4d6e2a9d0aac49fae41323211dbcee186a1eca04707
languageName: node
linkType: hard
"@testing-library/react-hooks@npm:^8.0.1":
version: 8.0.1
resolution: "@testing-library/react-hooks@npm:8.0.1"
@ -9800,7 +10040,7 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:20.11.19, @types/node@npm:^20.11.16":
"@types/node@npm:20.11.19":
version: 20.11.19
resolution: "@types/node@npm:20.11.19"
dependencies:
@ -9823,6 +10063,15 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^20.11.16":
version: 20.11.18
resolution: "@types/node@npm:20.11.18"
dependencies:
undici-types: "npm:~5.26.4"
checksum: 10/eeaa55032e6702867e96d7b6f98df1d60af09d37ab72f2b905b349ec7e458dfb9c4d9cfc562962f5a51b156a968eea773d8025688f88b735944c81e3ac0e3b7f
languageName: node
linkType: hard
"@types/normalize-package-data@npm:^2.4.0":
version: 2.4.1
resolution: "@types/normalize-package-data@npm:2.4.1"
@ -9939,7 +10188,16 @@ __metadata:
languageName: node
linkType: hard
"@types/react-dom@npm:*, @types/react-dom@npm:18.2.19, @types/react-dom@npm:^18.0.0":
"@types/react-dom@npm:*, @types/react-dom@npm:^18.0.0":
version: 18.2.7
resolution: "@types/react-dom@npm:18.2.7"
dependencies:
"@types/react": "npm:*"
checksum: 10/9b70ef66cbe2d2898ea37eb79ee3697e0e4ad3d950e769a601f79be94097d43b8ef45b98a0b29528203c7d731c81666f637b2b7032deeced99214b4bc0662614
languageName: node
linkType: hard
"@types/react-dom@npm:18.2.19":
version: 18.2.19
resolution: "@types/react-dom@npm:18.2.19"
dependencies:
@ -11386,6 +11644,15 @@ __metadata:
languageName: node
linkType: hard
"acorn@npm:^8.5.0":
version: 8.10.0
resolution: "acorn@npm:8.10.0"
bin:
acorn: bin/acorn
checksum: 10/522310c20fdc3c271caed3caf0f06c51d61cb42267279566edd1d58e83dbc12eebdafaab666a0f0be1b7ad04af9c6bc2a6f478690a9e6391c3c8b165ada917dd
languageName: node
linkType: hard
"add-dom-event-listener@npm:^1.1.0":
version: 1.1.0
resolution: "add-dom-event-listener@npm:1.1.0"
@ -12080,13 +12347,20 @@ __metadata:
languageName: node
linkType: hard
"axe-core@npm:=4.7.0, axe-core@npm:^4.2.0":
"axe-core@npm:=4.7.0":
version: 4.7.0
resolution: "axe-core@npm:4.7.0"
checksum: 10/615c0f7722c3c9fcf353dbd70b00e2ceae234d4c17cbc839dd85c01d16797c4e4da45f8d27c6118e9e6b033fb06efd196106e13651a1b2f3a10e0f11c7b2f660
languageName: node
linkType: hard
"axe-core@npm:^4.2.0":
version: 4.6.3
resolution: "axe-core@npm:4.6.3"
checksum: 10/280f6a7067129875380f733ae84093ce29c4b8cfe36e1a8ff46bd5d2bcd57d093f11b00223ddf5fef98ca147e0e6568ddd0ada9415cf8ae15d379224bf3cbb51
languageName: node
linkType: hard
"axios@npm:^1.0.0":
version: 1.5.1
resolution: "axios@npm:1.5.1"
@ -12651,7 +12925,21 @@ __metadata:
languageName: node
linkType: hard
"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.2":
"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.4, browserslist@npm:^4.22.2":
version: 4.22.2
resolution: "browserslist@npm:4.22.2"
dependencies:
caniuse-lite: "npm:^1.0.30001565"
electron-to-chromium: "npm:^1.4.601"
node-releases: "npm:^2.0.14"
update-browserslist-db: "npm:^1.0.13"
bin:
browserslist: cli.js
checksum: 10/e3590793db7f66ad3a50817e7b7f195ce61e029bd7187200244db664bfbe0ac832f784e4f6b9c958aef8ea4abe001ae7880b7522682df521f4bc0a5b67660b5e
languageName: node
linkType: hard
"browserslist@npm:^4.21.10":
version: 4.22.3
resolution: "browserslist@npm:4.22.3"
dependencies:
@ -12968,6 +13256,13 @@ __metadata:
languageName: node
linkType: hard
"caniuse-lite@npm:^1.0.30001565":
version: 1.0.30001579
resolution: "caniuse-lite@npm:1.0.30001579"
checksum: 10/2cd0c02e5d66b09888743ad2b624dbde697ace5c76b55bfd6065ea033f6abea8ac3f5d3c9299c042f91b396e2141b49bc61f5e17086dc9ba3a866cc6790134c0
languageName: node
linkType: hard
"canvas-hypertxt@npm:^1.0.3":
version: 1.0.3
resolution: "canvas-hypertxt@npm:1.0.3"
@ -13239,14 +13534,14 @@ __metadata:
languageName: node
linkType: hard
"cli-spinners@npm:2.6.1, cli-spinners@npm:^2.5.0":
"cli-spinners@npm:2.6.1":
version: 2.6.1
resolution: "cli-spinners@npm:2.6.1"
checksum: 10/3e2dc5df72cf02120bebe256881fc8e3ec49867e5023d39f1e7340d7da57964f5236f4c75e568aa9dea6460b56f7a6d5870b89453c743c6c15e213cb52be2122
languageName: node
linkType: hard
"cli-spinners@npm:^2.9.2":
"cli-spinners@npm:^2.5.0, cli-spinners@npm:^2.9.2":
version: 2.9.2
resolution: "cli-spinners@npm:2.9.2"
checksum: 10/a0a863f442df35ed7294424f5491fa1756bd8d2e4ff0c8736531d886cec0ece4d85e8663b77a5afaf1d296e3cbbebff92e2e99f52bbea89b667cbe789b994794
@ -14168,7 +14463,7 @@ __metadata:
languageName: node
linkType: hard
"css-loader@npm:6.10.0, css-loader@npm:^6.7.1":
"css-loader@npm:6.10.0":
version: 6.10.0
resolution: "css-loader@npm:6.10.0"
dependencies:
@ -14192,6 +14487,24 @@ __metadata:
languageName: node
linkType: hard
"css-loader@npm:^6.7.1":
version: 6.9.1
resolution: "css-loader@npm:6.9.1"
dependencies:
icss-utils: "npm:^5.1.0"
postcss: "npm:^8.4.33"
postcss-modules-extract-imports: "npm:^3.0.0"
postcss-modules-local-by-default: "npm:^4.0.4"
postcss-modules-scope: "npm:^3.1.1"
postcss-modules-values: "npm:^4.0.0"
postcss-value-parser: "npm:^4.2.0"
semver: "npm:^7.5.4"
peerDependencies:
webpack: ^5.0.0
checksum: 10/6f897406188ed7f6db03daab0602ed86df1e967b48a048ab72d0ee223e59ab9e13c5235481b12deb79e12aadf0be43bc3bdee71e1dc1e875e4bcd91c05b464af
languageName: node
linkType: hard
"css-minimizer-webpack-plugin@npm:6.0.0":
version: 6.0.0
resolution: "css-minimizer-webpack-plugin@npm:6.0.0"
@ -15696,6 +16009,13 @@ __metadata:
languageName: node
linkType: hard
"electron-to-chromium@npm:^1.4.601":
version: 1.4.625
resolution: "electron-to-chromium@npm:1.4.625"
checksum: 10/610a4eaabf6a064d8f6d4dfa25c55a3940f09a3b25edc8a271821d1b270bb28c4c9f19225d81bfc59deaa12c1f8f0144f3b4510631c6b6b47e0b6216737e216a
languageName: node
linkType: hard
"electron-to-chromium@npm:^1.4.648":
version: 1.4.648
resolution: "electron-to-chromium@npm:1.4.648"
@ -24128,14 +24448,7 @@ __metadata:
languageName: node
linkType: hard
"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0":
version: 1.4.0
resolution: "outvariant@npm:1.4.0"
checksum: 10/07b9bcb9b3a2ff1b3db02af6b07d70e663082b30ddc08ff475d7c85fc623fdcc4433a4ab5b88f6902b62dbb284eef1be386aa537e14cef0519fad887ec483054
languageName: node
linkType: hard
"outvariant@npm:^1.4.2":
"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0, outvariant@npm:^1.4.2":
version: 1.4.2
resolution: "outvariant@npm:1.4.2"
checksum: 10/f16ba035fb65d1cbe7d2e06693dd42183c46bc8456713d9ddb5182d067defa7d78217edab0a2d3e173d3bacd627b2bd692195c7087c225b82548fbf52c677b38
@ -25167,7 +25480,17 @@ __metadata:
languageName: node
linkType: hard
"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.13, postcss-selector-parser@npm:^6.0.15, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4":
"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.13, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4":
version: 6.0.13
resolution: "postcss-selector-parser@npm:6.0.13"
dependencies:
cssesc: "npm:^3.0.0"
util-deprecate: "npm:^1.0.2"
checksum: 10/e779aa1f8ca9ee45d562400aac6109a2bccc59559b6e15adec8bc2a71d395ca563a378fd68f6a61963b4ef2ca190e0c0486e6dc6c41d755f3b82dd6e480e6941
languageName: node
linkType: hard
"postcss-selector-parser@npm:^6.0.15":
version: 6.0.15
resolution: "postcss-selector-parser@npm:6.0.15"
dependencies:
@ -26841,7 +27164,7 @@ __metadata:
languageName: node
linkType: hard
"react-use@npm:17.5.0, react-use@npm:^17.4.2":
"react-use@npm:17.5.0":
version: 17.5.0
resolution: "react-use@npm:17.5.0"
dependencies:
@ -26866,6 +27189,31 @@ __metadata:
languageName: node
linkType: hard
"react-use@npm:^17.4.2":
version: 17.4.2
resolution: "react-use@npm:17.4.2"
dependencies:
"@types/js-cookie": "npm:^2.2.6"
"@xobotyi/scrollbar-width": "npm:^1.9.5"
copy-to-clipboard: "npm:^3.3.1"
fast-deep-equal: "npm:^3.1.3"
fast-shallow-equal: "npm:^1.0.0"
js-cookie: "npm:^2.2.1"
nano-css: "npm:^5.6.1"
react-universal-interface: "npm:^0.6.2"
resize-observer-polyfill: "npm:^1.5.1"
screenfull: "npm:^5.1.0"
set-harmonic-interval: "npm:^1.0.1"
throttle-debounce: "npm:^3.0.1"
ts-easing: "npm:^0.2.0"
tslib: "npm:^2.1.0"
peerDependencies:
react: "*"
react-dom: "*"
checksum: 10/56d2da474d949d22eb34ff3ffccf5526986d51ed68a8f4e64f4b79bdcff3f0ea55d322c104e3fc0819b08b8765e8eb3fa47d8b506e9d61ff1fdc7bd1374c17d6
languageName: node
linkType: hard
"react-virtual@npm:2.10.4, react-virtual@npm:^2.8.2":
version: 2.10.4
resolution: "react-virtual@npm:2.10.4"
@ -28260,14 +28608,7 @@ __metadata:
languageName: node
linkType: hard
"signal-exit@npm:^4.0.1":
version: 4.0.2
resolution: "signal-exit@npm:4.0.2"
checksum: 10/99d49eab7f24aeed79e44999500d5ff4b9fbb560b0e1f8d47096c54d625b995aeaec3032cce44527adf2de0c303731a8356e234a348d6801214a8a3385a1ff8e
languageName: node
linkType: hard
"signal-exit@npm:^4.1.0":
"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0":
version: 4.1.0
resolution: "signal-exit@npm:4.1.0"
checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f
@ -29695,7 +30036,7 @@ __metadata:
languageName: node
linkType: hard
"terser-webpack-plugin@npm:5.3.10, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.1, terser-webpack-plugin@npm:^5.3.10, terser-webpack-plugin@npm:^5.3.7":
"terser-webpack-plugin@npm:5.3.10, terser-webpack-plugin@npm:^5.3.10":
version: 5.3.10
resolution: "terser-webpack-plugin@npm:5.3.10"
dependencies:
@ -29717,6 +30058,28 @@ __metadata:
languageName: node
linkType: hard
"terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.1, terser-webpack-plugin@npm:^5.3.7":
version: 5.3.9
resolution: "terser-webpack-plugin@npm:5.3.9"
dependencies:
"@jridgewell/trace-mapping": "npm:^0.3.17"
jest-worker: "npm:^27.4.5"
schema-utils: "npm:^3.1.1"
serialize-javascript: "npm:^6.0.1"
terser: "npm:^5.16.8"
peerDependencies:
webpack: ^5.1.0
peerDependenciesMeta:
"@swc/core":
optional: true
esbuild:
optional: true
uglify-js:
optional: true
checksum: 10/339737a407e034b7a9d4a66e31d84d81c10433e41b8eae2ca776f0e47c2048879be482a9aa08e8c27565a2a949bc68f6e07f451bf4d9aa347dd61b3d000f5353
languageName: node
linkType: hard
"terser@npm:^5.0.0, terser@npm:^5.15.1, terser@npm:^5.26.0, terser@npm:^5.7.2":
version: 5.27.0
resolution: "terser@npm:5.27.0"
@ -29731,6 +30094,20 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:^5.16.8":
version: 5.17.2
resolution: "terser@npm:5.17.2"
dependencies:
"@jridgewell/source-map": "npm:^0.3.2"
acorn: "npm:^8.5.0"
commander: "npm:^2.20.0"
source-map-support: "npm:~0.5.20"
bin:
terser: bin/terser
checksum: 10/6df529586a4913657547dd8bfe2b5a59704b7acbe4e49ac938a16f829a62226f98dafb19c88b7af66b245ea281ee5dbeec33a41349ac3c035855417b06ebd646
languageName: node
linkType: hard
"test-exclude@npm:^6.0.0":
version: 6.0.0
resolution: "test-exclude@npm:6.0.0"