From 6b19d20187491b98cdefc8550d525911da996eb2 Mon Sep 17 00:00:00 2001 From: Lukas Holzer Date: Mon, 11 Mar 2024 16:32:57 +0100 Subject: [PATCH 1/4] fix: broken deploy with --skip-functions-cache as the getPathInProject function is not monorepo aware this breaks on monorepos --- .eslintrc.cjs | 1 + src/commands/base-command.ts | 7 ++ src/commands/deploy/deploy.ts | 134 +++++++++++++++-------------- src/commands/dev/dev.ts | 3 +- src/commands/serve/serve.ts | 1 + src/lib/edge-functions/deploy.ts | 11 +-- src/lib/edge-functions/proxy.ts | 104 ++++++++++------------ src/lib/edge-functions/registry.ts | 10 ++- src/lib/settings.ts | 1 + src/utils/deploy/deploy-site.ts | 32 +++---- src/utils/deploy/hash-fns.ts | 46 ++++++---- src/utils/proxy-server.ts | 3 + src/utils/proxy.ts | 21 +++-- 13 files changed, 194 insertions(+), 180 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6f9c2c6354f..1011b414bef 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -42,6 +42,7 @@ module.exports = { 'unicorn/consistent-destructuring': 0, // TODO: harmonize with filename snake_case in other Netlify Dev projects 'unicorn/filename-case': [2, { case: 'kebabCase' }], + 'max-params': 'off', }, overrides: [ ...overrides, diff --git a/src/commands/base-command.ts b/src/commands/base-command.ts index 01fbedeacb0..b7400b4e201 100644 --- a/src/commands/base-command.ts +++ b/src/commands/base-command.ts @@ -695,6 +695,13 @@ export default class BaseCommand extends Command { } } + /** + * get a path inside the `.netlify` project folder + */ + getPathInProject(...paths: string[]): string { + return join(this.workspacePackage || '', '.netlify', ...paths) + } + /** * Returns the context that should be used in case one hasn't been explicitly * set. The default context is `dev` most of the time, but some commands may diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index cda892b521e..8e27b299231 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -1,3 +1,4 @@ +import { Stats } from 'fs' import { stat } from 'fs/promises' import { basename, resolve } from 'path' @@ -37,6 +38,7 @@ import openBrowser from '../../utils/open-browser.js' import BaseCommand from '../base-command.js' import { link } from '../link/link.js' import { sitesCreate } from '../sites/sites-create.js' +import { $TSFixMe } from '../types.js' // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message const triggerDeploy = async ({ api, options, siteData, siteId }) => { @@ -65,19 +67,21 @@ const triggerDeploy = async ({ api, options, siteData, siteId }) => { } } -/** - * Retrieves the folder containing the static files that need to be deployed - * @param {object} config - * @param {import('../base-command.js').default} config.command The process working directory - * @param {object} config.config - * @param {import('commander').OptionValues} config.options - * @param {object} config.site - * @param {object} config.siteData - * @returns {Promise} - */ -// @ts-expect-error TS(7031) FIXME: Binding element 'command' implicitly has an 'any' ... Remove this comment to see the full error message -const getDeployFolder = async ({ command, config, options, site, siteData }) => { - let deployFolder +/** Retrieves the folder containing the static files that need to be deployed */ +const getDeployFolder = async ({ + command, + config, + options, + site, + siteData, +}: { + command: BaseCommand + config: $TSFixMe + options: OptionValues + site: $TSFixMe + siteData: $TSFixMe +}): Promise => { + let deployFolder: string | undefined // if the `--dir .` flag is provided we should resolve it to the working directory. // - in regular sites this is the `process.cwd` // - in mono repositories this will be the root of the jsWorkspace @@ -102,31 +106,26 @@ const getDeployFolder = async ({ command, config, options, site, siteData }) => filter: (input) => resolve(command.workingDir, input), }, ]) - deployFolder = promptPath + deployFolder = promptPath as string } return deployFolder } -/** - * @param {string} deployFolder - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'deployFolder' implicitly has an 'any' t... Remove this comment to see the full error message -const validateDeployFolder = async (deployFolder) => { - /** @type {import('fs').Stats} */ - let stats +const validateDeployFolder = async (deployFolder: string) => { + let stats: Stats try { stats = await stat(deployFolder) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.code === 'ENOENT') { - return error(`No such directory ${deployFolder}! Did you forget to run a build?`) - } + if (error_ && typeof error_ === 'object' && 'code' in error_) { + if (error_.code === 'ENOENT') { + return error(`No such directory ${deployFolder}! Did you forget to run a build?`) + } - // Improve the message of permission errors - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.code === 'EACCES') { - return error('Permission error when trying to access deploy folder') + // Improve the message of permission errors + if (error_.code === 'EACCES') { + return error('Permission error when trying to access deploy folder') + } } throw error_ } @@ -137,19 +136,22 @@ const validateDeployFolder = async (deployFolder) => { return stats } -/** - * get the functions directory - * @param {object} config - * @param {object} config.config - * @param {import('commander').OptionValues} config.options - * @param {object} config.site - * @param {object} config.siteData - * @param {string} config.workingDir // The process working directory - * @returns {string|undefined} - */ -// @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message -const getFunctionsFolder = ({ config, options, site, siteData, workingDir }) => { - let functionsFolder +/** get the functions directory */ +const getFunctionsFolder = ({ + config, + options, + site, + siteData, + workingDir, +}: { + config: $TSFixMe + options: OptionValues + site: $TSFixMe + siteData: $TSFixMe + /** The process working directory where the build command is executed */ + workingDir: string +}): string | undefined => { + let functionsFolder: string | undefined // Support "functions" and "Functions" const funcConfig = config.functionsDirectory if (options.functions) { @@ -162,30 +164,24 @@ const getFunctionsFolder = ({ config, options, site, siteData, workingDir }) => return functionsFolder } -/** - * - * @param {string|undefined} functionsFolder - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'functionsFolder' implicitly has an 'any... Remove this comment to see the full error message -const validateFunctionsFolder = async (functionsFolder) => { - /** @type {import('fs').Stats|undefined} */ - let stats +const validateFunctionsFolder = async (functionsFolder: string | undefined) => { + let stats: Stats | undefined if (functionsFolder) { // we used to hard error if functions folder is specified but doesn't exist // but this was too strict for onboarding. we can just log a warning. try { stats = await stat(functionsFolder) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.code === 'ENOENT') { - log( - `Functions folder "${functionsFolder}" specified but it doesn't exist! Will proceed without deploying functions`, - ) - } - // Improve the message of permission errors - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.code === 'EACCES') { - error('Permission error when trying to access functions folder') + if (error_ && typeof error_ === 'object' && 'code' in error_) { + if (error_.code === 'ENOENT') { + log( + `Functions folder "${functionsFolder}" specified but it doesn't exist! Will proceed without deploying functions`, + ) + } + // Improve the message of permission errors + if (error_.code === 'EACCES') { + error('Permission error when trying to access functions folder') + } } } } @@ -197,8 +193,13 @@ const validateFunctionsFolder = async (functionsFolder) => { return stats } -// @ts-expect-error TS(7031) FIXME: Binding element 'deployFolder' implicitly has an '... Remove this comment to see the full error message -const validateFolders = async ({ deployFolder, functionsFolder }) => { +const validateFolders = async ({ + deployFolder, + functionsFolder, +}: { + deployFolder: string + functionsFolder?: string +}) => { const deployFolderStat = await validateDeployFolder(deployFolder) const functionsFolderStat = await validateFunctionsFolder(functionsFolder) return { deployFolderStat, functionsFolderStat } @@ -353,13 +354,14 @@ const uploadDeployBlobs = async ({ silent, siteId, }: { - cachedConfig: any + cachedConfig: $TSFixMe deployId: string options: OptionValues packagePath?: string silent: boolean siteId: string }) => { + // eslint-disable-next-line @typescript-eslint/no-empty-function const statusCb = silent ? () => {} : deployProgressCb() statusCb({ @@ -402,7 +404,6 @@ const runDeploy = async ({ alias, // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message api, - // @ts-expect-error TS(7031) FIXME: Binding element 'command' implicitly has an 'any' ... Remove this comment to see the full error message command, // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message config, @@ -433,6 +434,7 @@ const runDeploy = async ({ title, }: { functionsFolder?: string + command: BaseCommand }) => { let results let deployId @@ -486,11 +488,12 @@ const runDeploy = async ({ packagePath: command.workspacePackage, }) - results = await deploySite(api, siteId, deployFolder, { + results = await deploySite(command, api, siteId, deployFolder, { // @ts-expect-error FIXME config, fnDir: functionDirectories, functionsConfig, + // eslint-disable-next-line @typescript-eslint/no-empty-function statusCb: silent ? () => {} : deployProgressCb(), deployTimeout, syncFileLimit: SYNC_FILE_LIMIT, @@ -572,6 +575,7 @@ const bundleEdgeFunctions = async (options, command: BaseCommand) => { // eslint-disable-next-line n/prefer-global/process, unicorn/prefer-set-has const argv = process.argv.slice(2) const statusCb = + // eslint-disable-next-line @typescript-eslint/no-empty-function options.silent || argv.includes('--json') || argv.includes('--silent') ? () => {} : deployProgressCb() statusCb({ diff --git a/src/commands/dev/dev.ts b/src/commands/dev/dev.ts index a9e583ea60e..15e574bacf0 100644 --- a/src/commands/dev/dev.ts +++ b/src/commands/dev/dev.ts @@ -1,8 +1,8 @@ import process from 'process' -import { OptionValues, Option } from 'commander' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@net... Remove this comment to see the full error message import { applyMutations } from '@netlify/config' +import { OptionValues, Option } from 'commander' import { BLOBS_CONTEXT_VARIABLE, encodeBlobsContext, getBlobsContext } from '../../lib/blobs/blobs.js' import { promptEditorHelper } from '../../lib/edge-functions/editor-helper.js' @@ -216,6 +216,7 @@ export const dev = async (options: OptionValues, command: BaseCommand) => { await startProxyServer({ addonsUrls, blobsContext, + command, config: mutatedConfig, configPath: configPathOverride, debug: options.debug, diff --git a/src/commands/serve/serve.ts b/src/commands/serve/serve.ts index 58809950c0c..9f61d0d487b 100644 --- a/src/commands/serve/serve.ts +++ b/src/commands/serve/serve.ts @@ -141,6 +141,7 @@ export const serve = async (options: OptionValues, command: BaseCommand) => { // @ts-expect-error TS(2345) FIXME: Argument of type '{ addonsUrls: { [k: string]: any... Remove this comment to see the full error message const url = await startProxyServer({ addonsUrls, + command, config, configPath: configPathOverride, debug: options.debug, diff --git a/src/lib/edge-functions/deploy.ts b/src/lib/edge-functions/deploy.ts index f593ffe3523..41494dd92ba 100644 --- a/src/lib/edge-functions/deploy.ts +++ b/src/lib/edge-functions/deploy.ts @@ -12,7 +12,7 @@ const distPath = getPathInProject([EDGE_FUNCTIONS_FOLDER]) * @param {*} file */ // @ts-expect-error TS(7006) FIXME: Parameter 'workingDir' implicitly has an 'any' typ... Remove this comment to see the full error message -export const deployFileNormalizer = (workingDir, file) => { +export const deployFileNormalizer = (workingDir: string, file) => { const absoluteDistPath = join(workingDir, distPath) const isEdgeFunction = file.root === absoluteDistPath const normalizedPath = isEdgeFunction ? `${PUBLIC_URL_PATH}/${file.normalizedPath}` : file.normalizedPath @@ -23,11 +23,7 @@ export const deployFileNormalizer = (workingDir, file) => { } } -/** - * @param {string} workingDir - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'workingDir' implicitly has an 'any' typ... Remove this comment to see the full error message -export const getDistPathIfExists = async (workingDir) => { +export const getDistPathIfExists = async (workingDir: string) => { try { const absoluteDistPath = join(workingDir, distPath) const stats = await stat(absoluteDistPath) @@ -42,5 +38,4 @@ export const getDistPathIfExists = async (workingDir) => { } } -// @ts-expect-error TS(7006) FIXME: Parameter 'filePath' implicitly has an 'any' type. -export const isEdgeFunctionFile = (filePath) => filePath.startsWith(`${PUBLIC_URL_PATH}/`) +export const isEdgeFunctionFile = (filePath: string) => filePath.startsWith(`${PUBLIC_URL_PATH}/`) diff --git a/src/lib/edge-functions/proxy.ts b/src/lib/edge-functions/proxy.ts index 1dbef13b61f..839c20366b6 100644 --- a/src/lib/edge-functions/proxy.ts +++ b/src/lib/edge-functions/proxy.ts @@ -6,16 +6,18 @@ import { join, resolve } from 'path' import * as bundler from '@netlify/edge-bundler' import getAvailablePort from 'get-port' +import BaseCommand from '../../commands/base-command.js' +import { $TSFixMe } from '../../commands/types.js' import { NETLIFYDEVERR, chalk, error as printError } from '../../utils/command-helpers.js' -import { getFeatureFlagsFromSiteInfo } from '../../utils/feature-flags.js' +import { FeatureFlags, getFeatureFlagsFromSiteInfo } from '../../utils/feature-flags.js' +import { BlobsContext } from '../blobs/blobs.js' import { getGeoLocation } from '../geo-location.js' -import { getPathInProject } from '../settings.js' import { startSpinner, stopSpinner } from '../spinner.js' import { getBootstrapURL } from './bootstrap.js' import { DIST_IMPORT_MAP_PATH, EDGE_FUNCTIONS_SERVE_FOLDER } from './consts.js' -import { headers, getFeatureFlagsHeader, getInvocationMetadataHeader } from './headers.js' -import { EdgeFunctionsRegistry } from './registry.js' +import { getFeatureFlagsHeader, getInvocationMetadataHeader, headers } from './headers.js' +import { EdgeFunctionsRegistry, type Config } from './registry.js' const headersSymbol = Symbol('Edge Functions Headers') @@ -75,65 +77,46 @@ export const createAccountInfoHeader = (accountInfo = {}) => { return Buffer.from(accountString).toString('base64') } -/** - * - * @param {object} config - * @param {*} config.accountId - * @param {import("../blobs/blobs.js").BlobsContext} config.blobsContext - * @param {*} config.config - * @param {*} config.configPath - * @param {*} config.debug - * @param {*} config.env - * @param {*} config.geoCountry - * @param {*} config.geolocationMode - * @param {*} config.getUpdatedConfig - * @param {*} config.inspectSettings - * @param {*} config.mainPort - * @param {boolean=} config.offline - * @param {*} config.passthroughPort - * @param {*} config.projectDir - * @param {*} config.settings - * @param {*} config.siteInfo - * @param {*} config.state - * @returns - */ export const initializeProxy = async ({ - // @ts-expect-error TS(7031) FIXME: Binding element 'accountId' implicitly has an 'any... Remove this comment to see the full error message accountId, - // @ts-expect-error TS(7031) FIXME: Binding element 'blobsContext' implicitly has an '... Remove this comment to see the full error message blobsContext, - // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message + command, config, - // @ts-expect-error TS(7031) FIXME: Binding element 'configPath' implicitly has an 'an... Remove this comment to see the full error message configPath, - // @ts-expect-error TS(7031) FIXME: Binding element 'debug' implicitly has an 'any' ty... Remove this comment to see the full error message debug, - // @ts-expect-error TS(7031) FIXME: Binding element 'configEnv' implicitly has an 'any... Remove this comment to see the full error message env: configEnv, - // @ts-expect-error TS(7031) FIXME: Binding element 'geoCountry' implicitly has an 'an... Remove this comment to see the full error message geoCountry, - // @ts-expect-error TS(7031) FIXME: Binding element 'geolocationMode' implicitly has a... Remove this comment to see the full error message geolocationMode, - // @ts-expect-error TS(7031) FIXME: Binding element 'getUpdatedConfig' implicitly has ... Remove this comment to see the full error message getUpdatedConfig, - // @ts-expect-error TS(7031) FIXME: Binding element 'inspectSettings' implicitly has a... Remove this comment to see the full error message inspectSettings, - // @ts-expect-error TS(7031) FIXME: Binding element 'mainPort' implicitly has an 'any'... Remove this comment to see the full error message mainPort, - // @ts-expect-error TS(7031) FIXME: Binding element 'offline' implicitly has an 'any' ... Remove this comment to see the full error message offline, - // @ts-expect-error TS(7031) FIXME: Binding element 'passthroughPort' implicitly has a... Remove this comment to see the full error message passthroughPort, - // @ts-expect-error TS(7031) FIXME: Binding element 'projectDir' implicitly has an 'an... Remove this comment to see the full error message projectDir, - // @ts-expect-error TS(7031) FIXME: Binding element 'repositoryRoot' implicitly has an... Remove this comment to see the full error message repositoryRoot, - // @ts-expect-error TS(7031) FIXME: Binding element 'settings' implicitly has an 'any'... Remove this comment to see the full error message settings, - // @ts-expect-error TS(7031) FIXME: Binding element 'siteInfo' implicitly has an 'any'... Remove this comment to see the full error message siteInfo, - // @ts-expect-error TS(7031) FIXME: Binding element 'state' implicitly has an 'any' ty... Remove this comment to see the full error message state, +}: { + accountId: string + blobsContext: BlobsContext + command: BaseCommand + config: $TSFixMe + configPath: string + debug: boolean + env: $TSFixMe + offline: $TSFixMe + geoCountry: $TSFixMe + geolocationMode: $TSFixMe + getUpdatedConfig: $TSFixMe + inspectSettings: $TSFixMe + mainPort: $TSFixMe + passthroughPort: $TSFixMe + projectDir: string + repositoryRoot?: string + settings: $TSFixMe + siteInfo: $TSFixMe + state: $TSFixMe }) => { const userFunctionsPath = config.build.edge_functions const isolatePort = await getAvailablePort() @@ -145,6 +128,7 @@ export const initializeProxy = async ({ // the network if needed. We don't want to wait for that to be completed, or // the command will be left hanging. const server = prepareServer({ + command, config, configPath, debug, @@ -213,32 +197,35 @@ export const initializeProxy = async ({ export const isEdgeFunctionsRequest = (req) => req[headersSymbol] !== undefined const prepareServer = async ({ - // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message + command, config, - // @ts-expect-error TS(7031) FIXME: Binding element 'configPath' implicitly has an 'an... Remove this comment to see the full error message configPath, - // @ts-expect-error TS(7031) FIXME: Binding element 'debug' implicitly has an 'any' ty... Remove this comment to see the full error message debug, - // @ts-expect-error TS(7031) FIXME: Binding element 'directory' implicitly has an 'any... Remove this comment to see the full error message directory, - // @ts-expect-error TS(7031) FIXME: Binding element 'configEnv' implicitly has an 'any... Remove this comment to see the full error message env: configEnv, - // @ts-expect-error TS(7031) FIXME: Binding element 'featureFlags' implicitly has an '... Remove this comment to see the full error message featureFlags, - // @ts-expect-error TS(7031) FIXME: Binding element 'getUpdatedConfig' implicitly has ... Remove this comment to see the full error message getUpdatedConfig, - // @ts-expect-error TS(7031) FIXME: Binding element 'inspectSettings' implicitly has a... Remove this comment to see the full error message inspectSettings, - // @ts-expect-error TS(7031) FIXME: Binding element 'port' implicitly has an 'any' typ... Remove this comment to see the full error message port, - // @ts-expect-error TS(7031) FIXME: Binding element 'projectDir' implicitly has an 'an... Remove this comment to see the full error message projectDir, - // @ts-expect-error TS(7031) FIXME: Binding element 'repositoryRoot' implicitly has an... Remove this comment to see the full error message repositoryRoot, +}: { + command: BaseCommand + config: $TSFixMe + configPath: string + debug: boolean + directory?: string + env: Record + featureFlags: FeatureFlags + getUpdatedConfig: () => Promise + inspectSettings: Parameters[0]['inspectSettings'] + port: number + projectDir: string + repositoryRoot?: string }) => { try { - const distImportMapPath = getPathInProject([DIST_IMPORT_MAP_PATH]) - const servePath = resolve(projectDir, getPathInProject([EDGE_FUNCTIONS_SERVE_FOLDER])) + const distImportMapPath = command.getPathInProject(DIST_IMPORT_MAP_PATH) + const servePath = resolve(projectDir, command.getPathInProject(EDGE_FUNCTIONS_SERVE_FOLDER)) await rm(servePath, { force: true, recursive: true }) @@ -261,11 +248,12 @@ const prepareServer = async ({ servePath, }) const registry = new EdgeFunctionsRegistry({ + command, bundler, config, configPath, debug, - directories: [directory].filter(Boolean), + directories: [directory].filter(Boolean) as string[], env: configEnv, featureFlags, getUpdatedConfig, diff --git a/src/lib/edge-functions/registry.ts b/src/lib/edge-functions/registry.ts index 35932648971..b4829b7fd63 100644 --- a/src/lib/edge-functions/registry.ts +++ b/src/lib/edge-functions/registry.ts @@ -4,6 +4,7 @@ import { fileURLToPath } from 'url' import type { Declaration, EdgeFunction, FunctionConfig, Manifest, ModuleGraph } from '@netlify/edge-bundler' +import BaseCommand from '../../commands/base-command.js' import { NETLIFYDEVERR, NETLIFYDEVLOG, @@ -17,12 +18,11 @@ import { } from '../../utils/command-helpers.js' import type { FeatureFlags } from '../../utils/feature-flags.js' import { MultiMap } from '../../utils/multimap.js' -import { getPathInProject } from '../settings.js' import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from './consts.js' // TODO: Replace with a proper type for the entire config object. -interface Config { +export interface Config { edge_functions?: Declaration[] [key: string]: unknown } @@ -33,6 +33,7 @@ type RunIsolate = Awaited { /** * get a path inside the project folder * @param {string[]} paths + * @deprecated This does not work in monorepos use Basecommand.getPathInProject instead * @returns {string} */ // @ts-expect-error TS(7006) FIXME: Parameter 'paths' implicitly has an 'any' type. diff --git a/src/utils/deploy/deploy-site.ts b/src/utils/deploy/deploy-site.ts index 32b0f5965c0..324b5627cfb 100644 --- a/src/utils/deploy/deploy-site.ts +++ b/src/utils/deploy/deploy-site.ts @@ -3,6 +3,8 @@ import { rm } from 'fs/promises' import cleanDeep from 'clean-deep' import { temporaryDirectory } from 'tempy' +import BaseCommand from '../../commands/base-command.js' +import { type $TSFixMe } from '../../commands/types.js' import { deployFileNormalizer, getDistPathIfExists, isEdgeFunctionFile } from '../../lib/edge-functions/deploy.js' import { warn } from '../command-helpers.js' @@ -19,9 +21,16 @@ import hashFns from './hash-fns.js' import uploadFiles from './upload-files.js' import { getUploadList, waitForDeploy, waitForDiff } from './util.js' +const buildStatsString = (possibleParts: Array) => { + const parts = possibleParts.filter(Boolean) + const message = parts.slice(0, -1).join(', ') + + return parts.length > 1 ? `${message} and ${parts[parts.length - 1]}` : message +} + export const deploySite = async ( - // @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type. - api, + command: BaseCommand, + api: $TSFixMe, // @ts-expect-error TS(7006) FIXME: Parameter 'siteId' implicitly has an 'any' type. siteId, // @ts-expect-error TS(7006) FIXME: Parameter 'dir' implicitly has an 'any' type. @@ -50,8 +59,6 @@ export const deploySite = async ( manifestPath, maxRetry = DEFAULT_MAX_RETRY, // @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message - siteEnv, - // @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message siteRoot, // @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message skipFunctionsCache, @@ -60,7 +67,6 @@ export const deploySite = async ( }, syncFileLimit = DEFAULT_SYNC_LIMIT, tmpDir = temporaryDirectory(), - // @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message workingDir, }: { concurrentHash?: number @@ -72,7 +78,8 @@ export const deploySite = async ( syncFileLimit?: number tmpDir?: string fnDir?: string[] - } = {}, + workingDir: string + }, ) => { statusCb({ type: 'hashing', @@ -95,18 +102,15 @@ export const deploySite = async ( normalizer: deployFileNormalizer.bind(null, workingDir), statusCb, }), - hashFns(fnDir, { + hashFns(command, fnDir, { functionsConfig, tmpDir, concurrentHash, hashAlgorithm, statusCb, assetType, - // @ts-expect-error TS(2345) FIXME: Argument of type '{ functionsConfig: any; tmpDir: ... Remove this comment to see the full error message - workingDir, manifestPath, skipFunctionsCache, - siteEnv, rootDir: siteRoot, }), hashConfig({ config }), @@ -211,11 +215,3 @@ For more information, visit https://ntl.fyi/cli-native-modules.`) } return deployManifest } - -// @ts-expect-error TS(7006) FIXME: Parameter 'possibleParts' implicitly has an 'any' ... Remove this comment to see the full error message -const buildStatsString = (possibleParts) => { - const parts = possibleParts.filter(Boolean) - const message = parts.slice(0, -1).join(', ') - - return parts.length > 1 ? `${message} and ${parts[parts.length - 1]}` : message -} diff --git a/src/utils/deploy/hash-fns.ts b/src/utils/deploy/hash-fns.ts index 08a40c1773b..ea11b93af59 100644 --- a/src/utils/deploy/hash-fns.ts +++ b/src/utils/deploy/hash-fns.ts @@ -8,7 +8,8 @@ import fromArray from 'from2-array' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'pump... Remove this comment to see the full error message import pumpModule from 'pump' -import { getPathInProject } from '../../lib/settings.js' +import BaseCommand from '../../commands/base-command.js' +import { $TSFixMe } from '../../commands/types.js' import { INTERNAL_FUNCTIONS_FOLDER } from '../functions/functions.js' import { hasherCtor, manifestCollectorCtor } from './hasher-segments.js' @@ -19,20 +20,23 @@ const pump = promisify(pumpModule) const MANIFEST_FILE_TTL = 12e4 const getFunctionZips = async ({ - // @ts-expect-error TS(7031) FIXME: Binding element 'directories' implicitly has an 'a... Remove this comment to see the full error message + command, directories, - // @ts-expect-error TS(7031) FIXME: Binding element 'functionsConfig' implicitly has a... Remove this comment to see the full error message functionsConfig, - // @ts-expect-error TS(7031) FIXME: Binding element 'manifestPath' implicitly has an '... Remove this comment to see the full error message manifestPath, - // @ts-expect-error TS(7031) FIXME: Binding element 'rootDir' implicitly has an 'any' ... Remove this comment to see the full error message rootDir, - // @ts-expect-error TS(7031) FIXME: Binding element 'skipFunctionsCache' implicitly ha... Remove this comment to see the full error message skipFunctionsCache, - // @ts-expect-error TS(7031) FIXME: Binding element 'statusCb' implicitly has an 'any'... Remove this comment to see the full error message statusCb, - // @ts-expect-error TS(7031) FIXME: Binding element 'tmpDir' implicitly has an 'any' t... Remove this comment to see the full error message tmpDir, +}: { + command: BaseCommand + directories: string[] + functionsConfig: $TSFixMe + manifestPath: $TSFixMe + rootDir: $TSFixMe + skipFunctionsCache: $TSFixMe + statusCb: $TSFixMe + tmpDir: $TSFixMe }) => { statusCb({ type: 'functions-manifest', @@ -79,31 +83,36 @@ const getFunctionZips = async ({ return await zipFunctions(directories, tmpDir, { basePath: rootDir, - configFileDirectories: [getPathInProject([INTERNAL_FUNCTIONS_FOLDER])], + configFileDirectories: [command.getPathInProject(INTERNAL_FUNCTIONS_FOLDER)], config: functionsConfig, }) } const hashFns = async ( - // @ts-expect-error TS(7006) FIXME: Parameter 'directories' implicitly has an 'any' ty... Remove this comment to see the full error message - directories, + command: BaseCommand, + directories: string[], { assetType = 'function', - // @ts-expect-error TS(7031) FIXME: Binding element 'concurrentHash' implicitly has an... Remove this comment to see the full error message concurrentHash, - // @ts-expect-error TS(7031) FIXME: Binding element 'functionsConfig' implicitly has a... Remove this comment to see the full error message functionsConfig, hashAlgorithm = 'sha256', - // @ts-expect-error TS(7031) FIXME: Binding element 'manifestPath' implicitly has an '... Remove this comment to see the full error message manifestPath, - // @ts-expect-error TS(7031) FIXME: Binding element 'rootDir' implicitly has an 'any' ... Remove this comment to see the full error message rootDir, - // @ts-expect-error TS(7031) FIXME: Binding element 'skipFunctionsCache' implicitly ha... Remove this comment to see the full error message skipFunctionsCache, - // @ts-expect-error TS(7031) FIXME: Binding element 'statusCb' implicitly has an 'any'... Remove this comment to see the full error message statusCb, - // @ts-expect-error TS(7031) FIXME: Binding element 'tmpDir' implicitly has an 'any' t... Remove this comment to see the full error message tmpDir, + }: { + /** @default 'function' */ + assetType?: string + concurrentHash?: number + functionsConfig: $TSFixMe + /** @default 'sha256' */ + hashAlgorithm?: string + manifestPath: $TSFixMe + rootDir: $TSFixMe + skipFunctionsCache: $TSFixMe + statusCb: $TSFixMe + tmpDir: $TSFixMe }, ) => { // Early out if no functions directories are configured. @@ -116,6 +125,7 @@ const hashFns = async ( } const functionZips = await getFunctionZips({ + command, directories, functionsConfig, manifestPath, diff --git a/src/utils/proxy-server.ts b/src/utils/proxy-server.ts index 1c69f4e74d4..0cd4f5ff6e7 100644 --- a/src/utils/proxy-server.ts +++ b/src/utils/proxy-server.ts @@ -64,6 +64,8 @@ export const startProxyServer = async ({ addonsUrls, // @ts-expect-error TS(7031) FIXME: Binding element 'blobsContext' implicitly has an '... Remove this comment to see the full error message blobsContext, + // @ts-expect-error TS(7031) FIXME: Binding element 'accountId' implicitly has an 'any... Remove this comment to see the full error message + command, // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message config, // @ts-expect-error TS(7031) FIXME: Binding element 'configPath' implicitly has an 'an... Remove this comment to see the full error message @@ -100,6 +102,7 @@ export const startProxyServer = async ({ const url = await startProxy({ addonsUrls, blobsContext, + command, config, configPath: configPath || site.configPath, debug, diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts index 4ce8054b7bd..f1278a2d12d 100644 --- a/src/utils/proxy.ts +++ b/src/utils/proxy.ts @@ -426,23 +426,23 @@ const MILLISEC_TO_SEC = 1e3 const initializeProxy = async function ({ // @ts-expect-error TS(7031) FIXME: Binding element 'configPath' implicitly has an 'any... Remove this comment to see the full error message - configPath, + config, // @ts-expect-error TS(7031) FIXME: Binding element 'distDir' implicitly has an 'any... Remove this comment to see the full error message - distDir, + configPath, // @ts-expect-error TS(7031) FIXME: Binding element 'env' implicitly has an 'any... Remove this comment to see the full error message - env, + distDir, // @ts-expect-error TS(7031) FIXME: Binding element 'host' implicitly has an 'any... Remove this comment to see the full error message - host, + env, // @ts-expect-error TS(7031) FIXME: Binding element 'imageProxy' implicitly has an 'any... Remove this comment to see the full error message - imageProxy, + host, // @ts-expect-error TS(7031) FIXME: Binding element 'port' implicitly has an 'any... Remove this comment to see the full error message - port, + imageProxy, // @ts-expect-error TS(7031) FIXME: Binding element 'projectDir' implicitly has an 'any... Remove this comment to see the full error message - projectDir, + port, // @ts-expect-error TS(7031) FIXME: Binding element 'siteInfo' implicitly has an 'any... Remove this comment to see the full error message - siteInfo, + projectDir, // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any... Remove this comment to see the full error message - config, + siteInfo, }) { const proxy = httpProxy.createProxyServer({ selfHandleResponse: true, @@ -809,6 +809,8 @@ export const startProxy = async function ({ addonsUrls, // @ts-expect-error TS(7031) FIXME: Binding element 'blobsContext' implicitly has an '... Remove this comment to see the full error message blobsContext, + // @ts-expect-error TS(7031) FIXME: Binding element 'accountId' implicitly has an 'any... Remove this comment to see the full error message + command, // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message config, // @ts-expect-error TS(7031) FIXME: Binding element 'configPath' implicitly has an 'an... Remove this comment to see the full error message @@ -843,6 +845,7 @@ export const startProxy = async function ({ const secondaryServerPort = settings.https ? await getAvailablePort() : null const functionsServer = settings.functionsPort ? `http://127.0.0.1:${settings.functionsPort}` : null const edgeFunctionsProxy = await initializeEdgeFunctionsProxy({ + command, blobsContext, config, configPath, From 8492ae09896f695d4603e9ffde2b735a7b593fae Mon Sep 17 00:00:00 2001 From: Lukas Holzer Date: Wed, 13 Mar 2024 11:24:50 +0100 Subject: [PATCH 2/4] chore: fix test --- src/utils/deploy/hash-fns.ts | 23 ++++++++++++----------- tests/unit/utils/deploy/hash-fns.test.js | 3 ++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/utils/deploy/hash-fns.ts b/src/utils/deploy/hash-fns.ts index ea11b93af59..49e3d093048 100644 --- a/src/utils/deploy/hash-fns.ts +++ b/src/utils/deploy/hash-fns.ts @@ -91,17 +91,7 @@ const getFunctionZips = async ({ const hashFns = async ( command: BaseCommand, directories: string[], - { - assetType = 'function', - concurrentHash, - functionsConfig, - hashAlgorithm = 'sha256', - manifestPath, - rootDir, - skipFunctionsCache, - statusCb, - tmpDir, - }: { + config: { /** @default 'function' */ assetType?: string concurrentHash?: number @@ -115,6 +105,17 @@ const hashFns = async ( tmpDir: $TSFixMe }, ) => { + const { + assetType = 'function', + concurrentHash, + functionsConfig, + hashAlgorithm = 'sha256', + manifestPath, + rootDir, + skipFunctionsCache, + statusCb, + tmpDir, + } = config || {} // Early out if no functions directories are configured. if (directories.length === 0) { return { functions: {}, functionsWithNativeModules: [], shaMap: {} } diff --git a/tests/unit/utils/deploy/hash-fns.test.js b/tests/unit/utils/deploy/hash-fns.test.js index 8083ff94d65..1c22c620a32 100644 --- a/tests/unit/utils/deploy/hash-fns.test.js +++ b/tests/unit/utils/deploy/hash-fns.test.js @@ -1,6 +1,7 @@ import { temporaryDirectory } from 'tempy' import { expect, test } from 'vitest' +import BaseCommand from '../../../../dist/commands/base-command.js' import { DEFAULT_CONCURRENT_HASH } from '../../../../dist/utils/deploy/constants.js' import hashFns from '../../../../dist/utils/deploy/hash-fns.js' import { withSiteBuilder } from '../../../integration/utils/site-builder.ts' @@ -20,7 +21,7 @@ test('Hashes files in a folder', async () => { .buildAsync() const expectedFunctions = ['hello', 'goodbye'] - const { fnShaMap, functions } = await hashFns(`${builder.directory}/functions`, { + const { fnShaMap, functions } = await hashFns(new BaseCommand(), `${builder.directory}/functions`, { tmpDir: temporaryDirectory(), concurrentHash: DEFAULT_CONCURRENT_HASH, statusCb() {}, From 20392ec0cc902bd2832a80b3132028d2c4d5f3a3 Mon Sep 17 00:00:00 2001 From: Lukas Holzer Date: Wed, 13 Mar 2024 12:17:02 +0100 Subject: [PATCH 3/4] Update src/commands/deploy/deploy.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eduardo Bouças --- src/commands/deploy/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index 8e27b299231..7a69e10248b 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -119,7 +119,7 @@ const validateDeployFolder = async (deployFolder: string) => { } catch (error_) { if (error_ && typeof error_ === 'object' && 'code' in error_) { if (error_.code === 'ENOENT') { - return error(`No such directory ${deployFolder}! Did you forget to run a build?`) + return error(`The deploy directory "${deployFolder}" has not been found. Did you forget to run 'netlify build'?`) } // Improve the message of permission errors From 8692793284577d609b53a39fdd821d4e2099b7bd Mon Sep 17 00:00:00 2001 From: Lukas Holzer Date: Wed, 13 Mar 2024 14:13:23 +0100 Subject: [PATCH 4/4] chore: fix linting errors and disable rules globally --- .eslintrc.cjs | 3 +++ src/commands/deploy/deploy.ts | 8 ++++---- tests/integration/commands/sites/sites.test.ts | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1011b414bef..0a0378304b8 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -43,6 +43,8 @@ module.exports = { // TODO: harmonize with filename snake_case in other Netlify Dev projects 'unicorn/filename-case': [2, { case: 'kebabCase' }], 'max-params': 'off', + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': 'off', }, overrides: [ ...overrides, @@ -137,6 +139,7 @@ module.exports = { rules: { '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-empty-function': 'off', }, }, ], diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index 7a69e10248b..c07919ff4b7 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -119,7 +119,9 @@ const validateDeployFolder = async (deployFolder: string) => { } catch (error_) { if (error_ && typeof error_ === 'object' && 'code' in error_) { if (error_.code === 'ENOENT') { - return error(`The deploy directory "${deployFolder}" has not been found. Did you forget to run 'netlify build'?`) + return error( + `The deploy directory "${deployFolder}" has not been found. Did you forget to run 'netlify build'?`, + ) } // Improve the message of permission errors @@ -361,7 +363,6 @@ const uploadDeployBlobs = async ({ silent: boolean siteId: string }) => { - // eslint-disable-next-line @typescript-eslint/no-empty-function const statusCb = silent ? () => {} : deployProgressCb() statusCb({ @@ -493,7 +494,7 @@ const runDeploy = async ({ config, fnDir: functionDirectories, functionsConfig, - // eslint-disable-next-line @typescript-eslint/no-empty-function + statusCb: silent ? () => {} : deployProgressCb(), deployTimeout, syncFileLimit: SYNC_FILE_LIMIT, @@ -575,7 +576,6 @@ const bundleEdgeFunctions = async (options, command: BaseCommand) => { // eslint-disable-next-line n/prefer-global/process, unicorn/prefer-set-has const argv = process.argv.slice(2) const statusCb = - // eslint-disable-next-line @typescript-eslint/no-empty-function options.silent || argv.includes('--json') || argv.includes('--silent') ? () => {} : deployProgressCb() statusCb({ diff --git a/tests/integration/commands/sites/sites.test.ts b/tests/integration/commands/sites/sites.test.ts index 1727e3f702b..f0dfbc0a605 100644 --- a/tests/integration/commands/sites/sites.test.ts +++ b/tests/integration/commands/sites/sites.test.ts @@ -13,7 +13,6 @@ import { getEnvironmentVariables, withMockApi } from '../../utils/mock-api.js' vi.mock('../../../../src/utils/command-helpers.js', async () => ({ ...(await vi.importActual('../../../../src/utils/command-helpers.js')), - // eslint-disable-next-line @typescript-eslint/no-empty-function log: () => {}, }))