From e7c5a5cc93f05771d426c5b35cc30078f8fcd52c Mon Sep 17 00:00:00 2001 From: Anton Nikolaev Date: Fri, 9 Apr 2021 05:15:14 -0700 Subject: [PATCH] Check that all dependencies provided by Flipper core are specified as peers in plugins Summary: This is a guard to ensure all Flipper built-in packages are declared as peer dependencies. It also removes all of them from nested node_modules after installation to be 100% sure they always loaded from the root folder, because e.g. react can be still installed as a transitive dependency even it is not declared as dependency directly. Reviewed By: passy Differential Revision: D27040267 fbshipit-source-id: 1e315a6b280b36ab20778ee261aa386b51d9f964 --- desktop/plugins/package.json | 12 +++ desktop/plugins/postinstall.ts | 146 ++++++++++++++++++++-------- desktop/plugins/public/package.json | 3 - 3 files changed, 118 insertions(+), 43 deletions(-) diff --git a/desktop/plugins/package.json b/desktop/plugins/package.json index b190ca7f5..bbd6dacb6 100644 --- a/desktop/plugins/package.json +++ b/desktop/plugins/package.json @@ -10,6 +10,18 @@ "fs-extra": "^9.0.1", "p-map": "^4.0.0" }, + "peerDependencies": { + "flipper": "*", + "flipper-plugin": "*", + "antd": "*", + "react": "*", + "react-dom": "*", + "@emotion/styled": "*", + "@ant-design/icons": "*", + "@types/react": "*", + "@types/react-dom": "*", + "@types/node": "*" + }, "scripts": { "postinstall": "../ts-node ./postinstall.ts" } diff --git a/desktop/plugins/postinstall.ts b/desktop/plugins/postinstall.ts index 3c35a5148..c98578245 100644 --- a/desktop/plugins/postinstall.ts +++ b/desktop/plugins/postinstall.ts @@ -12,60 +12,126 @@ import path from 'path'; import fs from 'fs-extra'; import pmap from 'p-map'; -async function postinstall() { - const publicPluginsDir = path.join(__dirname, 'public'); +const publicPluginsDir = path.join(__dirname, 'public'); +const rootDir = path.resolve(__dirname, '..'); +const fbPluginsDir = path.join(__dirname, 'fb'); + +async function postinstall(): Promise { + const [publicPackages, fbPackages, pluginsPackageJson] = await Promise.all([ + fs.readdir(publicPluginsDir), + fs.readdir(fbPluginsDir).catch(() => [] as string[]), + fs.readJson(path.join(__dirname, 'package.json')), + ]); + const peerDependencies = pluginsPackageJson.peerDependencies ?? {}; + const packages = [ + ...publicPackages.map((p) => path.join(publicPluginsDir, p)), + ...fbPackages.map((p) => path.join(fbPluginsDir, p)), + ]; + const errors: string[] = []; + const modulesDir = path.join(__dirname, 'node_modules'); + await pmap( + packages, + async (packageDir) => { + const packageJsonPath = path.join(packageDir, 'package.json'); + if (!(await fs.pathExists(packageJsonPath))) { + return; + } + const packageJson = await fs.readJson( + path.join(packageDir, 'package.json'), + ); + const allDependencies = Object.assign( + {}, + packageJson.optionalDependencies ?? {}, + packageJson.devDependencies ?? {}, + packageJson.dependencies ?? {}, + ); + for (const dependency of Object.keys(allDependencies)) { + if (peerDependencies[dependency]) { + errors.push( + `[ERROR] Dependency "${dependency}" in plugin package "${path.relative( + rootDir, + packageDir, + )}" must be specified as peer dependency, because it is provided by Flipper.`, + ); + } + } + if ( + packageJson.keywords && + packageJson.keywords.includes('flipper-plugin') + ) { + return; + } + const destPath = path.join(modulesDir, packageJson.name); + if (await fs.pathExists(destPath)) { + await fs.remove(destPath); + } else { + await fs.ensureDir(path.dirname(destPath)); + } + await fs.symlink(packageDir, destPath, 'junction'); + }, + { + concurrency: 4, + }, + ); + if (errors.length) { + console.error(''); + for (const err of errors) { + console.error(err); + } + return 1; + } execSync('yarn install --mutex network:30330', { cwd: publicPluginsDir, stdio: 'inherit', }); - const fbPluginsDir = path.join(__dirname, 'fb'); if (await fs.pathExists(fbPluginsDir)) { execSync('yarn install --mutex network:30330', { cwd: fbPluginsDir, stdio: 'inherit', }); } - const [publicPackages, fbPackages] = await Promise.all([ - fs.readdir(publicPluginsDir), - fs.readdir(fbPluginsDir).catch(() => [] as string[]), + const peerDependenciesArray = Object.keys(peerDependencies); + await Promise.all([ + removeInstalledModules(modulesDir, peerDependenciesArray), + removeInstalledModules( + path.join(publicPluginsDir, 'node_modules'), + peerDependenciesArray, + ), + removeInstalledModules( + path.join(fbPluginsDir, 'node_modules'), + peerDependenciesArray, + ), ]); - const packages = [ - ...publicPackages.map((p) => path.join(publicPluginsDir, p)), - ...fbPackages.map((p) => path.join(fbPluginsDir, p)), - ]; - const modulesDir = path.join(__dirname, 'node_modules'); - await pmap(packages, async (packageDir) => { - const packageJsonPath = path.join(packageDir, 'package.json'); - if (!(await fs.pathExists(packageJsonPath))) { - return; - } - const packageJson = await fs.readJson( - path.join(packageDir, 'package.json'), - ); - if ( - packageJson.keywords && - packageJson.keywords.includes('flipper-plugin') - ) { - return; - } - const destPath = path.join(modulesDir, packageJson.name); - console.log( - `linking ${path.relative(__dirname, destPath)} to ${path.relative( - __dirname, - packageDir, - )}`, - ); - if (await fs.pathExists(destPath)) { - return; - } - await fs.ensureDir(path.dirname(destPath)); - await fs.symlink(packageDir, destPath, 'junction'); - }); + await pmap( + packages, + async (packageDir) => { + await removeInstalledModules( + path.join(packageDir, 'node_modules'), + peerDependenciesArray, + ); + }, + {concurrency: 4}, + ); + + return 0; +} + +async function removeInstalledModules(dir: string, modules: string[]) { + await pmap( + modules, + async (d) => { + const fullPath = path.join(dir, d); + if (await fs.pathExists(fullPath)) { + await fs.remove(path.join(dir, d)); + } + }, + {concurrency: 1}, + ); } postinstall() - .then(() => { - process.exit(0); + .then((code) => { + process.exit(code); }) .catch((err: any) => { console.error(err); diff --git a/desktop/plugins/public/package.json b/desktop/plugins/public/package.json index 477954d5c..f7890d7b4 100644 --- a/desktop/plugins/public/package.json +++ b/desktop/plugins/public/package.json @@ -21,8 +21,5 @@ "devDependencies": { "rimraf": "^3.0.2", "patch-package": "^6.2.0" - }, - "scripts": { - "postinstall": "rimraf ./node_modules/react ./node_modules/react-dom ./node_modules/@types/react ./node_modules/@types/react-dom" } }