Skip to content

Commit

Permalink
types: fix more misc. util types
Browse files Browse the repository at this point in the history
  • Loading branch information
serhalp committed Feb 27, 2025
1 parent bde81c2 commit 4cce78d
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 154 deletions.
2 changes: 1 addition & 1 deletion src/commands/functions/functions-create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ const handleAddonDidInstall = async ({ addonCreated, addonDidInstall, command, f
return
}

await injectEnvVariables({
injectEnvVariables({
devConfig: { ...config.dev },
env: command.netlify.cachedConfig.env,
site: command.netlify.site,
Expand Down
4 changes: 2 additions & 2 deletions src/commands/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { NetlifyAPI } from 'netlify'
import type { FrameworksAPIPaths } from "../utils/frameworks-api.ts";
import type StateConfig from '../utils/state-config.js'
import type { Account } from "../utils/types.ts";
import type { CachedConfig } from "../utils/build.js"
import type { CachedConfig } from "../lib/build.js"

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type $TSFixMe = any;
Expand All @@ -15,7 +15,7 @@ export type NetlifySite = {
configPath?: string
siteId?: string
get id(): string | undefined
set id(id: string): void
set id(id: string)
}

type PatchedConfig = NetlifyTOML & Pick<NetlifyConfig, 'images'> & {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/functions/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ interface GetFunctionsServerOptions {
functionsRegistry: FunctionsRegistry
siteUrl: string
siteInfo?: $TSFixMe
accountId: string
accountId?: string | undefined
geoCountry: string
offline: boolean
state: $TSFixMe
Expand Down
83 changes: 38 additions & 45 deletions src/utils/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { supportsBackgroundFunctions } from '../lib/account.js'

import { NETLIFYDEVLOG, chalk, error, log, warn, APIError } from './command-helpers.js'
import { loadDotEnvFiles } from './dot-env.js'
import type { NetlifyAPI } from 'netlify'
import type { SiteInfo } from './types.js'
import { CachedConfig } from '../lib/build.js'
import { NetlifySite } from '../commands/types.js'
import { DevConfig } from '../commands/dev/types.js'

// Possible sources of environment variables. For the purpose of printing log messages only. Order does not matter.
const ENV_VAR_SOURCES = {
Expand Down Expand Up @@ -39,15 +44,13 @@ const ENV_VAR_SOURCES = {
const ERROR_CALL_TO_ACTION =
"Double-check your login status with 'netlify status' or contact support with details of your error."

// @ts-expect-error TS(7031) FIXME: Binding element 'site' implicitly has an 'any' typ... Remove this comment to see the full error message
const validateSiteInfo = ({ site, siteInfo }) => {
const validateSiteInfo = ({ site, siteInfo }: { site: NetlifySite; siteInfo: SiteInfo }): void => {
if (isEmpty(siteInfo)) {
error(`Failed retrieving site information for site ${chalk.yellow(site.id)}. ${ERROR_CALL_TO_ACTION}`)
}
}

// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
const getAccounts = async ({ api }) => {
const getAccounts = async ({ api }: { api: NetlifyAPI }) => {
try {
const accounts = await api.listAccountsForUser()
return accounts
Expand All @@ -56,9 +59,9 @@ const getAccounts = async ({ api }) => {
}
}

// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
const getAddons = async ({ api, site }) => {
const getAddons = async ({ api, site }: { api: NetlifyAPI; site: NetlifySite }) => {
try {
// @ts-expect-error(serhalp) One of three types is incorrect here (is `site.id` optional?). Dig and fix.
const addons = await api.listServiceInstancesForSite({ siteId: site.id })
return addons
} catch (error_) {
Expand All @@ -70,20 +73,17 @@ const getAddons = async ({ api, site }) => {
}
}

// @ts-expect-error TS(7031) FIXME: Binding element 'addons' implicitly has an 'any' t... Remove this comment to see the full error message
const getAddonsInformation = ({ addons, siteInfo }) => {
type Addons = Awaited<ReturnType<NetlifyAPI['listServiceInstancesForSite']>>
const getAddonsInformation = ({ addons, siteInfo }: { addons: Addons; siteInfo: SiteInfo }) => {
const urls = Object.fromEntries(
// @ts-expect-error TS(7006) FIXME: Parameter 'addon' implicitly has an 'any' type.
addons.map((addon) => [addon.service_slug, `${siteInfo.ssl_url}${addon.service_path}`]),
)
// @ts-expect-error TS(7006) FIXME: Parameter 'addon' implicitly has an 'any' type.
const env = Object.assign({}, ...addons.map((addon) => addon.env))
return { urls, env }
}

// @ts-expect-error TS(7031) FIXME: Binding element 'accounts' implicitly has an 'any'... Remove this comment to see the full error message
const getSiteAccount = ({ accounts, siteInfo }) => {
// @ts-expect-error TS(7006) FIXME: Parameter 'account' implicitly has an 'any' type.
type Accounts = Awaited<ReturnType<NetlifyAPI['listAccountsForUser']>>
const getSiteAccount = ({ accounts, siteInfo }: { accounts: Accounts; siteInfo: SiteInfo }) => {
const siteAccount = accounts.find((account) => account.slug === siteInfo.account_slug)
if (!siteAccount) {
warn(`Could not find account for site '${siteInfo.name}' with account slug '${siteInfo.account_slug}'`)
Expand All @@ -98,17 +98,17 @@ const SYNCHRONOUS_FUNCTION_TIMEOUT = 30
// default 15 minutes for background functions
const BACKGROUND_FUNCTION_TIMEOUT = 900

/**
*
* @param {object} config
* @param {boolean} config.offline
* @param {*} config.api
* @param {*} config.site
* @param {*} config.siteInfo
* @returns
*/
// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
export const getSiteInformation = async ({ api, offline, site, siteInfo }) => {
export const getSiteInformation = async ({
api,
offline,
site,
siteInfo,
}: {
api: NetlifyAPI
offline: boolean
site: NetlifySite
siteInfo: SiteInfo
}) => {
if (site.id && !offline) {
validateSiteInfo({ site, siteInfo })
const [accounts, addons] = await Promise.all([getAccounts({ api }), getAddons({ api, site })])
Expand Down Expand Up @@ -142,22 +142,22 @@ export const getSiteInformation = async ({ api, offline, site, siteInfo }) => {
}
}

// @ts-expect-error TS(7006) FIXME: Parameter 'source' implicitly has an 'any' type.
const getEnvSourceName = (source) => {
// @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
const { name = source, printFn = chalk.green } = ENV_VAR_SOURCES[source] || {}
const getEnvSourceName = (source: string) => {
const { name = source, printFn = chalk.green } = ENV_VAR_SOURCES[source] ?? {}

return printFn(name)
}

/**
* @param {{devConfig: any, env: Record<string, { sources: string[], value: string}>, site: any}} param0
* @returns {Promise<Record<string, { sources: string[], value: string}>>}
*/
// @ts-expect-error TS(7031) FIXME: Binding element 'devConfig' implicitly has an 'any... Remove this comment to see the full error message
export const getDotEnvVariables = async ({ devConfig, env, site }) => {
export const getDotEnvVariables = async ({
devConfig,
env,
site,
}: {
devConfig: DevConfig
env: CachedConfig['env']
site: NetlifySite
}): Promise<Record<string, { sources: string[]; value: string }>> => {
const dotEnvFiles = await loadDotEnvFiles({ envFiles: devConfig.envFiles, projectDir: site.root })
// @ts-expect-error TS(2339) FIXME: Property 'env' does not exist on type '{ warning: ... Remove this comment to see the full error message
dotEnvFiles.forEach(({ env: fileEnv, file }) => {
const newSourceName = `${file} file`

Expand All @@ -169,6 +169,7 @@ export const getDotEnvVariables = async ({ devConfig, env, site }) => {
}

env[key] = {
// @ts-expect-error(serhalp) Something isn't right with these types but it's a can of worms.
sources,
value: fileEnv[key],
}
Expand All @@ -180,20 +181,14 @@ export const getDotEnvVariables = async ({ devConfig, env, site }) => {

/**
* Takes a set of environment variables in the format provided by @netlify/config and injects them into `process.env`
* @param {Record<string, { sources: string[], value: string}>} env
* @return {void}
*/
// @ts-expect-error TS(7006) FIXME: Parameter 'env' implicitly has an 'any' type.
export const injectEnvVariables = (env) => {
export const injectEnvVariables = (env: Record<string, { sources: string[]; value: string }>): void => {
for (const [key, variable] of Object.entries(env)) {
const existsInProcess = process.env[key] !== undefined
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
const [usedSource, ...overriddenSources] = existsInProcess ? ['process', ...variable.sources] : variable.sources
const usedSourceName = getEnvSourceName(usedSource)
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
const isInternal = variable.sources.includes('internal')

// @ts-expect-error TS(7006) FIXME: Parameter 'source' implicitly has an 'any' type.
overriddenSources.forEach((source) => {
const sourceName = getEnvSourceName(source)

Expand All @@ -212,7 +207,6 @@ export const injectEnvVariables = (env) => {
log(`${NETLIFYDEVLOG} Injected ${usedSourceName} env var: ${chalk.yellow(key)}`)
}

// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
process.env[key] = variable.value
}
}
Expand All @@ -234,8 +228,7 @@ export const acquirePort = async ({
return acquiredPort
}

// @ts-expect-error TS(7006) FIXME: Parameter 'fn' implicitly has an 'any' type.
export const processOnExit = (fn) => {
export const processOnExit = (fn: (...args: unknown[]) => void) => {
const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT', 'SIGHUP', 'exit']
signals.forEach((signal) => {
process.on(signal, fn)
Expand Down
50 changes: 32 additions & 18 deletions src/utils/dot-env.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,58 @@
import { readFile } from 'fs/promises'
import path from 'path'

import dotenv from 'dotenv'
import dotenv, { type DotenvParseOutput } from 'dotenv'

import { isFileAsync } from '../lib/fs.js'

import { warn } from './command-helpers.js'

// @ts-expect-error TS(7031) FIXME: Binding element 'envFiles' implicitly has an 'any'... Remove this comment to see the full error message
export const loadDotEnvFiles = async function ({ envFiles, projectDir }) {
const response = await tryLoadDotEnvFiles({ projectDir, dotenvFiles: envFiles })
interface LoadedDotEnvFile {
file: string
env: DotenvParseOutput
}

export const loadDotEnvFiles = async function ({
envFiles,
projectDir,
}: {
envFiles?: string[]
projectDir?: string
}): Promise<LoadedDotEnvFile[]> {
const loadedDotEnvFiles = await tryLoadDotEnvFiles({ projectDir, dotenvFiles: envFiles })

// @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'.
const filesWithWarning = response.filter((el) => el.warning)
filesWithWarning.forEach((el) => {
// @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'.
warn(el.warning)
})
loadedDotEnvFiles
.filter((el): el is { warning: string } => 'warning' in el)
.forEach((el) => {
warn(el.warning)
})

// @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'.
return response.filter((el) => el.file && el.env)
return loadedDotEnvFiles.filter((el): el is LoadedDotEnvFile => 'file' in el && 'env' in el)
}

// in the user configuration, the order is highest to lowest
const defaultEnvFiles = ['.env.development.local', '.env.local', '.env.development', '.env']

// @ts-expect-error TS(7031) FIXME: Binding element 'projectDir' implicitly has an 'an... Remove this comment to see the full error message
export const tryLoadDotEnvFiles = async ({ dotenvFiles = defaultEnvFiles, projectDir }) => {
export const tryLoadDotEnvFiles = async ({
dotenvFiles = defaultEnvFiles,
projectDir,
}: {
dotenvFiles?: string[]
projectDir?: string
}): Promise<Array<LoadedDotEnvFile | { warning: string }>> => {
const results = await Promise.all(
dotenvFiles.map(async (file) => {
const filepath = path.resolve(projectDir, file)
const filepath = path.resolve(projectDir ?? '', file)
try {
const isFile = await isFileAsync(filepath)
if (!isFile) {
return
}
} catch (error) {
return {
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
warning: `Failed reading env variables from file: ${filepath}: ${error.message}`,
warning: `Failed reading env variables from file: ${filepath}: ${
error instanceof Error ? error.message : error?.toString()
}`,
}
}
const content = await readFile(filepath, 'utf-8')
Expand All @@ -48,5 +62,5 @@ export const tryLoadDotEnvFiles = async ({ dotenvFiles = defaultEnvFiles, projec
)

// we return in order of lowest to highest priority
return results.filter(Boolean).reverse()
return results.filter((result): result is LoadedDotEnvFile => result != null).reverse()
}
4 changes: 2 additions & 2 deletions src/utils/proxy-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export const startProxyServer = async ({
siteInfo,
state,
}: {
accountId: string
addonsUrls: $TSFixMe
accountId?: string | undefined
addonsUrls: Record<string, string>
api?: NetlifyOptions['api']
blobsContext?: BlobsContextWithEdgeAccess
command: BaseCommand
Expand Down
Loading

0 comments on commit 4cce78d

Please sign in to comment.