diff --git a/eslint_temporary_suppressions.js b/eslint_temporary_suppressions.js index dbac8d090d..15ff2f0399 100644 --- a/eslint_temporary_suppressions.js +++ b/eslint_temporary_suppressions.js @@ -2228,26 +2228,6 @@ export default [ '@typescript-eslint/no-unsafe-return': 'off', }, }, - { - files: ['packages/functions-utils/src/main.ts'], - rules: { - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - }, - }, - { - files: ['packages/functions-utils/tests/main.test.ts'], - rules: { - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/restrict-template-expressions': 'off', - }, - }, { files: ['packages/git-utils/src/commits.ts'], rules: { diff --git a/package-lock.json b/package-lock.json index 5ddbda5a9a..7427817204 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24822,8 +24822,7 @@ "license": "MIT", "dependencies": { "@netlify/zip-it-and-ship-it": "13.2.0", - "cpy": "^11.0.0", - "path-exists": "^5.0.0" + "cpy": "^11.0.0" }, "devDependencies": { "@types/node": "^18.19.111", diff --git a/packages/functions-utils/package.json b/packages/functions-utils/package.json index 92606baf36..cbab4c1988 100644 --- a/packages/functions-utils/package.json +++ b/packages/functions-utils/package.json @@ -51,8 +51,7 @@ "license": "MIT", "dependencies": { "@netlify/zip-it-and-ship-it": "13.2.0", - "cpy": "^11.0.0", - "path-exists": "^5.0.0" + "cpy": "^11.0.0" }, "devDependencies": { "@types/node": "^18.19.111", diff --git a/packages/functions-utils/src/main.ts b/packages/functions-utils/src/main.ts index 7ce0eb52bd..a2e4b9ec8a 100644 --- a/packages/functions-utils/src/main.ts +++ b/packages/functions-utils/src/main.ts @@ -1,23 +1,35 @@ -import { promises as fs } from 'fs' import { basename, dirname, join } from 'path' +import { access, stat } from 'fs/promises' import { listFunctions, listFunctionsFiles } from '@netlify/zip-it-and-ship-it' import cpy from 'cpy' -import { pathExists } from 'path-exists' + +interface FailOptions { + fail?: (message: string, options?: { error?: unknown }) => void +} // Add a Netlify Function file to the `functions` directory, so it is processed // by `@netlify/plugin-functions-core` -export const add = async function (src?: string, dist?: string, { fail = defaultFail } = {}): Promise { +export const add = async function ( + src?: string, + dist?: string, + { fail = defaultFail }: FailOptions = {}, +): Promise { if (src === undefined) { - return fail('No function source directory was specified') + fail('No function source directory was specified') + return } - if (!(await pathExists(src))) { - return fail(`No function file or directory found at "${src}"`) + try { + await access(src) + } catch { + fail(`No function file or directory found at "${src}"`) + return } if (dist === undefined) { - return fail('No function directory was specified') + fail('No function directory was specified') + return } const srcBasename = basename(src) @@ -26,7 +38,7 @@ export const add = async function (src?: string, dist?: string, { fail = default } const getSrcAndDest = async function (src: string, srcBasename: string, dist: string): Promise<[string, string]> { - const srcStat = await fs.stat(src) + const srcStat = await stat(src) if (srcStat.isDirectory()) { return [`${srcBasename}/**`, join(dist, srcBasename)] @@ -35,9 +47,10 @@ const getSrcAndDest = async function (src: string, srcBasename: string, dist: st return [srcBasename, dist] } -export const list = async function (functionsSrc, { fail = defaultFail } = {} as any) { - if (functionsSrc === undefined || functionsSrc.length === 0) { - return fail('No function directory was specified') +export const list = async function (functionsSrc: string | string[], { fail = defaultFail }: FailOptions = {}) { + if (Array.isArray(functionsSrc) ? functionsSrc.length === 0 : !functionsSrc) { + fail('No function directory was specified') + return } try { @@ -47,9 +60,10 @@ export const list = async function (functionsSrc, { fail = defaultFail } = {} as } } -export const listAll = async function (functionsSrc, { fail = defaultFail } = {} as any) { - if (functionsSrc === undefined || functionsSrc.length === 0) { - return fail('No function directory was specified') +export const listAll = async function (functionsSrc: string | string[], { fail = defaultFail }: FailOptions = {}) { + if (Array.isArray(functionsSrc) ? functionsSrc.length === 0 : !functionsSrc) { + fail('No function directory was specified') + return } try { @@ -59,6 +73,6 @@ export const listAll = async function (functionsSrc, { fail = defaultFail } = {} } } -const defaultFail = function (message) { +const defaultFail = function (message: string): never { throw new Error(message) } diff --git a/packages/functions-utils/tests/main.test.ts b/packages/functions-utils/tests/main.test.ts index 692b0b0ae9..c92719b249 100644 --- a/packages/functions-utils/tests/main.test.ts +++ b/packages/functions-utils/tests/main.test.ts @@ -1,9 +1,8 @@ -import { readFile, rm } from 'fs/promises' +import { readFile, rm, access } from 'fs/promises' import { normalize, resolve } from 'path' import { fileURLToPath } from 'url' import cpy from 'cpy' -import { pathExists } from 'path-exists' import sortOn from 'sort-on' import { expect, test, vi } from 'vitest' @@ -13,6 +12,15 @@ import { getDist, createDist } from './helpers/main.js' const FIXTURES_DIR = fileURLToPath(new URL('fixtures', import.meta.url)) +const pathExists = async (path: string): Promise => { + try { + await access(path) + return true + } catch { + return false + } +} + test('Should copy a source file to a dist directory', async () => { const dist = await getDist() try { @@ -45,7 +53,8 @@ test('Should throw when source is undefined', async () => { test('Should throw when source is empty array', async () => { const dist = await getDist() try { - await expect(() => add([] as any, dist)).rejects.toThrow() + // @ts-expect-error testing invalid input + await expect(() => add([], dist)).rejects.toThrow() } finally { await rm(dist, { force: true, recursive: true }) } @@ -101,13 +110,26 @@ test('Should overwrite dist file if it already exists', async () => { }) test('Should allow "fail" option to customize failures', async () => { - const fail = vi.fn() as any + const fail = vi.fn() await add(undefined, undefined, { fail }) expect(fail).toHaveBeenCalledOnce() expect(fail).toHaveBeenCalledWith('No function source directory was specified') }) -const normalizeFiles = function (fixtureDir, { name, mainFile, runtime, extension, srcDir, srcFile, schedule }) { +interface FileData { + name: string + mainFile: string + runtime: string + extension: string + srcDir?: string + srcFile?: string + schedule?: string +} + +const normalizeFiles = function ( + fixtureDir: string, + { name, mainFile, runtime, extension, srcDir, srcFile, schedule }: FileData, +) { const mainFileA = normalize(`${fixtureDir}/${mainFile}`) const srcFileA = srcFile === undefined ? {} : { srcFile: normalize(`${fixtureDir}/${srcFile}`) } const srcDirA = srcDir ? { srcDir: resolve(fixtureDir, srcDir) } : {} @@ -117,7 +139,8 @@ const normalizeFiles = function (fixtureDir, { name, mainFile, runtime, extensio test('Can list function main files with list()', async () => { const fixtureDir = `${FIXTURES_DIR}/list` const functions = await list(fixtureDir) - expect(sortOn(functions, ['mainFile', 'extension'])).toEqual( + expect(functions).toBeDefined() + expect(sortOn(functions!, ['mainFile', 'extension'])).toEqual( [ { name: 'four', mainFile: 'four.js/four.js.js', runtime: 'js', extension: '.js', srcDir: 'four.js' }, { @@ -138,7 +161,8 @@ test('Can list function main files with list()', async () => { test('Can list all function files with listAll()', async () => { const fixtureDir = `${FIXTURES_DIR}/list` const functions = await listAll(fixtureDir) - expect(sortOn(functions, ['mainFile', 'extension'])).toEqual( + expect(functions).toBeDefined() + expect(sortOn(functions!, ['mainFile', 'extension'])).toEqual( [ { name: 'four',