mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:31:43 +00:00
Add Code Insights independent MVP (#19600)
* Add sandboxes directory for MVPs * Change code insights imports in consumers * Add new API context approach for mocking data in MVP
This commit is contained in:
parent
6d2604a09d
commit
7dfc4416f2
@ -7,6 +7,7 @@
|
||||
- **eslint-plugin-sourcegraph**: Not published package with custom ESLint rules for Sourcegraph. Isn't intended for reuse by other repositories in the Sourcegraph org.
|
||||
- **extension-api**: The Sourcegraph extension API types for the _Sourcegraph extensions_. Published as `sourcegraph`.
|
||||
- **extension-api-types**: The Sourcegraph extension API types for _client applications_ that embed Sourcegraph extensions and need to communicate with them. Published as `@sourcegraph/extension-api-types`.
|
||||
- **sandboxes**: All demos-mvp (minimum viable product) for the Sourcegraph web application.
|
||||
- **shared**: Contains common TypeScript/React/SCSS client code shared between the browser extension and the web app. Everything in this package is code-host agnostic.
|
||||
- **branded**: Contains React components and implements the visual design language we use across our web app and e.g. in the options menu of the browser extension. Over time, components from `shared` and `branded` packages should be moved into the `wildcard` package.
|
||||
- **wildcard**: Package that encapsulates storybook configuration and contains our Wildcard design system components. If we're using a component in two or more different areas (e.g. `web-app` and `browser-extension`) then it should live in the `wildcard` package. Otherwise the components should be better colocated with the code where they're actually used.
|
||||
|
||||
13
client/sandboxes/code-insights/.eslintrc.js
Normal file
13
client/sandboxes/code-insights/.eslintrc.js
Normal file
@ -0,0 +1,13 @@
|
||||
// @ts-check
|
||||
|
||||
const baseConfig = require('../../../.eslintrc.js')
|
||||
|
||||
module.exports = {
|
||||
extends: '../../../.eslintrc.js',
|
||||
parserOptions: {
|
||||
...baseConfig.parserOptions,
|
||||
project: [__dirname + '/tsconfig.json'],
|
||||
},
|
||||
rules: {},
|
||||
overrides: baseConfig.overrides,
|
||||
}
|
||||
3
client/sandboxes/code-insights/.stylelintrc.json
Normal file
3
client/sandboxes/code-insights/.stylelintrc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@sourcegraph/stylelint-config"]
|
||||
}
|
||||
9
client/sandboxes/code-insights/babel.config.js
Normal file
9
client/sandboxes/code-insights/babel.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@babel/core').TransformOptions} */
|
||||
const config = {
|
||||
extends: '../../../babel.config.js',
|
||||
plugins: ['react-refresh/babel'],
|
||||
}
|
||||
|
||||
module.exports = config
|
||||
13
client/sandboxes/code-insights/package.json
Normal file
13
client/sandboxes/code-insights/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@sourcegraph/code-insights-demo",
|
||||
"version": "0.0.1",
|
||||
"description": "Sourcegraph code insight mvp",
|
||||
"sideEffects": false,
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"eslint": "eslint --cache '**/*.[jt]s?(x)'",
|
||||
"stylelint": "stylelint 'src/**/*.scss' --quiet",
|
||||
"serve": "webpack serve --hot"
|
||||
}
|
||||
}
|
||||
50
client/sandboxes/code-insights/src/demo.tsx
Normal file
50
client/sandboxes/code-insights/src/demo.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { createBrowserHistory } from 'history'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import { render } from 'react-dom'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
|
||||
import { setLinkComponent } from '@sourcegraph/shared/src/components/Link'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
|
||||
import { EMPTY_SETTINGS_CASCADE } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { RouterLinkOrAnchor } from '@sourcegraph/web/src/components/RouterLinkOrAnchor'
|
||||
import { InsightsApiContext, InsightsPage } from '@sourcegraph/web/src/insights'
|
||||
|
||||
import { MockInsightsApi } from './mock-api'
|
||||
|
||||
import '@sourcegraph/web/src/SourcegraphWebApp.scss'
|
||||
|
||||
const history = createBrowserHistory()
|
||||
const mockAPI = new MockInsightsApi()
|
||||
|
||||
setLinkComponent(RouterLinkOrAnchor)
|
||||
|
||||
export function App(): ReactElement {
|
||||
const [patternType, setPatterType] = useState(SearchPatternType.literal)
|
||||
const [caseSensitive, setCaseSensitive] = useState(false)
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<InsightsApiContext.Provider value={mockAPI}>
|
||||
<InsightsPage
|
||||
versionContext={undefined}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
copyQueryButton={false}
|
||||
caseSensitive={caseSensitive}
|
||||
setCaseSensitivity={setCaseSensitive}
|
||||
setPatternType={setPatterType}
|
||||
patternType={patternType}
|
||||
settingsCascade={EMPTY_SETTINGS_CASCADE}
|
||||
globbing={false}
|
||||
location={history.location}
|
||||
history={history}
|
||||
/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
|
||||
// @ts-ignore
|
||||
extensionsController={null}
|
||||
/>
|
||||
</InsightsApiContext.Provider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
||||
render(<App />, document.querySelector('#root'))
|
||||
11
client/sandboxes/code-insights/src/index.html
Normal file
11
client/sandboxes/code-insights/src/index.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Code insights magic developer environment</title>
|
||||
</head>
|
||||
|
||||
<body class="theme-light ">
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
160
client/sandboxes/code-insights/src/mock-api.ts
Normal file
160
client/sandboxes/code-insights/src/mock-api.ts
Normal file
@ -0,0 +1,160 @@
|
||||
import { Observable, of } from 'rxjs'
|
||||
|
||||
import { InsightsAPI } from '@sourcegraph/web/src/insights/core/backend/insights-api'
|
||||
import {
|
||||
ViewInsightProviderResult,
|
||||
ViewInsightProviderSourceType,
|
||||
} from '@sourcegraph/web/src/insights/core/backend/types'
|
||||
|
||||
export const MOCK_VIEWS = [
|
||||
{
|
||||
id: 'searchInsights.searchInsights.insight.graphQLTypesMigration.insightsPage',
|
||||
view: {
|
||||
title: 'Migration to new GraphQL TS types',
|
||||
content: [
|
||||
{
|
||||
chart: 'line' as const,
|
||||
data: [
|
||||
{
|
||||
date: 1595624400000,
|
||||
'Imports of old GQL.* types': 259,
|
||||
'Imports of new graphql-operations types': 7,
|
||||
},
|
||||
{
|
||||
date: 1599253200000,
|
||||
'Imports of old GQL.* types': 190,
|
||||
'Imports of new graphql-operations types': 191,
|
||||
},
|
||||
{
|
||||
date: 1602882000000,
|
||||
'Imports of old GQL.* types': 182,
|
||||
'Imports of new graphql-operations types': 210,
|
||||
},
|
||||
{
|
||||
date: 1606510800000,
|
||||
'Imports of old GQL.* types': 179,
|
||||
'Imports of new graphql-operations types': 256,
|
||||
},
|
||||
{
|
||||
date: 1610139600000,
|
||||
'Imports of old GQL.* types': 139,
|
||||
'Imports of new graphql-operations types': 335,
|
||||
},
|
||||
{
|
||||
date: 1613768400000,
|
||||
'Imports of old GQL.* types': 139,
|
||||
'Imports of new graphql-operations types': 352,
|
||||
},
|
||||
{
|
||||
date: 1617397200000,
|
||||
'Imports of old GQL.* types': 139,
|
||||
'Imports of new graphql-operations types': 362,
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
dataKey: 'Imports of old GQL.* types',
|
||||
name: 'Imports of old GQL.* types',
|
||||
stroke: 'var(--oc-red-7)',
|
||||
linkURLs: [
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-06-13T00%3A00%3A00%2B03%3A00+before%3A2020-07-25T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-07-25T00%3A00%3A00%2B03%3A00+before%3A2020-09-05T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-09-05T00%3A00%3A00%2B03%3A00+before%3A2020-10-17T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-10-17T00%3A00%3A00%2B03%3A00+before%3A2020-11-28T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-11-28T00%3A00%3A00%2B03%3A00+before%3A2021-01-09T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2021-01-09T00%3A00%3A00%2B03%3A00+before%3A2021-02-20T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2021-02-20T00%3A00%3A00%2B03%3A00+before%3A2021-04-03T00%3A00%3A00%2B03%3A00+patternType%3Aregex+case%3Ayes+%5C*%5Csas%5CsGQL',
|
||||
],
|
||||
},
|
||||
{
|
||||
dataKey: 'Imports of new graphql-operations types',
|
||||
name: 'Imports of new graphql-operations types',
|
||||
stroke: 'var(--oc-blue-7)',
|
||||
linkURLs: [
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-06-13T00%3A00%3A00%2B03%3A00+before%3A2020-07-25T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-07-25T00%3A00%3A00%2B03%3A00+before%3A2020-09-05T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-09-05T00%3A00%3A00%2B03%3A00+before%3A2020-10-17T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-10-17T00%3A00%3A00%2B03%3A00+before%3A2020-11-28T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2020-11-28T00%3A00%3A00%2B03%3A00+before%3A2021-01-09T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2021-01-09T00%3A00%3A00%2B03%3A00+before%3A2021-02-20T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
'https://sourcegraph.com/search?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24+type%3Adiff+after%3A2021-02-20T00%3A00%3A00%2B03%3A00+before%3A2021-04-03T00%3A00%3A00%2B03%3A00+patternType%3Aregexp+case%3Ayes+%2Fgraphql-operations%27',
|
||||
],
|
||||
},
|
||||
],
|
||||
xAxis: { dataKey: 'date', type: 'number', scale: 'time' },
|
||||
},
|
||||
],
|
||||
},
|
||||
source: ViewInsightProviderSourceType.Extension,
|
||||
},
|
||||
{
|
||||
id: 'codeStatsInsights.languages.insightsPage',
|
||||
view: {
|
||||
title: 'Language usage',
|
||||
content: [
|
||||
{
|
||||
chart: 'pie',
|
||||
pies: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
name: 'Go',
|
||||
totalLines: 363432,
|
||||
fill: '#00ADD8',
|
||||
linkURL:
|
||||
'https://sourcegraph.com/stats?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24',
|
||||
},
|
||||
{
|
||||
name: 'HTML',
|
||||
totalLines: 224961,
|
||||
fill: '#e34c26',
|
||||
linkURL:
|
||||
'https://sourcegraph.com/stats?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24',
|
||||
},
|
||||
{
|
||||
name: 'TypeScript',
|
||||
totalLines: 155381,
|
||||
fill: '#2b7489',
|
||||
linkURL:
|
||||
'https://sourcegraph.com/stats?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24',
|
||||
},
|
||||
{
|
||||
name: 'Markdown',
|
||||
totalLines: 46675,
|
||||
fill: '#083fa1',
|
||||
linkURL:
|
||||
'https://sourcegraph.com/stats?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24',
|
||||
},
|
||||
{
|
||||
name: 'YAML',
|
||||
totalLines: 25412,
|
||||
fill: '#cb171e',
|
||||
linkURL:
|
||||
'https://sourcegraph.com/stats?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
totalLines: 56846,
|
||||
fill: 'gray',
|
||||
linkURL:
|
||||
'https://sourcegraph.com/stats?q=repo%3A%5Egithub%5C.com%2Fsourcegraph%2Fsourcegraph%24',
|
||||
},
|
||||
],
|
||||
dataKey: 'totalLines',
|
||||
nameKey: 'name',
|
||||
fillKey: 'fill',
|
||||
linkURLKey: 'linkURL',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
source: 'Extension',
|
||||
},
|
||||
] as ViewInsightProviderResult[]
|
||||
|
||||
export class MockInsightsApi implements InsightsAPI {
|
||||
public getCombinedViews = (): Observable<ViewInsightProviderResult[]> => of(MOCK_VIEWS)
|
||||
|
||||
public getInsightCombinedViews = (): Observable<ViewInsightProviderResult[]> => this.getCombinedViews()
|
||||
}
|
||||
28
client/sandboxes/code-insights/tsconfig.json
Normal file
28
client/sandboxes/code-insights/tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["src/types/*", "../../shared/src/types/*", "*"],
|
||||
},
|
||||
"jsx": "react",
|
||||
"rootDir": ".",
|
||||
"outDir": "out",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "ts-graphql-plugin",
|
||||
"schema": "../../../cmd/frontend/graphqlbackend/schema.graphql",
|
||||
"tag": "gql",
|
||||
},
|
||||
],
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../../web" },
|
||||
{ "path": "../../shared" },
|
||||
{ "path": "../../branded" },
|
||||
{ "path": "../../../schema" },
|
||||
],
|
||||
"include": ["**/*", ".*", "./src/**/*.json"],
|
||||
"exclude": ["../../node_modules", "./node_modules", "./out", "src/end-to-end", "src/regression", "src/integration"],
|
||||
}
|
||||
47
client/sandboxes/code-insights/webpack.config.js
Normal file
47
client/sandboxes/code-insights/webpack.config.js
Normal file
@ -0,0 +1,47 @@
|
||||
const path = require('path')
|
||||
|
||||
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve(__dirname, './src/demo.tsx'),
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'), // Note: Physical files are only output by the production build task `npm run build`.
|
||||
publicPath: '/',
|
||||
filename: '[name].bundle.js',
|
||||
},
|
||||
target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test
|
||||
mode: 'development',
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
alias: { react: require.resolve('react') },
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|js)x?$/,
|
||||
exclude: /node_modules/,
|
||||
use: ['babel-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(scss)$/i,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
},
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new ReactRefreshPlugin({
|
||||
overlay: false,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: './src/index.html',
|
||||
}),
|
||||
],
|
||||
}
|
||||
@ -121,6 +121,7 @@ body,
|
||||
@import './user/UserAvatar';
|
||||
@import './user/area/UserArea';
|
||||
@import './org/OrgsArea';
|
||||
@import 'insights/pages/InsightsPage';
|
||||
@import './components/Badge';
|
||||
@import './components/Collapsible';
|
||||
@import './components/DismissibleAlert';
|
||||
|
||||
@ -32,7 +32,7 @@ import { ExtensionAreaRoute } from './extensions/extension/ExtensionArea'
|
||||
import { ExtensionAreaHeaderNavItem } from './extensions/extension/ExtensionAreaHeader'
|
||||
import { ExtensionsAreaRoute } from './extensions/ExtensionsArea'
|
||||
import { ExtensionsAreaHeaderActionButton } from './extensions/ExtensionsAreaHeader'
|
||||
import { logCodeInsightsChanges } from './insights/analytics'
|
||||
import { logCodeInsightsChanges } from './insights'
|
||||
import { KeyboardShortcutsProps } from './keyboardShortcuts/keyboardShortcuts'
|
||||
import { Layout, LayoutProps } from './Layout'
|
||||
import { updateUserSessionStores } from './marketing/util'
|
||||
|
||||
@ -1,116 +0,0 @@
|
||||
import { combineLatest, Observable, of } from 'rxjs'
|
||||
import { catchError, map } from 'rxjs/operators'
|
||||
import { LineChartContent } from 'sourcegraph'
|
||||
|
||||
import { ViewProviderResult } from '@sourcegraph/shared/src/api/extension/extensionHostApi'
|
||||
import { dataOrThrowErrors, gql } from '@sourcegraph/shared/src/graphql/graphql'
|
||||
import { asError } from '@sourcegraph/shared/src/util/errors'
|
||||
|
||||
import { requestGraphQL } from '../backend/graphql'
|
||||
import { InsightsResult, InsightFields } from '../graphql-operations'
|
||||
|
||||
const insightFieldsFragment = gql`
|
||||
fragment InsightFields on Insight {
|
||||
title
|
||||
description
|
||||
series {
|
||||
label
|
||||
points {
|
||||
dateTime
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
function fetchBackendInsights(): Observable<InsightFields[]> {
|
||||
return requestGraphQL<InsightsResult>(gql`
|
||||
query Insights {
|
||||
insights {
|
||||
nodes {
|
||||
...InsightFields
|
||||
}
|
||||
}
|
||||
}
|
||||
${insightFieldsFragment}
|
||||
`).pipe(
|
||||
map(dataOrThrowErrors),
|
||||
map(data => data.insights?.nodes ?? [])
|
||||
)
|
||||
}
|
||||
|
||||
export enum ViewInsightProviderSourceType {
|
||||
Backend = 'Backend',
|
||||
Extension = 'Extension',
|
||||
}
|
||||
|
||||
export interface ViewInsightProviderResult extends ViewProviderResult {
|
||||
/** The source of view provider to distinguish between data from extension and data from backend */
|
||||
source: ViewInsightProviderSourceType
|
||||
}
|
||||
|
||||
export function getCombinedViews(
|
||||
getExtensionsInsights: () => Observable<ViewProviderResult[]>
|
||||
): Observable<ViewInsightProviderResult[]> {
|
||||
return combineLatest([
|
||||
getExtensionsInsights().pipe(
|
||||
map(extensionInsights =>
|
||||
extensionInsights.map(insight => ({ ...insight, source: ViewInsightProviderSourceType.Extension }))
|
||||
)
|
||||
),
|
||||
fetchBackendInsights().pipe(
|
||||
map(backendInsights =>
|
||||
backendInsights.map(
|
||||
(insight, index): ViewInsightProviderResult => ({
|
||||
id: `Backend insight ${index + 1}`,
|
||||
view: {
|
||||
title: insight.title,
|
||||
subtitle: insight.description,
|
||||
content: [backendInsightToViewContent(insight)],
|
||||
},
|
||||
source: ViewInsightProviderSourceType.Backend,
|
||||
})
|
||||
)
|
||||
),
|
||||
catchError(error =>
|
||||
of<ViewInsightProviderResult[]>([
|
||||
{
|
||||
id: 'Backend insight',
|
||||
view: asError(error),
|
||||
source: ViewInsightProviderSourceType.Backend,
|
||||
},
|
||||
])
|
||||
)
|
||||
),
|
||||
]).pipe(map(([extensionViews, backendInsights]) => [...backendInsights, ...extensionViews]))
|
||||
}
|
||||
|
||||
function backendInsightToViewContent(
|
||||
insight: InsightFields
|
||||
): LineChartContent<{ dateTime: number; [seriesKey: string]: number }, 'dateTime'> {
|
||||
const dataByXValue = new Map<string, { dateTime: number; [seriesKey: string]: number }>()
|
||||
for (const [seriesIndex, series] of insight.series.entries()) {
|
||||
for (const point of series.points) {
|
||||
let dataObject = dataByXValue.get(point.dateTime)
|
||||
if (!dataObject) {
|
||||
dataObject = {
|
||||
dateTime: Date.parse(point.dateTime),
|
||||
}
|
||||
dataByXValue.set(point.dateTime, dataObject)
|
||||
}
|
||||
dataObject[`series${seriesIndex}`] = point.value
|
||||
}
|
||||
}
|
||||
return {
|
||||
chart: 'line',
|
||||
data: [...dataByXValue.values()],
|
||||
series: insight.series.map((series, index) => ({
|
||||
name: series.label,
|
||||
dataKey: `series${index}`,
|
||||
})),
|
||||
xAxis: {
|
||||
dataKey: 'dateTime',
|
||||
scale: 'time',
|
||||
type: 'number',
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
import { LinkWithIcon } from '../components/LinkWithIcon'
|
||||
|
||||
import { InsightsIcon } from './icon'
|
||||
import { LinkWithIcon } from '../../../components/LinkWithIcon'
|
||||
import { InsightsIcon } from '../Icons'
|
||||
|
||||
export const InsightsNavItem: React.FunctionComponent = () => (
|
||||
<LinkWithIcon
|
||||
@ -1,8 +1,8 @@
|
||||
@import '../../views/ViewContent';
|
||||
@import '../../../views/ViewContent';
|
||||
@import 'react-grid-layout/css/styles';
|
||||
@import 'react-resizable/css/styles';
|
||||
|
||||
.view-grid {
|
||||
.insights-view-grid {
|
||||
.react-resizable-handle {
|
||||
background: none;
|
||||
cursor: nwse-resize;
|
||||
@ -9,16 +9,16 @@ import { LoadingSpinner } from '@sourcegraph/react-loading-spinner'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { isErrorLike } from '@sourcegraph/shared/src/util/errors'
|
||||
|
||||
import { ErrorAlert } from '../../components/alerts'
|
||||
import { ErrorBoundary } from '../../components/ErrorBoundary'
|
||||
import { ViewInsightProviderResult, ViewInsightProviderSourceType } from '../../insights/backend'
|
||||
import { ViewContent, ViewContentProps } from '../../views/ViewContent'
|
||||
import { ErrorAlert } from '../../../components/alerts'
|
||||
import { ErrorBoundary } from '../../../components/ErrorBoundary'
|
||||
import { ViewContent, ViewContentProps } from '../../../views/ViewContent'
|
||||
import { ViewInsightProviderResult, ViewInsightProviderSourceType } from '../../core/backend/types'
|
||||
|
||||
// TODO use a method to get width that also triggers when file explorer is closed
|
||||
// (WidthProvider only listens to window resize events)
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive)
|
||||
|
||||
export interface ViewGridProps
|
||||
export interface InsightsViewGridProps
|
||||
extends Omit<ViewContentProps, 'viewContent' | 'viewID' | 'containerClassName'>,
|
||||
TelemetryProps {
|
||||
views: ViewInsightProviderResult[]
|
||||
@ -97,7 +97,7 @@ const getInsightViewIcon = (source: ViewInsightProviderSourceType): MdiReactIcon
|
||||
}
|
||||
}
|
||||
|
||||
export const ViewGrid: React.FunctionComponent<ViewGridProps> = props => {
|
||||
export const InsightsViewGrid: React.FunctionComponent<InsightsViewGridProps> = props => {
|
||||
const onResizeOrDragStart: ReactGridLayout.ItemCallback = useCallback(
|
||||
(_layout, item) => {
|
||||
try {
|
||||
@ -110,7 +110,7 @@ export const ViewGrid: React.FunctionComponent<ViewGridProps> = props => {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={classNames(props.className, 'view-grid')}>
|
||||
<div className={classNames(props.className, 'insights-view-grid')}>
|
||||
<ResponsiveGridLayout
|
||||
breakpoints={breakpoints}
|
||||
layouts={viewsToReactGridLayouts(props.views)}
|
||||
@ -123,7 +123,7 @@ export const ViewGrid: React.FunctionComponent<ViewGridProps> = props => {
|
||||
onDragStart={onResizeOrDragStart}
|
||||
>
|
||||
{props.views.map(({ id, view, source }) => (
|
||||
<div key={id} className={classNames('card view-grid__item')}>
|
||||
<div key={id} className={classNames('card insights-view-grid__item')}>
|
||||
<ErrorBoundary
|
||||
location={props.location}
|
||||
extraContext={
|
||||
@ -156,14 +156,16 @@ export const ViewGrid: React.FunctionComponent<ViewGridProps> = props => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h3 className="view-grid__view-title">{view.title}</h3>
|
||||
{view.subtitle && <div className="view-grid__view-subtitle">{view.subtitle}</div>}
|
||||
<h3 className="insights-view-grid__view-title">{view.title}</h3>
|
||||
{view.subtitle && (
|
||||
<div className="insights-view-grid__view-subtitle">{view.subtitle}</div>
|
||||
)}
|
||||
<ViewContent
|
||||
{...props}
|
||||
settingsCascade={props.settingsCascade}
|
||||
viewContent={view.content}
|
||||
viewID={id}
|
||||
containerClassName="view-grid__item"
|
||||
containerClassName="insights-view-grid__item"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
4
client/web/src/insights/components/index.ts
Normal file
4
client/web/src/insights/components/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export { InsightsIcon } from './Icons'
|
||||
export { InsightsNavItem } from './InsightsNavLink/InsightsNavLink'
|
||||
export { InsightsViewGrid } from './InsightsViewGrid/InsightsViewGrid'
|
||||
export type { InsightsViewGridProps } from './InsightsViewGrid/InsightsViewGrid'
|
||||
6
client/web/src/insights/core/backend/api-provider.ts
Normal file
6
client/web/src/insights/core/backend/api-provider.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { InsightsAPI } from './insights-api'
|
||||
import { ApiService } from './types'
|
||||
|
||||
export const InsightsApiContext = React.createContext<ApiService>(new InsightsAPI())
|
||||
60
client/web/src/insights/core/backend/insights-api.ts
Normal file
60
client/web/src/insights/core/backend/insights-api.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { Remote } from 'comlink'
|
||||
import { combineLatest, from, Observable, of } from 'rxjs'
|
||||
import { catchError, map, switchMap } from 'rxjs/operators'
|
||||
|
||||
import { wrapRemoteObservable } from '@sourcegraph/shared/src/api/client/api/common'
|
||||
import { FlatExtensionHostAPI } from '@sourcegraph/shared/src/api/contract'
|
||||
import { ViewProviderResult } from '@sourcegraph/shared/src/api/extension/extensionHostApi'
|
||||
import { asError } from '@sourcegraph/shared/src/util/errors'
|
||||
|
||||
import { fetchBackendInsights } from './requests/fetch-backend-insights'
|
||||
import { ApiService, ViewInsightProviderResult, ViewInsightProviderSourceType } from './types'
|
||||
import { createViewContent } from './utils/create-view-content'
|
||||
|
||||
/** Main API service to get data for code insights */
|
||||
export class InsightsAPI implements ApiService {
|
||||
/** Get combined (backend and extensions) code insights */
|
||||
public getCombinedViews = (
|
||||
getExtensionsInsights: () => Observable<ViewProviderResult[]>
|
||||
): Observable<ViewInsightProviderResult[]> =>
|
||||
combineLatest([
|
||||
getExtensionsInsights().pipe(
|
||||
map(extensionInsights =>
|
||||
extensionInsights.map(insight => ({ ...insight, source: ViewInsightProviderSourceType.Extension }))
|
||||
)
|
||||
),
|
||||
fetchBackendInsights().pipe(
|
||||
map(backendInsights =>
|
||||
backendInsights.map(
|
||||
(insight, index): ViewInsightProviderResult => ({
|
||||
id: `Backend insight ${index + 1}`,
|
||||
view: {
|
||||
title: insight.title,
|
||||
subtitle: insight.description,
|
||||
content: [createViewContent(insight)],
|
||||
},
|
||||
source: ViewInsightProviderSourceType.Backend,
|
||||
})
|
||||
)
|
||||
),
|
||||
catchError(error =>
|
||||
of<ViewInsightProviderResult[]>([
|
||||
{
|
||||
id: 'Backend insight',
|
||||
view: asError(error),
|
||||
source: ViewInsightProviderSourceType.Backend,
|
||||
},
|
||||
])
|
||||
)
|
||||
),
|
||||
]).pipe(map(([extensionViews, backendInsights]) => [...backendInsights, ...extensionViews]))
|
||||
|
||||
public getInsightCombinedViews = (
|
||||
extensionApi: Promise<Remote<FlatExtensionHostAPI>>
|
||||
): Observable<ViewInsightProviderResult[]> =>
|
||||
this.getCombinedViews(() =>
|
||||
from(extensionApi).pipe(
|
||||
switchMap(extensionHostAPI => wrapRemoteObservable(extensionHostAPI.getInsightsViews({})))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import { Observable } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
import { dataOrThrowErrors, gql } from '@sourcegraph/shared/src/graphql/graphql'
|
||||
|
||||
import { requestGraphQL } from '../../../../backend/graphql'
|
||||
import { InsightFields, InsightsResult } from '../../../../graphql-operations'
|
||||
|
||||
const insightFieldsFragment = gql`
|
||||
fragment InsightFields on Insight {
|
||||
title
|
||||
description
|
||||
series {
|
||||
label
|
||||
points {
|
||||
dateTime
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
export function fetchBackendInsights(): Observable<InsightFields[]> {
|
||||
return requestGraphQL<InsightsResult>(gql`
|
||||
query Insights {
|
||||
insights {
|
||||
nodes {
|
||||
...InsightFields
|
||||
}
|
||||
}
|
||||
}
|
||||
${insightFieldsFragment}
|
||||
`).pipe(
|
||||
map(dataOrThrowErrors),
|
||||
map(data => data.insights?.nodes ?? [])
|
||||
)
|
||||
}
|
||||
24
client/web/src/insights/core/backend/types.ts
Normal file
24
client/web/src/insights/core/backend/types.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Remote } from 'comlink'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { FlatExtensionHostAPI } from '@sourcegraph/shared/src/api/contract'
|
||||
import { ViewProviderResult } from '@sourcegraph/shared/src/api/extension/extensionHostApi'
|
||||
|
||||
export enum ViewInsightProviderSourceType {
|
||||
Backend = 'Backend',
|
||||
Extension = 'Extension',
|
||||
}
|
||||
|
||||
export interface ViewInsightProviderResult extends ViewProviderResult {
|
||||
/** The source of view provider to distinguish between data from extension and data from backend */
|
||||
source: ViewInsightProviderSourceType
|
||||
}
|
||||
|
||||
export interface ApiService {
|
||||
getCombinedViews: (
|
||||
getExtensionsInsights: () => Observable<ViewProviderResult[]>
|
||||
) => Observable<ViewInsightProviderResult[]>
|
||||
getInsightCombinedViews: (
|
||||
extensionApi: Promise<Remote<FlatExtensionHostAPI>>
|
||||
) => Observable<ViewInsightProviderResult[]>
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import { LineChartContent } from 'sourcegraph'
|
||||
|
||||
import { InsightFields } from '../../../../graphql-operations'
|
||||
|
||||
export function createViewContent(
|
||||
insight: InsightFields
|
||||
): LineChartContent<{ dateTime: number; [seriesKey: string]: number }, 'dateTime'> {
|
||||
const dataByXValue = new Map<string, { dateTime: number; [seriesKey: string]: number }>()
|
||||
for (const [seriesIndex, series] of insight.series.entries()) {
|
||||
for (const point of series.points) {
|
||||
let dataObject = dataByXValue.get(point.dateTime)
|
||||
if (!dataObject) {
|
||||
dataObject = {
|
||||
dateTime: Date.parse(point.dateTime),
|
||||
}
|
||||
dataByXValue.set(point.dateTime, dataObject)
|
||||
}
|
||||
dataObject[`series${seriesIndex}`] = point.value
|
||||
}
|
||||
}
|
||||
return {
|
||||
chart: 'line',
|
||||
data: [...dataByXValue.values()],
|
||||
series: insight.series.map((series, index) => ({
|
||||
name: series.label,
|
||||
dataKey: `series${index}`,
|
||||
})),
|
||||
xAxis: {
|
||||
dataKey: 'dateTime',
|
||||
scale: 'time',
|
||||
type: 'number',
|
||||
},
|
||||
}
|
||||
}
|
||||
10
client/web/src/insights/index.ts
Normal file
10
client/web/src/insights/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// Pages exports
|
||||
export { InsightsPage } from './pages/InsightsPage'
|
||||
|
||||
// Core insights exports
|
||||
export { InsightsApiContext } from './core/backend/api-provider'
|
||||
export * from './core/analytics'
|
||||
|
||||
// Public Insights components
|
||||
export { InsightsViewGrid } from './components'
|
||||
export type { InsightsViewGridProps } from './components'
|
||||
1
client/web/src/insights/pages/InsightsPage.scss
Normal file
1
client/web/src/insights/pages/InsightsPage.scss
Normal file
@ -0,0 +1 @@
|
||||
@import '../components/InsightsViewGrid/InsightsViewGrid';
|
||||
@ -1,37 +1,29 @@
|
||||
import GearIcon from 'mdi-react/GearIcon'
|
||||
import PlusIcon from 'mdi-react/PlusIcon'
|
||||
import React, { useCallback, useEffect, useMemo } from 'react'
|
||||
import { from } from 'rxjs'
|
||||
import { switchMap } from 'rxjs/operators'
|
||||
import React, { useCallback, useEffect, useMemo, useContext } from 'react'
|
||||
|
||||
import { LoadingSpinner } from '@sourcegraph/react-loading-spinner'
|
||||
import { wrapRemoteObservable } from '@sourcegraph/shared/src/api/client/api/common'
|
||||
import { Link } from '@sourcegraph/shared/src/components/Link'
|
||||
import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { useObservable } from '@sourcegraph/shared/src/util/useObservable'
|
||||
|
||||
import { FeedbackBadge } from '../components/FeedbackBadge'
|
||||
import { Page } from '../components/Page'
|
||||
import { PageHeader } from '../components/PageHeader'
|
||||
import { ViewGrid, ViewGridProps } from '../repo/tree/ViewGrid'
|
||||
import { FeedbackBadge } from '../../components/FeedbackBadge'
|
||||
import { Page } from '../../components/Page'
|
||||
import { PageHeader } from '../../components/PageHeader'
|
||||
import { InsightsIcon, InsightsViewGrid, InsightsViewGridProps } from '../components'
|
||||
import { InsightsApiContext } from '../core/backend/api-provider'
|
||||
|
||||
import { getCombinedViews } from './backend'
|
||||
import { InsightsIcon } from './icon'
|
||||
|
||||
interface InsightsPageProps extends ExtensionsControllerProps, Omit<ViewGridProps, 'views'>, TelemetryProps {}
|
||||
interface InsightsPageProps extends ExtensionsControllerProps, Omit<InsightsViewGridProps, 'views'>, TelemetryProps {}
|
||||
|
||||
export const InsightsPage: React.FunctionComponent<InsightsPageProps> = props => {
|
||||
const { getInsightCombinedViews } = useContext(InsightsApiContext)
|
||||
|
||||
const views = useObservable(
|
||||
useMemo(
|
||||
() =>
|
||||
getCombinedViews(() =>
|
||||
from(props.extensionsController.extHostAPI).pipe(
|
||||
switchMap(extensionHostAPI => wrapRemoteObservable(extensionHostAPI.getInsightsViews({})))
|
||||
)
|
||||
),
|
||||
[props.extensionsController]
|
||||
)
|
||||
useMemo(() => getInsightCombinedViews(props.extensionsController?.extHostAPI), [
|
||||
props.extensionsController,
|
||||
getInsightCombinedViews,
|
||||
])
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@ -73,7 +65,7 @@ export const InsightsPage: React.FunctionComponent<InsightsPageProps> = props =>
|
||||
<LoadingSpinner className="my-4" />
|
||||
</div>
|
||||
) : (
|
||||
<ViewGrid {...props} views={views} />
|
||||
<InsightsViewGrid {...props} views={views} />
|
||||
)}
|
||||
</Page>
|
||||
</div>
|
||||
@ -4,7 +4,7 @@ import React from 'react'
|
||||
import { BatchChangesNavItem } from '../batches/BatchChangesNavItem'
|
||||
import { CodeMonitoringNavItem } from '../code-monitoring/CodeMonitoringNavItem'
|
||||
import { WebStory } from '../components/WebStory'
|
||||
import { InsightsNavItem } from '../insights/InsightsNavLink'
|
||||
import { InsightsNavItem } from '../insights/components/InsightsNavLink/InsightsNavLink'
|
||||
|
||||
import { MenuNavItem } from './MenuNavItem'
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import { CodeMonitoringProps } from '../code-monitoring'
|
||||
import { CodeMonitoringNavItem } from '../code-monitoring/CodeMonitoringNavItem'
|
||||
import { LinkWithIcon } from '../components/LinkWithIcon'
|
||||
import { WebActionsNavItems, WebCommandListPopoverButton } from '../components/shared'
|
||||
import { InsightsNavItem } from '../insights/InsightsNavLink'
|
||||
import { InsightsNavItem } from '../insights/components/InsightsNavLink/InsightsNavLink'
|
||||
import {
|
||||
KeyboardShortcutsProps,
|
||||
KEYBOARD_SHORTCUT_SHOW_COMMAND_PALETTE,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@import '../../search/input/SearchButton';
|
||||
@import './ViewGrid';
|
||||
@import '../../insights/components/InsightsViewGrid/InsightsViewGrid';
|
||||
@import './TreeEntriesSection';
|
||||
|
||||
.tree-page {
|
||||
|
||||
@ -8,7 +8,7 @@ import SourceCommitIcon from 'mdi-react/SourceCommitIcon'
|
||||
import SourceRepositoryIcon from 'mdi-react/SourceRepositoryIcon'
|
||||
import TagIcon from 'mdi-react/TagIcon'
|
||||
import UserIcon from 'mdi-react/UserIcon'
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react'
|
||||
import React, { useState, useMemo, useCallback, useEffect, useContext } from 'react'
|
||||
import { Link, Redirect } from 'react-router-dom'
|
||||
import { Observable, EMPTY, from } from 'rxjs'
|
||||
import { catchError, map, switchMap } from 'rxjs/operators'
|
||||
@ -42,7 +42,7 @@ import { BreadcrumbSetters } from '../../components/Breadcrumbs'
|
||||
import { FilteredConnection } from '../../components/FilteredConnection'
|
||||
import { PageTitle } from '../../components/PageTitle'
|
||||
import { GitCommitFields, Scalars, TreePageRepositoryFields } from '../../graphql-operations'
|
||||
import { getCombinedViews } from '../../insights/backend'
|
||||
import { InsightsApiContext, InsightsViewGrid } from '../../insights'
|
||||
import { Settings } from '../../schema/settings.schema'
|
||||
import { PatternTypeProps, CaseSensitivityProps, CopyQueryButtonProps, SearchContextProps } from '../../search'
|
||||
import { basename } from '../../util/path'
|
||||
@ -52,7 +52,6 @@ import { gitCommitFragment } from '../commits/RepositoryCommitsPage'
|
||||
import { FilePathBreadcrumbs } from '../FilePathBreadcrumbs'
|
||||
|
||||
import { TreeEntriesSection } from './TreeEntriesSection'
|
||||
import { ViewGrid } from './ViewGrid'
|
||||
|
||||
const fetchTreeCommits = memoizeObservable(
|
||||
(args: {
|
||||
@ -263,6 +262,8 @@ export const TreePage: React.FunctionComponent<Props> = ({
|
||||
[props.extensionsController]
|
||||
)
|
||||
)
|
||||
|
||||
const { getCombinedViews } = useContext(InsightsApiContext)
|
||||
const views = useObservable(
|
||||
useMemo(
|
||||
() =>
|
||||
@ -287,7 +288,7 @@ export const TreePage: React.FunctionComponent<Props> = ({
|
||||
)
|
||||
)
|
||||
: EMPTY,
|
||||
[showCodeInsights, workspaceUri, uri, props.extensionsController]
|
||||
[getCombinedViews, showCodeInsights, workspaceUri, uri, props.extensionsController]
|
||||
)
|
||||
)
|
||||
|
||||
@ -419,7 +420,7 @@ export const TreePage: React.FunctionComponent<Props> = ({
|
||||
)}
|
||||
</header>
|
||||
{views && (
|
||||
<ViewGrid
|
||||
<InsightsViewGrid
|
||||
{...props}
|
||||
className="tree-page__section"
|
||||
views={views}
|
||||
|
||||
@ -191,7 +191,7 @@ export const routes: readonly LayoutRouteProps<any>[] = [
|
||||
{
|
||||
path: '/insights',
|
||||
exact: true,
|
||||
render: lazyComponent(() => import('./insights/InsightsPage'), 'InsightsPage'),
|
||||
render: lazyComponent(() => import('./insights/pages/InsightsPage'), 'InsightsPage'),
|
||||
condition: props =>
|
||||
!isErrorLike(props.settingsCascade.final) &&
|
||||
!!props.settingsCascade.final?.experimentalFeatures?.codeInsights &&
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames'
|
||||
import * as H from 'history'
|
||||
import React, { useCallback, useEffect, useMemo } from 'react'
|
||||
import React, { useCallback, useContext, useEffect, useMemo } from 'react'
|
||||
import { EMPTY, from } from 'rxjs'
|
||||
import { switchMap } from 'rxjs/operators'
|
||||
|
||||
@ -30,9 +30,8 @@ import {
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { BrandLogo } from '../../components/branding/BrandLogo'
|
||||
import { SyntaxHighlightedSearchQuery } from '../../components/SyntaxHighlightedSearchQuery'
|
||||
import { getCombinedViews } from '../../insights/backend'
|
||||
import { InsightsApiContext, InsightsViewGrid } from '../../insights'
|
||||
import { KeyboardShortcutsProps } from '../../keyboardShortcuts/keyboardShortcuts'
|
||||
import { ViewGrid } from '../../repo/tree/ViewGrid'
|
||||
import { repogroupList, homepageLanguageList } from '../../repogroups/HomepageConfig'
|
||||
import { Settings } from '../../schema/settings.schema'
|
||||
import { VersionContext } from '../../schema/site.schema'
|
||||
@ -97,6 +96,7 @@ export const SearchPage: React.FunctionComponent<SearchPageProps> = props => {
|
||||
!!props.settingsCascade.final?.experimentalFeatures?.codeInsights &&
|
||||
props.settingsCascade.final['insights.displayLocation.homepage'] !== false
|
||||
|
||||
const { getCombinedViews } = useContext(InsightsApiContext)
|
||||
const views = useObservable(
|
||||
useMemo(
|
||||
() =>
|
||||
@ -107,7 +107,7 @@ export const SearchPage: React.FunctionComponent<SearchPageProps> = props => {
|
||||
)
|
||||
)
|
||||
: EMPTY,
|
||||
[showCodeInsights, props.extensionsController]
|
||||
[getCombinedViews, showCodeInsights, props.extensionsController]
|
||||
)
|
||||
)
|
||||
return (
|
||||
@ -121,7 +121,7 @@ export const SearchPage: React.FunctionComponent<SearchPageProps> = props => {
|
||||
})}
|
||||
>
|
||||
<SearchPageInput {...props} source="home" />
|
||||
{views && <ViewGrid {...props} className="mt-5" views={views} />}
|
||||
{views && <InsightsViewGrid {...props} className="mt-5" views={views} />}
|
||||
</div>
|
||||
{props.isSourcegraphDotCom &&
|
||||
props.showRepogroupHomepage &&
|
||||
|
||||
@ -48,6 +48,7 @@ export const ViewContent: React.FunctionComponent<ViewContentProps> = ({
|
||||
}) => {
|
||||
// Track user intent to interact with extension-contributed views
|
||||
const viewContentReference = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let viewContentElement = viewContentReference.current
|
||||
|
||||
@ -70,7 +71,7 @@ export const ViewContent: React.FunctionComponent<ViewContentProps> = ({
|
||||
}
|
||||
|
||||
// If containerClassName is specified, the element with this class is the element
|
||||
// that embodies the view in the eyes of the user. e.g. ViewGrid
|
||||
// that embodies the view in the eyes of the user. e.g. InsightsViewGrid
|
||||
if (containerClassName) {
|
||||
viewContentElement = viewContentElement?.closest(`.${containerClassName}`) as HTMLDivElement
|
||||
}
|
||||
|
||||
13
package.json
13
package.json
@ -20,6 +20,7 @@
|
||||
"graphql-lint": "graphql-schema-linter cmd/frontend/graphqlbackend/schema.graphql",
|
||||
"build-web": "yarn workspace @sourcegraph/web run build",
|
||||
"watch-web": "yarn workspace @sourcegraph/web run watch",
|
||||
"code-insights-demo": "yarn workspace @sourcegraph/code-insights-demo run serve",
|
||||
"generate": "gulp generate",
|
||||
"watch-generate": "gulp watchGenerate",
|
||||
"test": "jest --testPathIgnorePatterns end-to-end regression integration storybook",
|
||||
@ -76,7 +77,8 @@
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"client/*"
|
||||
"client/*",
|
||||
"client/sandboxes/*"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -97,6 +99,7 @@
|
||||
"@mermaid-js/mermaid-cli": "^8.7.0",
|
||||
"@octokit/rest": "^16.36.0",
|
||||
"@percy/puppeteer": "^1.1.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
|
||||
"@pollyjs/adapter": "^5.0.0",
|
||||
"@pollyjs/core": "^5.1.0",
|
||||
"@pollyjs/persister-fs": "^5.0.0",
|
||||
@ -133,6 +136,8 @@
|
||||
"@types/d3-scale": "2.2.0",
|
||||
"@types/d3-selection": "1.4.1",
|
||||
"@types/d3-shape": "1.3.2",
|
||||
"@types/d3-format": "^2.0.0",
|
||||
"@types/d3-time-format": "^3.0.0",
|
||||
"@types/enzyme": "3.10.8",
|
||||
"@types/expect": "24.3.0",
|
||||
"@types/fancy-log": "1.3.1",
|
||||
@ -243,8 +248,9 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-docgen-typescript-webpack-plugin": "^1.1.0",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"react-spring": "^9.0.0",
|
||||
"react-refresh": "^0.10.0",
|
||||
"react-test-renderer": "^16.14.0",
|
||||
"react-spring": "^9.0.0",
|
||||
"sass": "^1.32.4",
|
||||
"sass-loader": "^10.1.0",
|
||||
"shelljs": "^0.8.4",
|
||||
@ -270,6 +276,7 @@
|
||||
"web-ext": "^4.2.0",
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-bundle-analyzer": "^3.9.0",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"webpack-dev-server": "^3.11.1",
|
||||
"worker-loader": "^3.0.8",
|
||||
"yarn-deduplicate": "^3.1.0"
|
||||
@ -287,8 +294,6 @@
|
||||
"@sourcegraph/extension-api-classes": "^1.1.0",
|
||||
"@sourcegraph/react-loading-spinner": "0.0.7",
|
||||
"@sqs/jsonc-parser": "^1.0.3",
|
||||
"@types/d3-format": "^2.0.0",
|
||||
"@types/d3-time-format": "^3.0.0",
|
||||
"@visx/annotation": "^1.7.2",
|
||||
"@visx/axis": "^1.7.0",
|
||||
"@visx/glyph": "^1.7.0",
|
||||
|
||||
119
yarn.lock
119
yarn.lock
@ -1367,6 +1367,11 @@
|
||||
enabled "2.0.x"
|
||||
kuler "^2.0.0"
|
||||
|
||||
"@discoveryjs/json-ext@^0.5.0":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752"
|
||||
integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==
|
||||
|
||||
"@dsherret/to-absolute-glob@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1f6475dc8bd974cea07a2daf3864b317b1dd332c"
|
||||
@ -2408,7 +2413,7 @@
|
||||
dependencies:
|
||||
esquery "^1.0.1"
|
||||
|
||||
"@pmmmwh/react-refresh-webpack-plugin@^0.4.2":
|
||||
"@pmmmwh/react-refresh-webpack-plugin@^0.4.2", "@pmmmwh/react-refresh-webpack-plugin@^0.4.3":
|
||||
version "0.4.3"
|
||||
resolved "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz#1eec460596d200c0236bf195b078a5d1df89b766"
|
||||
integrity sha512-br5Qwvh8D2OQqSXpd1g/xqXKnK0r+Jz6qVKBbWmpUcrbGOxUrf39V5oZ1876084CGn18uMdR5uvPqBv9UqtBjQ==
|
||||
@ -5232,6 +5237,23 @@
|
||||
"@webassemblyjs/wast-parser" "1.9.0"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webpack-cli/configtest@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.2.tgz#2a20812bfb3a2ebb0b27ee26a52eeb3e3f000836"
|
||||
integrity sha512-3OBzV2fBGZ5TBfdW50cha1lHDVf9vlvRXnjpVbJBa20pSZQaSkMJZiwA8V2vD9ogyeXn8nU5s5A6mHyf5jhMzA==
|
||||
|
||||
"@webpack-cli/info@^1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.3.tgz#ef819d10ace2976b6d134c7c823a3e79ee31a92c"
|
||||
integrity sha512-lLek3/T7u40lTqzCGpC6CAbY6+vXhdhmwFRxZLMnRm6/sIF/7qMpT8MocXCRQfz0JAh63wpbXLMnsQ5162WS7Q==
|
||||
dependencies:
|
||||
envinfo "^7.7.3"
|
||||
|
||||
"@webpack-cli/serve@^1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.3.1.tgz#911d1b3ff4a843304b9c3bacf67bb34672418441"
|
||||
integrity sha512-0qXvpeYO6vaNoRBI52/UsbcaBydJCggoBBnIo/ovQQdn6fug0BgwsjorV1hVS7fMqGVTZGcVxv8334gjmbj5hw==
|
||||
|
||||
"@webpack-contrib/schema-utils@^1.0.0-beta.0":
|
||||
version "1.0.0-beta.0"
|
||||
resolved "https://registry.npmjs.org/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz#bf9638c9464d177b48209e84209e23bee2eb4f65"
|
||||
@ -5562,7 +5584,7 @@ ansi-align@^3.0.0:
|
||||
dependencies:
|
||||
string-width "^3.0.0"
|
||||
|
||||
ansi-colors@4.1.1:
|
||||
ansi-colors@4.1.1, ansi-colors@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
||||
@ -5574,7 +5596,7 @@ ansi-colors@^1.0.1:
|
||||
dependencies:
|
||||
ansi-wrap "^0.1.0"
|
||||
|
||||
ansi-colors@^3.0.0, ansi-colors@^3.2.1:
|
||||
ansi-colors@^3.0.0:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
|
||||
integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==
|
||||
@ -7874,6 +7896,15 @@ clone-buffer@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
|
||||
integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg=
|
||||
|
||||
clone-deep@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
|
||||
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
|
||||
dependencies:
|
||||
is-plain-object "^2.0.4"
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clone-regexp@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f"
|
||||
@ -8103,6 +8134,11 @@ commander@^6.0.0, commander@^6.1.0:
|
||||
resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
||||
commander@^7.0.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||
|
||||
comment-parser@^0.7.6:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12"
|
||||
@ -9825,12 +9861,12 @@ enhanced-resolve@^4.3.0:
|
||||
memory-fs "^0.5.0"
|
||||
tapable "^1.0.0"
|
||||
|
||||
enquirer@^2.3.5:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381"
|
||||
integrity sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==
|
||||
enquirer@^2.3.5, enquirer@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
|
||||
dependencies:
|
||||
ansi-colors "^3.2.1"
|
||||
ansi-colors "^4.1.1"
|
||||
|
||||
entities@^1.1.1, entities@~1.1.1:
|
||||
version "1.1.2"
|
||||
@ -9850,6 +9886,11 @@ env-ci@^5.0.2:
|
||||
execa "^4.0.0"
|
||||
java-properties "^1.0.0"
|
||||
|
||||
envinfo@^7.7.3:
|
||||
version "7.8.1"
|
||||
resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
|
||||
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
|
||||
|
||||
enzyme-adapter-react-16@^1.15.4:
|
||||
version "1.15.4"
|
||||
resolved "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.4.tgz#328a782365a363ecb424f99283c4833dd92c0f21"
|
||||
@ -13290,10 +13331,10 @@ interpret@^1.0.0, interpret@^1.1.0:
|
||||
resolved "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
|
||||
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
|
||||
|
||||
interpret@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/interpret/-/interpret-2.0.0.tgz#b783ffac0b8371503e9ab39561df223286aa5433"
|
||||
integrity sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA==
|
||||
interpret@^2.0.0, interpret@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
|
||||
|
||||
invariant@2.2.4, invariant@^2.2.1, invariant@^2.2.3, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
@ -18983,6 +19024,11 @@ react-popper@^2.2.4:
|
||||
react-fast-compare "^3.0.1"
|
||||
warning "^4.0.2"
|
||||
|
||||
react-refresh@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
|
||||
integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==
|
||||
|
||||
react-refresh@^0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
@ -19380,6 +19426,13 @@ rechoir@^0.6.2:
|
||||
dependencies:
|
||||
resolve "^1.1.6"
|
||||
|
||||
rechoir@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
|
||||
integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==
|
||||
dependencies:
|
||||
resolve "^1.9.0"
|
||||
|
||||
recursive-readdir@2.2.2, recursive-readdir@^2.0.0:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
|
||||
@ -19909,7 +19962,7 @@ resolve@1.1.7:
|
||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
|
||||
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
|
||||
|
||||
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1:
|
||||
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1, resolve@^1.9.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
@ -20408,6 +20461,13 @@ sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
shallow-clone@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
|
||||
shallowequal@1.1.0, shallowequal@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
|
||||
@ -23155,6 +23215,26 @@ webpack-bundle-analyzer@^3.9.0:
|
||||
opener "^1.5.1"
|
||||
ws "^6.0.0"
|
||||
|
||||
webpack-cli@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.6.0.tgz#27ae86bfaec0cf393fcfd58abdc5a229ad32fd16"
|
||||
integrity sha512-9YV+qTcGMjQFiY7Nb1kmnupvb1x40lfpj8pwdO/bom+sQiP4OBMKjHq29YQrlDWDPZO9r/qWaRRywKaRDKqBTA==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.5.0"
|
||||
"@webpack-cli/configtest" "^1.0.2"
|
||||
"@webpack-cli/info" "^1.2.3"
|
||||
"@webpack-cli/serve" "^1.3.1"
|
||||
colorette "^1.2.1"
|
||||
commander "^7.0.0"
|
||||
enquirer "^2.3.6"
|
||||
execa "^5.0.0"
|
||||
fastest-levenshtein "^1.0.12"
|
||||
import-local "^3.0.2"
|
||||
interpret "^2.2.0"
|
||||
rechoir "^0.7.0"
|
||||
v8-compile-cache "^2.2.0"
|
||||
webpack-merge "^5.7.3"
|
||||
|
||||
webpack-dev-middleware@^3.7.0, webpack-dev-middleware@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3"
|
||||
@ -23238,6 +23318,14 @@ webpack-log@^2.0.0:
|
||||
ansi-colors "^3.0.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
webpack-merge@^5.7.3:
|
||||
version "5.7.3"
|
||||
resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213"
|
||||
integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==
|
||||
dependencies:
|
||||
clone-deep "^4.0.1"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
|
||||
@ -23405,6 +23493,11 @@ widest-line@^3.1.0:
|
||||
dependencies:
|
||||
string-width "^4.0.0"
|
||||
|
||||
wildcard@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
|
||||
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
|
||||
|
||||
windows-release@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user