Update dependency @sourcegraph/eslint-config to ^0.19.1 (#11225)

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Felix Becker <felix.b@outlook.com>
This commit is contained in:
renovate[bot] 2020-06-03 12:33:59 +02:00 committed by GitHub
parent 0211f65837
commit f425fda6e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
465 changed files with 4111 additions and 3839 deletions

View File

@ -2,7 +2,7 @@ import initStoryshots from '@storybook/addon-storyshots'
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer'
import * as path from 'path'
import { pathToFileURL } from 'url'
import { recordCoverage } from '../shared/src/e2e/coverage'
import { recordCoverage } from '../shared/src/testing/coverage'
// This test suite does not actually test anything.
// It just loads up the storybook in Puppeteer and records its coverage,

View File

@ -3,7 +3,7 @@ module.exports = {
extends: '../.eslintrc.js',
parserOptions: {
...baseConfig.parserOptions,
project: [__dirname + '/tsconfig.json', __dirname + '/src/e2e/tsconfig.json'],
project: [__dirname + '/tsconfig.json', __dirname + '/src/end-to-end/tsconfig.json'],
},
overrides: baseConfig.overrides,
}

View File

@ -50,7 +50,7 @@ It works as follows:
Code shared between multiple code hosts.
- `config/`
Configuration code that is bundled via webpack. The configuration code adds properties to `window` that make it easier to tell what environment the script is running in. This is useful because the code can be run in the content script, background, options page, or in the actual page when injected by Phabricator and each environment will have different ways to do different things.
- `e2e/`
- `end-to-end/`
E2E test suite.
- `scripts/`
Build scripts.
@ -130,16 +130,16 @@ Click reload for Sourcegraph at `about:debugging`
### e2e tests
The test suite in e2e/github.test.ts runs on the release branch `bext/release` in both Chrome and Firefox against a Sourcegraph Docker instance.
The test suite in `end-to-end/github.test.ts` runs on the release branch `bext/release` in both Chrome and Firefox against a Sourcegraph Docker instance.
The test suite in e2e/phabricator.test.ts tests the Phabricator native integration.
The test suite in end-to-end/phabricator.test.ts tests the Phabricator native integration.
It assumes an existing Sourcegraph and Phabricator instance that has the Phabricator extension installed.
There are automated scripts to set up the Phabricator instance, see https://docs.sourcegraph.com/dev/phabricator_gitolite.
It currently does not run in CI and is intended to be run manually for release testing.
e2e/bitbucket.test.ts tests the browser extension on a Bitbucket Server instance.
`end-to-end/bitbucket.test.ts` tests the browser extension on a Bitbucket Server instance.
e2e/gitlab.test.ts tests the browser extension on gitlab.com (or a private Gitlab instance).
`end-to-end/gitlab.test.ts` tests the browser extension on gitlab.com (or a private Gitlab instance).
## Deploy

View File

@ -9,14 +9,18 @@ const contentEntry = '../../src/config/content.entry.js'
const backgroundEntry = '../../src/config/background.entry.js'
const optionsEntry = '../../src/config/options.entry.js'
const pageEntry = '../../src/config/page.entry.js'
const extEntry = '../../src/config/extension.entry.js'
const extensionEntry = '../../src/config/extension.entry.js'
const config: webpack.Configuration = {
entry: {
// Browser extension
background: buildEntry(extEntry, backgroundEntry, '../../src/browser-extension/scripts/backgroundPage.main.ts'),
options: buildEntry(extEntry, optionsEntry, '../../src/browser-extension/scripts/optionsPage.main.tsx'),
inject: buildEntry(extEntry, contentEntry, '../../src/browser-extension/scripts/contentPage.main.ts'),
background: buildEntry(
extensionEntry,
backgroundEntry,
'../../src/browser-extension/scripts/backgroundPage.main.ts'
),
options: buildEntry(extensionEntry, optionsEntry, '../../src/browser-extension/scripts/optionsPage.main.tsx'),
inject: buildEntry(extensionEntry, contentEntry, '../../src/browser-extension/scripts/contentPage.main.ts'),
// Common native integration entry point (Gitlab, Bitbucket)
integration: buildEntry(pageEntry, '../../src/native-integration/integration.main.ts'),

View File

@ -5,7 +5,7 @@
"yarn": ">1.10.0"
},
"scripts": {
"dev": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 TS_NODE_COMPILER_OPTIONS=\"{\\\"module\\\":\\\"commonjs\\\"}\" node -r ts-node/register scripts/dev",
"dev": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 TS_NODE_COMPILER_OPTIONS=\"{\\\"module\\\":\\\"commonjs\\\"}\" node -r ts-node/register scripts/development",
"dev:no-reload": "AUTO_RELOAD=false yarn run dev",
"dev:firefox": "if type web-ext 2>/dev/null; then web-ext run --source-dir ./build/firefox; else echo 'web-ext not found. Install it with: yarn global add web-ext'; exit 1; fi",
"build": "NODE_ENV=production NODE_OPTIONS=--max_old_space_size=4096 TS_NODE_COMPILER_OPTIONS=\"{\\\"module\\\":\\\"commonjs\\\"}\" node -r ts-node/register scripts/build",
@ -17,8 +17,8 @@
"eslint": "eslint --cache '**/*.ts?(x)'",
"stylelint": "stylelint 'src/**/*.scss'",
"clean": "rm -rf build/ dist/ *.zip *.xpi .checksum",
"test": "jest --testPathIgnorePatterns e2e",
"test-e2e": "mocha './src/e2e/**/*.test.ts'",
"test": "jest --testPathIgnorePatterns end-to-end",
"test-e2e": "mocha './src/end-to-end/**/*.test.ts'",
"bundlesize": "GITHUB_TOKEN= bundlesize"
},
"browserslist": [

View File

@ -1,5 +1,5 @@
import signale from 'signale'
import io from 'socket.io'
import socketIo from 'socket.io'
/**
* Returns a trigger function that notifies the extension to reload itself.
@ -9,7 +9,7 @@ export const initializeServer = (): (() => void) => {
logger.config({ displayTimestamp: true })
// Since this port is hard-coded, it must match background.ts
const socketIOServer = io.listen(8890)
const socketIOServer = socketIo.listen(8890)
logger.await('Ready for a browser extension to connect')
socketIOServer.on('connect', () => {
logger.info('Browser extension connected')

View File

@ -34,6 +34,6 @@ function addVersionsToManifest(links: string[]): void {
fs.writeFileSync(updatesManifestPath, JSON.stringify(updatesManifest, null, 2), 'utf8')
}
const links = process.argv.slice(2).filter(l => !l.match(/latest.xpi$/) && !l.match(/updates.json$/))
const links = process.argv.slice(2).filter(link => !link.match(/latest.xpi$/) && !link.match(/updates.json$/))
addVersionsToManifest(links)

View File

@ -1,6 +1,6 @@
import signale from 'signale'
import webpack from 'webpack'
import config from '../config/webpack/prod.config'
import config from '../config/webpack/production.config'
import * as tasks from './tasks'
const buildChrome = tasks.buildChrome('prod')
@ -12,7 +12,7 @@ const compiler = webpack(config)
signale.await('Webpack compilation')
compiler.run((err, stats) => {
compiler.run((error, stats) => {
console.log(stats.toString(tasks.WEBPACK_STATS_OPTIONS))
if (stats.hasErrors()) {

View File

@ -1,7 +1,7 @@
import { noop } from 'lodash'
import signale from 'signale'
import webpack from 'webpack'
import config from '../config/webpack/dev.config'
import config from '../config/webpack/development.config'
import * as autoReloading from './auto-reloading'
import * as tasks from './tasks'
@ -24,10 +24,10 @@ compiler.watch(
{
aggregateTimeout: 300,
},
(err, stats) => {
(error, stats) => {
signale.complete(stats.toString(tasks.WEBPACK_STATS_OPTIONS))
if (err || stats.hasErrors()) {
if (error || stats.hasErrors()) {
signale.error('Webpack compilation error')
return
}

View File

@ -40,25 +40,25 @@ function ensurePaths(): void {
export function copyAssets(): void {
signale.await('Copy assets')
const dir = 'build/dist'
shelljs.rm('-rf', dir)
shelljs.mkdir('-p', dir)
shelljs.cp('-R', 'assets/*', dir)
shelljs.cp('-R', 'src/browser-extension/pages/*', dir)
const directory = 'build/dist'
shelljs.rm('-rf', directory)
shelljs.mkdir('-p', directory)
shelljs.cp('-R', 'assets/*', directory)
shelljs.cp('-R', 'src/browser-extension/pages/*', directory)
signale.success('Assets copied')
}
function copyExtensionAssets(toDir: string): void {
shelljs.mkdir('-p', `${toDir}/js`, `${toDir}/css`, `${toDir}/img`)
shelljs.cp('build/dist/js/background.bundle.js', `${toDir}/js`)
shelljs.cp('build/dist/js/inject.bundle.js', `${toDir}/js`)
shelljs.cp('build/dist/js/options.bundle.js', `${toDir}/js`)
shelljs.cp('build/dist/css/style.bundle.css', `${toDir}/css`)
shelljs.cp('build/dist/css/options-style.bundle.css', `${toDir}/css`)
shelljs.cp('build/dist/css/options-style.bundle.css', `${toDir}/css`)
shelljs.cp('-R', 'build/dist/img/*', `${toDir}/img`)
shelljs.cp('build/dist/background.html', toDir)
shelljs.cp('build/dist/options.html', toDir)
function copyExtensionAssets(toDirectory: string): void {
shelljs.mkdir('-p', `${toDirectory}/js`, `${toDirectory}/css`, `${toDirectory}/img`)
shelljs.cp('build/dist/js/background.bundle.js', `${toDirectory}/js`)
shelljs.cp('build/dist/js/inject.bundle.js', `${toDirectory}/js`)
shelljs.cp('build/dist/js/options.bundle.js', `${toDirectory}/js`)
shelljs.cp('build/dist/css/style.bundle.css', `${toDirectory}/css`)
shelljs.cp('build/dist/css/options-style.bundle.css', `${toDirectory}/css`)
shelljs.cp('build/dist/css/options-style.bundle.css', `${toDirectory}/css`)
shelljs.cp('-R', 'build/dist/img/*', `${toDirectory}/img`)
shelljs.cp('build/dist/background.html', toDirectory)
shelljs.cp('build/dist/options.html', toDirectory)
}
export function copyIntegrationAssets(): void {
@ -89,16 +89,16 @@ const BROWSER_BLACKLIST = {
firefox: ['key'] as const,
}
function writeSchema(env: BuildEnv, browser: Browser, writeDir: string): void {
fs.writeFileSync(`${writeDir}/schema.json`, JSON.stringify(schema, null, 4))
function writeSchema(environment: BuildEnv, browser: Browser, writeDirectory: string): void {
fs.writeFileSync(`${writeDirectory}/schema.json`, JSON.stringify(schema, null, 4))
}
const version = utcVersion()
function writeManifest(env: BuildEnv, browser: Browser, writeDir: string): void {
function writeManifest(environment: BuildEnv, browser: Browser, writeDirectory: string): void {
const manifest = {
...omit(extensionInfo, ['dev', 'prod', ...BROWSER_BLACKLIST[browser]]),
...omit(extensionInfo[env], BROWSER_BLACKLIST[browser]),
...omit(extensionInfo[environment], BROWSER_BLACKLIST[browser]),
}
if (EXTENSION_PERMISSIONS_ALL_URLS) {
@ -113,22 +113,22 @@ function writeManifest(env: BuildEnv, browser: Browser, writeDir: string): void
delete manifest.$schema
if (env === 'prod') {
if (environment === 'prod') {
manifest.version = version
}
fs.writeFileSync(`${writeDir}/manifest.json`, JSON.stringify(manifest, null, 4))
fs.writeFileSync(`${writeDirectory}/manifest.json`, JSON.stringify(manifest, null, 4))
}
function buildForBrowser(browser: Browser): (env: BuildEnv) => () => void {
ensurePaths()
return env => {
return environment => {
const title = BROWSER_TITLES[browser]
const buildDir = path.resolve(process.cwd(), `${BUILDS_DIR}/${browser}`)
const buildDirectory = path.resolve(process.cwd(), `${BUILDS_DIR}/${browser}`)
writeManifest(env, browser, buildDir)
writeSchema(env, browser, buildDir)
writeManifest(environment, browser, buildDirectory)
writeSchema(environment, browser, buildDirectory)
return () => {
// Allow only building for specific browser targets.
@ -137,17 +137,17 @@ function buildForBrowser(browser: Browser): (env: BuildEnv) => () => void {
return
}
signale.await(`Building the ${title} ${env} bundle`)
signale.await(`Building the ${title} ${environment} bundle`)
copyExtensionAssets(buildDir)
copyExtensionAssets(buildDirectory)
const zipDest = path.resolve(process.cwd(), `${BUILDS_DIR}/bundles/${BROWSER_BUNDLE_ZIPS[browser]}`)
if (zipDest) {
const zipDestination = path.resolve(process.cwd(), `${BUILDS_DIR}/bundles/${BROWSER_BUNDLE_ZIPS[browser]}`)
if (zipDestination) {
shelljs.mkdir('-p', `./${BUILDS_DIR}/bundles`)
shelljs.exec(`cd ${buildDir} && zip -q -r ${zipDest} *`)
shelljs.exec(`cd ${buildDirectory} && zip -q -r ${zipDestination} *`)
}
signale.success(`Done building the ${title} ${env} bundle`)
signale.success(`Done building the ${title} ${environment} bundle`)
}
}
}

View File

@ -1,10 +1,10 @@
export function assertEnv(env: typeof window['EXTENSION_ENV']): void {
if (window.EXTENSION_ENV !== env) {
export function assertEnvironment(environment: typeof window['EXTENSION_ENV']): void {
if (window.EXTENSION_ENV !== environment) {
throw new Error(
'Detected transitive import of an entrypoint! ' +
window.EXTENSION_ENV +
' attempted to import a file that is only intended to be imported by ' +
env +
environment +
'.'
)
}

View File

@ -65,13 +65,13 @@ describe('OptionsContainer', () => {
const buildRenderer = (): ((ui: React.ReactElement) => void) => {
let rerender: RenderResult['rerender'] | undefined
return ui => {
return element => {
if (rerender) {
rerender(ui)
rerender(element)
} else {
const renderedRes = render(ui)
const renderedResult = render(element)
rerender = renderedRes.rerender
rerender = renderedResult.rerender
}
}
}

View File

@ -72,28 +72,28 @@ export class OptionsContainer extends React.Component<OptionsContainerProps, Opt
this.setState({ status: 'connecting', connectionError: undefined })
return this.props.ensureValidSite(url).pipe(
map(() => url),
catchError(err => of(asError(err)))
catchError(error => of(asError(error)))
)
}),
catchError(err => of(asError(err))),
catchError(error => of(asError(error))),
share()
)
this.subscriptions.add(
fetchingSite.subscribe(async res => {
fetchingSite.subscribe(async result => {
let url = ''
if (isErrorLike(res)) {
if (isErrorLike(result)) {
this.setState({
status: 'error',
connectionError: isHTTPAuthError(res)
connectionError: isHTTPAuthError(result)
? ConnectionErrors.AuthError
: ConnectionErrors.UnableToConnect,
})
url = this.state.sourcegraphURL
} else {
this.setState({ status: 'connected' })
url = res
url = result
}
const urlHasPermissions = await props.hasPermissions(url)

View File

@ -101,7 +101,7 @@ describe('ServerUrlForm', () => {
a: 'https://different.com',
}
const submitObs = cold('a', urls).pipe(
const submitObservable = cold('a', urls).pipe(
switchMap(url => {
const emit = of(undefined).pipe(
tap(() => {
@ -114,7 +114,7 @@ describe('ServerUrlForm', () => {
})
)
expectObservable(submitObs).toBe('5s a', { a: undefined })
expectObservable(submitObservable).toBe('5s a', { a: undefined })
})
})
@ -148,7 +148,7 @@ describe('ServerUrlForm', () => {
a: 'https://different.com',
}
const submitObs = cold('a', urls).pipe(
const submitObservable = cold('a', urls).pipe(
switchMap(url => {
const emit = of(undefined).pipe(
tap(() => {
@ -161,7 +161,7 @@ describe('ServerUrlForm', () => {
})
)
expectObservable(submitObs).toBe('a', { a: undefined })
expectObservable(submitObservable).toBe('a', { a: undefined })
})
})
})

View File

@ -1,6 +1,6 @@
import '../../shared/polyfills'
import io from 'socket.io-client'
import socketIoClient from 'socket.io-client'
/**
* Reloads the extension when notified from the development server. Only enabled
@ -10,7 +10,7 @@ async function main(): Promise<void> {
const self = await browser.management.getSelf()
if (self.installType === 'development') {
// Since the port is hard-coded, it must match scripts/dev.ts
io.connect('http://localhost:8890').on('file.change', () => browser.runtime.reload())
socketIoClient.connect('http://localhost:8890').on('file.change', () => browser.runtime.reload())
}
}

View File

@ -16,7 +16,7 @@ import { createBlobURLForBundle } from '../../shared/platform/worker'
import { getHeaders } from '../../shared/backend/headers'
import { fromBrowserEvent } from '../web-extension-api/fromBrowserEvent'
import { observeSourcegraphURL } from '../../shared/util/context'
import { assertEnv } from '../envAssertion'
import { assertEnvironment } from '../environmentAssertion'
import { observeStorageKey, storage } from '../web-extension-api/storage'
import { isDefined } from '../../../../shared/src/util/types'
import { browserPortToMessagePort, findMessagePorts } from '../../shared/platform/ports'
@ -24,7 +24,7 @@ import { EndpointPair } from '../../../../shared/src/platform/context'
const IS_EXTENSION = true
assertEnv('BACKGROUND')
assertEnvironment('BACKGROUND')
initSentry('background')
@ -232,8 +232,8 @@ async function main(): Promise<void> {
next: browserPortPair => {
subscriptions.add(handleBrowserPortPair(browserPortPair))
},
error: err => {
console.error('Error handling extension host client connection', err)
error: error => {
console.error('Error handling extension host client connection', error)
},
})
)

View File

@ -16,12 +16,12 @@ import {
} from '../../shared/code-hosts/sourcegraph/inject'
import { DEFAULT_SOURCEGRAPH_URL, getAssetsURL } from '../../shared/util/context'
import { featureFlags } from '../../shared/util/featureFlags'
import { assertEnv } from '../envAssertion'
import { assertEnvironment } from '../environmentAssertion'
const subscriptions = new Subscription()
window.addEventListener('unload', () => subscriptions.unsubscribe(), { once: true })
assertEnv('CONTENT')
assertEnvironment('CONTENT')
const codeHost = determineCodeHost()
initSentry('content', codeHost?.type)
@ -71,7 +71,7 @@ async function main(): Promise<void> {
// Add style sheet and wait for it to load to avoid rendering unstyled elements (which causes an
// annoying flash/jitter when the stylesheet loads shortly thereafter).
const styleSheet = (() => {
let styleSheet = document.querySelector('#ext-style-sheet') as HTMLLinkElement | null
let styleSheet = document.querySelector<HTMLLinkElement>('#ext-style-sheet')
// If does not exist, create
if (!styleSheet) {
styleSheet = document.createElement('link')

View File

@ -14,10 +14,10 @@ import { OptionsMenuProps } from '../options-page/OptionsMenu'
import { initSentry } from '../../shared/sentry'
import { fetchSite } from '../../shared/backend/server'
import { featureFlags } from '../../shared/util/featureFlags'
import { assertEnv } from '../envAssertion'
import { assertEnvironment } from '../environmentAssertion'
import { observeSourcegraphURL } from '../../shared/util/context'
assertEnv('OPTIONS')
assertEnvironment('OPTIONS')
initSentry('options')
@ -29,7 +29,7 @@ type State = Pick<
> & { sourcegraphURL: string | null; isActivated: boolean }
const keyIsFeatureFlag = (key: string): key is keyof FeatureFlags =>
!!Object.keys(featureFlagDefaults).find(k => key === k)
!!Object.keys(featureFlagDefaults).find(featureFlag => key === featureFlag)
const toggleFeatureFlag = (key: string): void => {
if (keyIsFeatureFlag(key)) {

View File

@ -1,10 +1,10 @@
import expect from 'expect'
import { saveScreenshotsUponFailures } from '../../../shared/src/e2e/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/e2e/driver'
import { retry } from '../../../shared/src/e2e/e2e-test-utils'
import { saveScreenshotsUponFailures } from '../../../shared/src/testing/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/testing/driver'
import { retry } from '../../../shared/src/testing/utils'
import { ExternalServiceKind } from '../../../shared/src/graphql/schema'
import { testSingleFilePage } from './shared'
import { getConfig } from '../../../shared/src/e2e/config'
import { getConfig } from '../../../shared/src/testing/config'
// By default, these tests run against a local Bitbucket instance and a local Sourcegraph instance.
// You can run them against other instances by setting the below env vars in addition to SOURCEGRAPH_BASE_URL.
@ -38,7 +38,7 @@ async function createProject(driver: Driver): Promise<void> {
await driver.page.goto(BITBUCKET_BASE_URL + '/projects')
await driver.page.waitForSelector('.entity-table')
const existingProject = await driver.page.evaluate(() =>
[...document.querySelectorAll('span.project-name')].some(p => p.textContent === 'SOURCEGRAPH')
[...document.querySelectorAll('span.project-name')].some(project => project.textContent === 'SOURCEGRAPH')
)
if (existingProject) {
return

View File

@ -1,8 +1,8 @@
import { saveScreenshotsUponFailures } from '../../../shared/src/e2e/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/e2e/driver'
import { saveScreenshotsUponFailures } from '../../../shared/src/testing/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/testing/driver'
import { ExternalServiceKind } from '../../../shared/src/graphql/schema'
import { testSingleFilePage } from './shared'
import { getConfig } from '../../../shared/src/e2e/config'
import { getConfig } from '../../../shared/src/testing/config'
const GHE_BASE_URL = process.env.GHE_BASE_URL || 'https://ghe.sgdev.org'
const GHE_USERNAME = process.env.GHE_USERNAME

View File

@ -1,10 +1,10 @@
import { startCase } from 'lodash'
import assert from 'assert'
import { saveScreenshotsUponFailures } from '../../../shared/src/e2e/screenshotReporter'
import { Driver, createDriverForTest } from '../../../shared/src/e2e/driver'
import { saveScreenshotsUponFailures } from '../../../shared/src/testing/screenshotReporter'
import { Driver, createDriverForTest } from '../../../shared/src/testing/driver'
import { testSingleFilePage } from './shared'
import { retry } from '../../../shared/src/e2e/e2e-test-utils'
import { getConfig } from '../../../shared/src/e2e/config'
import { retry } from '../../../shared/src/testing/utils'
import { getConfig } from '../../../shared/src/testing/config'
import { fromEvent } from 'rxjs'
import { first, filter, timeout, mergeMap } from 'rxjs/operators'
import { Target, Page } from 'puppeteer'
@ -97,7 +97,7 @@ describe('Sourcegraph browser extension on github.com', function () {
)
).asElement()
assert(tokenElement, 'Expected token element to exist')
return tokenElement!
return tokenElement
})
// Retry is here to wait for listeners to be registered
await retry(async () => {

View File

@ -1,8 +1,8 @@
import { saveScreenshotsUponFailures } from '../../../shared/src/e2e/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/e2e/driver'
import { saveScreenshotsUponFailures } from '../../../shared/src/testing/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/testing/driver'
import { ExternalServiceKind } from '../../../shared/src/graphql/schema'
import { testSingleFilePage } from './shared'
import { getConfig } from '../../../shared/src/e2e/config'
import { getConfig } from '../../../shared/src/testing/config'
// By default, these tests run against gitlab.com and a local Sourcegraph instance.
// You can run them against other instances by setting the below env vars in addition to SOURCEGRAPH_BASE_URL.

View File

@ -1,11 +1,11 @@
import expect from 'expect'
import { saveScreenshotsUponFailures } from '../../../shared/src/e2e/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/e2e/driver'
import { saveScreenshotsUponFailures } from '../../../shared/src/testing/screenshotReporter'
import { createDriverForTest, Driver } from '../../../shared/src/testing/driver'
import { ExternalServiceKind } from '../../../shared/src/graphql/schema'
import { PhabricatorMapping } from '../browser-extension/web-extension-api/types'
import { isEqual } from 'lodash'
import { getConfig } from '../../../shared/src/e2e/config'
import { retry } from '../../../shared/src/e2e/e2e-test-utils'
import { getConfig } from '../../../shared/src/testing/config'
import { retry } from '../../../shared/src/testing/utils'
// By default, these tests run against a local Phabricator instance and a local Sourcegraph instance.
// To run them against phabricator.sgdev.org and umami.sgdev.org, set the below env vars in addition to SOURCEGRAPH_BASE_URL.
@ -108,10 +108,10 @@ async function configureSourcegraphIntegration(driver: Driver): Promise<void> {
// Configure the repository mappings
await driver.page.goto(PHABRICATOR_BASE_URL + '/config/edit/sourcegraph.callsignMappings/')
const callSignConfigStr = await driver.page.evaluate(() =>
const callSignConfigString = await driver.page.evaluate(() =>
document.querySelector<HTMLTextAreaElement>('textarea[name="value"]')!.value.trim()
)
const callSignConfig: PhabricatorMapping[] = (callSignConfigStr && JSON.parse(callSignConfigStr)) || []
const callSignConfig: PhabricatorMapping[] = (callSignConfigString && JSON.parse(callSignConfigString)) || []
const jsonRpc2Mapping: PhabricatorMapping = {
path: 'github.com/sourcegraph/jsonrpc2',
callsign: 'JRPC',

View File

@ -1,6 +1,6 @@
import expect from 'expect'
import { Driver } from '../../../shared/src/e2e/driver'
import { retry } from '../../../shared/src/e2e/e2e-test-utils'
import { Driver } from '../../../shared/src/testing/driver'
import { retry } from '../../../shared/src/testing/utils'
import assert from 'assert'
/**

View File

@ -1,11 +1,11 @@
{
"extends": "../../tsconfig.json",
"references": [{ "path": "../.." }, { "path": "../../../shared/src/e2e" }],
"references": [{ "path": "../.." }, { "path": "../../../shared/src/testing" }],
"compilerOptions": {
"types": ["mocha", "node"],
"module": "commonjs",
"rootDir": ".",
"outDir": "../../out/src/e2e",
"outDir": "../../out/src/end-to-end",
"plugins": [
{
"name": "ts-graphql-plugin",

View File

@ -5,12 +5,12 @@
* TODO could this be eliminated with shadow DOM?
*/
export function metaClickOverride(): void {
const JX = (window as any).JX
if (JX.Stratcom._dispatchProxyPreMeta) {
const javelin = (window as any).JX
if (javelin.Stratcom._dispatchProxyPreMeta) {
return
}
JX.Stratcom._dispatchProxyPreMeta = JX.Stratcom._dispatchProxy
JX.Stratcom._dispatchProxy = (proxyEvent: {
javelin.Stratcom._dispatchProxyPreMeta = javelin.Stratcom._dispatchProxy
javelin.Stratcom._dispatchProxy = (proxyEvent: {
__auto__type: string
__auto__rawEvent: KeyboardEvent
__auto__target: HTMLElement
@ -22,6 +22,6 @@ export function metaClickOverride(): void {
) {
return
}
return JX.Stratcom._dispatchProxyPreMeta(proxyEvent)
return javelin.Stratcom._dispatchProxyPreMeta(proxyEvent)
}
}

View File

@ -1,15 +0,0 @@
import { TextDocumentIdentifier } from '../../../../shared/src/api/client/types/textDocument'
import { TextDocumentPositionParams } from '../../../../shared/src/api/protocol'
import { AbsoluteRepoFilePosition, FileSpec, RepoSpec, ResolvedRevSpec } from '../../../../shared/src/util/url'
export const toTextDocumentIdentifier = (pos: RepoSpec & ResolvedRevSpec & FileSpec): TextDocumentIdentifier => ({
uri: `git://${pos.repoName}?${pos.commitID}#${pos.filePath}`,
})
export const toTextDocumentPositionParams = (pos: AbsoluteRepoFilePosition): TextDocumentPositionParams => ({
textDocument: toTextDocumentIdentifier(pos),
position: {
character: pos.position.character - 1,
line: pos.position.line - 1,
},
})

View File

@ -0,0 +1,17 @@
import { TextDocumentIdentifier } from '../../../../shared/src/api/client/types/textDocument'
import { TextDocumentPositionParams } from '../../../../shared/src/api/protocol'
import { AbsoluteRepoFilePosition, FileSpec, RepoSpec, ResolvedRevisionSpec } from '../../../../shared/src/util/url'
export const toTextDocumentIdentifier = (
position: RepoSpec & ResolvedRevisionSpec & FileSpec
): TextDocumentIdentifier => ({
uri: `git://${position.repoName}?${position.commitID}#${position.filePath}`,
})
export const toTextDocumentPositionParameters = (position: AbsoluteRepoFilePosition): TextDocumentPositionParams => ({
textDocument: toTextDocumentIdentifier(position),
position: {
character: position.position.character - 1,
line: position.position.line - 1,
},
})

View File

@ -80,9 +80,9 @@ function createSuggestion(item: GQL.SearchSuggestion): Suggestion | null {
}
case 'File': {
const descriptionParts: string[] = []
const dir = dirname(item.path)
if (dir !== undefined && dir !== '.') {
descriptionParts.push(`${dir}/`)
const directory = dirname(item.path)
if (directory !== undefined && directory !== '.') {
descriptionParts.push(`${directory}/`)
}
descriptionParts.push(basename(item.repository.name))
if (item.isDirectory) {

View File

@ -1,7 +1,7 @@
import { AdjustmentDirection, PositionAdjuster } from '@sourcegraph/codeintellify'
import { of } from 'rxjs'
import { Omit } from 'utility-types'
import { FileSpec, RepoSpec, ResolvedRevSpec, RevSpec } from '../../../../../shared/src/util/url'
import { FileSpec, RepoSpec, ResolvedRevisionSpec, RevisionSpec } from '../../../../../shared/src/util/url'
import { querySelectorOrSelf } from '../../util/dom'
import { CodeHost, MountGetter } from '../shared/codeHost'
import { CodeView, DOMFunctions } from '../shared/codeViews'
@ -48,28 +48,32 @@ export const getToolbarMount = (codeView: HTMLElement): HTMLElement => {
*/
const createPositionAdjuster = (
dom: DOMFunctions
): PositionAdjuster<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec> => ({ direction, codeView, position }) => {
): PositionAdjuster<RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec> => ({
direction,
codeView,
position,
}) => {
const codeElement = dom.getCodeElementFromLineNumber(codeView, position.line, position.part)
if (!codeElement) {
throw new Error('(adjustPosition) could not find code element for line provided')
}
let delta = 0
for (const modifiedTextElem of codeElement.querySelectorAll('[cm-text]')) {
const actualText = modifiedTextElem.getAttribute('cm-text') || ''
const adjustedText = modifiedTextElem.textContent || ''
for (const modifiedTextElement of codeElement.querySelectorAll('[cm-text]')) {
const actualText = modifiedTextElement.getAttribute('cm-text') || ''
const adjustedText = modifiedTextElement.textContent || ''
delta += actualText.length - adjustedText.length
}
const modifier = direction === AdjustmentDirection.ActualToCodeView ? -1 : 1
const newPos = {
const newPosition = {
line: position.line,
character: position.character + modifier * delta,
}
return of(newPos)
return of(newPosition)
}
const toolbarButtonProps = {
@ -155,14 +159,14 @@ const getCommandPaletteMount: MountGetter = (container: HTMLElement): HTMLElemen
if (!headerElement) {
return null
}
const classes = ['command-palette-button', 'command-palette-button--bitbucket-server']
const classNames = ['command-palette-button', 'command-palette-button--bitbucket-server']
const create = (): HTMLElement => {
const mount = document.createElement('li')
mount.className = classes.join(' ')
mount.className = classNames.join(' ')
headerElement.append(mount)
return mount
}
const preexisting = headerElement.querySelector<HTMLElement>(classes.map(c => `.${c}`).join(''))
const preexisting = headerElement.querySelector<HTMLElement>(classNames.map(className => `.${className}`).join(''))
return preexisting || create()
}

View File

@ -1,4 +1,4 @@
import { RawRepoSpec, RevSpec } from '../../../../../shared/src/util/url'
import { RawRepoSpec, RevisionSpec } from '../../../../../shared/src/util/url'
import { CodeHostContext } from '../shared/codeHost'
// example pathname: /projects/TEST/repos/some-repo/browse/src/extension.ts
@ -20,39 +20,41 @@ interface RevisionRefInfo {
latestCommit?: string
}
function getRevSpecFromRevisionSelector(): RevSpec {
function getRevisionSpecFromRevisionSelector(): RevisionSpec {
const branchNameElement = document.querySelector('#repository-layout-revision-selector .name[data-revision-ref]')
if (!branchNameElement) {
throw new Error('branchNameElement not found')
}
const revisionRefStr = branchNameElement.getAttribute('data-revision-ref')
let revisionRefInfo: RevisionRefInfo | null = null
if (revisionRefStr) {
const revisionReferenceString = branchNameElement.getAttribute('data-revision-ref')
let revisionReferenceInfo: RevisionRefInfo | null = null
if (revisionReferenceString) {
try {
revisionRefInfo = JSON.parse(revisionRefStr)
revisionReferenceInfo = JSON.parse(revisionReferenceString)
} catch {
throw new Error(`Could not parse revisionRefStr: ${revisionRefStr}`)
throw new Error(`Could not parse revisionRefStr: ${revisionReferenceString}`)
}
}
if (revisionRefInfo?.latestCommit) {
if (revisionReferenceInfo?.latestCommit) {
return {
rev: revisionRefInfo.latestCommit,
revision: revisionReferenceInfo.latestCommit,
}
}
throw new Error(`revisionRefInfo is empty or has no latestCommit (revisionRefStr: ${String(revisionRefStr)})`)
throw new Error(
`revisionRefInfo is empty or has no latestCommit (revisionRefStr: ${String(revisionReferenceString)})`
)
}
export function getContext(): CodeHostContext {
const repoSpec = getRawRepoSpecFromLocation(window.location)
let revSpec: Partial<RevSpec> = {}
let revisionSpec: Partial<RevisionSpec> = {}
try {
revSpec = getRevSpecFromRevisionSelector()
revisionSpec = getRevisionSpecFromRevisionSelector()
} catch {
// RevSpec is optional in CodeHostContext
}
return {
...repoSpec,
...revSpec,
...revisionSpec,
privateRepository: window.location.hostname !== 'bitbucket.org',
}
}

View File

@ -2,17 +2,17 @@ import { DiffPart } from '@sourcegraph/codeintellify'
import { DOMFunctions } from '../shared/codeViews'
const getSingleFileLineElementFromLineNumber = (codeView: HTMLElement, line: number): HTMLElement => {
const lineNumElem = codeView.querySelector<HTMLElement>(`[data-line-number="${line}"]`)
if (!lineNumElem) {
const lineNumberElement = codeView.querySelector<HTMLElement>(`[data-line-number="${line}"]`)
if (!lineNumberElement) {
throw new Error(`Line ${line} not found in code view`)
}
const lineElem = lineNumElem.closest<HTMLElement>('.line')
if (!lineElem) {
const lineElement = lineNumberElement.closest<HTMLElement>('.line')
if (!lineElement) {
throw new Error('Could not find line elem for line element')
}
return lineElem
return lineElement
}
export const singleFileDOMFunctions: DOMFunctions = {
@ -27,17 +27,17 @@ export const singleFileDOMFunctions: DOMFunctions = {
throw new Error('Could not find line containing code element')
}
const lineNumElem = line.querySelector<HTMLElement>('.line-locator')
if (!lineNumElem) {
const lineNumberElement = line.querySelector<HTMLElement>('.line-locator')
if (!lineNumberElement) {
throw new Error('Could not find the line number in a line container')
}
const lineNum = parseInt(lineNumElem.dataset.lineNumber || '', 10)
if (isNaN(lineNum)) {
const lineNumber = parseInt(lineNumberElement.dataset.lineNumber || '', 10)
if (isNaN(lineNumber)) {
throw new TypeError('data-line-number not set on line number element')
}
return lineNum
return lineNumber
},
getLineElementFromLineNumber: getSingleFileLineElementFromLineNumber,
getCodeElementFromLineNumber: (codeView, line) =>
@ -47,15 +47,15 @@ export const singleFileDOMFunctions: DOMFunctions = {
}
const getDiffLineElementFromLineNumber = (codeView: HTMLElement, line: number, part?: DiffPart): HTMLElement => {
for (const lineNumElem of codeView.querySelectorAll(`.line-number-${part === 'head' ? 'to' : 'from'}`)) {
const lineNum = parseInt((lineNumElem.textContent || '').trim(), 10)
if (!isNaN(lineNum) && lineNum === line) {
const lineElem = lineNumElem.closest<HTMLElement>('.line')
if (!lineElem) {
for (const lineNumberElement of codeView.querySelectorAll(`.line-number-${part === 'head' ? 'to' : 'from'}`)) {
const lineNumber = parseInt((lineNumberElement.textContent || '').trim(), 10)
if (!isNaN(lineNumber) && lineNumber === line) {
const lineElement = lineNumberElement.closest<HTMLElement>('.line')
if (!lineElement) {
throw new Error('Could not find lineElem from lineNumElem')
}
return lineElem
return lineElement
}
}
@ -70,19 +70,19 @@ export const diffDOMFunctions: DOMFunctions = {
throw new Error('Could not find line containing code element')
}
const lineNumTo = line.querySelector<HTMLElement>('.line-number-to')
if (lineNumTo) {
const lineNum = parseInt((lineNumTo.textContent || '').trim(), 10)
if (!isNaN(lineNum)) {
return lineNum
const lineNumberTo = line.querySelector<HTMLElement>('.line-number-to')
if (lineNumberTo) {
const lineNumber = parseInt((lineNumberTo.textContent || '').trim(), 10)
if (!isNaN(lineNumber)) {
return lineNumber
}
}
const lineNumFrom = line.querySelector<HTMLElement>('.line-number-from')
if (lineNumFrom) {
const lineNum = parseInt((lineNumFrom.textContent || '').trim(), 10)
if (!isNaN(lineNum)) {
return lineNum
const lineNumberFrom = line.querySelector<HTMLElement>('.line-number-from')
if (lineNumberFrom) {
const lineNumber = parseInt((lineNumberFrom.textContent || '').trim(), 10)
if (!isNaN(lineNumber)) {
return lineNumber
}
}

View File

@ -25,7 +25,7 @@ describe('Bitbucket scrape.ts', () => {
project: 'SOUR',
rawRepoName: 'bitbucket.test/SOUR/mux',
repoSlug: 'mux',
rev: 'master',
revision: 'master',
})
})
})

View File

@ -23,11 +23,11 @@ const bitbucketToSourcegraphRepoName = ({ repoSlug, project }: BitbucketRepoInfo
* - project name
* - repo name
* - file path
* - rev (through the query parameter `at`)
* - revision (through the query parameter `at`)
*/
const getFileInfoFromLinkInSingleFileView = (
codeView: HTMLElement
): Pick<FileInfo, 'rawRepoName' | 'filePath' | 'rev'> & BitbucketRepoInfo => {
): Pick<FileInfo, 'rawRepoName' | 'filePath' | 'revision'> & BitbucketRepoInfo => {
const errors: Error[] = []
for (const selector of LINK_SELECTORS) {
try {
@ -38,7 +38,7 @@ const getFileInfoFromLinkInSingleFileView = (
const url = new URL(linkElement.href)
const path = url.pathname
// Looks like /projects/<project>/repos/<repo>/(browse|raw)/<file path>?at=<rev>
// Looks like /projects/<project>/repos/<repo>/(browse|raw)/<file path>?at=<revision>
const pathMatch = path.match(/\/projects\/(.*?)\/repos\/(.*?)\/(?:browse|raw)\/(.*)$/)
if (!pathMatch) {
throw new Error(`Path of link matching selector ${selector} did not match path regex: ${path}`)
@ -46,22 +46,22 @@ const getFileInfoFromLinkInSingleFileView = (
const [, project, repoSlug, filePath] = pathMatch
// Looks like 'refs/heads/<rev>'
const at = url.searchParams.get('at')
if (!at) {
// Looks like 'refs/heads/<revision>'
const atParameter = url.searchParams.get('at')
if (!atParameter) {
throw new Error(
`href of link matching selector ${selector} did not have 'at' search param: ${url.href}`
)
}
const atMatch = at.match(/refs\/heads\/(.*?)$/)
const atMatch = atParameter.match(/refs\/heads\/(.*?)$/)
const rev = atMatch ? atMatch[1] : at
const revision = atMatch ? atMatch[1] : atParameter
return {
rawRepoName: bitbucketToSourcegraphRepoName({ repoSlug, project }),
filePath: decodeURIComponent(filePath),
rev,
revision,
project,
repoSlug,
}
@ -107,13 +107,13 @@ const getCommitIDFromRevisionSelector = (): string => {
*/
export const getFileInfoFromSingleFileSourceCodeView = (
codeViewElement: HTMLElement
): BitbucketRepoInfo & Pick<FileInfo, 'rawRepoName' | 'filePath' | 'rev' | 'commitID'> => {
const { rawRepoName, filePath, rev, project, repoSlug } = getFileInfoFromLinkInSingleFileView(codeViewElement)
): BitbucketRepoInfo & Pick<FileInfo, 'rawRepoName' | 'filePath' | 'revision' | 'commitID'> => {
const { rawRepoName, filePath, revision, project, repoSlug } = getFileInfoFromLinkInSingleFileView(codeViewElement)
const commitID = getCommitIDFromRevisionSelector()
return {
rawRepoName,
filePath,
rev,
revision,
commitID,
project,
repoSlug,
@ -161,7 +161,7 @@ const getChangeType = ({ changeTypeElement }: { changeTypeElement: HTMLElement |
if (!changeTypeElement) {
return 'MODIFY'
}
const className = [...changeTypeElement.classList].find(c => /^change-type-[A-Z]+/.test(c))
const className = [...changeTypeElement.classList].find(className => /^change-type-[A-Z]+/.test(className))
if (!className) {
throw new Error('Could not detect change type from change type element')
}
@ -261,7 +261,7 @@ export const getFileInfoFromSingleFileDiffCodeView = (
*/
export const getFileInfoWithoutCommitIDsFromMultiFileDiffCodeView = (
codeViewElement: HTMLElement
): BitbucketRepoInfo & Pick<FileInfo, 'rawRepoName' | 'baseRawRepoName' | 'filePath' | 'baseFilePath' | 'rev'> => {
): BitbucketRepoInfo & Pick<FileInfo, 'rawRepoName' | 'baseRawRepoName' | 'filePath' | 'baseFilePath' | 'revision'> => {
// Get the file path from the breadcrumbs
const breadcrumbsElement = codeViewElement.querySelector('.breadcrumbs')
if (!breadcrumbsElement) {
@ -301,7 +301,7 @@ export const getFileInfoFromCommitDiffCodeView = (
): BitbucketRepoInfo &
Pick<
FileInfo,
'rawRepoName' | 'baseRawRepoName' | 'filePath' | 'baseFilePath' | 'rev' | 'commitID' | 'baseCommitID'
'rawRepoName' | 'baseRawRepoName' | 'filePath' | 'baseFilePath' | 'revision' | 'commitID' | 'baseCommitID'
> => {
const commitID = getCommitIDFromLink('.commit-badge-oneline .commitid')
const baseCommitID = getCommitIDFromLink('.commit-parents .commitid')

View File

@ -4,7 +4,7 @@ exports[`util parseURL() blob page 1`] = `
Object {
"pageType": "blob",
"rawRepoName": "github.com/sourcegraph/sourcegraph",
"revAndFilePath": "3.3/shared/src/hover/HoverOverlay.tsx",
"revisionAndFilePath": "3.3/shared/src/hover/HoverOverlay.tsx",
}
`;
@ -12,7 +12,7 @@ exports[`util parseURL() branch name with forward slashes 1`] = `
Object {
"pageType": "blob",
"rawRepoName": "ghe.sgdev.org/beyang/mux",
"revAndFilePath": "jr/branch/mux.go",
"revisionAndFilePath": "jr/branch/mux.go",
}
`;
@ -48,7 +48,7 @@ exports[`util parseURL() selections - range 1`] = `
Object {
"pageType": "blob",
"rawRepoName": "github.com/sourcegraph/sourcegraph",
"revAndFilePath": "master/jest.config.base.js",
"revisionAndFilePath": "master/jest.config.base.js",
}
`;
@ -56,7 +56,7 @@ exports[`util parseURL() selections - single line 1`] = `
Object {
"pageType": "blob",
"rawRepoName": "github.com/sourcegraph/sourcegraph",
"revAndFilePath": "master/jest.config.base.js",
"revisionAndFilePath": "master/jest.config.base.js",
}
`;
@ -64,7 +64,7 @@ exports[`util parseURL() snippet permalink 1`] = `
Object {
"pageType": "blob",
"rawRepoName": "github.com/sourcegraph/sourcegraph",
"revAndFilePath": "6a91ccec97a46bfb511b7ff58d790554a7d075c8/client/browser/src/shared/repo/backend.tsx",
"revisionAndFilePath": "6a91ccec97a46bfb511b7ff58d790554a7d075c8/client/browser/src/shared/repo/backend.tsx",
}
`;
@ -72,7 +72,7 @@ exports[`util parseURL() tree page 1`] = `
Object {
"pageType": "tree",
"rawRepoName": "github.com/sourcegraph/sourcegraph",
"revAndFilePath": "master/client",
"revisionAndFilePath": "master/client",
}
`;

View File

@ -83,7 +83,7 @@ describe('github/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'github.com/sourcegraph/sourcegraph',
rev: 'master',
revision: 'master',
filePath: 'browser/src/shared/code-hosts/code_intelligence.tsx',
position: {
line: 5,
@ -105,7 +105,7 @@ describe('github/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'ghe.sgdev.org/sourcegraph/sourcegraph',
rev: 'master',
revision: 'master',
filePath: 'browser/src/shared/code-hosts/code_intelligence.tsx',
position: {
line: 5,
@ -125,7 +125,7 @@ describe('github/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'github.com/sourcegraph/sourcegraph',
rev: 'master',
revision: 'master',
filePath: 'browser/src/shared/code-hosts/code_intelligence.tsx',
position: {
line: 5,
@ -154,7 +154,7 @@ describe('github/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'github.com/sourcegraph/sourcegraph',
rev: 'core/gitserver-tracing',
revision: 'core/gitserver-tracing',
filePath: 'cmd/gitserver/server/server.go',
position: {
line: 1335,

View File

@ -3,7 +3,13 @@ import { trimStart } from 'lodash'
import { map } from 'rxjs/operators'
import { Omit } from 'utility-types'
import { PlatformContext } from '../../../../../shared/src/platform/context'
import { FileSpec, RepoSpec, ResolvedRevSpec, RevSpec, toAbsoluteBlobURL } from '../../../../../shared/src/util/url'
import {
FileSpec,
RepoSpec,
ResolvedRevisionSpec,
RevisionSpec,
toAbsoluteBlobURL,
} from '../../../../../shared/src/util/url'
import { fetchBlobContentLines } from '../../repo/backend'
import { querySelectorOrSelf } from '../../util/dom'
import { CodeHost, MountGetter } from '../shared/codeHost'
@ -33,8 +39,8 @@ export function createFileActionsToolbarMount(codeView: HTMLElement): HTMLElemen
return existingMount
}
const mountEl = document.createElement('div')
mountEl.className = className
const mountElement = document.createElement('div')
mountElement.className = className
const fileActions = codeView.querySelector('.file-actions')
if (!fileActions) {
@ -48,12 +54,12 @@ export function createFileActionsToolbarMount(codeView: HTMLElement): HTMLElemen
// Old GitHub Enterprise PR views have a "☑ show comments" text that we want to insert *after*
const showCommentsElement = codeView.querySelector('.show-file-notes')
if (showCommentsElement) {
showCommentsElement.after(mountEl)
showCommentsElement.after(mountElement)
} else {
fileActions.prepend(mountEl)
fileActions.prepend(mountElement)
}
return mountEl
return mountElement
}
const toolbarButtonProps = {
@ -94,7 +100,7 @@ const singleFileCodeView: Omit<CodeView, 'element'> = {
*/
const getSnippetPositionAdjuster = (
requestGraphQL: PlatformContext['requestGraphQL']
): PositionAdjuster<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec> => ({ direction, codeView, position }) =>
): PositionAdjuster<RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec> => ({ direction, codeView, position }) =>
fetchBlobContentLines({ ...position, requestGraphQL }).pipe(
map(lines => {
const codeElement = singleFileDOMFunctions.getCodeElementFromLineNumber(
@ -143,18 +149,18 @@ export const createFileLineContainerToolbarMount: NonNullable<CodeView['getToolb
if (existingMount) {
return existingMount
}
const mountEl = document.createElement('div')
mountEl.style.display = 'inline-flex'
mountEl.style.verticalAlign = 'middle'
mountEl.style.alignItems = 'center'
mountEl.className = className
const mountElement = document.createElement('div')
mountElement.style.display = 'inline-flex'
mountElement.style.verticalAlign = 'middle'
mountElement.style.alignItems = 'center'
mountElement.className = className
const rawURLLink = codeViewElement.querySelector('#raw-url')
const buttonGroup = rawURLLink?.closest('.BtnGroup')
if (!buttonGroup?.parentNode) {
throw new Error('File actions not found')
}
buttonGroup.parentNode.insertBefore(mountEl, buttonGroup)
return mountEl
buttonGroup.parentNode.insertBefore(mountElement, buttonGroup)
return mountElement
}
/**
@ -195,14 +201,14 @@ export const fileLineContainerResolver: ViewResolver<CodeView> = {
const genericCodeViewResolver: ViewResolver<CodeView> = {
selector: '.file',
resolveView: (elem: HTMLElement): CodeView | null => {
if (elem.querySelector('article.markdown-body')) {
resolveView: (element: HTMLElement): CodeView | null => {
if (element.querySelector('article.markdown-body')) {
// This code view is rendered markdown, we shouldn't add code intelligence
return null
}
// This is a suggested change on a GitHub PR
if (elem.closest('.js-suggested-changes-blob')) {
if (element.closest('.js-suggested-changes-blob')) {
return null
}
@ -213,15 +219,15 @@ const genericCodeViewResolver: ViewResolver<CodeView> = {
document.querySelectorAll('.diff-view').length === 0
if (isSingleCodeFile) {
return { element: elem, ...singleFileCodeView }
return { element, ...singleFileCodeView }
}
if (elem.closest('.discussion-item-body') || elem.classList.contains('js-comment-container')) {
if (element.closest('.discussion-item-body') || element.classList.contains('js-comment-container')) {
// This code view is embedded on a PR conversation page.
return { element: elem, ...diffConversationCodeView }
return { element, ...diffConversationCodeView }
}
return { element: elem, ...diffCodeView }
return { element, ...diffCodeView }
},
}
@ -287,7 +293,8 @@ export const githubCodeHost: CodeHost = {
const parsedURL = parseURL()
return {
...parsedURL,
rev: parsedURL.pageType === 'blob' || parsedURL.pageType === 'tree' ? resolveFileInfo().rev : undefined,
revision:
parsedURL.pageType === 'blob' || parsedURL.pageType === 'tree' ? resolveFileInfo().revision : undefined,
privateRepository: window.location.hostname !== 'github.com' || repoHeaderHasPrivateMarker,
}
},
@ -357,12 +364,12 @@ export const githubCodeHost: CodeHost = {
return toAbsoluteBlobURL(sourcegraphURL, target)
}
const rev = target.rev || 'HEAD'
const revision = target.revision || 'HEAD'
// If we're provided options, we can make the j2d URL more specific.
const { rawRepoName } = parseURL()
// Stay on same page in PR if possible.
// TODO to be entirely correct, this would need to compare the rev of the code view with the target rev.
// TODO to be entirely correct, this would need to compare the revision of the code view with the target revision.
const isSameRepo = rawRepoName === target.rawRepoName
if (isSameRepo && context.part !== undefined) {
const containers = getFileContainers()
@ -397,7 +404,7 @@ export const githubCodeHost: CodeHost = {
const fragment = target.position
? `#L${target.position.line}${target.position.character ? ':' + target.position.character : ''}`
: ''
return `https://${target.rawRepoName}/blob/${rev}/${target.filePath}${fragment}`
return `https://${target.rawRepoName}/blob/${revision}/${target.filePath}${fragment}`
},
codeViewsRequireTokenization: true,
}

View File

@ -3,13 +3,13 @@ import { DOMFunctions } from '../shared/codeViews'
import { isDiffPageType, parseURL } from './util'
const getDiffCodePart = (codeElement: HTMLElement): DiffPart => {
const td = codeElement.closest('td')!
const tableCell = codeElement.closest('td')!
if (td.classList.contains('blob-code-addition')) {
if (tableCell.classList.contains('blob-code-addition')) {
return 'head'
}
if (td.classList.contains('blob-code-deletion')) {
if (tableCell.classList.contains('blob-code-deletion')) {
return 'base'
}
// If we can't determine the diff part the code element's parent `<td>`
@ -18,7 +18,7 @@ const getDiffCodePart = (codeElement: HTMLElement): DiffPart => {
// code view to determine whether this is a split or unified diff view.
if (isDomSplitDiff(codeElement)) {
// If there are more cells on the right, this is the base, otherwise the head
return td.nextElementSibling ? 'base' : 'head'
return tableCell.nextElementSibling ? 'base' : 'head'
}
return 'head'
@ -161,8 +161,8 @@ export const diffDomFunctions: DOMFunctions = {
)
// Some versions of GitHub have data-code-marker attributes instead of the first character diff indicator.
const tr = codeElement.closest('tr')
const hasDataCodeMarkerUnified = tr?.querySelector('td[data-code-marker]')
const tableRow = codeElement.closest('tr')
const hasDataCodeMarkerUnified = tableRow?.querySelector('td[data-code-marker]')
const hasDataCodeMarkerSplit = blobCodeInner?.hasAttribute('data-code-marker')
const hasDataCodeMarker = hasDataCodeMarkerUnified || hasDataCodeMarkerSplit

View File

@ -1,6 +1,6 @@
import { FileInfo } from '../shared/codeHost'
import { getCommitIDFromPermalink } from './scrape'
import { getDiffFileName, getDiffResolvedRev, getFilePath, parseURL } from './util'
import { getDiffFileName, getDiffResolvedRevision, getFilePath, parseURL } from './util'
export const resolveDiffFileInfo = (codeView: HTMLElement): FileInfo => {
const { rawRepoName } = parseURL()
@ -8,20 +8,20 @@ export const resolveDiffFileInfo = (codeView: HTMLElement): FileInfo => {
if (!headFilePath) {
throw new Error('cannot determine file path')
}
const diffResolvedRev = getDiffResolvedRev(codeView)
if (!diffResolvedRev) {
const diffResolvedRevision = getDiffResolvedRevision(codeView)
if (!diffResolvedRevision) {
throw new Error('cannot determine delta info')
}
const { headCommitID, baseCommitID } = diffResolvedRev
const { headCommitID, baseCommitID } = diffResolvedRevision
return {
rawRepoName,
filePath: headFilePath,
commitID: headCommitID,
rev: headCommitID,
revision: headCommitID,
baseRawRepoName: rawRepoName,
baseFilePath,
baseCommitID,
baseRev: baseCommitID,
baseRevision: baseCommitID,
}
}
@ -30,20 +30,20 @@ export const resolveFileInfo = (): FileInfo => {
if (parsedURL.pageType !== 'blob' && parsedURL.pageType !== 'tree') {
throw new Error(`Current URL does not match a blob or tree url: ${window.location.href}`)
}
const { revAndFilePath, rawRepoName } = parsedURL
const { revisionAndFilePath, rawRepoName } = parsedURL
const filePath = getFilePath()
const filePathWithLeadingSlash = filePath.startsWith('/') ? filePath : `/${filePath}`
if (!revAndFilePath.endsWith(filePathWithLeadingSlash)) {
if (!revisionAndFilePath.endsWith(filePathWithLeadingSlash)) {
throw new Error(
`The file path ${filePathWithLeadingSlash} should always be a suffix of revAndFilePath ${revAndFilePath}, but isn't in this case.`
`The file path ${filePathWithLeadingSlash} should always be a suffix of revAndFilePath ${revisionAndFilePath}, but isn't in this case.`
)
}
return {
rawRepoName,
filePath,
commitID: getCommitIDFromPermalink(),
rev: revAndFilePath.slice(0, -filePathWithLeadingSlash.length),
revision: revisionAndFilePath.slice(0, -filePathWithLeadingSlash.length),
}
}
@ -70,17 +70,17 @@ export const resolveSnippetFileInfo = (codeView: HTMLElement): FileInfo => {
if (parsedURL.pageType !== 'blob') {
throw new Error(`Snippet URL does not match a blob url: ${snippetPermalinkURL.href}`)
}
const { revAndFilePath, rawRepoName } = parsedURL
if (!revAndFilePath.startsWith(commitID)) {
const { revisionAndFilePath, rawRepoName } = parsedURL
if (!revisionAndFilePath.startsWith(commitID)) {
throw new Error(
`Could not parse filePath: revAndFilePath ${revAndFilePath} does not start with commitID ${commitID}`
`Could not parse filePath: revAndFilePath ${revisionAndFilePath} does not start with commitID ${commitID}`
)
}
const filePath = revAndFilePath.slice(commitID.length + 1)
const filePath = revisionAndFilePath.slice(commitID.length + 1)
return {
rawRepoName,
filePath,
commitID,
rev: commitID,
revision: commitID,
}
}

View File

@ -54,7 +54,7 @@ function getPathNamesFromElement(element: HTMLElement): { headFilePath: string;
/**
* getDiffResolvedRev returns the base and head revision SHA, or null for non-diff views.
*/
export function getDiffResolvedRev(codeView: HTMLElement): DiffResolvedRevSpec | null {
export function getDiffResolvedRevision(codeView: HTMLElement): DiffResolvedRevSpec | null {
const { pageType } = parseURL()
if (!isDiffPageType(pageType)) {
return null
@ -66,9 +66,9 @@ export function getDiffResolvedRev(codeView: HTMLElement): DiffResolvedRevSpec |
const isCommentedSnippet = codeView.classList.contains('js-comment-container')
if (pageType === 'pull') {
if (fetchContainers && fetchContainers.length === 1) {
for (const el of fetchContainers) {
for (const element of fetchContainers) {
// for conversation view of pull request
const url = el.getAttribute('data-url')
const url = element.getAttribute('data-url')
if (!url) {
continue
}
@ -96,14 +96,14 @@ export function getDiffResolvedRev(codeView: HTMLElement): DiffResolvedRevSpec |
// Refined GitHub adds a `.patch-diff-links` element
const shaContainers = document.querySelectorAll('.sha-block:not(.patch-diff-links)')
if (shaContainers && shaContainers.length === 2) {
const baseShaEl = shaContainers[0].querySelector('a')
if (baseShaEl) {
const baseShaElement = shaContainers[0].querySelector('a')
if (baseShaElement) {
// e.g "https://github.com/gorilla/mux/commit/0b13a922203ebdbfd236c818efcd5ed46097d690"
baseCommitID = baseShaEl.href.split('/').slice(-1)[0]
baseCommitID = baseShaElement.href.split('/').slice(-1)[0]
}
const headShaEl = shaContainers[1].querySelector('span.sha') as HTMLElement
if (headShaEl) {
headCommitID = headShaEl.innerHTML
const headShaElement = shaContainers[1].querySelector('span.sha') as HTMLElement
if (headShaElement) {
headCommitID = headShaElement.innerHTML
}
}
} else if (pageType === 'compare') {
@ -114,7 +114,7 @@ export function getDiffResolvedRev(codeView: HTMLElement): DiffResolvedRevSpec |
}
if (baseCommitID === '' || headCommitID === '') {
return getDiffResolvedRevFromPageSource(document.documentElement.innerHTML, pageType === 'pull')
return getDiffResolvedRevisionFromPageSource(document.documentElement.innerHTML, pageType === 'pull')
}
return { baseCommitID, headCommitID }
}
@ -137,10 +137,13 @@ function getResolvedDiffFromCommentedSnippet(codeView: HTMLElement): DiffResolve
}
const headCommitID = match[3]
// The file header may not contain the base commit ID, so we get it from the page source.
const resolvedRevFromPageSource = getDiffResolvedRevFromPageSource(document.documentElement.innerHTML, true)
return headCommitID && resolvedRevFromPageSource
const resolvedRevisionFromPageSource = getDiffResolvedRevisionFromPageSource(
document.documentElement.innerHTML,
true
)
return headCommitID && resolvedRevisionFromPageSource
? {
...resolvedRevFromPageSource,
...resolvedRevisionFromPageSource,
headCommitID,
}
: null
@ -157,7 +160,7 @@ function getResolvedDiffForCompare(): DiffResolvedRevSpec | undefined {
return undefined
}
function getDiffResolvedRevFromPageSource(pageSource: string, isPullRequest: boolean): DiffResolvedRevSpec | null {
function getDiffResolvedRevisionFromPageSource(pageSource: string, isPullRequest: boolean): DiffResolvedRevSpec | null {
if (!isPullRequest) {
return null
}
@ -220,8 +223,8 @@ type GitHubURL = RawRepoSpec &
| { pageType: 'commit' | 'pull' | 'compare' | 'other' }
| {
pageType: 'blob' | 'tree'
/** rev and file path separated by a slash, URL-decoded. */
revAndFilePath: string
/** revision and file path separated by a slash, URL-decoded. */
revisionAndFilePath: string
}
)
@ -236,11 +239,11 @@ export function isDiffPageType(pageType: GitHubURL['pageType']): boolean {
}
}
export function parseURL(loc: Pick<Location, 'host' | 'pathname'> = window.location): GitHubURL {
const { host, pathname } = loc
export function parseURL(location: Pick<Location, 'host' | 'pathname' | 'href'> = window.location): GitHubURL {
const { host, pathname } = location
const [user, ghRepoName, pageType, ...rest] = pathname.slice(1).split('/')
if (!user || !ghRepoName) {
throw new Error(`Could not parse repoName from GitHub url: ${window.location.href}`)
throw new Error(`Could not parse repoName from GitHub url: ${location.href}`)
}
const rawRepoName = `${host}/${user}/${ghRepoName}`
switch (pageType) {
@ -249,7 +252,7 @@ export function parseURL(loc: Pick<Location, 'host' | 'pathname'> = window.locat
return {
pageType,
rawRepoName,
revAndFilePath: decodeURIComponent(rest.join('/')),
revisionAndFilePath: decodeURIComponent(rest.join('/')),
}
case 'pull':
case 'commit':

View File

@ -26,7 +26,7 @@ describe('gitlab/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'gitlab.com/sourcegraph/sourcegraph',
rev: 'master',
revision: 'master',
filePath: 'browser/src/shared/code-hosts/code_intelligence.tsx',
position: {
line: 5,
@ -48,7 +48,7 @@ describe('gitlab/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'gitlab.sgdev.org/sourcegraph/sourcegraph',
rev: 'master',
revision: 'master',
filePath: 'browser/src/shared/code-hosts/code_intelligence.tsx',
position: {
line: 5,
@ -68,7 +68,7 @@ describe('gitlab/codeHost', () => {
{
repoName: 'sourcegraph/sourcegraph',
rawRepoName: 'gitlab.com/sourcegraph/sourcegraph',
rev: 'master',
revision: 'master',
filePath: 'browser/src/shared/code-hosts/code_intelligence.tsx',
position: {
line: 5,
@ -88,7 +88,7 @@ describe('gitlab/codeHost', () => {
{
repoName: 'sourcegraph/jsonrpc2',
rawRepoName: 'gitlab.com/sourcegraph/jsonrpc2',
rev: 'changes',
revision: 'changes',
filePath: 'call_opt.go',
position: {
line: 5,

View File

@ -7,7 +7,7 @@ import { diffDOMFunctions, singleFileDOMFunctions } from './domFunctions'
import { getCommandPaletteMount } from './extensions'
import { resolveCommitFileInfo, resolveDiffFileInfo, resolveFileInfo } from './fileInfo'
import { getPageInfo, GitLabPageKind, getFilePathsFromCodeView } from './scrape'
import { subTypeOf } from '../../../../../shared/src/util/types'
import { subtypeOf } from '../../../../../shared/src/util/types'
import { NotificationType } from '../../../../../shared/src/api/client/services/notifications'
import { toAbsoluteBlobURL } from '../../../../../shared/src/util/url'
@ -132,7 +132,7 @@ const notificationClassNames = {
[NotificationType.Error]: 'alert alert-danger',
}
export const gitlabCodeHost = subTypeOf<CodeHost>()({
export const gitlabCodeHost = subtypeOf<CodeHost>()({
type: 'gitlab',
name: 'GitLab',
check: checkIsGitlab,
@ -152,7 +152,7 @@ export const gitlabCodeHost = subTypeOf<CodeHost>()({
}
// Stay on same page in MR if possible.
// TODO to be entirely correct, this would need to compare the rev of the code view with the target rev.
// TODO to be entirely correct, this would need to compare the revision of the code view with the target revision.
const currentPage = getPageInfo()
if (currentPage.rawRepoName === target.rawRepoName && context.part !== undefined) {
const codeViews = document.querySelectorAll<HTMLElement>(codeViewResolver.selector)
@ -178,7 +178,7 @@ export const gitlabCodeHost = subTypeOf<CodeHost>()({
}
// Go to specific URL on this Gitlab instance.
const url = new URL(`https://${target.rawRepoName}/blob/${target.rev}/${target.filePath}`)
const url = new URL(`https://${target.rawRepoName}/blob/${target.revision}/${target.filePath}`)
if (target.position) {
const { line } = target.position
url.hash = `#L${line}`

View File

@ -2,16 +2,16 @@ import { querySelectorOrSelf } from '../../util/dom'
import { MountGetter } from '../shared/codeHost'
export const getCommandPaletteMount: MountGetter = (container: HTMLElement): HTMLElement | null => {
const headerElem = querySelectorOrSelf(container, '.navbar-collapse')
if (!headerElem) {
const headerElement = querySelectorOrSelf(container, '.navbar-collapse')
if (!headerElement) {
return null
}
const commandListClass = 'command-palette-button'
const createCommandList = (): HTMLElement => {
const mount = document.createElement('div')
mount.className = commandListClass
headerElem.prepend(mount)
headerElement.prepend(mount)
return mount
}
return headerElem.querySelector<HTMLElement>('.' + commandListClass) || createCommandList()
return headerElement.querySelector<HTMLElement>('.' + commandListClass) || createCommandList()
}

View File

@ -19,14 +19,14 @@ import { asObservable } from '../../../../../shared/src/util/rxjs/asObservable'
* Resolves file information for a page with a single file, not including diffs with only one file.
*/
export const resolveFileInfo = (): FileInfo => {
const { rawRepoName, filePath, rev } = getFilePageInfo()
const { rawRepoName, filePath, revision } = getFilePageInfo()
if (!filePath) {
throw new Error(
`Unable to determine the file path of the current file because the current URL (window.location ${window.location.href}) does not have a file path.`
)
}
const commitID = getCommitIDFromPermalink()
return { rawRepoName, filePath, commitID, rev }
return { rawRepoName, filePath, commitID, revision }
}
/**

View File

@ -1,6 +1,6 @@
import { last, take } from 'lodash'
import { FileSpec, RawRepoSpec, RevSpec } from '../../../../../shared/src/util/url'
import { FileSpec, RawRepoSpec, RevisionSpec } from '../../../../../shared/src/util/url'
import { commitIDFromPermalink } from '../../util/dom'
import { FileInfo } from '../shared/codeHost'
import { isExtension } from '../../context'
@ -25,7 +25,7 @@ export interface GitLabInfo extends RawRepoSpec {
/**
* Information about single file pages.
*/
interface GitLabFileInfo extends RawRepoSpec, FileSpec, RevSpec {}
interface GitLabFileInfo extends RawRepoSpec, FileSpec, RevisionSpec {}
export const getPageKindFromPathName = (owner: string, projectName: string, pathname: string): GitLabPageKind => {
const pageKindMatch = pathname.match(new RegExp(`^/${owner}/${projectName}(/-)?/(commit|merge_requests|blob)/`))
@ -81,12 +81,12 @@ export function getFilePageInfo(): GitLabFileInfo {
throw new Error('Unable to determine revision or file path')
}
const rev = decodeURIComponent(matches[1])
const revision = decodeURIComponent(matches[1])
const filePath = decodeURIComponent(matches[2])
return {
rawRepoName,
filePath,
rev,
revision,
}
}
@ -106,12 +106,12 @@ export const getMergeRequestID = (): string => {
* The diff ID represents a specific revision in a merge request.
*/
export const getDiffID = (): string | undefined => {
const params = new URLSearchParams(window.location.search)
return params.get('diff_id') ?? undefined
const parameters = new URLSearchParams(window.location.search)
return parameters.get('diff_id') ?? undefined
}
const getFilePathFromElem = (elem: HTMLElement): string => {
const filePath = elem.dataset.originalTitle || elem.dataset.title || elem.title
const getFilePathFromElement = (element: HTMLElement): string => {
const filePath = element.dataset.originalTitle || element.dataset.title || element.title
if (!filePath) {
throw new Error('Unable to get file paths from code view: no file title')
}
@ -129,11 +129,11 @@ export function getFilePathsFromCodeView(codeView: HTMLElement): Pick<FileInfo,
}
const filePathDidChange = filePathElements.length > 1
const filePath = getFilePathFromElem(filePathElements.item(filePathDidChange ? 1 : 0))
const filePath = getFilePathFromElement(filePathElements.item(filePathDidChange ? 1 : 0))
return {
filePath,
baseFilePath: filePathDidChange ? getFilePathFromElem(filePathElements.item(0)) : filePath,
baseFilePath: filePathDidChange ? getFilePathFromElement(filePathElements.item(0)) : filePath,
}
}

View File

@ -9,7 +9,7 @@ import { isExtension } from '../../context'
import { resolveRepo } from '../../repo/backend'
import { normalizeRepoName } from './util'
import { isRepoNotFoundErrorLike } from '../../../../../shared/src/backend/errors'
import { RepoSpec, FileSpec, ResolvedRevSpec } from '../../../../../shared/src/util/url'
import { RepoSpec, FileSpec, ResolvedRevisionSpec } from '../../../../../shared/src/util/url'
import { RevisionSpec, DiffSpec, BaseDiffSpec } from '.'
import { checkOk } from '../../../../../shared/src/backend/fetch'
import { fromFetch } from '../../../../../shared/src/graphql/fromFetch'
@ -124,9 +124,9 @@ export type QueryConduitHelper<T> = (endpoint: string, params: {}) => Observable
/**
* Generic helper to query the Phabricator Conduit API.
*/
export function queryConduitHelper<T>(endpoint: string, params: {}): Observable<T> {
export function queryConduitHelper<T>(endpoint: string, parameters: {}): Observable<T> {
const form = createConduitRequestForm()
for (const [key, value] of Object.entries(params)) {
for (const [key, value] of Object.entries(parameters)) {
form.set(`params[${key}]`, JSON.stringify(value))
}
return fromFetch(
@ -410,7 +410,10 @@ interface ResolveStagingOptions extends Pick<PlatformContext, 'requestGraphQL'>,
* Returns the commit ID of the one-off commit created on the Sourcegraph instance for the given
* repo/diffID/patch, creating that commit if needed.
*/
const resolveStagingRev = ({ requestGraphQL, ...variables }: ResolveStagingOptions): Observable<ResolvedRevSpec> =>
const resolveStagingRevision = ({
requestGraphQL,
...variables
}: ResolveStagingOptions): Observable<ResolvedRevisionSpec> =>
requestGraphQL<GQL.IMutation>({
request: gql`
mutation ResolveStagingRev(
@ -447,7 +450,7 @@ const resolveStagingRev = ({ requestGraphQL, ...variables }: ResolveStagingOptio
}
const { oid } = resolvePhabricatorDiff
if (!oid) {
throw new Error('Could not resolve staging rev: empty oid')
throw new Error('Could not resolve staging revision: empty oid')
}
return { commitID: oid }
})
@ -514,13 +517,13 @@ function getStagingDetails(
const type = propsWithInfo.useBaseForDiff ? 'base' : 'diff'
key = `refs/tags/phabricator/${type}/${propsWithInfo.diffID}`
}
for (const ref of propsWithInfo.diffDetails.properties['arc.staging'].refs) {
if (ref.ref === key) {
const remote = ref.remote.uri
for (const reference of propsWithInfo.diffDetails.properties['arc.staging'].refs) {
if (reference.ref === key) {
const remote = reference.remote.uri
if (remote) {
return {
repoName: normalizeRepoName(remote),
ref,
ref: reference,
unconfigured: stagingInfo.status === 'repository.unconfigured',
}
}
@ -529,7 +532,7 @@ function getStagingDetails(
return undefined
}
interface ResolvedDiff extends ResolvedRevSpec {
interface ResolvedDiff extends ResolvedRevisionSpec {
/**
* The name of the staging repository, if it is synced to the Sourcegraph instance.
*/
@ -551,7 +554,7 @@ interface ResolvedDiff extends ResolvedRevSpec {
* be returned ({@see resolveStagingRev}).
*
*/
export function resolveDiffRev(
export function resolveDiffRevision(
props: ResolveDiffOpt,
requestGraphQL: PlatformContext['requestGraphQL'],
queryConduit: QueryConduitHelper<any>
@ -580,7 +583,7 @@ export function resolveDiffRev(
// create a one-off commit on the Sourcegraph instance from the patch,
// and resolve to the commit ID returned by the Sourcegraph instance.
return getRawDiffFromConduit({ diffID: props.diffID, queryConduit }).pipe(
switchMap(patch => resolveStagingRev({ ...conduitProps, patch, requestGraphQL }))
switchMap(patch => resolveStagingRevision({ ...conduitProps, patch, requestGraphQL }))
)
}
@ -599,7 +602,7 @@ export function resolveDiffRev(
throw error
}
return getRawDiffFromConduit({ diffID: props.diffID, queryConduit }).pipe(
switchMap(patch => resolveStagingRev({ ...conduitProps, patch, requestGraphQL }))
switchMap(patch => resolveStagingRevision({ ...conduitProps, patch, requestGraphQL }))
)
})
)

View File

@ -2,7 +2,7 @@ import { AdjustmentDirection, PositionAdjuster } from '@sourcegraph/codeintellif
import { Position } from '@sourcegraph/extension-api-types'
import { map } from 'rxjs/operators'
import { PlatformContext } from '../../../../../shared/src/platform/context'
import { FileSpec, RepoSpec, ResolvedRevSpec, RevSpec } from '../../../../../shared/src/util/url'
import { FileSpec, RepoSpec, ResolvedRevisionSpec, RevisionSpec } from '../../../../../shared/src/util/url'
import { fetchBlobContentLines } from '../../repo/backend'
import { CodeHost } from '../shared/codeHost'
import { CodeView, toCodeViewResolver } from '../shared/codeViews'
@ -48,7 +48,7 @@ const adjustCharacter = (position: Position, adjustment: number): Position => ({
const getPositionAdjuster = (
requestGraphQL: PlatformContext['requestGraphQL']
): PositionAdjuster<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec> => ({ direction, codeView, position }) =>
): PositionAdjuster<RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec> => ({ direction, codeView, position }) =>
fetchBlobContentLines({ ...position, requestGraphQL }).pipe(
map(lines => {
const codeElement = diffDomFunctions.getCodeElementFromLineNumber(codeView, position.line, position.part)

View File

@ -21,12 +21,12 @@ const getLineNumber = (lineNumberCell: HTMLElement): number =>
* and `<td>` line number cells with a `data-n` attribtue (recent Phabricator versions).
*/
const getLineNumberCellFromCodeElement = (codeElement: HTMLElement): HTMLElement | null => {
let elem: HTMLElement | null = codeElement
while (elem) {
if (isLineNumberCell(elem)) {
return elem
let element: HTMLElement | null = codeElement
while (element) {
if (isLineNumberCell(element)) {
return element
}
elem = elem.previousElementSibling as HTMLElement | null
element = element.previousElementSibling as HTMLElement | null
}
return null
}
@ -78,20 +78,20 @@ export const diffDomFunctions: DOMFunctions = {
return null
}
const td = target.closest('td')
if (!td) {
const tableCell = target.closest('td')
if (!tableCell) {
return null
}
if (td.classList.contains('show-more') || td.classList.contains('show-context')) {
if (tableCell.classList.contains('show-more') || tableCell.classList.contains('show-context')) {
// This element represents a collapsed part of the diff, it's not a code element.
return null
}
if (!getLineNumberCellFromCodeElement(td)) {
if (!getLineNumberCellFromCodeElement(tableCell)) {
// The element has no associated line number cell: this can be the case when hovering
// 'empty' lines in the base part of a split diff that has added lines.
return null
}
return td
return tableCell
},
getCodeElementFromLineNumber: getDiffCodeElementFromLineNumber,
getLineElementFromLineNumber: getDiffCodeElementFromLineNumber,

View File

@ -44,11 +44,11 @@ const DEFAULT_CONDUIT_RESPONSES: ConduitResponseMap = {
repositoryPHID: '1',
},
}),
'/api/differential.querydiffs': (params: { ids: string[]; revisionIDs: string[] }) =>
'/api/differential.querydiffs': (parameters: { ids: string[]; revisionIDs: string[] }) =>
of({
[params.ids[0]]: {
id: params.ids[0],
revisionID: params.revisionIDs[0],
[parameters.ids[0]]: {
id: parameters.ids[0],
revisionID: parameters.revisionIDs[0],
dateCreated: '1566329300',
dateModified: '1566329305',
sourceControlBaseRevision: 'base-revision',
@ -67,15 +67,15 @@ const DEFAULT_CONDUIT_RESPONSES: ConduitResponseMap = {
status: 'pushed',
refs: [
{
ref: `refs/tags/phabricator/base/${params.ids[0]}`,
ref: `refs/tags/phabricator/base/${parameters.ids[0]}`,
type: 'base',
commit: `base-${params.ids[0]}`,
commit: `base-${parameters.ids[0]}`,
remote: { uri: 'https://github.com/lguychard/testing.git' },
},
{
ref: `refs/tags/phabricator/diff/${params.ids[0]}`,
ref: `refs/tags/phabricator/diff/${parameters.ids[0]}`,
type: 'diff',
commit: `diff-${params.ids[0]}`,
commit: `diff-${parameters.ids[0]}`,
remote: { uri: 'https://github.com/lguychard/testing.git' },
},
],
@ -103,18 +103,18 @@ const DEFAULT_GRAPHQL_RESPONSES: GraphQLResponseMap = {
} as SuccessGraphQLResult<IQuery>),
ResolveStagingRev: () =>
of({
data: { resolvePhabricatorDiff: { oid: 'staging-rev' } },
data: { resolvePhabricatorDiff: { oid: 'staging-revision' } },
errors: undefined,
} as SuccessGraphQLResult<IMutation>),
}
function mockQueryConduit(responseMap?: ConduitResponseMap): QueryConduitHelper<any> {
return (endpoint, params) => {
return (endpoint, parameters) => {
const mock = responseMap?.[endpoint] || DEFAULT_CONDUIT_RESPONSES[endpoint]
if (!mock) {
return throwError(new Error(`No mock for endpoint ${endpoint}`))
}
return mock(params)
return mock(parameters)
}
}
@ -304,11 +304,11 @@ describe('Phabricator file info', () => {
codeViewSelector: '.differential-changeset',
conduitResponseMap: {
// Returns diff details without staging details
'/api/differential.querydiffs': params =>
'/api/differential.querydiffs': parameters =>
of({
[params.ids[0]]: {
id: params.ids[0],
revisionID: params.revisionIDs[0],
[parameters.ids[0]]: {
id: parameters.ids[0],
revisionID: parameters.revisionIDs[0],
dateCreated: '1566329300',
dateModified: '1566329305',
sourceControlBaseRevision: 'base-revision',
@ -328,7 +328,7 @@ describe('Phabricator file info', () => {
baseCommitID: 'base-revision',
baseFilePath: 'helpers/add.go',
baseRawRepoName: 'github.com/gorilla/mux',
commitID: 'staging-rev',
commitID: 'staging-revision',
filePath: 'helpers/add.go',
rawRepoName: 'github.com/gorilla/mux',
})
@ -347,7 +347,7 @@ describe('Phabricator file info', () => {
baseCommitID: 'base-revision',
baseFilePath: 'helpers/add.go',
baseRawRepoName: 'github.com/gorilla/mux',
commitID: 'staging-rev',
commitID: 'staging-revision',
filePath: 'helpers/add.go',
rawRepoName: 'github.com/gorilla/mux',
})
@ -395,23 +395,25 @@ describe('Phabricator file info', () => {
ResolveStagingRev: (variables: any) =>
of({
data: {
resolvePhabricatorDiff: { oid: `staging-rev-${variables.patch as string}` },
resolvePhabricatorDiff: {
oid: `staging-revision-${variables.patch as string}`,
},
},
errors: undefined,
} as SuccessGraphQLResult<IMutation>),
},
conduitResponseMap: {
'/api/differential.getrawdiff': params =>
of(`raw-diff-for-diffid-${params.diffID as string}`),
'/api/differential.getrawdiff': parameters =>
of(`raw-diff-for-diffid-${parameters.diffID as string}`),
},
},
resolveDiffFileInfo
)
).toEqual({
baseCommitID: 'staging-rev-raw-diff-for-diffid-2',
baseCommitID: 'staging-revision-raw-diff-for-diffid-2',
baseFilePath: '.arcconfig',
baseRawRepoName: 'github.com/gorilla/mux',
commitID: 'staging-rev-raw-diff-for-diffid-3',
commitID: 'staging-revision-raw-diff-for-diffid-3',
filePath: '.arcconfig',
rawRepoName: 'github.com/gorilla/mux',
})

View File

@ -3,7 +3,7 @@ import { map, switchMap } from 'rxjs/operators'
import { PlatformContext } from '../../../../../shared/src/platform/context'
import { FileInfo } from '../shared/codeHost'
import { PhabricatorMode } from '.'
import { queryConduitHelper, resolveDiffRev } from './backend'
import { queryConduitHelper, resolveDiffRevision } from './backend'
import { getFilepathFromFileForDiff, getFilePathFromFileForRevision } from './scrape'
import { getPhabricatorState } from './util'
@ -42,7 +42,7 @@ export const resolveDiffFileInfo = (
throw new Error(`Unexpected PhabricatorState for resolveDiffFileInfo, PhabricatorMode: ${state.mode}`)
}
const { filePath, baseFilePath } = getFilepathFromFileForDiff(codeView)
const resolveBaseCommitID = resolveDiffRev(
const resolveBaseCommitID = resolveDiffRevision(
{
repoName: state.baseRawRepoName,
revisionID: state.revisionID,
@ -64,7 +64,7 @@ export const resolveDiffFileInfo = (
})
)
)
const resolveHeadCommitID = resolveDiffRev(
const resolveHeadCommitID = resolveDiffRevision(
{
repoName: state.headRawRepoName,
revisionID: state.revisionID,

View File

@ -1,4 +1,4 @@
import { FileSpec, RawRepoSpec, ResolvedRevSpec } from '../../../../../shared/src/util/url'
import { FileSpec, RawRepoSpec, ResolvedRevisionSpec } from '../../../../../shared/src/util/url'
export enum PhabricatorMode {
Diffusion = 1,
@ -7,7 +7,7 @@ export enum PhabricatorMode {
Change,
}
export interface DiffusionState extends RawRepoSpec, ResolvedRevSpec, FileSpec {
export interface DiffusionState extends RawRepoSpec, ResolvedRevisionSpec, FileSpec {
mode: PhabricatorMode.Diffusion
}
@ -53,7 +53,7 @@ export interface RevisionState extends RawRepoSpec {
* Refers to a URL like http://phabricator.aws.sgdev.org/source/nzap/change/master/checked_message_bench_test.go,
* which a user gets to by clicking "Show Last Change" on a differential page.
*/
export interface ChangeState extends RawRepoSpec, FileSpec, ResolvedRevSpec {
export interface ChangeState extends RawRepoSpec, FileSpec, ResolvedRevisionSpec {
mode: PhabricatorMode.Change
}

View File

@ -6,11 +6,11 @@ import { Observable, throwError } from 'rxjs'
const TAG_PATTERN = /r([\dA-z]+)([\da-f]{40})/
function matchPageTag(): RegExpExecArray | null {
const el = document.querySelectorAll('.phui-tag-core').item(0)
if (!el) {
const element = document.querySelectorAll('.phui-tag-core').item(0)
if (!element) {
throw new Error('Could not find Phabricator page tag')
}
return TAG_PATTERN.exec(el.children[0].getAttribute('href') as string)
return TAG_PATTERN.exec(element.children[0].getAttribute('href') as string)
}
function getCallsignFromPageTag(): string {
@ -77,12 +77,12 @@ function getBaseCommitIDFromRevisionPage(): string {
}
export function getPhabricatorState(
loc: Location,
location: Location,
requestGraphQL: PlatformContext['requestGraphQL'],
queryConduit: QueryConduitHelper<any>
): Observable<DiffusionState | DifferentialState | RevisionState | ChangeState> {
try {
const stateUrl = loc.href.replace(loc.origin, '')
const stateUrl = location.href.replace(location.origin, '')
const diffusionMatch = PHAB_DIFFUSION_REGEX.exec(stateUrl)
if (diffusionMatch) {
const filePath = diffusionMatch[4]

View File

@ -9,14 +9,14 @@ export class MockIntersectionObserver implements IntersectionObserver {
public readonly rootMargin: string
public readonly thresholds: readonly number[]
constructor(private cb: IntersectionObserverCallback, options?: IntersectionObserverInit) {
constructor(private callback: IntersectionObserverCallback, options?: IntersectionObserverInit) {
this.root = options?.root ?? null
this.rootMargin = options?.rootMargin ?? '0px 0px 0px 0px'
this.thresholds = castArray(options?.threshold)
}
public observe(target: Element): void {
this.cb(
this.callback(
[
{
target,

View File

@ -40,7 +40,7 @@ describe('<ViewOnSourcegraphButton />', () => {
expect(root!).toMatchSnapshot()
})
it('renders a link with the rev when provided', () => {
it('renders a link with the revision when provided', () => {
let root: ReactTestRenderer
renderer.act(() => {
root = renderer.create(
@ -49,7 +49,7 @@ describe('<ViewOnSourcegraphButton />', () => {
sourcegraphURL="https://test.com"
getContext={() => ({
rawRepoName: 'test',
rev: 'test',
revision: 'test',
privateRepository: false,
})}
className="test"
@ -72,7 +72,7 @@ describe('<ViewOnSourcegraphButton />', () => {
sourcegraphURL="https://sourcegraph.com"
getContext={() => ({
rawRepoName: 'test',
rev: 'test',
revision: 'test',
privateRepository: false,
})}
className="test"
@ -94,7 +94,7 @@ describe('<ViewOnSourcegraphButton />', () => {
sourcegraphURL="https://sourcegraph.test"
getContext={() => ({
rawRepoName: 'test',
rev: 'test',
revision: 'test',
privateRepository: false,
})}
className="test"
@ -121,7 +121,7 @@ describe('<ViewOnSourcegraphButton />', () => {
sourcegraphURL="https://test.com"
getContext={() => ({
rawRepoName: 'test',
rev: 'test',
revision: 'test',
privateRepository: false,
})}
showSignInButton={true}
@ -147,7 +147,7 @@ describe('<ViewOnSourcegraphButton />', () => {
sourcegraphURL="https://test.com"
getContext={() => ({
rawRepoName: 'test',
rev: 'test',
revision: 'test',
privateRepository: false,
})}
showSignInButton={true}

View File

@ -55,8 +55,8 @@ export const ViewOnSourcegraphButton: React.FunctionComponent<ViewOnSourcegraphB
return null
}
const { rawRepoName, rev } = getContext()
const url = new URL(`/${rawRepoName}${rev ? `@${rev}` : ''}`, sourcegraphURL).href
const { rawRepoName, revision } = getContext()
const url = new URL(`/${rawRepoName}${revision ? `@${revision}` : ''}`, sourcegraphURL).href
if (isErrorLike(repoExistsOrError)) {
// If the problem is the user is not signed in, show a sign in CTA (if not shown elsewhere)

View File

@ -210,7 +210,7 @@ exports[`<ViewOnSourcegraphButton /> repository exists on the instance renders a
</a>
`;
exports[`<ViewOnSourcegraphButton /> repository exists on the instance renders a link with the rev when provided 1`] = `
exports[`<ViewOnSourcegraphButton /> repository exists on the instance renders a link with the revision when provided 1`] = `
<a
aria-label="View repository on Sourcegraph"
className="open-on-sourcegraph test"

View File

@ -14,7 +14,7 @@ import { SuccessGraphQLResult } from '../../../../../shared/src/graphql/graphql'
import { IQuery } from '../../../../../shared/src/graphql/schema'
import { NOOP_TELEMETRY_SERVICE } from '../../../../../shared/src/telemetry/telemetryService'
import { resetAllMemoizationCaches } from '../../../../../shared/src/util/memoizeObservable'
import { isDefined, subTypeOf, allOf, check, isTaggedUnionMember } from '../../../../../shared/src/util/types'
import { isDefined, subtypeOf, allOf, check, isTaggedUnionMember } from '../../../../../shared/src/util/types'
import { DEFAULT_SOURCEGRAPH_URL } from '../../util/context'
import { MutationRecordLike } from '../../util/dom'
import {
@ -51,10 +51,10 @@ const elementRenderedAtMount = (mount: Element): renderer.ReactTestRendererJSON
const scheduler = (): TestScheduler => new TestScheduler((a, b) => expect(a).toEqual(b))
const createTestElement = (): HTMLElement => {
const el = document.createElement('div')
el.className = `test test-${uniqueId()}`
document.body.append(el)
return el
const element = document.createElement('div')
element.className = `test test-${uniqueId()}`
document.body.append(element)
return element
}
jest.mock('uuid', () => ({
@ -80,8 +80,8 @@ const createMockPlatformContext = (
...partialMocks,
})
const commonArgs = () =>
subTypeOf<Partial<HandleCodeHostOptions>>()({
const commonArguments = () =>
subtypeOf<Partial<HandleCodeHostOptions>>()({
mutations: of([{ addedNodes: [document.body], removedNodes: [] }]),
showGlobalDebug: false,
platformContext: createMockPlatformContext(),
@ -134,7 +134,7 @@ describe('codeHost', () => {
const { services } = await integrationTestContext()
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -157,7 +157,7 @@ describe('codeHost', () => {
const commandPaletteMount = createTestElement()
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -177,7 +177,7 @@ describe('codeHost', () => {
const { services } = await integrationTestContext()
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -208,7 +208,7 @@ describe('codeHost', () => {
}
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -281,7 +281,7 @@ describe('codeHost', () => {
codeView.append(line)
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -395,7 +395,7 @@ describe('codeHost', () => {
}
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -435,7 +435,7 @@ describe('codeHost', () => {
const baseEditor = viewers.find(
allOf(
isTaggedUnionMember('type', 'CodeEditor' as const),
check(e => e.document.uri === 'git://foo?1#/bar.ts')
check(editor => editor.document.uri === 'git://foo?1#/bar.ts')
)
)!
const baseDecorations = [
@ -469,7 +469,7 @@ describe('codeHost', () => {
const headEditor = viewers.find(
allOf(
isTaggedUnionMember('type', 'CodeEditor' as const),
check(e => e.document.uri === 'git://foo?2#/bar.ts')
check(editor => editor.document.uri === 'git://foo?2#/bar.ts')
)
)!
const headDecorations = [
@ -545,7 +545,7 @@ describe('codeHost', () => {
])
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
mutations,
codeHost: {
type: 'github',
@ -624,7 +624,7 @@ describe('codeHost', () => {
}
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -667,7 +667,7 @@ describe('codeHost', () => {
}
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -724,7 +724,7 @@ describe('codeHost', () => {
}
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -775,7 +775,7 @@ describe('codeHost', () => {
}
subscriptions.add(
handleCodeHost({
...commonArgs(),
...commonArguments(),
codeHost: {
type: 'github',
name: 'GitHub',
@ -843,7 +843,7 @@ describe('codeHost', () => {
})
test('emits a custom mount location if a node matching the selector is in addedNodes()', () => {
const el = createTestElement()
const element = createTestElement()
scheduler().run(({ cold, expectObservable }) => {
expectObservable(
observeHoverOverlayMountLocation(
@ -851,7 +851,7 @@ describe('codeHost', () => {
cold<MutationRecordLike[]>('-b', {
b: [
{
addedNodes: [el],
addedNodes: [element],
removedNodes: [],
},
],
@ -859,16 +859,16 @@ describe('codeHost', () => {
)
).toBe('ab', {
a: document.body,
b: el,
b: element,
})
})
})
test('emits a custom mount location if a node matching the selector is nested in an addedNode', () => {
const el = createTestElement()
const element = createTestElement()
const nested = document.createElement('div')
nested.classList.add('nested')
el.append(nested)
element.append(nested)
scheduler().run(({ cold, expectObservable }) => {
expectObservable(
observeHoverOverlayMountLocation(
@ -876,7 +876,7 @@ describe('codeHost', () => {
cold<MutationRecordLike[]>('-b', {
b: [
{
addedNodes: [el],
addedNodes: [element],
removedNodes: [],
},
],
@ -890,7 +890,7 @@ describe('codeHost', () => {
})
test('emits document.body if a node matching the selector is removed', () => {
const el = createTestElement()
const element = createTestElement()
scheduler().run(({ cold, expectObservable }) => {
expectObservable(
observeHoverOverlayMountLocation(
@ -898,21 +898,21 @@ describe('codeHost', () => {
cold<MutationRecordLike[]>('-bc', {
b: [
{
addedNodes: [el],
addedNodes: [element],
removedNodes: [],
},
],
c: [
{
addedNodes: [],
removedNodes: [el],
removedNodes: [element],
},
],
})
)
).toBe('abc', {
a: document.body,
b: el,
b: element,
c: document.body,
})
})

View File

@ -73,8 +73,8 @@ import {
UIPositionSpec,
RawRepoSpec,
RepoSpec,
ResolvedRevSpec,
RevSpec,
ResolvedRevisionSpec,
RevisionSpec,
toRootURI,
toURIWithPath,
ViewStateSpec,
@ -82,9 +82,9 @@ import {
import { observeStorageKey } from '../../../browser-extension/web-extension-api/storage'
import { isInPage } from '../../context'
import { SourcegraphIntegrationURLs, BrowserPlatformContext } from '../../platform/context'
import { toTextDocumentIdentifier, toTextDocumentPositionParams } from '../../backend/ext-api-conversion'
import { toTextDocumentIdentifier, toTextDocumentPositionParameters } from '../../backend/extension-api-conversion'
import { CodeViewToolbar, CodeViewToolbarClassProps } from '../../components/CodeViewToolbar'
import { resolveRev, retryWhenCloneInProgressError } from '../../repo/backend'
import { resolveRevision, retryWhenCloneInProgressError } from '../../repo/backend'
import { EventLogger } from '../../tracking/eventLogger'
import { MutationRecordLike, querySelectorOrSelf } from '../../util/dom'
import { featureFlags } from '../../util/featureFlags'
@ -135,7 +135,7 @@ export type MountGetter = (container: HTMLElement) => HTMLElement | null
/**
* The context the code host is in on the current page.
*/
export type CodeHostContext = RawRepoSpec & Partial<RevSpec> & { privateRepository: boolean }
export type CodeHostContext = RawRepoSpec & Partial<RevisionSpec> & { privateRepository: boolean }
type CodeHostType = 'github' | 'phabricator' | 'bitbucket-server' | 'gitlab'
@ -232,7 +232,7 @@ export interface CodeHost extends ApplyLinkPreviewOptions {
*/
urlToFile?: (
sourcegraphURL: string,
target: RepoSpec & RawRepoSpec & RevSpec & FileSpec & Partial<UIPositionSpec> & Partial<ViewStateSpec>,
target: RepoSpec & RawRepoSpec & RevisionSpec & FileSpec & Partial<UIPositionSpec> & Partial<ViewStateSpec>,
context: URLToFileContext
) => string
@ -276,9 +276,9 @@ export interface FileInfo {
*/
commitID: string
/**
* The revision the code view is at. If a `baseRev` is provided, this value is treated as the head rev.
* The revision the code view is at. If a `baseRev` is provided, this value is treated as the head revision.
*/
rev?: string
revision?: string
/**
* The repo name for the BASE side of a diff. This is useful for Phabricator
* staging areas since they are separate repos.
@ -295,7 +295,7 @@ export interface FileInfo {
/**
* Revision for the BASE side of the diff.
*/
baseRev?: string
baseRevision?: string
}
export interface FileInfoWithRepoNames extends FileInfo, RepoSpec {
@ -349,7 +349,7 @@ function initCodeIntelligence({
mutations: Observable<MutationRecordLike[]>
}): {
hoverifier: Hoverifier<
RepoSpec & RevSpec & FileSpec & ResolvedRevSpec,
RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec,
HoverData<ExtensionHoverAlertType>,
ActionItemAction
>
@ -378,7 +378,7 @@ function initCodeIntelligence({
// Code views come and go, but there is always a single hoverifier on the page
const hoverifier = createHoverifier<
RepoSpec & RevSpec & FileSpec & ResolvedRevSpec,
RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec,
HoverData<ExtensionHoverAlertType>,
ActionItemAction
>({
@ -392,7 +392,7 @@ function initCodeIntelligence({
getHover: ({ line, character, part, ...rest }) =>
combineLatest([
extensionsController.services.textDocumentHover.getHover(
toTextDocumentPositionParams({ ...rest, position: { line, character } })
toTextDocumentPositionParameters({ ...rest, position: { line, character } })
),
getActiveHoverAlerts(hoverAlerts),
]).pipe(
@ -666,8 +666,8 @@ export function handleCodeHost({
const repoExistsOrErrors = signInCloses.pipe(
startWith(null),
switchMap(() => {
const { rawRepoName, rev } = getContext()
return resolveRev({ repoName: rawRepoName, rev, requestGraphQL }).pipe(
const { rawRepoName, revision } = getContext()
return resolveRevision({ repoName: rawRepoName, revision, requestGraphQL }).pipe(
retryWhenCloneInProgressError(),
mapTo(true),
catchError(error => {
@ -738,12 +738,12 @@ export function handleCodeHost({
}))
)
),
catchError(err => {
catchError(error => {
// Ignore PrivateRepoPublicSourcegraph errors (don't initialize those code views)
if (isPrivateRepoPublicSourcegraphComErrorLike(err)) {
if (isPrivateRepoPublicSourcegraphComErrorLike(error)) {
return EMPTY
}
throw err
throw error
}),
tap({
error: error => {
@ -791,7 +791,7 @@ export function handleCodeHost({
)
/** Map from workspace URI to number of editors referencing it */
const rootRefCounts = new Map<string, number>()
const rootReferenceCounts = new Map<string, number>()
/**
* Adds root referenced by a code editor to the worskpace.
@ -799,9 +799,9 @@ export function handleCodeHost({
* Will only cause `workspace.roots` to emit if no root with
* the given `uri` existed.
*/
const addRootRef = (uri: string, inputRevision: string | undefined): void => {
rootRefCounts.set(uri, (rootRefCounts.get(uri) || 0) + 1)
if (rootRefCounts.get(uri) === 1) {
const addRootReference = (uri: string, inputRevision: string | undefined): void => {
rootReferenceCounts.set(uri, (rootReferenceCounts.get(uri) || 0) + 1)
if (rootReferenceCounts.get(uri) === 1) {
extensionsController.services.workspace.roots.next([
...extensionsController.services.workspace.roots.value,
{ uri, inputRevision },
@ -815,18 +815,18 @@ export function handleCodeHost({
* Will only cause `workspace.roots` to emit if the root
* with the given `uri` has no more references.
*/
const deleteRootRef = (uri: string): void => {
const currentRefCount = rootRefCounts.get(uri)
if (!currentRefCount) {
const deleteRootReference = (uri: string): void => {
const currentReferenceCount = rootReferenceCounts.get(uri)
if (!currentReferenceCount) {
throw new Error(`No preexisting root refs for uri ${uri}`)
}
const updatedRefCount = currentRefCount - 1
if (updatedRefCount === 0) {
const updatedReferenceCount = currentReferenceCount - 1
if (updatedReferenceCount === 0) {
extensionsController.services.workspace.roots.next(
extensionsController.services.workspace.roots.value.filter(root => root.uri !== uri)
)
} else {
rootRefCounts.set(uri, updatedRefCount)
rootReferenceCounts.set(uri, updatedReferenceCount)
}
}
@ -857,9 +857,9 @@ export function handleCodeHost({
model,
}
const rootURI = toRootURI(fileInfo)
addRootRef(rootURI, fileInfo.rev)
addRootReference(rootURI, fileInfo.revision)
codeViewEvent.subscriptions.add(() => {
deleteRootRef(rootURI)
deleteRootReference(rootURI)
extensionsController.services.viewer.removeViewer(editorId)
})
@ -900,9 +900,9 @@ export function handleCodeHost({
repoName: fileInfo.baseRepoName,
commitID: fileInfo.baseCommitID,
})
addRootRef(baseRootURI, fileInfo.baseRev)
addRootReference(baseRootURI, fileInfo.baseRevision)
codeViewEvent.subscriptions.add(() => {
deleteRootRef(baseRootURI)
deleteRootReference(baseRootURI)
extensionsController.services.viewer.removeViewer(editor)
})
}
@ -981,11 +981,16 @@ export function handleCodeHost({
}
// Add hover code intelligence
const resolveContext: ContextResolver<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec> = ({ part }) => ({
const resolveContext: ContextResolver<RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec> = ({
part,
}) => ({
repoName: part === 'base' ? fileInfo.baseRepoName || fileInfo.repoName : fileInfo.repoName,
commitID: part === 'base' ? fileInfo.baseCommitID! : fileInfo.commitID,
filePath: part === 'base' ? fileInfo.baseFilePath || fileInfo.filePath : fileInfo.filePath,
rev: part === 'base' ? fileInfo.baseRev || fileInfo.baseCommitID! : fileInfo.rev || fileInfo.commitID,
revision:
part === 'base'
? fileInfo.baseRevision || fileInfo.baseCommitID!
: fileInfo.revision || fileInfo.commitID,
})
const adjustPosition = getPositionAdjuster?.(platformContext.requestGraphQL)
let hoverSubscription = new Subscription()

View File

@ -225,15 +225,17 @@ export function testDOMFunctions(
beforeEach(setCodeElement)
it('should return correctly whether the first character is a diff indicator', () => {
// Default is false
const is = Boolean(
const actualFirstCharacterIsDiffIndicator = Boolean(
domFunctions.isFirstCharacterDiffIndicator &&
domFunctions.isFirstCharacterDiffIndicator(codeElement)
)
expect(is).toBe(Boolean(firstCharacterIsDiffIndicator))
if (is) {
expect(actualFirstCharacterIsDiffIndicator).toBe(Boolean(firstCharacterIsDiffIndicator))
if (actualFirstCharacterIsDiffIndicator) {
// Check that the first character is truly a diff indicator
const diffIndicators = new Set(['+', '-', ' '])
expect(is).toBe(diffIndicators.has(codeElement.textContent![0]))
expect(actualFirstCharacterIsDiffIndicator).toBe(
diffIndicators.has(codeElement.textContent![0])
)
}
})
})

View File

@ -5,7 +5,7 @@ import { catchError, map, switchMap } from 'rxjs/operators'
import { Omit } from 'utility-types'
import { isPrivateRepoPublicSourcegraphComErrorLike } from '../../../../../shared/src/backend/errors'
import { PlatformContext } from '../../../../../shared/src/platform/context'
import { FileSpec, RepoSpec, ResolvedRevSpec, RevSpec } from '../../../../../shared/src/util/url'
import { FileSpec, RepoSpec, ResolvedRevisionSpec, RevisionSpec } from '../../../../../shared/src/util/url'
import { ButtonProps } from '../../components/CodeViewToolbar'
import { fetchBlobContentLines } from '../../repo/backend'
import { CodeHost, FileInfo, FileInfoWithRepoNames } from './codeHost'
@ -57,7 +57,7 @@ export interface CodeView {
*/
getPositionAdjuster?: (
requestGraphQL: PlatformContext['requestGraphQL']
) => PositionAdjuster<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec>
) => PositionAdjuster<RepoSpec & RevisionSpec & FileSpec & ResolvedRevisionSpec>
/** Props for styling the buttons in the `CodeViewToolbar`. */
toolbarButtonProps?: ButtonProps
/**
@ -134,10 +134,10 @@ export const fetchFileContents = (
catchError(() => [info])
)
}),
catchError(err => {
if (isPrivateRepoPublicSourcegraphComErrorLike(err)) {
catchError(error => {
if (isPrivateRepoPublicSourcegraphComErrorLike(error)) {
return [info]
}
throw err
throw error
})
)

View File

@ -21,10 +21,10 @@ describe('contentViews', () => {
})
const createTestElement = (): HTMLElement => {
const el = document.createElement('div')
el.className = `test test-${uniqueId()}`
document.body.append(el)
return el
const element = document.createElement('div')
element.className = `test test-${uniqueId()}`
document.body.append(element)
return element
}
test('detects addition, mutation, and removal of content views (and annotates them)', async () => {
@ -75,8 +75,8 @@ describe('contentViews', () => {
},
{
contentViewResolvers: [{ selector: 'div', resolveView: () => ({ element }) }],
setElementTooltip: (e, text) =>
text !== null ? (e.dataset.tooltip = text) : e.removeAttribute('data-tooltip'),
setElementTooltip: (element, text) =>
text !== null ? (element.dataset.tooltip = text) : element.removeAttribute('data-tooltip'),
}
)
)
@ -124,8 +124,8 @@ describe('contentViews', () => {
},
{
contentViewResolvers: [{ selector: 'div', resolveView: () => ({ element }) }],
setElementTooltip: (e, text) =>
text !== null ? (e.dataset.tooltip = text) : e.removeAttribute('data-tooltip'),
setElementTooltip: (element, text) =>
text !== null ? (element.dataset.tooltip = text) : element.removeAttribute('data-tooltip'),
}
)
)

View File

@ -182,7 +182,7 @@ export const applyDecorations = (
if (decoration.after) {
const style = decorationAttachmentStyleForTheme(decoration.after, IS_LIGHT_THEME)
const linkTo = (url: string) => (e: HTMLElement): HTMLElement => {
const linkTo = (url: string) => (element: HTMLElement): HTMLElement => {
const link = document.createElement('a')
link.setAttribute('href', url)
@ -194,7 +194,7 @@ export const applyDecorations = (
link.setAttribute('rel', 'noreferrer noopener')
link.style.color = style.color || ''
link.append(e)
link.append(element)
return link
}

View File

@ -24,8 +24,8 @@ export function getActiveHoverAlerts(
map(alerts => (dismissedAlerts ? alerts.filter(({ type }) => !dismissedAlerts[type]) : alerts))
)
),
catchError(err => {
console.error('Error getting hover alerts', err)
catchError(error => {
console.error('Error getting hover alerts', error)
return [undefined]
}),
startWith([])

View File

@ -64,7 +64,7 @@ export function nativeTooltipsEnabledFromSettings(settings: PlatformContext['set
map(({ final }) => final),
filter(isDefined),
filter(isNot<ErrorLike | Settings, ErrorLike>(isErrorLike)),
map(s => !!s['codeHost.useNativeTooltips']),
map(settings => !!settings['codeHost.useNativeTooltips']),
distinctUntilChanged((a, b) => isEqual(a, b)),
publishReplay(1),
refCount()

View File

@ -33,10 +33,10 @@ describe('textFields', () => {
})
const createTestElement = (): HTMLTextAreaElement => {
const el = document.createElement('textarea')
el.className = `test test-${uniqueId()}`
document.body.append(el)
return el
const element = document.createElement('textarea')
element.className = `test test-${uniqueId()}`
document.body.append(element)
return element
}
test('detects addition and removal of text fields', async () => {

View File

@ -3,7 +3,7 @@ import { catchError, map } from 'rxjs/operators'
import { isPrivateRepoPublicSourcegraphComErrorLike } from '../../../../../../shared/src/backend/errors'
import { PlatformContext } from '../../../../../../shared/src/platform/context'
import { resolveRepo, resolveRev, retryWhenCloneInProgressError } from '../../../repo/backend'
import { resolveRepo, resolveRevision, retryWhenCloneInProgressError } from '../../../repo/backend'
import { FileInfo, FileInfoWithRepoNames } from '../codeHost'
export const ensureRevisionsAreCloned = (
@ -15,18 +15,18 @@ export const ensureRevisionsAreCloned = (
// revision cloned.
// Head
const resolvingHeadRev = resolveRev({ repoName, rev: commitID, requestGraphQL }).pipe(
const resolvingHeadRevision = resolveRevision({ repoName, revision: commitID, requestGraphQL }).pipe(
retryWhenCloneInProgressError()
)
const requests = [resolvingHeadRev]
const requests = [resolvingHeadRevision]
// If theres a base, resolve it as well.
if (baseCommitID) {
const resolvingBaseRev = resolveRev({ repoName, rev: baseCommitID, requestGraphQL }).pipe(
const resolvingBaseRevision = resolveRevision({ repoName, revision: baseCommitID, requestGraphQL }).pipe(
retryWhenCloneInProgressError()
)
requests.push(resolvingBaseRev)
requests.push(resolvingBaseRevision)
}
return zip(...requests).pipe(map(() => ({ repoName, commitID, baseCommitID, ...rest })))
@ -52,11 +52,11 @@ export const resolveRepoNames = (
// without having pointed his browser extension to a self-hosted Sourcegraph instance that
// has access to that code. In that case, it's impossible to resolve the repo names,
// so we keep the repo names inferred from the code host's DOM.
catchError(err => {
if (isPrivateRepoPublicSourcegraphComErrorLike(err)) {
catchError(error => {
if (isPrivateRepoPublicSourcegraphComErrorLike(error)) {
return [{ rawRepoName, baseRawRepoName, repoName: rawRepoName, baseRepoName: baseRawRepoName, ...rest }]
}
throw err
throw error
})
)
}

View File

@ -190,11 +190,11 @@ describe('trackViews()', () => {
.pipe(
trackViews([{ selector: '.view', resolveView: element => ({ element }) }]),
bufferCount(3),
switchMap(async ([v1, v2, v3]) => {
switchMap(async ([view1, view2, view3]) => {
const v2Removed = sinon.spy(() => undefined)
v2.subscriptions.add(v2Removed)
const v1Removed = new Promise(resolve => v1.subscriptions.add(resolve))
const v3Removed = new Promise(resolve => v3.subscriptions.add(resolve))
view2.subscriptions.add(v2Removed)
const v1Removed = new Promise(resolve => view1.subscriptions.add(resolve))
const v3Removed = new Promise(resolve => view3.subscriptions.add(resolve))
await Promise.all([v1Removed, v3Removed])
sinon.assert.notCalled(v2Removed)
})
@ -288,8 +288,8 @@ describe('delayUntilIntersecting()', () => {
subscriptions.add(
from(views)
.pipe(
delayUntilIntersecting({}, cb => {
observerCallback = cb
delayUntilIntersecting({}, callback => {
observerCallback = callback
return {
observe,
unobserve,

View File

@ -120,20 +120,20 @@ export type IntersectionObserverLike = Pick<IntersectionObserver, 'observe' | 'u
export function delayUntilIntersecting<T extends View>(
options: IntersectionObserverInit,
createIntersectionObserver = (
cb: IntersectionObserverCallbackLike,
callback: IntersectionObserverCallbackLike,
options: IntersectionObserverInit
): IntersectionObserverLike => new IntersectionObserver(cb, options)
): IntersectionObserverLike => new IntersectionObserver(callback, options)
): OperatorFunction<ViewWithSubscriptions<T>, ViewWithSubscriptions<T>> {
return views =>
new Observable(viewObserver => {
const subscriptions = new Subscription()
const delayedViews = new Map<HTMLElement, ViewWithSubscriptions<T>>()
const intersectionObserver = createIntersectionObserver((entries, obs) => {
const intersectionObserver = createIntersectionObserver((entries, observer) => {
for (const entry of entries) {
const target = entry.target as HTMLElement
if (entry.isIntersecting && delayedViews.get(target)) {
viewObserver.next(delayedViews.get(target))
obs.unobserve(entry.target)
observer.unobserve(entry.target)
delayedViews.delete(target)
}
}

View File

@ -80,15 +80,15 @@ export const CodeViewToolbar: React.FunctionComponent<CodeViewToolbarProps> = pr
sourcegraphURL: props.sourcegraphURL,
repoName: props.fileInfoOrError.baseRepoName || props.fileInfoOrError.repoName,
filePath: props.fileInfoOrError.baseFilePath || props.fileInfoOrError.filePath,
rev: props.fileInfoOrError.baseRev || props.fileInfoOrError.baseCommitID,
revision: props.fileInfoOrError.baseRevision || props.fileInfoOrError.baseCommitID,
query: {
diff: {
rev: props.fileInfoOrError.baseCommitID,
revision: props.fileInfoOrError.baseCommitID,
},
},
commit: {
baseRev: props.fileInfoOrError.baseRev || props.fileInfoOrError.baseCommitID,
headRev: props.fileInfoOrError.rev || props.fileInfoOrError.commitID,
baseRev: props.fileInfoOrError.baseRevision || props.fileInfoOrError.baseCommitID,
headRev: props.fileInfoOrError.revision || props.fileInfoOrError.commitID,
},
}}
/>
@ -109,7 +109,7 @@ export const CodeViewToolbar: React.FunctionComponent<CodeViewToolbarProps> = pr
sourcegraphURL: props.sourcegraphURL,
repoName: props.fileInfoOrError.repoName,
filePath: props.fileInfoOrError.filePath,
rev: props.fileInfoOrError.rev || props.fileInfoOrError.commitID,
revision: props.fileInfoOrError.revision || props.fileInfoOrError.commitID,
}}
/>
</li>

View File

@ -47,8 +47,8 @@ export class OpenDiffOnSourcegraph extends React.Component<Props, State> {
// Only include the relevant file diff.
nodes: fileDiff.nodes.filter(node => node.oldPath === this.props.openProps.filePath),
})),
catchError(err => {
console.error(err)
catchError(error => {
console.error(error)
return [undefined]
})
)

View File

@ -27,15 +27,15 @@ export class OpenOnSourcegraph extends React.Component<Props, {}> {
if (props.commit) {
return `${url}/-/compare/${props.commit.baseRev}...${props.commit.headRev}?utm_source=${getPlatformName()}`
}
if (props.rev) {
url = `${url}@${props.rev}`
if (props.revision) {
url = `${url}@${props.revision}`
}
if (props.filePath) {
url = `${url}/-/blob/${props.filePath}`
}
if (props.query) {
if (props.query.diff) {
url = `${url}?diff=${props.query.diff.rev}&utm_source=${getPlatformName()}`
url = `${url}?diff=${props.query.diff.revision}&utm_source=${getPlatformName()}`
} else if (props.query.search) {
url = `${url}?q=${props.query.search}&utm_source=${getPlatformName()}`
}

View File

@ -1,43 +1,43 @@
enum AppEnv {
enum AppEnvironment {
Extension,
Page,
}
enum ScriptEnv {
enum ScriptEnvironment {
Content,
Background,
Options,
}
interface AppContext {
appEnv: AppEnv
scriptEnv: ScriptEnv
appEnvironment: AppEnvironment
scriptEnvironment: ScriptEnvironment
}
function getContext(): AppContext {
const appEnv = window.SG_ENV === 'EXTENSION' ? AppEnv.Extension : AppEnv.Page
const appEnvironment = window.SG_ENV === 'EXTENSION' ? AppEnvironment.Extension : AppEnvironment.Page
let scriptEnv: ScriptEnv = ScriptEnv.Content
if (appEnv === AppEnv.Extension) {
let scriptEnvironment: ScriptEnvironment = ScriptEnvironment.Content
if (appEnvironment === AppEnvironment.Extension) {
if (window.location.pathname.includes('options.html')) {
scriptEnv = ScriptEnv.Options
scriptEnvironment = ScriptEnvironment.Options
} else if (globalThis.browser && browser.runtime.getBackgroundPage) {
scriptEnv = ScriptEnv.Background
scriptEnvironment = ScriptEnvironment.Background
}
}
return {
appEnv,
scriptEnv,
appEnvironment,
scriptEnvironment,
}
}
const ctx = getContext()
const context = getContext()
export const isBackground = ctx.scriptEnv === ScriptEnv.Background
export const isOptions = ctx.scriptEnv === ScriptEnv.Options
export const isBackground = context.scriptEnvironment === ScriptEnvironment.Background
export const isOptions = context.scriptEnvironment === ScriptEnvironment.Options
export const isExtension = ctx.appEnv === AppEnv.Extension
export const isExtension = context.appEnvironment === AppEnvironment.Extension
export const isInPage = !isExtension
export const isPhabricator = Boolean(document.querySelector('.phabricator-wordmark'))

View File

@ -85,7 +85,7 @@ export function browserPortToMessagePort(
const adapterListener = (event: MessageEvent): void => {
const data: Message = event.data
// Message from comlink needing to be forwarded to browser port with MessagePorts removed
const portRefs: PortRef[] = []
const portReferences: PortReference[] = []
// Find message port references and connect to the other side with the found IDs.
for (const { value, path, key, parent } of iteratePropertiesDeep(data)) {
if (value instanceof MessagePort) {
@ -96,11 +96,11 @@ export function browserPortToMessagePort(
const browserPort = connect(prefix + id)
link(browserPort, value)
// Include the ID of the browser port in the message
portRefs.push({ path, id })
portReferences.push({ path, id })
}
}
// Wrap message for the browser port to include all port IDs
const browserPortMessage: BrowserPortMessage = { message: data, portRefs }
const browserPortMessage: BrowserPortMessage = { message: data, portRefs: portReferences }
browserPort.postMessage(browserPortMessage)
@ -115,15 +115,15 @@ export function browserPortToMessagePort(
const browserPortListener = ({ message, portRefs }: BrowserPortMessage): void => {
const transfer: MessagePort[] = []
for (const portRef of portRefs) {
for (const portReference of portRefs) {
const { port1: comlinkMessagePort, port2: intermediateMessagePort } = new MessageChannel()
// Replace the port reference at the given path with a MessagePort that will be transferred.
replaceValueAtPath(message, portRef.path, comlinkMessagePort)
replaceValueAtPath(message, portReference.path, comlinkMessagePort)
transfer.push(comlinkMessagePort)
// Once the port with the mentioned ID is connected, link it up
whenConnected(portRef.id, browserPort => link(browserPort, intermediateMessagePort))
whenConnected(portReference.id, browserPort => link(browserPort, intermediateMessagePort))
}
// Forward message, with MessagePorts
@ -154,7 +154,7 @@ export function browserPortToMessagePort(
}
}
interface PortRef {
interface PortReference {
/** Path at which the MessagePort appeared. */
path: Path
@ -174,7 +174,7 @@ interface BrowserPortMessage {
/**
* Where in the message `MessagePort`s were referenced and the ID of the `browser.runtime.Port` created for each.
*/
portRefs: PortRef[]
portRefs: PortReference[]
}
type Key = string | number | symbol
@ -209,12 +209,12 @@ interface PropertyIteratorEntry<T = unknown> {
* @returns The old value at the path.
*/
function replaceValueAtPath(value: any, path: Path, newValue: unknown): unknown {
const lastProp = path[path.length - 1]
for (const prop of path.slice(0, -1)) {
value = value[prop]
const lastProperty = path[path.length - 1]
for (const property of path.slice(0, -1)) {
value = value[property]
}
const oldValue = value[lastProp]
value[lastProp] = newValue
const oldValue = value[lastProperty]
value[lastProperty] = newValue
return oldValue
}

View File

@ -153,30 +153,30 @@ export function fetchViewerSettings(
* Applies an edit and persists the result to client settings.
*/
export async function editClientSettings(edit: SettingsEdit | string): Promise<void> {
const getNext = (prev: string): string =>
const getNext = (previous: string): string =>
typeof edit === 'string'
? edit
: applyEdits(
prev,
previous,
// TODO(chris): remove `.slice()` (which guards against mutation) once
// https://github.com/Microsoft/node-jsonc-parser/pull/12 is merged in.
setProperty(prev, edit.path.slice(), edit.value, {
setProperty(previous, edit.path.slice(), edit.value, {
tabSize: 2,
insertSpaces: true,
eol: '\n',
})
)
if (isInPage) {
const prev = localStorage.getItem(inPageClientSettingsKey) || ''
const next = getNext(prev)
const previous = localStorage.getItem(inPageClientSettingsKey) || ''
const next = getNext(previous)
localStorage.setItem(inPageClientSettingsKey, next)
return Promise.resolve()
}
const { clientSettings: prev = '{}' } = await storage.sync.get()
const next = getNext(prev)
const { clientSettings: previous = '{}' } = await storage.sync.get()
const next = getNext(previous)
await storage.sync.set({ clientSettings: next })
}

View File

@ -3,7 +3,7 @@ import { delay, filter, map, retryWhen } from 'rxjs/operators'
import {
CloneInProgressError,
RepoNotFoundError,
RevNotFoundError,
RevisionNotFoundError,
isCloneInProgressErrorLike,
} from '../../../../shared/src/backend/errors'
import { dataOrThrowErrors, gql } from '../../../../shared/src/graphql/graphql'
@ -11,7 +11,14 @@ import * as GQL from '../../../../shared/src/graphql/schema'
import { PlatformContext } from '../../../../shared/src/platform/context'
import { createAggregateError } from '../../../../shared/src/util/errors'
import { memoizeObservable } from '../../../../shared/src/util/memoizeObservable'
import { FileSpec, makeRepoURI, RawRepoSpec, RepoSpec, ResolvedRevSpec, RevSpec } from '../../../../shared/src/util/url'
import {
FileSpec,
makeRepoURI,
RawRepoSpec,
RepoSpec,
ResolvedRevisionSpec,
RevisionSpec,
} from '../../../../shared/src/util/url'
/**
* @returns Observable that emits if the repo exists on the instance.
@ -46,39 +53,39 @@ export const resolveRepo = memoizeObservable(
/**
* @returns Observable that emits the commit ID. Errors with a `CloneInProgressError` if the repo is still being cloned.
*/
export const resolveRev = memoizeObservable(
export const resolveRevision = memoizeObservable(
({
requestGraphQL,
...ctx
}: RepoSpec & Partial<RevSpec> & Pick<PlatformContext, 'requestGraphQL'>): Observable<string> =>
...context
}: RepoSpec & Partial<RevisionSpec> & Pick<PlatformContext, 'requestGraphQL'>): Observable<string> =>
from(
requestGraphQL<GQL.IQuery>({
request: gql`
query ResolveRev($repoName: String!, $rev: String!) {
query ResolveRev($repoName: String!, $revision: String!) {
repository(name: $repoName) {
mirrorInfo {
cloned
}
commit(rev: $rev) {
commit(rev: $revision) {
oid
}
}
}
`,
variables: { ...ctx, rev: ctx.rev || '' },
variables: { ...context, revision: context.revision || '' },
mightContainPrivateInfo: true,
})
).pipe(
map(dataOrThrowErrors),
map(({ repository }) => {
if (!repository) {
throw new RepoNotFoundError(ctx.repoName)
throw new RepoNotFoundError(context.repoName)
}
if (!repository.mirrorInfo.cloned) {
throw new CloneInProgressError(ctx.repoName)
throw new CloneInProgressError(context.repoName)
}
if (!repository.commit) {
throw new RevNotFoundError(ctx.rev)
throw new RevisionNotFoundError(context.revision)
}
return repository.commit.oid
})
@ -91,13 +98,13 @@ export function retryWhenCloneInProgressError<T>(): (v: Observable<T>) => Observ
maybeErrors.pipe(
retryWhen(errors =>
errors.pipe(
filter(err => {
if (isCloneInProgressErrorLike(err)) {
filter(error => {
if (isCloneInProgressErrorLike(error)) {
return true
}
// Don't swallow other errors.
throw err
throw error
}),
delay(1000)
)
@ -114,8 +121,8 @@ export function retryWhenCloneInProgressError<T>(): (v: Observable<T>) => Observ
export const fetchBlobContentLines = memoizeObservable(
({
requestGraphQL,
...ctx
}: RepoSpec & ResolvedRevSpec & FileSpec & Pick<PlatformContext, 'requestGraphQL'>): Observable<string[]> =>
...context
}: RepoSpec & ResolvedRevisionSpec & FileSpec & Pick<PlatformContext, 'requestGraphQL'>): Observable<string[]> =>
from(
requestGraphQL<GQL.IQuery>({
request: gql`
@ -129,7 +136,7 @@ export const fetchBlobContentLines = memoizeObservable(
}
}
`,
variables: ctx,
variables: context,
mightContainPrivateInfo: true,
})
).pipe(
@ -139,9 +146,9 @@ export const fetchBlobContentLines = memoizeObservable(
}
if (errors) {
if (errors.length === 1) {
const err = errors[0]
const isFileContent = err.path.join('.') === 'repository.commit.file.content'
const isDNE = err.message.includes('does not exist')
const error = errors[0]
const isFileContent = error.path.join('.') === 'repository.commit.file.content'
const isDNE = error.message.includes('does not exist')
// The error is the file DNE. Just ignore it and pass an empty array
// to represent this.

View File

@ -1,12 +1,12 @@
import { RepoSpec, RevisionSpec } from '../../../../shared/src/util/url'
export interface DiffResolvedRevSpec {
baseCommitID: string
headCommitID: string
}
export interface OpenInSourcegraphProps {
export interface OpenInSourcegraphProps extends RepoSpec, RevisionSpec {
sourcegraphURL: string
repoName: string
rev: string
filePath?: string
commit?: {
baseRev: string
@ -20,7 +20,7 @@ export interface OpenInSourcegraphProps {
query?: {
search?: string
diff?: {
rev: string
revision: string
}
}
withModifierKey?: boolean

View File

@ -37,9 +37,9 @@ const createFeatureFlagStorage = ({ get, set }: FeatureFlagUtilities): FeatureFl
return typeof value === 'boolean' ? value : featureFlagDefaults[key]
},
async toggle<K extends keyof FeatureFlags>(key: K): Promise<boolean> {
const val = await get(key)
await set(key, !val)
return !val
const value = await get(key)
await set(key, !value)
return !value
},
})
@ -48,9 +48,9 @@ async function bextGet<K extends keyof FeatureFlags>(key: K): Promise<boolean |
return featureFlags[key]
}
async function bextSet<K extends keyof FeatureFlags>(key: K, val: FeatureFlags[K]): Promise<void> {
async function bextSet<K extends keyof FeatureFlags>(key: K, value: FeatureFlags[K]): Promise<void> {
const { featureFlags } = await storage.sync.get('featureFlags')
await storage.sync.set({ featureFlags: { ...featureFlags, [key]: val } })
await storage.sync.set({ featureFlags: { ...featureFlags, [key]: value } })
}
const browserExtensionFeatureFlags = createFeatureFlagStorage({
@ -65,8 +65,8 @@ const inPageFeatureFlags = createFeatureFlagStorage({
return value === null ? undefined : value === 'true'
},
// eslint-disable-next-line @typescript-eslint/require-await
set: async (key, val) => {
localStorage.setItem(key, String(val))
set: async (key, value) => {
localStorage.setItem(key, String(value))
},
})

View File

@ -27,6 +27,6 @@
"./build/**/*",
"coverage",
"stories", // TODO fix type errors and include
"src/e2e",
"src/end-to-end",
],
}

View File

@ -8,7 +8,7 @@ import * as path from 'path'
import execa from 'execa'
const mkdtemp = promisify(original_mkdtemp)
const formatDate = (d: Date): string => `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`
const formatDate = (date: Date): string => `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
export async function ensureTrackingIssue({
majorVersion,
@ -48,7 +48,7 @@ export async function ensureTrackingIssue({
per_page: 100,
direction: 'desc',
})
const milestone = milestones.data.filter(m => m.title === milestoneTitle)
const milestone = milestones.data.filter(milestone => milestone.title === milestoneTitle)
if (milestone.length === 0) {
console.log(
`Milestone ${JSON.stringify(
@ -95,15 +95,15 @@ export async function ensurePatchReleaseIssue({
async function getContent(
octokit: Octokit,
params: {
parameters: {
owner: string
repo: string
path: string
}
): Promise<string> {
const resp = await octokit.repos.getContents(params)
const resp = await octokit.repos.getContents(parameters)
if (Array.isArray(resp.data)) {
throw new TypeError(`${params.path} is a directory`)
throw new TypeError(`${parameters.path} is a directory`)
}
return Buffer.from(resp.data.content as string, 'base64').toString()
}
@ -189,7 +189,7 @@ export interface CreateBranchWithChangesOptions {
export async function createBranchWithChanges({
owner,
repo,
base: baseRev,
base: baseRevision,
head: headBranch,
commitMessage,
bashEditCommands,
@ -202,7 +202,7 @@ export async function createBranchWithChanges({
cd ${tmpdir};
git clone --depth 10 git@github.com:${owner}/${repo} || git clone --depth 10 https://github.com/${owner}/${repo};
cd ./${repo};
git checkout ${baseRev};
git checkout ${baseRevision};
${bashEditCommands.join(';\n ')};
git add :/;
git commit -a -m ${JSON.stringify(commitMessage)};

View File

@ -90,12 +90,12 @@ export async function ensureEvent(
async function listEvents(auth: OAuth2Client): Promise<calendar_v3.Schema$Event[] | undefined> {
const calendar = google.calendar({ version: 'v3', auth })
const res = await calendar.events.list({
const result = await calendar.events.list({
calendarId: 'primary',
timeMin: new Date().toISOString(),
maxResults: 2500,
singleEvents: true,
orderBy: 'startTime',
})
return res.data.items
return result.data.items
}

View File

@ -20,12 +20,12 @@ import execa from 'execa'
const sed = process.platform === 'linux' ? 'sed' : 'gsed'
const formatDate = (d: Date): string =>
`${d.toLocaleString('en-US', {
const formatDate = (date: Date): string =>
`${date.toLocaleString('en-US', {
timeZone: 'America/Los_Angeles',
dateStyle: 'medium',
timeStyle: 'short',
} as Intl.DateTimeFormatOptions)} (SF time) / ${d.toLocaleString('en-US', {
} as Intl.DateTimeFormatOptions)} (SF time) / ${date.toLocaleString('en-US', {
timeZone: 'Europe/Berlin',
dateStyle: 'medium',
timeStyle: 'short',
@ -78,7 +78,9 @@ const steps: Step[] = [
({ id, argNames }) =>
'\t' +
id +
(argNames && argNames.length > 0 ? ' ' + argNames.map(n => `<${n}>`).join(' ') : '')
(argNames && argNames.length > 0
? ' ' + argNames.map(argumentName => `<${argumentName}>`).join(' ')
: '')
)
.join('\n')
)
@ -86,13 +88,13 @@ const steps: Step[] = [
},
{
id: '_test:google-calendar',
run: async c => {
run: async config => {
const googleCalendar = await getClient()
await ensureEvent(
{
title: 'TEST EVENT',
startDateTime: new Date(c.releaseDateTime).toISOString(),
endDateTime: addMinutes(new Date(c.releaseDateTime), 1).toISOString(),
startDateTime: new Date(config.releaseDateTime).toISOString(),
endDateTime: addMinutes(new Date(config.releaseDateTime), 1).toISOString(),
},
googleCalendar
)
@ -106,53 +108,53 @@ const steps: Step[] = [
},
{
id: 'add-timeline-to-calendar',
run: async c => {
run: async config => {
const googleCalendar = await getClient()
const events: EventOptions[] = [
{
title: 'Release captain: prepare for branch cut (5 working days until release)',
description: 'See the release tracking issue for TODOs',
startDateTime: new Date(c.fiveWorkingDaysBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(c.fiveWorkingDaysBeforeRelease), 1).toISOString(),
startDateTime: new Date(config.fiveWorkingDaysBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(config.fiveWorkingDaysBeforeRelease), 1).toISOString(),
},
{
title: 'Release captain: branch cut (4 working days until release)',
description: 'See the release tracking issue for TODOs',
startDateTime: new Date(c.fourWorkingDaysBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(c.fourWorkingDaysBeforeRelease), 1).toISOString(),
startDateTime: new Date(config.fourWorkingDaysBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(config.fourWorkingDaysBeforeRelease), 1).toISOString(),
},
...eachDayOfInterval({
start: addDays(new Date(c.fourWorkingDaysBeforeRelease), 1),
end: subDays(new Date(c.oneWorkingDayBeforeRelease), 1),
start: addDays(new Date(config.fourWorkingDaysBeforeRelease), 1),
end: subDays(new Date(config.oneWorkingDayBeforeRelease), 1),
})
.filter(d => !isWeekend(d))
.map(d => ({
.filter(date => !isWeekend(date))
.map(date => ({
title: 'Release captain: cut new release candidate',
description: 'See release tracking issue for TODOs',
startDateTime: d.toISOString(),
endDateTime: addMinutes(d, 1).toISOString(),
startDateTime: date.toISOString(),
endDateTime: addMinutes(date, 1).toISOString(),
})),
{
title: 'Release captain: tag final release (1 working day before release)',
description: 'See the release tracking issue for TODOs',
startDateTime: new Date(c.oneWorkingDayBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(c.oneWorkingDayBeforeRelease), 1).toISOString(),
startDateTime: new Date(config.oneWorkingDayBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(config.oneWorkingDayBeforeRelease), 1).toISOString(),
},
{
title: `Cut release branch ${c.majorVersion}.${c.minorVersion}`,
title: `Cut release branch ${config.majorVersion}.${config.minorVersion}`,
description: '(This is not an actual event to attend, just a calendar marker.)',
anyoneCanAddSelf: true,
attendees: [c.teamEmail],
startDateTime: new Date(c.fourWorkingDaysBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(c.fourWorkingDaysBeforeRelease), 1).toISOString(),
attendees: [config.teamEmail],
startDateTime: new Date(config.fourWorkingDaysBeforeRelease).toISOString(),
endDateTime: addMinutes(new Date(config.fourWorkingDaysBeforeRelease), 1).toISOString(),
},
{
title: `Release Sourcegraph ${c.majorVersion}.${c.minorVersion}`,
title: `Release Sourcegraph ${config.majorVersion}.${config.minorVersion}`,
description: '(This is not an actual event to attend, just a calendar marker.)',
anyoneCanAddSelf: true,
attendees: [c.teamEmail],
startDateTime: new Date(c.releaseDateTime).toISOString(),
endDateTime: addMinutes(new Date(c.releaseDateTime), 1).toISOString(),
attendees: [config.teamEmail],
startDateTime: new Date(config.releaseDateTime).toISOString(),
endDateTime: addMinutes(new Date(config.releaseDateTime), 1).toISOString(),
},
]
@ -187,25 +189,25 @@ const steps: Step[] = [
},
{
id: 'tracking-issue:announce',
run: async c => {
run: async config => {
const trackingIssueURL = await getIssueByTitle(
await getAuthenticatedGitHubClient(),
trackingIssueTitle(c.majorVersion, c.minorVersion)
trackingIssueTitle(config.majorVersion, config.minorVersion)
)
if (!trackingIssueURL) {
throw new Error(
`Tracking issue for version ${c.majorVersion}.${c.minorVersion} not found--has it been create yet?`
`Tracking issue for version ${config.majorVersion}.${config.minorVersion} not found--has it been create yet?`
)
}
await postMessage(
`:captain: ${c.majorVersion}.${c.minorVersion} Release :captain:
Release captain: @${c.captainSlackUsername}
`:captain: ${config.majorVersion}.${config.minorVersion} Release :captain:
Release captain: @${config.captainSlackUsername}
Tracking issue: ${trackingIssueURL}
Key dates:
- Release branch cut, testing commences: ${formatDate(new Date(c.fourWorkingDaysBeforeRelease))}
- Final release tag: ${formatDate(new Date(c.oneWorkingDayBeforeRelease))}
- Release: ${formatDate(new Date(c.releaseDateTime))}`,
c.slackAnnounceChannel
- Release branch cut, testing commences: ${formatDate(new Date(config.fourWorkingDaysBeforeRelease))}
- Final release tag: ${formatDate(new Date(config.oneWorkingDayBeforeRelease))}
- Release: ${formatDate(new Date(config.releaseDateTime))}`,
config.slackAnnounceChannel
)
},
},
@ -232,13 +234,13 @@ Key dates:
},
{
id: 'release-candidate:dev-announce',
run: async (c, version) => {
run: async (config, version) => {
const parsedVersion = semver.parse(version, { loose: false })
if (!parsedVersion) {
throw new Error(`version ${version} is not valid semver`)
}
const query = `is:open is:issue milestone:${c.majorVersion}.${c.minorVersion} label:release-blocker`
const query = `is:open is:issue milestone:${config.majorVersion}.${config.minorVersion} label:release-blocker`
const issues = await listIssues(await getAuthenticatedGitHubClient(), query)
const issuesURL = `https://github.com/issues?q=${encodeURIComponent(query)}`
const releaseBlockerMessage =
@ -255,7 +257,7 @@ Key dates:
- It will be deployed to k8s.sgdev.org within approximately one hour (https://k8s.sgdev.org/site-admin/updates)
- ${releaseBlockerMessage}
`
await postMessage(message, c.slackAnnounceChannel)
await postMessage(message, config.slackAnnounceChannel)
},
},
{
@ -299,11 +301,11 @@ Key dates:
throw new Error(`version ${version} is pre-release`)
}
const requiredCommands = ['comby', sed, 'find']
for (const cmd of requiredCommands) {
for (const command of requiredCommands) {
try {
await commandExists(cmd)
await commandExists(command)
} catch {
throw new Error(`Required command ${cmd} does not exist`)
throw new Error(`Required command ${command} does not exist`)
}
}
@ -361,13 +363,13 @@ Key dates:
},
]
async function run(config: Config, stepIDToRun: StepID, ...stepArgs: string[]): Promise<void> {
async function run(config: Config, stepIDToRun: StepID, ...stepArguments: string[]): Promise<void> {
await Promise.all(
steps
.filter(({ id }) => id === stepIDToRun)
.map(async step => {
if (step.run) {
await step.run(config, ...stepArgs)
await step.run(config, ...stepArguments)
}
})
)
@ -389,8 +391,8 @@ async function main(): Promise<void> {
console.error('Unrecognized step', JSON.stringify(step))
return
}
const stepArgs = args.slice(1)
await run(config, step as StepID, ...stepArgs)
const stepArguments = args.slice(1)
await run(config, step as StepID, ...stepArguments)
}
main().catch(error => console.error(error))

View File

@ -19,11 +19,11 @@ export async function readLine(prompt: string, cacheFile?: string): Promise<stri
}
async function readLineNoCache(prompt: string): Promise<string> {
const rl = readline.createInterface({
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
const userInput = await new Promise<string>(resolve => rl.question(prompt, resolve))
rl.close()
const userInput = await new Promise<string>(resolve => readlineInterface.question(prompt, resolve))
readlineInterface.close()
return userInput
}

View File

@ -20,8 +20,8 @@
"graphql": "gulp graphQLTypes",
"graphql-lint": "graphql-schema-linter --old-implements-syntax --comment-descriptions cmd/frontend/graphqlbackend/schema.graphql",
"prepublish": "gulp generate",
"test": "jest --testPathIgnorePatterns e2e regression integration storybook",
"test-e2e": "TS_NODE_PROJECT=web/src/e2e/tsconfig.json mocha ./web/src/e2e/e2e.test.ts",
"test": "jest --testPathIgnorePatterns end-to-end regression integration storybook",
"test-e2e": "TS_NODE_PROJECT=web/src/end-to-end/tsconfig.json mocha ./web/src/end-to-end/end-to-end.test.ts",
"cover-e2e": "nyc --hook-require=false yarn test-e2e",
"storybook": "start-storybook -p 9001 -c .storybook",
"build-storybook": "build-storybook -c .storybook",
@ -81,7 +81,7 @@
"@percy/storybook": "^3.3.0",
"@slack/web-api": "^5.8.1",
"@sourcegraph/babel-plugin-transform-react-hot-loader-wrapper": "^1.0.0",
"@sourcegraph/eslint-config": "^0.18.4",
"@sourcegraph/eslint-config": "^0.19.1",
"@sourcegraph/prettierrc": "^3.0.3",
"@sourcegraph/stylelint-config": "^1.1.9",
"@sourcegraph/tsconfig": "^4.0.1",

View File

@ -1 +1,2 @@
out/
src/graphql/schema.ts

View File

@ -3,7 +3,7 @@ module.exports = {
extends: '../.eslintrc.js',
parserOptions: {
...baseConfig.parserOptions,
project: [__dirname + '/tsconfig.json', __dirname + '/src/e2e/tsconfig.json'],
project: [__dirname + '/tsconfig.json', __dirname + '/src/testing/tsconfig.json'],
},
rules: {
'no-restricted-imports': [

View File

@ -120,7 +120,7 @@ describe('ActionItem', () => {
// Finish execution. (Use setTimeout to wait for the executeCommand resolution to result in the setState
// call.)
done()
await new Promise<void>(r => setTimeout(r))
await new Promise<void>(resolve => setTimeout(resolve))
tree = component.toJSON()
expect(tree).toMatchSnapshot()
})
@ -149,7 +149,7 @@ describe('ActionItem', () => {
// Finish execution. (Use setTimeout to wait for the executeCommand resolution to result in the setState
// call.)
done()
await new Promise<void>(r => setTimeout(r))
await new Promise<void>(resolve => setTimeout(resolve))
tree = component.toJSON()
expect(tree).toMatchSnapshot()
})
@ -174,7 +174,7 @@ describe('ActionItem', () => {
// to result in the setState call.)
let tree = component.toJSON()
tree!.props.onClick({ preventDefault: () => undefined, currentTarget: { blur: () => undefined } })
await new Promise<void>(r => setTimeout(r))
await new Promise<void>(resolve => setTimeout(resolve))
tree = component.toJSON()
expect(tree).toMatchSnapshot()
})
@ -199,7 +199,7 @@ describe('ActionItem', () => {
// to result in the setState call.)
let tree = component.toJSON()
tree!.props.onClick({ preventDefault: () => undefined, currentTarget: { blur: () => undefined } })
await new Promise<void>(r => setTimeout(r))
await new Promise<void>(resolve => setTimeout(resolve))
tree = component.toJSON()
expect(tree).toMatchSnapshot()
})

View File

@ -95,11 +95,13 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
this.subscriptions.add(
this.commandExecutions
.pipe(
mergeMap(params =>
from(this.props.extensionsController.executeCommand(params, this.props.showInlineError)).pipe(
mergeMap(parameters =>
from(
this.props.extensionsController.executeCommand(parameters, this.props.showInlineError)
).pipe(
mapTo(null),
catchError(error => [asError(error)]),
map(c => ({ actionOrError: c })),
map(actionOrError => ({ actionOrError })),
tap(() => {
if (this.props.onDidExecute) {
this.props.onDidExecute(this.props.action.id)
@ -116,18 +118,18 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
)
}
public componentDidUpdate(prevProps: ActionItemProps, prevState: State): void {
public componentDidUpdate(previousProps: ActionItemProps, previousState: State): void {
// If the tooltip changes while it's visible, we need to force-update it to show the new value.
const prevTooltip = prevProps.action.actionItem && prevProps.action.actionItem.description
const previousTooltip = previousProps.action.actionItem && previousProps.action.actionItem.description
const tooltip = this.props.action.actionItem && this.props.action.actionItem.description
const descriptionTooltipChanged = prevTooltip !== tooltip
const descriptionTooltipChanged = previousTooltip !== tooltip
const errorTooltipChanged =
this.props.showInlineError &&
(isErrorLike(prevState.actionOrError) !== isErrorLike(this.state.actionOrError) ||
(isErrorLike(prevState.actionOrError) &&
(isErrorLike(previousState.actionOrError) !== isErrorLike(this.state.actionOrError) ||
(isErrorLike(previousState.actionOrError) &&
isErrorLike(this.state.actionOrError) &&
prevState.actionOrError.message !== this.state.actionOrError.message))
previousState.actionOrError.message !== this.state.actionOrError.message))
if (descriptionTooltipChanged || errorTooltipChanged) {
this.props.platformContext.forceUpdateTooltip()
@ -239,8 +241,8 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
)
}
public runAction = (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>): void => {
const action = (isAltEvent(e) && this.props.altAction) || this.props.action
public runAction = (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>): void => {
const action = (isAltEvent(event) && this.props.altAction) || this.props.action
if (!action.command) {
// Unexpectedly arrived here; noop actions should not have event handlers that trigger
@ -252,7 +254,7 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
this.props.telemetryService.log(action.id)
if (urlForClientCommandOpen(action, this.props.location)) {
if (e.currentTarget.tagName === 'A' && e.currentTarget.hasAttribute('href')) {
if (event.currentTarget.tagName === 'A' && event.currentTarget.hasAttribute('href')) {
// Do not execute the command. The <LinkOrButton>'s default event handler will do what we want (which
// is to open a URL). The only case where this breaks is if both the action and alt action are "open"
// commands; in that case, this only ever opens the (non-alt) action.
@ -270,10 +272,10 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
// If the action we're running is *not* opening a URL by using the event target's default handler, then
// ensure the default event handler for the <LinkOrButton> doesn't run (which might open the URL).
e.preventDefault()
event.preventDefault()
// Do not show focus ring on element after running action.
e.currentTarget.blur()
event.currentTarget.blur()
this.commandExecutions.next({
command: action.command,
@ -302,6 +304,6 @@ function urlForClientCommandOpen(action: Evaluated<ActionContribution>, location
return undefined
}
function isAltEvent(e: React.KeyboardEvent | React.MouseEvent): boolean {
return e.altKey || e.metaKey || e.ctrlKey || ('button' in e && e.button === 1)
function isAltEvent(event: React.KeyboardEvent | React.MouseEvent): boolean {
return event.altKey || event.metaKey || event.ctrlKey || ('button' in event && event.button === 1)
}

View File

@ -55,11 +55,11 @@ export class ActionsContainer extends React.PureComponent<Props, ActionsState> {
this.scopeChanges.next(this.props.scope)
}
public componentDidUpdate(prevProps: Props): void {
if (prevProps.scope !== this.props.scope) {
public componentDidUpdate(previousProps: Props): void {
if (previousProps.scope !== this.props.scope) {
this.scopeChanges.next(this.props.scope)
}
if (prevProps.extraContext !== this.props.extraContext) {
if (previousProps.extraContext !== this.props.extraContext) {
this.extraContextChanges.next(this.props.extraContext)
}
}
@ -84,8 +84,8 @@ export class ActionsContainer extends React.PureComponent<Props, ActionsState> {
private defaultRenderItems = (items: ActionItemAction[]): JSX.Element | null => (
<>
{items.map((item, i) => (
<ActionItem {...this.props} key={i} {...item} />
{items.map((item, index) => (
<ActionItem {...this.props} key={index} {...item} />
))}
</>
)

View File

@ -71,11 +71,11 @@ export class ActionsNavItems extends React.PureComponent<ActionsNavItemsProps, A
this.extraContextChanges.next(this.props.extraContext)
}
public componentDidUpdate(prevProps: ActionsProps): void {
if (prevProps.scope !== this.props.scope) {
public componentDidUpdate(previousProps: ActionsProps): void {
if (previousProps.scope !== this.props.scope) {
this.scopeChanges.next(this.props.scope)
}
if (prevProps.extraContext !== this.props.extraContext) {
if (previousProps.extraContext !== this.props.extraContext) {
this.extraContextChanges.next(this.props.extraContext)
}
}

View File

@ -21,9 +21,11 @@ import { FeatureProviderRegistry } from '../services/registry'
export class ProxySubscription extends Subscription {
constructor(proxy: Pick<ProxyMethods, typeof releaseProxy>) {
super(() => {
const p = proxy
;(proxy as any) = null // null out closure reference to proxy
p[releaseProxy]()
proxy[releaseProxy]()
// Workaround for https://github.com/ReactiveX/rxjs/issues/5464
// Remove when fixed
;(this as any)._unsubscribe = null
})
}
}
@ -76,15 +78,15 @@ export const wrapRemoteObservable = <T>(
proxyObserver = {
[proxyMarker]: true,
next: args[0] || noop,
error: args[1] ? err => args[1](asError(err)) : noop,
error: args[1] ? error => args[1](asError(error)) : noop,
complete: args[2] || noop,
}
} else {
const partialObserver = args[0] || {}
proxyObserver = {
[proxyMarker]: true,
next: partialObserver.next ? val => partialObserver.next(val) : noop,
error: partialObserver.error ? err => partialObserver.error(asError(err)) : noop,
next: partialObserver.next ? value => partialObserver.next(value) : noop,
error: partialObserver.error ? error => partialObserver.error(asError(error)) : noop,
complete: partialObserver.complete ? () => partialObserver.complete() : noop,
}
}
@ -142,11 +144,11 @@ export function registerRemoteProvider<
const subscription = new Subscription()
subscription.add(
registry.registerProvider(registrationOptions, params =>
registry.registerProvider(registrationOptions, parameters =>
// Wrap the remote, proxied Observable in an ordinary Observable
// and add its underlying proxy subscription to our subscription
// to release the proxy when the provider gets unregistered.
wrapRemoteObservable(remoteProviderFunction(params), subscription)
wrapRemoteObservable(remoteProviderFunction(parameters), subscription)
)
)

View File

@ -25,15 +25,15 @@ export class ClientExtensions {
const toDeactivate: ExecutableExtension[] = []
const next: ExecutableExtension[] = []
if (oldExtensions) {
for (const x of oldExtensions) {
const newIndex = toActivate.findIndex(({ id }) => x.id === id)
for (const extension of oldExtensions) {
const newIndex = toActivate.findIndex(({ id }) => extension.id === id)
if (newIndex === -1) {
// Extension is no longer activated
toDeactivate.push(x)
toDeactivate.push(extension)
} else {
// Extension is already activated.
toActivate.splice(newIndex, 1)
next.push(x)
next.push(extension)
}
}
}
@ -43,16 +43,16 @@ export class ClientExtensions {
* {@link activeExtensions} never deactivates extensions, so this will never be
* called (in the current implementation).
*/
for (const x of toDeactivate) {
this.proxy.$deactivateExtension(x.id).catch(error => {
console.warn(`Error deactivating extension ${JSON.stringify(x.id)}:`, error)
for (const extension of toDeactivate) {
this.proxy.$deactivateExtension(extension.id).catch(error => {
console.warn(`Error deactivating extension ${JSON.stringify(extension.id)}:`, error)
})
}
// Activate extensions that haven't yet been activated.
for (const x of toActivate) {
this.proxy.$activateExtension(x.id, x.scriptURL).catch(error => {
console.error(`Error activating extension ${JSON.stringify(x.id)}:`, error)
for (const extension of toActivate) {
this.proxy.$activateExtension(extension.id, extension.scriptURL).catch(error => {
console.error(`Error activating extension ${JSON.stringify(extension.id)}:`, error)
})
}
})

View File

@ -61,7 +61,7 @@ export class ClientViews implements ClientViewsAPI {
combineLatest([
panelView.pipe(
map(data => omit(data, 'component')),
distinctUntilChanged((x, y) => isEqual(x, y))
distinctUntilChanged((a, b) => isEqual(a, b))
),
panelView.pipe(
map(({ component }) => component),
@ -74,11 +74,14 @@ export class ClientViews implements ClientViewsAPI {
return from(this.viewerService.activeViewerUpdates).pipe(
map(getActiveCodeEditorPosition),
switchMap(
(params): ObservableInput<MaybeLoadingResult<Location[]>> => {
if (!params) {
(parameters): ObservableInput<MaybeLoadingResult<Location[]>> => {
if (!parameters) {
return [{ isLoading: false, result: [] }]
}
return this.textDocumentLocations.getLocations(component.locationProvider, params)
return this.textDocumentLocations.getLocations(
component.locationProvider,
parameters
)
}
)
)

View File

@ -54,10 +54,10 @@ export class ClientWindows implements ClientWindowsAPI {
return this.showInput({
message: options?.prompt ? options.prompt : '',
defaultValue: options?.value,
}).then(v =>
}).then(input =>
// TODO(sqs): update the showInput API to unify null/undefined etc between the old internal API and the new
// external API.
v === null ? undefined : v
input === null ? undefined : input
)
}

View File

@ -97,14 +97,14 @@ export async function createExtensionHostClientConnection(
)
const clientWindows = new ClientWindows(
(params: ShowNotificationParams) => services.notifications.showMessages.next({ ...params }),
(params: ShowMessageRequestParams) =>
(parameters: ShowNotificationParams) => services.notifications.showMessages.next({ ...parameters }),
(parameters: ShowMessageRequestParams) =>
new Promise<MessageActionItem | null>(resolve => {
services.notifications.showMessageRequests.next({ ...params, resolve })
services.notifications.showMessageRequests.next({ ...parameters, resolve })
}),
(params: ShowInputParams) =>
(parameters: ShowInputParams) =>
new Promise<string | null>(resolve => {
services.notifications.showInputs.next({ ...params, resolve })
services.notifications.showInputs.next({ ...parameters, resolve })
}),
({ title }: ProgressOptions) => {
const reporter = new Subject<Progress>()

View File

@ -70,26 +70,30 @@ describe('getComputedContextProperty', () => {
test('returns null when there are no code editors', () =>
expect(getComputedContextProperty(undefined, EMPTY_SETTINGS_CASCADE, {}, 'component.type')).toBe(null))
function assertSelection(editor: CodeEditorWithPartialModel, expr: string, expected: Selection): void {
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, expr)).toEqual(expected)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expr}.start`)).toEqual(
function assertSelection(
editor: CodeEditorWithPartialModel,
expression: string,
expected: Selection
): void {
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, expression)).toEqual(expected)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expression}.start`)).toEqual(
expected.start
)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expr}.end`)).toEqual(
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expression}.end`)).toEqual(
expected.end
)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expr}.start.line`)).toBe(
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expression}.start.line`)).toBe(
expected.start.line
)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expr}.start.character`)).toBe(
expected.start.character
)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expr}.end.line`)).toBe(
expect(
getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expression}.start.character`)
).toBe(expected.start.character)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expression}.end.line`)).toBe(
expected.end.line
)
expect(getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expr}.end.character`)).toBe(
expected.end.character
)
expect(
getComputedContextProperty(editor, EMPTY_SETTINGS_CASCADE, {}, `${expression}.end.character`)
).toBe(expected.end.character)
}
test('provides primary selection', () =>

View File

@ -38,8 +38,8 @@ export function getComputedContextProperty(
scope?: ContributionScope
): any {
if (key.startsWith('config.')) {
const prop = key.slice('config.'.length)
const value = isSettingsValid(settings) ? settings.final[prop] : undefined
const property = key.slice('config.'.length)
const value = isSettingsValid(settings) ? settings.final[property] : undefined
// Map undefined to null because an undefined value is treated as "does not exist in
// context" and an error is thrown, which is undesirable for config values (for
// which a falsey null default is useful).
@ -56,8 +56,8 @@ export function getComputedContextProperty(
// TODO(sqs): Define these precisely. If the resource is in a repository, what is the "path"? Is it the
// path relative to the repository's root? If it's a file on disk, then "path" could also mean the
// (absolute) path on the file system. Clear up that ambiguity.
const prop = key.slice('resource.'.length)
switch (prop) {
const property = key.slice('resource.'.length)
switch (property) {
case 'uri':
return component.resource
case 'basename':
@ -76,8 +76,8 @@ export function getComputedContextProperty(
if (component?.type !== 'CodeEditor') {
return null
}
const prop = key.slice('component.'.length)
switch (prop) {
const property = key.slice('component.'.length)
switch (property) {
case 'type':
return 'CodeEditor'
case 'selections':
@ -102,8 +102,8 @@ export function getComputedContextProperty(
if (component?.type !== 'panelView') {
return null
}
const prop = key.slice('panel.activeView.'.length)
switch (prop) {
const property = key.slice('panel.activeView.'.length)
switch (property) {
case 'id':
return component.id
case 'hasLocations':

View File

@ -41,9 +41,9 @@ describe('Expression', () => {
'a || isnotdefined': 1, // short-circuit (if not, the use of an undefined ident would cause an error)
}
/* eslint-enable no-template-curly-in-string */
for (const [expr, want] of Object.entries(TESTS)) {
test(expr, () => {
const value = parse<unknown>(expr).exec(FIXTURE_CONTEXT)
for (const [expression, want] of Object.entries(TESTS)) {
test(expression, () => {
const value = parse<unknown>(expression).exec(FIXTURE_CONTEXT)
expect(value).toBe(want)
})
}

View File

@ -21,8 +21,8 @@ export class TemplateExpression<S extends string = string> extends Expression<S>
/**
* Evaluates an expression with the given context and returns the result.
*/
export function parse<T>(expr: string): Expression<T> {
return new Expression<T>(new Parser().parse(expr))
export function parse<T>(expression: string): Expression<T> {
return new Expression<T>(new Parser().parse(expression))
}
/**
@ -46,8 +46,8 @@ export const EMPTY_COMPUTED_CONTEXT: ComputedContext = {
}
const FUNCS: { [name: string]: (...args: any[]) => any } = {
get: (obj: any, key: string): any => obj?.[key] ?? undefined,
json: (obj: any): string => JSON.stringify(obj),
get: (object: any, key: string): any => object?.[key] ?? undefined,
json: (object: any): string => JSON.stringify(object),
}
function exec(node: ExpressionNode, context: ComputedContext): any {
@ -64,8 +64,8 @@ function exec(node: ExpressionNode, context: ComputedContext): any {
if ('Template' in node) {
const parts: any[] = []
for (const expr of node.Template.parts) {
parts.push(exec(expr, context))
for (const expression of node.Template.parts) {
parts.push(exec(expression, context))
}
return parts.join('')
}
@ -114,14 +114,14 @@ function exec(node: ExpressionNode, context: ComputedContext): any {
}
if ('Unary' in node) {
const expr = exec(node.Unary.expression, context)
const expression = exec(node.Unary.expression, context)
switch (node.Unary.operator) {
case '!':
return !expr
return !expression
case '+':
return expr
return expression
case '-':
return -expr
return -expression
default:
throw new SyntaxError(`Invalid operator: ${node.Unary.operator}`)
}
@ -142,13 +142,13 @@ function exec(node: ExpressionNode, context: ComputedContext): any {
}
if ('FunctionCall' in node) {
const expr = node.FunctionCall
const func = FUNCS[expr.name]
const expression = node.FunctionCall
const func = FUNCS[expression.name]
if (typeof func === 'function') {
const args = expr.args.map(arg => exec(arg, context))
const args = expression.args.map(argument => exec(argument, context))
return func(...args)
}
throw new SyntaxError(`Undefined function: ${expr.name}`)
throw new SyntaxError(`Undefined function: ${expression.name}`)
}
throw new SyntaxError('Unrecognized syntax node')

Some files were not shown because too many files have changed in this diff Show More