wip before using codeintel api directly

This commit is contained in:
Quinn Slack 2024-07-20 02:26:59 -07:00
parent f5705f4f47
commit 77bbca5722
18 changed files with 88 additions and 68 deletions

View File

@ -33,7 +33,7 @@ export function esbuildBuildOptions(mode: 'dev' | 'prod', extraPlugins: esbuild.
// Worker
path.resolve(browserSourcePath, 'shared/extensionHostWorker.ts'),
],
format: 'esm',
format: 'cjs',
platform: 'browser',
plugins: [stylePlugin, ...extraPlugins],
define: {

View File

@ -2,7 +2,7 @@
import fs from 'fs'
import path from 'path'
import { omit, cloneDeep, curry } from 'lodash'
import { cloneDeep, curry, omit } from 'lodash'
import shelljs from 'shelljs'
import signale from 'signale'
import utcVersion from 'utc-version'
@ -175,7 +175,7 @@ function writeManifest(environment: BuildEnvironment, browser: Browser, writeDir
if (EXTENSION_PERMISSIONS_ALL_URLS) {
manifest.permissions!.push('<all_urls>')
/** Set key to make extension id deterministic */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
manifest.key = manifestSpec.dev.key
signale.info('Adding <all_urls> to permissions because of env var setting')
@ -186,11 +186,6 @@ function writeManifest(environment: BuildEnvironment, browser: Browser, writeDir
delete manifest.storage
}
if (browser === 'firefox') {
// activeTab is not sufficient to call browser.scripting.executeScript (as it is in Chrome).
manifest.permissions.push('scripting')
}
if (browser === 'safari') {
// If any modifications need to be done to the manifest for Safari, they
// can be done here.
@ -206,6 +201,14 @@ function writeManifest(environment: BuildEnvironment, browser: Browser, writeDir
delete manifest.background!.service_worker
}
if (browser === 'firefox') {
manifest.browser_specific_settings = {
gecko: {
id: 'sourcegraph-for-firefox@sourcegraph.com',
},
}
}
delete manifest.$schema
if (environment === 'prod' && useUtcVersion) {

View File

@ -9,8 +9,10 @@ import { IsProductionVersion } from './util'
* on the state of the browser extension.
*/
/** State of the Sourcegraph browser extension, as represented by the browser
* action icon. */
/**
* State of the Sourcegraph browser extension, as represented by the browser
* action icon.
*/
export type BrowserActionIconState = 'active' | 'active-with-alert' | 'inactive'
interface BrowserActionIconPaths {
'32': string
@ -21,36 +23,36 @@ interface BrowserActionIconPaths {
const browserActionIconPaths: Record<BrowserActionIconState, BrowserActionIconPaths> = IsProductionVersion
? {
active: {
'32': 'img/icon-32.png',
'48': 'img/icon-48.png',
'128': 'img/icon-128.png',
'32': '/img/icon-32.png',
'48': '/img/icon-48.png',
'128': '/img/icon-128.png',
},
'active-with-alert': {
'32': 'img/icon-active-with-alert-32.png',
'48': 'img/icon-active-with-alert-48.png',
'128': 'img/icon-active-with-alert-128.png',
'32': '/img/icon-active-with-alert-32.png',
'48': '/img/icon-active-with-alert-48.png',
'128': '/img/icon-active-with-alert-128.png',
},
inactive: {
'32': 'img/icon-inactive-32.png',
'48': 'img/icon-inactive-48.png',
'128': 'img/icon-inactive-128.png',
'32': '/img/icon-inactive-32.png',
'48': '/img/icon-inactive-48.png',
'128': '/img/icon-inactive-128.png',
},
}
: {
active: {
'32': 'img/dev/icon-32.png',
'48': 'img/dev/icon-48.png',
'128': 'img/dev/icon-128.png',
'32': '/img/dev/icon-32.png',
'48': '/img/dev/icon-48.png',
'128': '/img/dev/icon-128.png',
},
'active-with-alert': {
'32': 'img/dev/icon-active-with-alert-32.png',
'48': 'img/dev/icon-active-with-alert-48.png',
'128': 'img/dev/icon-active-with-alert-128.png',
'32': '/img/dev/icon-active-with-alert-32.png',
'48': '/img/dev/icon-active-with-alert-48.png',
'128': '/img/dev/icon-active-with-alert-128.png',
},
inactive: {
'32': 'img/dev/icon-inactive-32.png',
'48': 'img/dev/icon-inactive-48.png',
'128': 'img/dev/icon-inactive-128.png',
'32': '/img/dev/icon-inactive-32.png',
'48': '/img/dev/icon-inactive-48.png',
'128': '/img/dev/icon-inactive-128.png',
},
}
@ -60,7 +62,7 @@ const browserActionIconPaths: Record<BrowserActionIconState, BrowserActionIconPa
export function setBrowserActionIconState(iconState: BrowserActionIconState): void {
const iconPaths = browserActionIconPaths[iconState]
console.log('Setting icons to', iconPaths)
browser.action.setIcon({ path: iconPaths }).catch(error => {
;(browser.action ?? browser.browserAction).setIcon({ path: iconPaths }).catch(error => {
console.error(error)
})
}

View File

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

View File

@ -28,7 +28,7 @@
"run_at": "document_idle"
}
],
"permissions": ["activeTab", "storage"],
"permissions": ["activeTab", "storage", "scripting"],
"host_permissions": ["https://github.com/*", "https://sourcegraph.com/*"],
"optional_host_permissions": ["https://*/*", "http://*/*"],
"options_ui": {
@ -40,18 +40,13 @@
},
"web_accessible_resources": [
{
"resources": ["img/*", "css/*"],
"resources": ["img/*", "css/*", "extensions/*", "extensions/sourcegraph-go/*"],
"matches": ["<all_urls>"]
}
],
"omnibox": {
"keyword": "src"
},
"browser_specific_settings": {
"gecko": {
"id": "sourcegraph-for-firefox@sourcegraph.com"
}
},
"dev": {
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCl2X/axNHMbP0K/NCpMzGo/pgBSsHB2xKx6tfohORKtEv2wUMBPmkK3++kirrwYO2f8Ficyq6pjlXV8LjwPSjSw9KZj6bkDn8QNoSdCp6x9i8ZOWPw6UTQ6s54b3rGQNyvrvfD7S6LphxGEx8rNlkjpWKcrvY3+DyoFKHP/hax7wIDAQAB"
}

View File

@ -281,7 +281,7 @@ async function main(): Promise<void> {
// The `popup=true` param is used by the options page to determine if it's
// loaded in the popup or in th standalone options page.
browser.action.setPopup({ popup: 'options.html?popup=true' })
;(browser.action ?? browser.browserAction).setPopup({ popup: 'options.html?popup=true' })
const ENDPOINT_KIND_REGEX = /^(proxy|expose)-/
@ -363,8 +363,8 @@ function handleBrowserPortPair(
const subscriptions = new Subscription()
console.log('Extension host client connected')
const { worker, clientEndpoints } = createExtensionHostWorker(workerBundleURL)
subscriptions.add(() => worker.terminate())
const { terminate: terminateExtensionHost, clientEndpoints } = createExtensionHostWorker(workerBundleURL)
subscriptions.add(() => terminateExtensionHost())
/** Forwards all messages between two endpoints (in one direction) */
const forwardEndpoint = (from: Endpoint, to: Endpoint): void => {

View File

@ -1,6 +1,6 @@
import '../../config/extension.entry'
// Set globals first before any imports.
import '../../config/content.entry'
import '../../config/extension.entry'
// Polyfill before other imports.
import '../../shared/polyfills'

View File

@ -1 +1 @@
window.EXTENSION_ENV = 'BACKGROUND'
globalThis.EXTENSION_ENV = 'BACKGROUND'

View File

@ -1 +1 @@
window.EXTENSION_ENV = 'CONTENT'
globalThis.EXTENSION_ENV = 'CONTENT'

View File

@ -1 +1 @@
window.SG_ENV = 'EXTENSION'
globalThis.SG_ENV = 'EXTENSION'

View File

@ -1 +1 @@
window.EXTENSION_ENV = 'OPTIONS'
globalThis.EXTENSION_ENV = 'OPTIONS'

View File

@ -1,2 +1,2 @@
window.SG_ENV = 'PAGE'
window.EXTENSION_ENV = null
globalThis.SG_ENV = 'PAGE'
globalThis.EXTENSION_ENV = null

View File

@ -72,7 +72,7 @@ import {
} from '@sourcegraph/shared/src/hover/HoverOverlay'
import { getModeFromPath } from '@sourcegraph/shared/src/languages'
import type { PlatformContext, URLToFileContext } from '@sourcegraph/shared/src/platform/context'
import { type TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { createURLWithUTM } from '@sourcegraph/shared/src/tracking/utm'
import {
@ -666,7 +666,7 @@ const isSafeToContinueCodeIntel = async ({
if (isExtension) {
// Notify to show extension alert-icon
background.notifyRepoSyncError({ sourcegraphURL, hasRepoSyncError: true }).catch(error => {
console.error('Error notifying background page of private cloud.', error)
console.error('Error notifying background page of repository sync error.', error)
})
}
@ -841,7 +841,7 @@ export async function handleCodeHost({
setRepoSyncError(hasRepoSyncError)
if (isExtension) {
background.notifyRepoSyncError({ sourcegraphURL, hasRepoSyncError }).catch(error => {
console.error('Error notifying background page of private cloud error:', error)
console.error('Error notifying background page of repository sync error:', error)
})
}
}

View File

@ -15,11 +15,11 @@ interface AppContext {
}
function getContext(): AppContext {
const appEnvironment = window.SG_ENV === 'EXTENSION' ? AppEnvironment.Extension : AppEnvironment.Page
const appEnvironment = globalThis.SG_ENV === 'EXTENSION' ? AppEnvironment.Extension : AppEnvironment.Page
let scriptEnvironment: ScriptEnvironment = ScriptEnvironment.Content
if (appEnvironment === AppEnvironment.Extension) {
if (window.location.pathname.includes('options.html')) {
if (globalThis.location.pathname.includes('options.html')) {
scriptEnvironment = ScriptEnvironment.Options
} else if (globalThis.browser && browser.runtime.getBackgroundPage) {
scriptEnvironment = ScriptEnvironment.Background
@ -40,4 +40,4 @@ export const isOptions = context.scriptEnvironment === ScriptEnvironment.Options
export const isExtension = context.appEnvironment === AppEnvironment.Extension
export const isInPage = !isExtension
export const isPhabricator = Boolean(document.querySelector('.phabricator-wordmark'))
export const isPhabricator = Boolean(typeof document !== 'undefined' && document.querySelector('.phabricator-wordmark'))

View File

@ -1,7 +1,7 @@
import { Subscription } from 'rxjs'
import * as uuid from 'uuid'
import type { EndpointPair, ClosableEndpointPair } from '@sourcegraph/shared/src/platform/context'
import type { ClosableEndpointPair, EndpointPair } from '@sourcegraph/shared/src/platform/context'
import { isInPage } from '../context'
@ -72,6 +72,7 @@ function createInPageExtensionHost({
export function createExtensionHost(
urls: Pick<SourcegraphIntegrationURLs, 'assetsURL'>
): Promise<ClosableEndpointPair> {
// TODO!(sqs): in-page ext host doesnt need a separate code path anymore if just importing the exthost entrypoint here works
if (isInPage) {
return createInPageExtensionHost(urls)
}

View File

@ -13,7 +13,9 @@ export function observeSourcegraphURL(isExtension: boolean): Observable<string>
map(sourcegraphURL => sourcegraphURL || DEFAULT_SOURCEGRAPH_URL)
)
}
return of(window.SOURCEGRAPH_URL || window.localStorage.getItem('SOURCEGRAPH_URL') || DEFAULT_SOURCEGRAPH_URL)
return of(
globalThis.SOURCEGRAPH_URL || globalThis.localStorage.getItem('SOURCEGRAPH_URL') || DEFAULT_SOURCEGRAPH_URL
)
}
/**
@ -27,7 +29,7 @@ export function observeSourcegraphURL(isExtension: boolean): Observable<string>
* Otherwise, the given `sourcegraphURL` will be used.
*/
export function getAssetsURL(sourcegraphURL: string): string {
const assetsURL = window.SOURCEGRAPH_ASSETS_URL || new URL('/.assets/extension/', sourcegraphURL).href
const assetsURL = globalThis.SOURCEGRAPH_ASSETS_URL || new URL('/.assets/extension/', sourcegraphURL).href
return assetsURL.endsWith('/') ? assetsURL : assetsURL + '/'
}
@ -38,11 +40,11 @@ export type PlatformName =
| 'safari-extension'
export function getPlatformName(): PlatformName {
if (window.SOURCEGRAPH_PHABRICATOR_EXTENSION) {
if (globalThis.SOURCEGRAPH_PHABRICATOR_EXTENSION) {
return 'phabricator-integration'
}
if (window.SOURCEGRAPH_INTEGRATION) {
return window.SOURCEGRAPH_INTEGRATION
if (globalThis.SOURCEGRAPH_INTEGRATION) {
return globalThis.SOURCEGRAPH_INTEGRATION
}
if (isSafari()) {
return 'safari-extension'
@ -51,13 +53,16 @@ export function getPlatformName(): PlatformName {
}
export function getTelemetryClientName(): string {
if (window.SOURCEGRAPH_PHABRICATOR_EXTENSION || window.SOURCEGRAPH_INTEGRATION === 'phabricator-integration') {
if (
globalThis.SOURCEGRAPH_PHABRICATOR_EXTENSION ||
globalThis.SOURCEGRAPH_INTEGRATION === 'phabricator-integration'
) {
return 'phabricator.integration'
}
if (window.SOURCEGRAPH_INTEGRATION === 'bitbucket-integration') {
if (globalThis.SOURCEGRAPH_INTEGRATION === 'bitbucket-integration') {
return 'bitbucket.integration'
}
if (window.SOURCEGRAPH_INTEGRATION === 'gitlab-integration') {
if (globalThis.SOURCEGRAPH_INTEGRATION === 'gitlab-integration') {
return 'gitlab.integration'
}
if (isSafari()) {
@ -78,7 +83,7 @@ export function getExtensionVersion(): string {
function isSafari(): boolean {
// Chrome's user agent contains "Safari" as well as "Chrome", so for Safari
// we must check that it does not include "Chrome"
return window.navigator.userAgent.includes('Safari') && !window.navigator.userAgent.includes('Chrome')
return globalThis.navigator.userAgent.includes('Safari') && !globalThis.navigator.userAgent.includes('Chrome')
}
export function isDefaultSourcegraphUrl(url?: string): boolean {

View File

@ -191,6 +191,11 @@ declare namespace browser.action {
const onClicked: EventEmitter<tabs.Tab>
}
declare namespace browser.browserAction {
function setIcon(details: IconViaPath | IconViaImageData | IconReset): Promise<void>
function setPopup(details: { popup: string | null; tabId?: number }): void
}
declare namespace browser.browsingData {
interface DataTypeSet {
cache?: boolean

View File

@ -1,20 +1,29 @@
import type { EndpointPair } from '../../platform/context'
import { startExtensionHost } from './extensionHost'
/**
* Creates a web worker with the extension host and sets up a bidirectional MessageChannel-based communication channel.
*/
export function createExtensionHostWorker(workerBundleURL: string): { worker: Worker; clientEndpoints: EndpointPair } {
export function createExtensionHostWorker(workerBundleURL: string): {
terminate: () => void
clientEndpoints: EndpointPair
} {
const clientAPIChannel = new MessageChannel()
const extensionHostAPIChannel = new MessageChannel()
const worker = new Worker(workerBundleURL)
// TODO!(sqs): remove worker bundle from esbuild config
// const worker = new Worker(workerBundleURL)
const workerEndpoints: EndpointPair = {
proxy: clientAPIChannel.port2,
expose: extensionHostAPIChannel.port2,
}
worker.postMessage({ endpoints: workerEndpoints }, Object.values(workerEndpoints))
const extensionHost = startExtensionHost(workerEndpoints)
// worker.postMessage({ endpoints: workerEndpoints }, Object.values(workerEndpoints))
const clientEndpoints = {
proxy: extensionHostAPIChannel.port1,
expose: clientAPIChannel.port1,
}
return { worker, clientEndpoints }
return { terminate: () => extensionHost.unsubscribe(), clientEndpoints }
}