diff --git a/desktop/.gitignore b/desktop/.gitignore index 1682a65fe..6c3e8c204 100644 --- a/desktop/.gitignore +++ b/desktop/.gitignore @@ -6,3 +6,4 @@ node_modules/ /app/src/defaultPlugins/index.tsx /coverage .env +tsc-error.log diff --git a/desktop/app/src/PluginContainer.tsx b/desktop/app/src/PluginContainer.tsx index a35c13f93..a47bac39b 100644 --- a/desktop/app/src/PluginContainer.tsx +++ b/desktop/app/src/PluginContainer.tsx @@ -49,7 +49,7 @@ import {theme, TrackingScope, _SandyPluginRenderer} from 'flipper-plugin'; import {isDevicePluginDefinition, isSandyPlugin} from './utils/pluginUtils'; import {ContentContainer} from './sandy-chrome/ContentContainer'; import {Alert, Typography} from 'antd'; -import {InstalledPluginDetails} from 'plugin-lib'; +import {InstalledPluginDetails} from 'flipper-plugin-lib'; import semver from 'semver'; import {loadPlugin} from './reducers/pluginManager'; import {produce} from 'immer'; diff --git a/desktop/package.json b/desktop/package.json index e904b3568..0b1a49b1e 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -227,9 +227,10 @@ "dev-server": "cross-env NODE_ENV=development ./ts-node scripts/start-dev-server.ts", "everything": "yarn reset && yarn install && yarn lint && yarn test && yarn test-electron && yarn build --mac --mac-dmg --win --linux --linux-deb && yarn start", "fix": "eslint . --fix --ext .js,.ts,.tsx", - "lint": "yarn lint:eslint && yarn lint:tsc", + "lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins", "lint:eslint": "eslint . --ext .js,.ts,.tsx", "lint:tsc": "tsc --noemit", + "tsc-plugins": "./ts-node scripts/tsc-plugins.ts", "list-plugins": "./ts-node scripts/list-plugins.ts", "open-dist": "open ../dist/mac/Flipper.app --args --launcher=false --inspect=9229", "postinstall": "patch-package && ./ts-node scripts/gen-type-index.ts && yarn --cwd plugins install --mutex network:30331 && yarn build:tsc && ./ts-node scripts/generate-plugin-entry-points.ts && yarn build:themes", diff --git a/desktop/plugins/public/package.json b/desktop/plugins/public/package.json index f7890d7b4..557f1e95c 100644 --- a/desktop/plugins/public/package.json +++ b/desktop/plugins/public/package.json @@ -7,7 +7,8 @@ "*" ], "nohoist": [ - "flipper-plugin-kaios-big-allocations/firefox-client" + "flipper-plugin-kaios-big-allocations/firefox-client", + "flipper-plugin-kaios-big-allocations/patch-package" ] }, "resolutions": { @@ -19,7 +20,6 @@ "url": "https://fb.workplace.com/groups/flippersupport/" }, "devDependencies": { - "rimraf": "^3.0.2", - "patch-package": "^6.2.0" + "rimraf": "^3.0.2" } } diff --git a/desktop/plugins/public/reactdevtools/index.tsx b/desktop/plugins/public/reactdevtools/index.tsx index 12638360c..15ec1fbbb 100644 --- a/desktop/plugins/public/reactdevtools/index.tsx +++ b/desktop/plugins/public/reactdevtools/index.tsx @@ -77,7 +77,7 @@ const GrabMetroDevice = connect< ReduxState >(({connections: {devices}}) => ({ metroDevice: devices.find( - (device) => device.os === 'Metro' && !device.isArchived, + (device: Device) => device.os === 'Metro' && !device.isArchived, ) as MetroDevice, }))(function ({ metroDevice, diff --git a/desktop/plugins/tsconfig.json b/desktop/plugins/tsconfig.json new file mode 100644 index 000000000..8377012e6 --- /dev/null +++ b/desktop/plugins/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "baseUrl": ".", + "rootDir": ".", + "outDir": "lib" + }, + "include": [ + "public/", + "fb/" + ], + "exclude": [ + "**/lib/", + "**/dist/", + "**/node_modules/", + "**/__tests__/", + "**/__test__/" + ] +} diff --git a/desktop/scripts/tsc-plugins.ts b/desktop/scripts/tsc-plugins.ts new file mode 100644 index 000000000..47dbb3b83 --- /dev/null +++ b/desktop/scripts/tsc-plugins.ts @@ -0,0 +1,148 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import fs from 'fs-extra'; +import path from 'path'; +import {exec} from 'child_process'; +import {EOL} from 'os'; +import pmap from 'p-map'; +import {rootDir} from './paths'; +import yargs from 'yargs'; + +const argv = yargs + .usage('yarn tsc-plugins [args]') + .version(false) + .options({ + dir: { + description: 'Plugins directory name ("plugins" by default)', + type: 'string', + default: 'plugins', + alias: 'd', + }, + }) + .help() + .strict() + .parse(process.argv.slice(1)); + +const pluginsDir = path.join(rootDir, argv.dir); +const fbPluginsDir = path.join(pluginsDir, 'fb'); +const publicPluginsDir = path.join(pluginsDir, 'public'); + +async function tscPlugins(): Promise { + const stdout = await new Promise((resolve) => + exec( + `./node_modules/.bin/tsc -p ./${argv.dir}/tsconfig.json`, + { + cwd: rootDir, + }, + (err, stdout) => { + if (err) { + console.error(err); + resolve(stdout); + } else { + resolve(undefined); + } + }, + ), + ); + if (stdout) { + console.error(stdout); + } + const errors = (stdout?.split(EOL) ?? []).filter((l) => l !== ''); + if (errors.length > 0) { + await findAffectedPlugins(errors); + } + return stdout ? 1 : 0; +} + +async function findAffectedPlugins(errors: string[]) { + const [publicPackages, fbPackages] = await Promise.all([ + fs.readdir(publicPluginsDir), + fs.readdir(fbPluginsDir).catch(() => [] as string[]), + ]); + const allPackages = await pmap( + [ + ...publicPackages.map((p) => path.join(publicPluginsDir, p)), + ...fbPackages.map((p) => path.join(fbPluginsDir, p)), + ], + async (p) => ({ + dir: p, + json: await fs + .readJson(path.join(p, 'package.json')) + .catch(() => undefined), + }), + ).then((dirs) => dirs.filter((dir) => !!dir.json)); + const packageByName = new Map( + allPackages.map((p) => [p.json.name as string, p]), + ); + const depsByName = new Map>(); + function getDependencies(name: string): Set { + if (!depsByName.has(name)) { + const set = new Set(); + const pkg = packageByName.get(name)!; + set.add(name); + const allDeps = Object.keys({ + ...(pkg.json.dependencies ?? {}), + ...(pkg.json.peerDependencies ?? {}), + }); + for (const dep of allDeps) { + if (packageByName.get(dep)) { + const subDeps = getDependencies(dep); + for (const subDep of subDeps) { + set.add(subDep); + } + } + } + depsByName.set(name, set); + } + return depsByName.get(name)!; + } + for (const name of packageByName.keys()) { + depsByName.set(name, getDependencies(name)); + } + for (const pkg of allPackages) { + if (!pkg.json?.keywords?.includes('flipper-plugin')) { + continue; + } + const logFile = path.join(pkg.dir, 'tsc-error.log'); + await fs.remove(logFile); + let logStream: fs.WriteStream | undefined; + for (const dep of depsByName.get(pkg.json.name)!) { + const relativeDir = path.relative(rootDir, packageByName.get(dep)!.dir); + for (const error of errors) { + if (error.startsWith(relativeDir)) { + if (!logStream) { + logStream = fs.createWriteStream(logFile); + console.error( + `Plugin ${path.relative( + rootDir, + pkg.dir, + )} has tsc errors. Check ${path.relative( + rootDir, + logFile, + )} for details.`, + ); + } + logStream.write(error); + logStream.write(EOL); + } + } + } + logStream?.close(); + } +} + +tscPlugins() + .then((code) => { + process.exit(code); + }) + .catch((err: any) => { + console.error(err); + process.exit(1); + });