Skip to content

Commit

Permalink
Merge branch 'main' into fix-ef-internal-functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Skn0tt committed Dec 14, 2023
2 parents 455040f + 8834fbc commit 361d09f
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 244 deletions.
221 changes: 79 additions & 142 deletions src/commands/base-command.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/commands/dev/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { getGeoCountryArgParser } from '../../utils/validation.js'
import BaseCommand from '../base-command.js'

import { createDevExecCommand } from './dev-exec.js'
import { type DevConfig } from './types.js'

/**
*
Expand Down Expand Up @@ -90,7 +91,6 @@ export const dev = async (options: OptionValues, command: BaseCommand) => {
const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify
config.dev = { ...config.dev }
config.build = { ...config.build }
/** @type {import('./types.js').DevConfig} */
const devConfig = {
framework: '#auto',
autoLaunch: Boolean(options.open),
Expand All @@ -99,7 +99,7 @@ export const dev = async (options: OptionValues, command: BaseCommand) => {
...(config.build.base && { base: config.build.base }),
...config.dev,
...options,
}
} as DevConfig

let { env } = cachedConfig

Expand Down
5 changes: 0 additions & 5 deletions src/commands/link/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
} catch (error_) {
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
if (error_.status === 404) {
// @ts-expect-error TS(2345) FIXME: Argument of type 'Error' is not assignable to para... Remove this comment to see the full error message
error(new Error(`Site ID '${siteId}' not found`))
} else {
// @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message
Expand All @@ -225,7 +224,6 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`)
}

if (!site) {
// @ts-expect-error TS(2345) FIXME: Argument of type 'Error' is not assignable to para... Remove this comment to see the full error message
error(new Error(`No site found`))
}

Expand Down Expand Up @@ -285,7 +283,6 @@ export const link = async (options: OptionValues, command: BaseCommand) => {
} catch (error_) {
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
if (error_.status === 404) {
// @ts-expect-error TS(2345) FIXME: Argument of type 'Error' is not assignable to para... Remove this comment to see the full error message
error(new Error(`Site id ${options.id} not found`))
} else {
// @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message
Expand Down Expand Up @@ -315,7 +312,6 @@ export const link = async (options: OptionValues, command: BaseCommand) => {
} catch (error_) {
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
if (error_.status === 404) {
// @ts-expect-error TS(2345) FIXME: Argument of type 'Error' is not assignable to para... Remove this comment to see the full error message
error(new Error(`${options.name} not found`))
} else {
// @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message
Expand All @@ -324,7 +320,6 @@ export const link = async (options: OptionValues, command: BaseCommand) => {
}

if (results.length === 0) {
// @ts-expect-error TS(2345) FIXME: Argument of type 'Error' is not assignable to para... Remove this comment to see the full error message
error(new Error(`No sites found named ${options.name}`))
}
const [firstSiteData] = results
Expand Down
4 changes: 2 additions & 2 deletions src/commands/serve/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ import { generateInspectSettings, startProxyServer } from '../../utils/proxy-ser
import { runBuildTimeline } from '../../utils/run-build.js'
import type { ServerSettings } from '../../utils/types.js'
import BaseCommand from '../base-command.js'
import { type DevConfig } from '../dev/types.js'

export const serve = async (options: OptionValues, command: BaseCommand) => {
const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify
config.dev = { ...config.dev }
config.build = { ...config.build }
/** @type {import('../dev/types').DevConfig} */
const devConfig = {
...(config.functionsDirectory && { functions: config.functionsDirectory }),
...(config.build.publish && { publish: config.build.publish }),
Expand All @@ -40,7 +40,7 @@ export const serve = async (options: OptionValues, command: BaseCommand) => {
// Override the `framework` value so that we start a static server and not
// the framework's development server.
framework: '#static',
}
} as DevConfig

let { env } = cachedConfig

Expand Down
26 changes: 20 additions & 6 deletions src/commands/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import type { NetlifyAPI } from 'netlify'

import StateConfig from '../utils/state-config.js'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type $TSFixMe = any;

export type NetlifySite = {
root?: string
configPath?: string
Expand All @@ -11,21 +14,32 @@ export type NetlifySite = {
set id(id: string): void
}

type PatchedConfig = NetlifyTOML & {
functionsDirectory?: string
build: NetlifyTOML['build'] & {
functionsSource?: string
}
dev: NetlifyTOML['dev'] & {
functions?: string
}
}

/**
* The netlify object inside each command with the state
*/
export type NetlifyOptions = {
api: NetlifyAPI
apiOpts: unknown
// poorly duck type the missing api functions
api: NetlifyAPI & Record<string, (...args: $TSFixMe) => Promise<$TSFixMe>>
apiOpts: $TSFixMe
repositoryRoot: string
/** Absolute path of the netlify configuration file */
configFilePath: string
/** Relative path of the netlify configuration file */
relConfigFilePath: string
site: NetlifySite
siteInfo: unknown
config: NetlifyTOML
cachedConfig: Record<string, unknown>
globalConfig: unknown
siteInfo: $TSFixMe
config: PatchedConfig
cachedConfig: Record<string, $TSFixMe>
globalConfig: $TSFixMe
state: StateConfig
}
13 changes: 3 additions & 10 deletions src/utils/command-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,23 +184,16 @@ export const warn = (message = '') => {
log(` ${bang} Warning: ${message}`)
}

/**
* throws an error or log it
* @param {unknown} message
* @param {object} [options]
* @param {boolean} [options.exit]
*/
export const error = (message = '', options = {}) => {
/** Throws an error or logs it */
export const error = (message: Error | string = '', options: { exit?: boolean } = {}) => {
const err =
// @ts-expect-error TS(2358) FIXME: The left-hand side of an 'instanceof' expression m... Remove this comment to see the full error message
message instanceof Error
? message
: // eslint-disable-next-line unicorn/no-nested-ternary
typeof message === 'string'
? new Error(message)
: /** @type {Error} */ { message, stack: undefined, name: 'Error' }
: { message, stack: undefined, name: 'Error' }

// @ts-expect-error TS(2339) FIXME: Property 'exit' does not exist on type '{}'.
if (options.exit === false) {
const bang = chalk.red(BANG)
if (process.env.DEBUG) {
Expand Down
28 changes: 12 additions & 16 deletions src/utils/detect-server-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { EOL } from 'os'
import { dirname, relative, resolve } from 'path'

import { Project, Settings, getFramework, getSettings } from '@netlify/build-info'
import { OptionValues } from 'commander'
import type { OptionValues } from 'commander'
import getPort from 'get-port'

import BaseCommand from '../commands/base-command.js'
import { DevConfig } from '../commands/dev/types.js'
import { type DevConfig } from '../commands/dev/types.js'

import { detectFrameworkSettings } from './build-info.js'
import { NETLIFYDEVWARN, chalk, log } from './command-helpers.js'
Expand Down Expand Up @@ -152,7 +152,7 @@ const handleStaticServer = async ({
/**
* Retrieves the settings from a framework
*/
const getSettingsFromDetectedSettings = (settings: Settings): BaseServerSettings | undefined => {
const getSettingsFromDetectedSettings = (command: BaseCommand, settings?: Settings) => {
if (!settings) {
return
}
Expand All @@ -164,7 +164,7 @@ const getSettingsFromDetectedSettings = (settings: Settings): BaseServerSettings
framework: settings.framework.name,
env: settings.env,
pollingStrategies: settings.pollingStrategies,
plugins: getPluginsToAutoInstall(settings.plugins_from_config_file, settings.plugins_recommended),
plugins: getPluginsToAutoInstall(command, settings.plugins_from_config_file, settings.plugins_recommended),
}
}

Expand Down Expand Up @@ -227,28 +227,23 @@ const mergeSettings = async ({
/**
* Handles a forced framework and retrieves the settings for it
*/
const handleForcedFramework = async ({
devConfig,
project,
workingDir,
workspacePackage,
}: {
const handleForcedFramework = async (options: {
command: BaseCommand
devConfig: DevConfig
project: Project
workingDir: string
workspacePackage?: string
}): Promise<BaseServerSettings> => {
// this throws if `devConfig.framework` is not a supported framework
const framework = await getFramework(devConfig.framework, project)
const settings = await getSettings(framework, project, workspacePackage || '')
const frameworkSettings = getSettingsFromDetectedSettings(settings)
return mergeSettings({ devConfig, workingDir, frameworkSettings })
const framework = await getFramework(options.devConfig.framework, options.project)
const settings = await getSettings(framework, options.project, options.workspacePackage || '')
const frameworkSettings = getSettingsFromDetectedSettings(options.command, settings)
return mergeSettings({ devConfig: options.devConfig, workingDir: options.workingDir, frameworkSettings })
}

/**
* Get the server settings based on the flags and the devConfig
*/

const detectServerSettings = async (
devConfig: DevConfig,
flags: OptionValues,
Expand All @@ -267,7 +262,7 @@ const detectServerSettings = async (

const runDetection = !hasCommandAndTargetPort(devConfig)
const frameworkSettings = runDetection
? getSettingsFromDetectedSettings(await detectFrameworkSettings(command, 'dev'))
? getSettingsFromDetectedSettings(command, await detectFrameworkSettings(command, 'dev'))
: undefined
if (frameworkSettings === undefined && runDetection) {
log(`${NETLIFYDEVWARN} No app server detected. Using simple static server`)
Expand All @@ -288,6 +283,7 @@ const detectServerSettings = async (
validateFrameworkConfig({ devConfig })
// this is when the user explicitly configures a framework, e.g. `framework = "gatsby"`
settings = await handleForcedFramework({
command,
devConfig,
project: command.project,
workingDir: command.workingDir,
Expand Down
50 changes: 26 additions & 24 deletions src/utils/init/utils.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,57 @@
import { writeFile } from 'fs/promises'
import path from 'path'

import { NetlifyConfig } from '@netlify/build'
import { Settings } from '@netlify/build-info'
import cleanDeep from 'clean-deep'
import inquirer from 'inquirer'

import BaseCommand from '../../commands/base-command.js'
import { fileExistsAsync } from '../../lib/fs.js'
import { normalizeBackslash } from '../../lib/path.js'
import { detectBuildSettings } from '../build-info.js'
import { chalk, error as failAndExit, log, warn } from '../command-helpers.js'

import { getRecommendPlugins, getUIPlugins } from './plugins.js'

// these plugins represent runtimes that are
// expected to be "automatically" installed. Even though
// they can be installed on package/toml, we always
// want them installed in the site settings. When installed
// there our build will automatically install the latest without
// user management of the versioning.
const pluginsToAlwaysInstall = new Set(['@netlify/plugin-nextjs'])
const formatTitle = (title: string) => chalk.cyan(title)

/**
* Retrieve a list of plugins to auto install
* @param pluginsToAlwaysInstall these plugins represent runtimes that are
* expected to be "automatically" installed. Even though
* they can be installed on package/toml, we always
* want them installed in the site settings. When installed
* there our build will automatically install the latest without
* user management of the versioning.
* @param pluginsInstalled
* @param pluginsRecommended
* @returns
*/
export const getPluginsToAutoInstall = (pluginsInstalled: string[] = [], pluginsRecommended: string[] = []) =>
pluginsRecommended.reduce(
export const getPluginsToAutoInstall = (
command: BaseCommand,
pluginsInstalled: string[] = [],
pluginsRecommended: string[] = [],
) => {
const nextRuntime = '@netlify/plugin-nextjs'
const pluginsToAlwaysInstall = new Set([nextRuntime])
return pluginsRecommended.reduce(
(acc, plugin) =>
pluginsInstalled.includes(plugin) && !pluginsToAlwaysInstall.has(plugin) ? acc : [...acc, plugin],
[] as string[],
)

}
/**
*
* @param {Partial<import('@netlify/build-info').Settings>} settings
* @param {*} config
* @param {import('../../commands/base-command.js').default} command
*/
// @ts-expect-error TS(7006) FIXME: Parameter 'settings' implicitly has an 'any' type.
const normalizeSettings = (settings, config, command) => {
const plugins = getPluginsToAutoInstall(settings.plugins_from_config_file, settings.plugins_recommended)
const normalizeSettings = (settings: Settings, config: NetlifyConfig, command: BaseCommand) => {
const plugins = getPluginsToAutoInstall(command, settings.plugins_from_config_file, settings.plugins_recommended)
const recommendedPlugins = getRecommendPlugins(plugins, config)

return {
defaultBaseDir: settings.baseDirectory ?? command.project.relativeBaseDirectory ?? '',
defaultBuildCmd: config.build.command || settings.buildCommand,
defaultBuildDir: settings.dist,
// @ts-expect-error types need to be fixed on @netlify/build
defaultFunctionsDir: config.build.functions || 'netlify/functions',
recommendedPlugins,
}
Expand Down Expand Up @@ -102,7 +110,7 @@ export const getBuildSettings = async ({ command, config }) => {
await normalizeSettings(setting, config, command)

if (recommendedPlugins.length !== 0 && setting.framework?.name) {
log(`Configuring ${formatTitle(setting.framework?.name)} runtime...`)
log(`Configuring ${formatTitle(setting.framework.name)} runtime...`)
log()
}

Expand Down Expand Up @@ -206,12 +214,6 @@ export const formatErrorMessage = ({ error, message }) => {
return `${message} with error: ${chalk.red(errorMessage)}`
}

/**
* @param {string} title
*/
// @ts-expect-error TS(7006) FIXME: Parameter 'title' implicitly has an 'any' type.
const formatTitle = (title) => chalk.cyan(title)

// @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 createDeployKey = async ({ api }) => {
try {
Expand Down
23 changes: 9 additions & 14 deletions tests/integration/commands/dev/scheduled-functions.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import { describe, expect, test } from 'vitest'

import { FixtureTestContext, setupFixtureTests } from '../../utils/fixture.js'
import got from '../../utils/got.js'
import fetch from 'node-fetch'
import { pause } from '../../utils/pause.js'

describe('scheduled functions', () => {
setupFixtureTests('dev-server-with-functions', { devServer: true }, () => {
test<FixtureTestContext>('should emulate next_run for scheduled functions', async ({ devServer }) => {
const response = await got(`http://localhost:${devServer.port}/.netlify/functions/scheduled-isc`, {
throwHttpErrors: false,
retry: { limit: 0 },
})
const response = await fetch(`http://localhost:${devServer.port}/.netlify/functions/scheduled-isc`, {})

expect(response.statusCode).toBe(200)
expect(response.status).toBe(200)
})
})

setupFixtureTests('dev-server-with-functions', { devServer: true }, () => {
test<FixtureTestContext>('should detect file changes to scheduled function', async ({ devServer, fixture }) => {
const { body } = await got(`http://localhost:${devServer.port}/.netlify/functions/ping`, {
throwHttpErrors: false,
retry: { limit: 0 },
})
const body = await fetch(`http://localhost:${devServer.port}/.netlify/functions/ping`, {}).then((res) =>
res.text(),
)

expect(body).toBe('ping')

Expand All @@ -43,10 +39,9 @@ describe('scheduled functions', () => {
const DETECT_FILE_CHANGE_DELAY = 500
await pause(DETECT_FILE_CHANGE_DELAY)

const { body: warning } = await got(`http://localhost:${devServer.port}/.netlify/functions/ping`, {
throwHttpErrors: false,
retry: { limit: 0 },
})
const warning = await fetch(`http://localhost:${devServer.port}/.netlify/functions/ping`, {}).then((res) =>
res.text(),
)

expect(warning).toContain('Your function returned `body`')
})
Expand Down
Loading

0 comments on commit 361d09f

Please sign in to comment.