Skip to content
This repository was archived by the owner on Jun 7, 2022. It is now read-only.

Rewrite node_modules URIs #28

Merged
merged 1 commit into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,15 @@ export async function activate(): Promise<void> {
capabilities: {},
initializationOptions: {
// until workspace/configuration is allowed during initialize
configuration: fromPairs(
Object.entries(sourcegraph.configuration.get().value).filter(([key]) =>
key.startsWith('typescript.')
)
),
configuration: {
// The server needs to use the API to resolve repositories
'sourcegraph.url': sourcegraph.internal.sourcegraphURL,
...fromPairs(
Object.entries(sourcegraph.configuration.get().value).filter(([key]) =>
key.startsWith('typescript.')
)
),
},
},
}
console.log('Initializing TypeScript backend...')
Expand Down
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
},
"configuration": {
"properties": {
"sourcegraph.url": {
"description": "The Sourcegraph endpoint to contact",
"type": "string",
"format": "url"
},
"typescript.serverUrl": {
"description": "The WebSocket language server to connect to",
"type": "string",
Expand Down Expand Up @@ -76,6 +81,7 @@
"@sourcegraph/tsconfig": "^3.0.0",
"@sourcegraph/tslint-config": "^12.1.0",
"@types/globby": "8.0.0",
"@types/got": "^9.2.1",
"@types/json5": "0.0.30",
"@types/lodash": "4.14.118",
"@types/mkdirp-promise": "5.0.0",
Expand All @@ -85,6 +91,7 @@
"@types/relateurl": "0.2.28",
"@types/request": "2.48.1",
"@types/rmfr": "2.0.0",
"@types/source-map": "^0.5.7",
"@types/tail": "1.2.0",
"@types/tar": "4.0.0",
"@types/uuid": "3.4.4",
Expand All @@ -101,6 +108,7 @@
"@sourcegraph/vscode-ws-jsonrpc": "0.0.3-fork",
"abort-controller": "^1.0.2",
"globby": "^8.0.1",
"got": "^9.3.2",
"json5": "^2.1.0",
"lightstep-tracer": "^0.20.13",
"lodash": "^4.17.11",
Expand All @@ -113,6 +121,7 @@
"request": "^2.88.0",
"rmfr": "^2.0.0",
"rxjs": "^6.3.3",
"source-map": "^0.7.3",
"source-map-support": "^0.5.9",
"sourcegraph": "^19.1.0",
"tagged-template-noop": "^2.1.0",
Expand Down
63 changes: 63 additions & 0 deletions server/src/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as fs from 'mz/fs'
import { Span, Tracer } from 'opentracing'
import fetchPackageJson from 'package-json'
import * as semver from 'semver'
import { fileURLToPath } from 'url'
import { CancellationToken } from 'vscode-jsonrpc'
import { throwIfCancelled } from './cancellation'
import { Logger } from './logging'
Expand Down Expand Up @@ -72,3 +73,65 @@ export async function filterDependencies(
return included.length > 0
})
}

export interface PackageJson {
name: string
version: string
repository?:
| string
| {
type: string
url: string

/**
* https://github.com/npm/rfcs/blob/d39184cdedc000aa8e60b4d63878b834aa5f0ff0/accepted/0000-monorepo-subdirectory-declaration.md
*/
directory?: string
}
/** Commit SHA1 of the repo at the time of publishing */
gitHead?: string
}

/**
* Finds the closest package.json for a given URL.
*/
export async function getClosestPackageJson(resource: URL, rootUri: URL): Promise<PackageJson> {
if (resource.protocol !== 'file:') {
throw new Error('Only file: URIs supported')
}
let parent: URL
while (true) {
parent = new URL('..', resource.href)
if (!parent.href.startsWith(rootUri.href)) {
throw new Error(`No package.json found for ${resource}`)
}
const packageJsonUri = new URL('package.json', parent.href)
try {
const content = await fs.readFile(fileURLToPath(packageJsonUri), 'utf-8')
return JSON.parse(content)
} catch (err) {
if (err.code === 'ENOENT') {
continue
}
throw err
}
}
}

/**
* @param filePath e.g. `/foo/node_modules/pkg/dist/bar.ts`
* @returns e.g. `foo/node_modules/pkg`
*/
export function resolveDependencyRootDir(filePath: string): string {
const parts = filePath.split('/')
while (
parts.length > 0 &&
!(
parts[parts.length - 2] === 'node_modules' ||
(parts[parts.length - 3] === 'node_modules' && parts[parts.length - 2].startsWith('@'))
)
) {
parts.pop()
}
return parts.join('/')
}
61 changes: 61 additions & 0 deletions server/src/graphql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import got from 'got'
import gql from 'tagged-template-noop'

interface Options {
instanceUrl: URL
accessToken?: string
}

/**
* Does a GraphQL request to the Sourcegraph GraphQL API
*
* @param query The GraphQL request (query or mutation)
* @param variables A key/value object with variable values
*/
export async function requestGraphQL(
query: string,
variables: any = {},
{ instanceUrl, accessToken }: Options
): Promise<{ data?: any; errors?: { message: string; path: string }[] }> {
const headers: Record<string, string> = {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': 'TypeScript language server',
}
if (accessToken) {
headers.Authorization = 'token ' + accessToken
}
const response = await got.post(new URL('/.api/graphql', instanceUrl), {
headers,
body: { query, variables },
json: true,
})
return response.body
}

/**
* Uses the Sourcegraph GraphQL API to resolve a git clone URL to a Sourcegraph repository name.
*
* @param cloneUrl A git clone URL
* @return The Sourcegraph repository name (can be used to construct raw API URLs)
*/
export async function resolveRepository(cloneUrl: string, options: Options): Promise<string> {
const { data, errors } = await requestGraphQL(
gql`
query($cloneUrl: String!) {
repository(cloneURL: $cloneUrl) {
name
}
}
`,
{ cloneUrl },
options
)
if (errors && errors.length > 0) {
throw new Error('GraphQL Error:' + errors.map(e => e.message).join('\n'))
}
if (!data.repository) {
throw new Error(`No repository found for clone URL ${cloneUrl} on instance ${options.instanceUrl}`)
}
return data.repository.name
}
Loading