UNPKG

@jsdevtools/npm-publish

Version:
131 lines (110 loc) 3.7 kB
import os from "node:os"; import * as errors from "./errors.js"; import { type Access, ACCESS_PUBLIC, ACCESS_RESTRICTED, type Logger, type Options, type Strategy, STRATEGY_ALL, STRATEGY_UPGRADE, } from "./options.js"; import type { PackageManifest } from "./read-manifest.js"; const REGISTRY_NPM = "https://registry.npmjs.org/"; export const TAG_LATEST = "latest"; /** Normalized and sanitized auth, publish, and runtime configurations. */ export interface NormalizedOptions { registry: URL; token: string | undefined; tag: ConfigValue<string>; access: ConfigValue<Access | undefined>; provenance: ConfigValue<boolean>; ignoreScripts: ConfigValue<boolean>; dryRun: ConfigValue<boolean>; strategy: ConfigValue<Strategy>; logger: Logger | undefined; temporaryDirectory: string; } /** A config value, and whether that value differs from default. */ export interface ConfigValue<TValue> { value: TValue; isDefault: boolean; } /** * Normalizes and sanitizes options, and fills-in any default values. * * @param manifest Package metadata from package.json. * @param options User-input options. * @returns Validated auth and publish configuration. */ export function normalizeOptions( manifest: PackageManifest, options: Options ): NormalizedOptions { const defaultTag = manifest.publishConfig?.tag ?? TAG_LATEST; const defaultRegistry = manifest.publishConfig?.registry ?? REGISTRY_NPM; const defaultAccess = manifest.publishConfig?.access ?? (manifest.scope === undefined ? ACCESS_PUBLIC : undefined); const defaultProvenance = manifest.publishConfig?.provenance ?? false; return { token: validateToken(options.token ?? undefined), registry: validateRegistry(options.registry ?? defaultRegistry), tag: setValue(options.tag, defaultTag, validateTag), access: setValue(options.access, defaultAccess, validateAccess), provenance: setValue(options.provenance, defaultProvenance, Boolean), ignoreScripts: setValue(options.ignoreScripts, true, Boolean), dryRun: setValue(options.dryRun, false, Boolean), strategy: setValue(options.strategy, STRATEGY_ALL, validateStrategy), logger: options.logger, temporaryDirectory: options.temporaryDirectory ?? os.tmpdir(), }; } const setValue = <TValue>( value: unknown, defaultValue: unknown, validate: (value: unknown) => TValue ): ConfigValue<TValue> => ({ value: validate(value ?? defaultValue), isDefault: value === undefined, }); const validateToken = (value: unknown): string | undefined => { if (typeof value !== "string" && value !== undefined && value !== null) { throw new errors.InvalidTokenError(); } return typeof value === "string" && value.length > 0 ? value : undefined; }; const validateRegistry = (value: unknown): URL => { try { return new URL(value as string | URL); } catch { throw new errors.InvalidRegistryUrlError(value); } }; const validateTag = (value: unknown): string => { if (typeof value === "string") { const trimmedValue = value.trim(); const encodedValue = encodeURIComponent(trimmedValue); if (trimmedValue.length > 0 && trimmedValue === encodedValue) { return value; } } throw new errors.InvalidTagError(value); }; const validateAccess = (value: unknown): Access | undefined => { if ( value === undefined || value === ACCESS_PUBLIC || value === ACCESS_RESTRICTED ) { return value; } throw new errors.InvalidAccessError(value); }; const validateStrategy = (value: unknown): Strategy => { if (value === STRATEGY_ALL || value === STRATEGY_UPGRADE) { return value; } throw new errors.InvalidStrategyError(value); };