Skip to content

Commit 571cc3a

Browse files
committed
refactor: extract npm registry resolution into a shared util
1 parent a04ec72 commit 571cc3a

4 files changed

Lines changed: 126 additions & 120 deletions

File tree

packages/nuxt-cli/src/commands/module/_utils.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { PackageJson } from 'pkg-types'
44
import { existsSync } from 'node:fs'
55

66
import { confirm, isCancel } from '@clack/prompts'
7-
import { parseINI } from 'confbox'
87
import { $fetch } from 'ofetch'
98
import { resolve } from 'pathe'
109
import colors from 'picocolors'
@@ -127,28 +126,6 @@ export function checkNuxtCompatibility(
127126
})
128127
}
129128

130-
export function getRegistryFromContent(content: string, scope: string | null) {
131-
try {
132-
const npmConfig = parseINI<Record<string, string | undefined>>(content)
133-
134-
if (scope) {
135-
const scopeKey = `${scope}:registry`
136-
if (npmConfig[scopeKey]) {
137-
return npmConfig[scopeKey].trim()
138-
}
139-
}
140-
141-
if (npmConfig.registry) {
142-
return npmConfig.registry.trim()
143-
}
144-
145-
return null
146-
}
147-
catch {
148-
return null
149-
}
150-
}
151-
152129
export function getProjectDependencies(projectPkg: PackageJson): Set<string> {
153130
return new Set([
154131
...Object.keys(projectPkg.dependencies || {}),

packages/nuxt-cli/src/commands/module/add.ts

Lines changed: 3 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import type { FileHandle } from 'node:fs/promises'
21
import type { PackageManager } from 'nypm'
32
import type { PackageJson } from 'pkg-types'
43

4+
import type { RegistryMeta } from '../../utils/registry'
55
import type { NuxtModule } from './_utils'
6-
import * as fs from 'node:fs'
7-
import { homedir } from 'node:os'
8-
import { join } from 'node:path'
96
import process from 'node:process'
107

118
import { cancel, confirm, isCancel, select, spinner } from '@clack/prompts'
@@ -23,22 +20,15 @@ import { runCommandDef as runCommand } from '../../run'
2320
import { createInstallLog, resolvePackageManagerDescriptor, runInstall, takeUnreportedIgnoredBuilds } from '../../utils/install'
2421
import { logger } from '../../utils/logger'
2522
import { logNetworkError } from '../../utils/network'
23+
import { detectNpmRegistry } from '../../utils/registry'
2624
import { getNuxtVersion } from '../../utils/versions'
2725
import { cwdArgs, logLevelArgs } from '../_shared'
2826
import prepareCommand from '../prepare'
2927
import { selectModulesAutocomplete } from './_autocomplete'
30-
import { checkNuxtCompatibility, ensureNuxtDependency, fetchModules, forwardCommandArgs, getProjectDependencies, getRegistryFromContent, isPnpmWorkspace, MODULES_API_URL } from './_utils'
28+
import { checkNuxtCompatibility, ensureNuxtDependency, fetchModules, forwardCommandArgs, getProjectDependencies, isPnpmWorkspace, MODULES_API_URL } from './_utils'
3129

32-
const PROTOCOL_RE = /^https?:\/\//
33-
const TRAILING_SLASH_RE = /\/$/
34-
const REGEX_SPECIAL_RE = /[.*+?^${}()|[\]\\]/g
3530
const WHITESPACE_RE = /\s/
3631

37-
interface RegistryMeta {
38-
registry: string
39-
authToken: string | null
40-
}
41-
4232
interface ResolvedModule {
4333
nuxtModule?: NuxtModule
4434
pkg: string
@@ -472,86 +462,3 @@ async function resolveModule(moduleName: string, cwd: string): Promise<ModuleRes
472462
.map(([name]) => name),
473463
}
474464
}
475-
476-
function getNpmrcPaths(): string[] {
477-
const userNpmrcPath = join(homedir(), '.npmrc')
478-
const cwdNpmrcPath = join(process.cwd(), '.npmrc')
479-
480-
return [cwdNpmrcPath, userNpmrcPath]
481-
}
482-
483-
async function getAuthToken(registry: RegistryMeta['registry']): Promise<RegistryMeta['authToken']> {
484-
const paths = getNpmrcPaths()
485-
const registryHost = registry.replace(PROTOCOL_RE, '').replace(TRAILING_SLASH_RE, '').replace(REGEX_SPECIAL_RE, '\\$&')
486-
const authTokenRegex = new RegExp(`^//${registryHost}/:_authToken=(.+)$`, 'm')
487-
488-
for (const npmrcPath of paths) {
489-
let fd: FileHandle | undefined
490-
try {
491-
fd = await fs.promises.open(npmrcPath, 'r')
492-
if (await fd.stat().then(r => r.isFile())) {
493-
const npmrcContent = await fd.readFile('utf-8')
494-
const authTokenMatch = npmrcContent.match(authTokenRegex)?.[1]
495-
496-
if (authTokenMatch) {
497-
return authTokenMatch.trim()
498-
}
499-
}
500-
}
501-
catch {
502-
// swallow errors as file does not exist
503-
}
504-
finally {
505-
await fd?.close()
506-
}
507-
}
508-
509-
return null
510-
}
511-
512-
async function detectNpmRegistry(scope: string | null): Promise<RegistryMeta> {
513-
const registry = await getRegistry(scope)
514-
const authToken = await getAuthToken(registry)
515-
516-
return {
517-
registry,
518-
authToken,
519-
}
520-
}
521-
522-
async function getRegistry(scope: string | null): Promise<string> {
523-
if (process.env.COREPACK_NPM_REGISTRY) {
524-
return process.env.COREPACK_NPM_REGISTRY
525-
}
526-
const registry = await getRegistryFromFile(getNpmrcPaths(), scope)
527-
528-
if (registry) {
529-
process.env.COREPACK_NPM_REGISTRY = registry
530-
}
531-
532-
return registry || 'https://registry.npmjs.org'
533-
}
534-
535-
async function getRegistryFromFile(paths: string[], scope: string | null) {
536-
for (const npmrcPath of paths) {
537-
let fd: FileHandle | undefined
538-
try {
539-
fd = await fs.promises.open(npmrcPath, 'r')
540-
if (await fd.stat().then(r => r.isFile())) {
541-
const npmrcContent = await fd.readFile('utf-8')
542-
const registry = getRegistryFromContent(npmrcContent, scope)
543-
544-
if (registry) {
545-
return registry
546-
}
547-
}
548-
}
549-
catch {
550-
// swallow errors as file does not exist
551-
}
552-
finally {
553-
await fd?.close()
554-
}
555-
}
556-
return null
557-
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import type { FileHandle } from 'node:fs/promises'
2+
3+
import * as fs from 'node:fs'
4+
import { homedir } from 'node:os'
5+
import { join } from 'node:path'
6+
import process from 'node:process'
7+
8+
import { parseINI } from 'confbox'
9+
10+
const PROTOCOL_RE = /^https?:\/\//
11+
const TRAILING_SLASH_RE = /\/$/
12+
const REGEX_SPECIAL_RE = /[.*+?^${}()|[\]\\]/g
13+
14+
export interface RegistryMeta {
15+
registry: string
16+
authToken: string | null
17+
}
18+
19+
export function getRegistryFromContent(content: string, scope: string | null): string | null {
20+
try {
21+
const npmConfig = parseINI<Record<string, string | undefined>>(content)
22+
23+
if (scope) {
24+
const scopeKey = `${scope}:registry`
25+
if (npmConfig[scopeKey]) {
26+
return npmConfig[scopeKey].trim()
27+
}
28+
}
29+
30+
if (npmConfig.registry) {
31+
return npmConfig.registry.trim()
32+
}
33+
34+
return null
35+
}
36+
catch {
37+
return null
38+
}
39+
}
40+
41+
function getNpmrcPaths(): string[] {
42+
const userNpmrcPath = join(homedir(), '.npmrc')
43+
const cwdNpmrcPath = join(process.cwd(), '.npmrc')
44+
45+
return [cwdNpmrcPath, userNpmrcPath]
46+
}
47+
48+
async function getRegistryFromFile(paths: string[], scope: string | null) {
49+
for (const npmrcPath of paths) {
50+
let fd: FileHandle | undefined
51+
try {
52+
fd = await fs.promises.open(npmrcPath, 'r')
53+
if (await fd.stat().then(r => r.isFile())) {
54+
const npmrcContent = await fd.readFile('utf-8')
55+
const registry = getRegistryFromContent(npmrcContent, scope)
56+
57+
if (registry) {
58+
return registry
59+
}
60+
}
61+
}
62+
catch {
63+
// swallow errors as file does not exist
64+
}
65+
finally {
66+
await fd?.close()
67+
}
68+
}
69+
return null
70+
}
71+
72+
async function getRegistry(scope: string | null): Promise<string> {
73+
if (process.env.COREPACK_NPM_REGISTRY) {
74+
return process.env.COREPACK_NPM_REGISTRY
75+
}
76+
const registry = await getRegistryFromFile(getNpmrcPaths(), scope)
77+
78+
if (registry) {
79+
process.env.COREPACK_NPM_REGISTRY = registry
80+
}
81+
82+
return registry || 'https://registry.npmjs.org'
83+
}
84+
85+
async function getAuthToken(registry: RegistryMeta['registry']): Promise<RegistryMeta['authToken']> {
86+
const paths = getNpmrcPaths()
87+
const registryHost = registry.replace(PROTOCOL_RE, '').replace(TRAILING_SLASH_RE, '').replace(REGEX_SPECIAL_RE, '\\$&')
88+
const authTokenRegex = new RegExp(`^//${registryHost}/:_authToken=(.+)$`, 'm')
89+
90+
for (const npmrcPath of paths) {
91+
let fd: FileHandle | undefined
92+
try {
93+
fd = await fs.promises.open(npmrcPath, 'r')
94+
if (await fd.stat().then(r => r.isFile())) {
95+
const npmrcContent = await fd.readFile('utf-8')
96+
const authTokenMatch = npmrcContent.match(authTokenRegex)?.[1]
97+
98+
if (authTokenMatch) {
99+
return authTokenMatch.trim()
100+
}
101+
}
102+
}
103+
catch {
104+
// swallow errors as file does not exist
105+
}
106+
finally {
107+
await fd?.close()
108+
}
109+
}
110+
111+
return null
112+
}
113+
114+
export async function detectNpmRegistry(scope: string | null): Promise<RegistryMeta> {
115+
const registry = await getRegistry(scope)
116+
const authToken = await getAuthToken(registry)
117+
118+
return {
119+
registry,
120+
authToken,
121+
}
122+
}

packages/nuxt-cli/test/unit/commands/module/_utils.spec.ts renamed to packages/nuxt-cli/test/unit/utils/registry.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest'
2-
import { getRegistryFromContent } from '../../../../src/commands/module/_utils'
2+
import { getRegistryFromContent } from '../../../src/utils/registry'
33

44
describe('getRegistryFromContent', () => {
55
it('extracts scoped registry when scope is provided', () => {

0 commit comments

Comments
 (0)