diff --git a/desktop/babel-transformer/package.json b/desktop/babel-transformer/package.json index 1cb14afc532..fa949e8cacf 100644 --- a/desktop/babel-transformer/package.json +++ b/desktop/babel-transformer/package.json @@ -24,7 +24,7 @@ "@babel/traverse": "^7.20.13", "@babel/types": "^7.20.7", "@emotion/babel-plugin": "^11.10.6", - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.0", "@types/node": "^17.0.31", "fs-extra": "^11.1.0", "tslib": "^2.4.1" diff --git a/desktop/pkg-lib/package.json b/desktop/pkg-lib/package.json index 12e03e910f0..4ddd8490996 100644 --- a/desktop/pkg-lib/package.json +++ b/desktop/pkg-lib/package.json @@ -22,7 +22,7 @@ "p-map": "^4" }, "devDependencies": { - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.1", "@types/mock-fs": "^4.13.1", "@types/node": "^17.0.31", "@types/npm-packlist": "^1.1.2", diff --git a/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx b/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx index 11d73d82d47..61ce26869d0 100644 --- a/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx +++ b/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx @@ -99,6 +99,7 @@ describe('getWatchFolders', () => { }; const readReadJson = fs.readJson; try { + // @ts-expect-error readJson is read only and it is fine, this is a test fs.readJson = readJsonMock as any; const resolvedFolders = await getWatchFolders( path.join(rootDir, 'local_module_2'), @@ -115,6 +116,7 @@ describe('getWatchFolders', () => { ] `); } finally { + // @ts-expect-error readJson is read only and it is fine, this is a test fs.readJson = readReadJson; } }); diff --git a/desktop/pkg/package.json b/desktop/pkg/package.json index 588598dffd3..e2ebfcc4bad 100644 --- a/desktop/pkg/package.json +++ b/desktop/pkg/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@oclif/dev-cli": "^1", - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.0", "@types/inquirer": "^7.3.3", "@types/node": "^17.0.31", "@types/recursive-readdir": "^2.2.1", diff --git a/desktop/pkg/src/__tests__/runLint.node.tsx b/desktop/pkg/src/__tests__/runLint.node.tsx index 863c50fa0ca..18082ceeca5 100644 --- a/desktop/pkg/src/__tests__/runLint.node.tsx +++ b/desktop/pkg/src/__tests__/runLint.node.tsx @@ -30,21 +30,28 @@ const validPackageJson = { }, }; -beforeEach(() => { - jest.mock('fs-extra', () => jest.fn()); - fs.pathExists = jest.fn().mockResolvedValue(true); - fs.pathExistsSync = jest.fn().mockReturnValue(true); - // Required by some inconsistent node types for rw access. - (fs.lstatSync as any) = jest.fn().mockReturnValue({ - isFile: function () { - return true; - }, - }); +jest.mock('fs-extra', () => { + const mod = { + ...jest.requireActual('fs-extra'), + readFile: jest.fn(), + pathExists: jest.fn().mockResolvedValue(true), + pathExistsSync: jest.fn().mockResolvedValue(true), + lstatSync: jest.fn().mockReturnValue({ + isFile: function () { + return true; + }, + }), + }; + + return { + ...mod, + default: mod, + }; }); test('valid package json', async () => { const json = JSON.stringify(validPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toBe(null); }); @@ -53,7 +60,7 @@ test('valid scoped package json', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.name = '@test/flipper-plugin-package'; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toBe(null); }); @@ -63,7 +70,7 @@ test('$schema field is required', async () => { // @ts-ignore cannot delete non-optional fields delete testPackageJson.$schema; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ @@ -82,7 +89,7 @@ test('supported schema is required', async () => { testPackageJson.$schema = 'https://fbflipper.com/schemas/plugin-package/v1.json'; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ @@ -97,7 +104,7 @@ test('name is required', async () => { // @ts-ignore cannot delete non-optional fields delete testPackageJson.name; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ @@ -110,7 +117,7 @@ test('name must start with "flipper-plugin-"', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.name = 'test-plugin'; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ @@ -123,7 +130,7 @@ test('keywords must contain "flipper-plugin"', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.keywords = ['flipper', 'network']; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ @@ -135,13 +142,11 @@ test('keywords must contain "flipper-plugin"', async () => { test('flippeBundlerEntry must point to an existing file', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.flipperBundlerEntry = 'unexisting/file'; - fs.pathExistsSync = jest - .fn() - .mockImplementation( - (filePath) => !filePath.includes(path.join('unexisting', 'file')), - ); + (fs.pathExistsSync as any as jest.Mock).mockImplementation( + (filePath) => !filePath.includes(path.join('unexisting', 'file')), + ); const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ @@ -156,7 +161,7 @@ test('multiple validation errors reported', async () => { // @ts-ignore cannot delete non-optional fields delete testPackageJson.flipperBundlerEntry; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` [ diff --git a/desktop/pkg/src/__tests__/runMigrate.node.tsx b/desktop/pkg/src/__tests__/runMigrate.node.tsx index 5ac421dc9cc..090cc8a2296 100644 --- a/desktop/pkg/src/__tests__/runMigrate.node.tsx +++ b/desktop/pkg/src/__tests__/runMigrate.node.tsx @@ -10,25 +10,6 @@ import runMigrate from '../utils/runMigrate'; import fs from 'fs-extra'; -const packageJsonV1 = { - name: 'Fresco', - version: '1.0.0', - main: 'index.tsx', - license: 'MIT', - keywords: ['images'], - dependencies: { - flipper: 'latest', - }, - scripts: { - prepack: 'yarn reset && yarn build', - }, - title: 'Images', - icon: 'profile', - bugs: { - email: 'example@test.com', - }, -}; - const packageJsonV2 = { $schema: 'https://fbflipper.com/schemas/plugin-package/v2.json', name: 'flipper-plugin-network', @@ -53,18 +34,52 @@ const packageJsonV2 = { let convertedPackageJsonString: string | undefined; +jest.mock('fs-extra', () => { + const packageJsonV1 = { + name: 'Fresco', + version: '1.0.0', + main: 'index.tsx', + license: 'MIT', + keywords: ['images'], + dependencies: { + flipper: 'latest', + }, + scripts: { + prepack: 'yarn reset && yarn build', + }, + title: 'Images', + icon: 'profile', + bugs: { + email: 'example@test.com', + }, + }; + + const mod = { + ...jest.requireActual('fs-extra'), + readFile: jest + .fn() + .mockResolvedValue(new Buffer(JSON.stringify(packageJsonV1))), + pathExists: jest.fn().mockResolvedValue(true), + pathExistsSync: jest.fn().mockResolvedValue(true), + readJson: jest.fn().mockResolvedValue(packageJsonV1), + writeFile: jest.fn(async (_path, content) => { + convertedPackageJsonString = content; + }), + lstatSync: jest.fn().mockReturnValue({ + isFile: function () { + return true; + }, + }), + }; + + return { + ...mod, + default: mod, + }; +}); + beforeEach(() => { - jest.mock('fs-extra', () => jest.fn()); - fs.pathExists = jest.fn().mockResolvedValue(true); - fs.pathExistsSync = jest.fn().mockReturnValue(true); - fs.readJson = jest.fn().mockResolvedValue(packageJsonV1); - fs.readFile = jest - .fn() - .mockResolvedValue(new Buffer(JSON.stringify(packageJsonV1))); convertedPackageJsonString = undefined; - fs.writeFile = jest.fn().mockImplementation(async (_path, content) => { - convertedPackageJsonString = content; - }); }); test('converts package.json and adds dependencies', async () => { @@ -134,10 +149,9 @@ test('converts package.json without changing dependencies', async () => { }); test('does not migrate already migrated packages', async () => { - fs.readJson = jest.fn().mockResolvedValue(packageJsonV2); - fs.readFile = jest - .fn() - .mockResolvedValue(new Buffer(JSON.stringify(packageJsonV2))); + (fs.readFile as any as jest.Mock).mockResolvedValue( + new Buffer(JSON.stringify(packageJsonV2)), + ); const error = await runMigrate('dir'); expect(error).toBeUndefined(); expect(convertedPackageJsonString).toBeUndefined(); diff --git a/desktop/plugin-lib/package.json b/desktop/plugin-lib/package.json index c57cc6f1a34..6be73d584e5 100644 --- a/desktop/plugin-lib/package.json +++ b/desktop/plugin-lib/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@types/decompress": "4.2.4", - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.1", "@types/mock-fs": "^4.13.1", "@types/node": "^17.0.31", "flipper-test-utils": "0.0.0", diff --git a/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx b/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx index dae80c5d246..e04838e1359 100644 --- a/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx +++ b/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx @@ -31,6 +31,7 @@ test('getPluginDetailsV1', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV1); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -74,6 +75,7 @@ test('getPluginDetailsV2', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -121,6 +123,7 @@ test('id used as title if the latter omited', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -167,6 +170,7 @@ test('name without "flipper-plugin-" prefix is used as title if the latter omite description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -216,6 +220,7 @@ test('flipper-plugin-version is parsed', async () => { 'flipper-plugin': '^0.45', }, }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -269,6 +274,7 @@ test('plugin type and supported devices parsed', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -338,6 +344,7 @@ test('plugin type and supported apps parsed', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); @@ -417,7 +424,7 @@ test('can merge two package.json files', async () => { }, }; const mockedFs = jest.mocked(fs); - mockedFs.readJson.mockImplementation((file) => { + mockedFs.readJson.mockImplementation((file): any => { if (file === path.join(pluginPath, 'package.json')) { return pluginBase; } else if (file === path.join(pluginPath, 'fb', 'package.json')) { diff --git a/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx b/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx index adb11a99762..c8a666e4da2 100644 --- a/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx +++ b/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx @@ -119,24 +119,32 @@ function collectFileContent( } } -describe('pluginInstaller', () => { - let readJson: any; - beforeEach(() => { - mockfs(installedPluginFiles); - readJson = fs.readJson; - fs.readJson = (file: string) => { +jest.mock('fs-extra', () => { + const mod = { + ...jest.requireActual('fs-extra'), + readJson: jest.fn((file: string) => { const content = fileContent.get(normalizePath(file)); if (content) { return Promise.resolve(JSON.parse(content)); } else { return Promise.resolve(undefined); } - }; + }), + }; + + return { + ...mod, + default: mod, + }; +}); + +describe('pluginInstaller', () => { + beforeEach(() => { + mockfs(installedPluginFiles); }); afterEach(() => { mockfs.restore(); - fs.readJson = readJson; }); test('getInstalledPlugins', async () => { diff --git a/desktop/scripts/build-utils.tsx b/desktop/scripts/build-utils.tsx index 314e3cc5e71..c99f843f545 100644 --- a/desktop/scripts/build-utils.tsx +++ b/desktop/scripts/build-utils.tsx @@ -57,7 +57,6 @@ export async function prepareDefaultPlugins(isInsidersBuild: boolean = false) { `⚙️ Copying the provided default plugins dir "${forcedDefaultPluginsDir}"...`, ); await fs.copy(forcedDefaultPluginsDir, defaultPluginsDir, { - recursive: true, overwrite: true, dereference: true, }); diff --git a/desktop/scripts/copy-package-with-dependencies.tsx b/desktop/scripts/copy-package-with-dependencies.tsx index 4465b05d1de..b726eebe18e 100644 --- a/desktop/scripts/copy-package-with-dependencies.tsx +++ b/desktop/scripts/copy-package-with-dependencies.tsx @@ -59,7 +59,6 @@ async function copyPackageWithDependenciesRecursive( .then((l: Array) => ignore().add(DEFAULT_BUILD_IGNORES.concat(l))); await fs.copy(packageDir, targetDir, { dereference: true, - recursive: true, filter: (src) => { const relativePath = path.relative(packageDir, src); return relativePath === '' || !ignores.ignores(relativePath); diff --git a/desktop/scripts/package.json b/desktop/scripts/package.json index 3008176f930..9d4c2695776 100644 --- a/desktop/scripts/package.json +++ b/desktop/scripts/package.json @@ -12,7 +12,7 @@ "@types/adobe__node-fetch-retry": "^1.0.4", "@types/babel__code-frame": "^7.0.3", "@types/detect-port": "^1.3.3", - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.0", "@types/node": "^17.0.31", "ansi-to-html": "^0.7.2", "app-builder-lib": "23.6.0", @@ -24,7 +24,7 @@ "flipper-common": "0.0.0", "flipper-pkg-lib": "0.0.0", "flipper-plugin-lib": "0.0.0", - "fs-extra": "^10.1.0", + "fs-extra": "^11.0.0", "glob": "^8.0.1", "ignore": "^5.2.4", "metro": "^0.70.2", diff --git a/desktop/yarn.lock b/desktop/yarn.lock index bf027bf0420..de2757d4935 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -4087,7 +4087,15 @@ resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.5.tgz#9ee342a5d1314bb0928375424a2f162f97c310c7" integrity sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ== -"@types/fs-extra@^9.0.11", "@types/fs-extra@^9.0.12", "@types/fs-extra@^9.0.13": +"@types/fs-extra@^11.0.0", "@types/fs-extra@^11.0.1": + version "11.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.1.tgz#f542ec47810532a8a252127e6e105f487e0a6ea5" + integrity sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + +"@types/fs-extra@^9.0.11", "@types/fs-extra@^9.0.12": version "9.0.13" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== @@ -4210,6 +4218,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/jsonfile@*": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.1.tgz#ac84e9aefa74a2425a0fb3012bdea44f58970f1b" + integrity sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png== + dependencies: + "@types/node" "*" + "@types/jsonwebtoken@^9.0.1": version "9.0.1" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#29b1369c4774200d6d6f63135bf3d1ba3ef997a4" @@ -8539,19 +8554,19 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" - integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== +fs-extra@^11.0.0, fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== +fs-extra@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1"