Skip to content
Open
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
7 changes: 5 additions & 2 deletions packages/next/src/cli/next-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,11 @@ async function printInfo() {
let versionInfo

try {
const registry = getRegistry()
const res = await fetch(`${registry}-/package/next/dist-tags`)
const { registry, authToken } = getRegistry()
const headers: HeadersInit = authToken
? { Authorization: `Bearer ${authToken}` }
: {}
const res = await fetch(`${registry}-/package/next/dist-tags`, { headers })
const tags = await res.json()

versionInfo = parseVersionInfo({
Expand Down
8 changes: 6 additions & 2 deletions packages/next/src/lib/download-swc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ async function extractBinary(
`${tarFileName}.temp-${Date.now()}`
)

const registry = getRegistry()
const { registry, authToken } = getRegistry()

const downloadUrl = `${registry}${pkgName}/-/${tarFileName}`

await fetch(downloadUrl).then((res) => {
const headers: HeadersInit = authToken
? { Authorization: `Bearer ${authToken}` }
: {}

await fetch(downloadUrl, { headers }).then((res) => {
const { ok, body } = res
if (!ok || !body) {
Log.error(`Failed to download swc package from ${downloadUrl}`)
Expand Down
33 changes: 31 additions & 2 deletions packages/next/src/lib/helpers/get-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { getFormattedNodeOptionsWithoutInspect } from '../../server/lib/utils'
* The URL will have a trailing slash.
* @default https://registry.npmjs.org/
*/
export function getRegistry(baseDir: string = process.cwd()) {
export function getRegistry(baseDir: string = process.cwd()): {
registry: string
authToken?: string
} {
const pkgManager = getPkgManager(baseDir)
// Since `npm config` command fails in npm workspace to prevent workspace config conflicts,
// add `--no-workspaces` flag to run under the context of the root project only.
Expand Down Expand Up @@ -39,5 +42,31 @@ export function getRegistry(baseDir: string = process.cwd()) {
})
}

return registry
// Read auth token for this registry from .npmrc, if configured.
let authToken: string | undefined
try {
const parsed = new URL(registry)
let scope = `//${parsed.host}${parsed.pathname}`
if (!scope.endsWith('/')) scope += '/'

const output = execSync(
`${pkgManager} config get "${scope}:_authToken" ${resolvedFlags}`,
{
env: {
...process.env,
NODE_OPTIONS: getFormattedNodeOptionsWithoutInspect(),
},
}
)
.toString()
.trim()

if (output && output !== 'undefined') {
authToken = output
}
} catch {
// Auth token is not required — proceed without it
}

return { registry, authToken }
}
10 changes: 7 additions & 3 deletions packages/next/src/lib/patch-incorrect-lockfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import type { UnwrapPromise } from './coalesced-function'
import { isCI } from '../server/ci-info'
import { getRegistry } from './helpers/get-registry'

let registry: string | undefined
let registryConfig: ReturnType<typeof getRegistry> | undefined

async function fetchPkgInfo(pkg: string) {
if (!registry) registry = getRegistry()
const res = await fetch(`${registry}${pkg}`)
if (!registryConfig) registryConfig = getRegistry()
const { registry, authToken } = registryConfig
const headers: HeadersInit = authToken
? { Authorization: `Bearer ${authToken}` }
: {}
const res = await fetch(`${registry}${pkg}`, { headers })

if (!res.ok) {
throw new Error(
Expand Down
81 changes: 81 additions & 0 deletions test/unit/get-registry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* eslint-env jest */
import { execSync } from 'child_process'
import { getRegistry } from 'next/dist/lib/helpers/get-registry'

jest.mock('child_process', () => ({
execSync: jest.fn(),
}))

jest.mock('next/dist/lib/helpers/get-pkg-manager', () => ({
getPkgManager: jest.fn(() => 'npm'),
}))

jest.mock('next/dist/server/lib/utils', () => ({
getFormattedNodeOptionsWithoutInspect: jest.fn(() => ''),
}))

const mockedExecSync = execSync as jest.MockedFunction<typeof execSync>

describe('getRegistry', () => {
beforeEach(() => {
jest.clearAllMocks()
})

it('should return registry URL and auth token', () => {
mockedExecSync
.mockReturnValueOnce(Buffer.from('https://registry.example.com\n'))
.mockReturnValueOnce(Buffer.from('my-secret-token\n'))

const result = getRegistry()
expect(result.registry).toBe('https://registry.example.com/')
expect(result.authToken).toBe('my-secret-token')
})

it('should return undefined authToken when npm returns "undefined"', () => {
mockedExecSync
.mockReturnValueOnce(Buffer.from('https://registry.npmjs.org/\n'))
.mockReturnValueOnce(Buffer.from('undefined\n'))

const result = getRegistry()
expect(result.registry).toBe('https://registry.npmjs.org/')
expect(result.authToken).toBeUndefined()
})

it('should return undefined authToken when config get throws', () => {
mockedExecSync
.mockReturnValueOnce(Buffer.from('https://registry.npmjs.org/\n'))
.mockImplementationOnce(() => {
throw new Error('not found')
})

const result = getRegistry()
expect(result.registry).toBe('https://registry.npmjs.org/')
expect(result.authToken).toBeUndefined()
})

it('should query the correct npmrc key for a registry with a path', () => {
mockedExecSync
.mockReturnValueOnce(
Buffer.from('https://my.jfrog.io/artifactory/api/npm/npm/\n')
)
.mockReturnValueOnce(Buffer.from('my-token\n'))

const result = getRegistry()
expect(result.authToken).toBe('my-token')
expect(mockedExecSync).toHaveBeenCalledWith(
expect.stringContaining(
'//my.jfrog.io/artifactory/api/npm/npm/:_authToken'
),
expect.any(Object)
)
})

it('should fall back to default registry when config get returns non-URL', () => {
mockedExecSync
.mockReturnValueOnce(Buffer.from('WARN something\n'))
.mockReturnValueOnce(Buffer.from('undefined\n'))

const result = getRegistry()
expect(result.registry).toBe('https://registry.npmjs.org/')
})
})