Device plugin management (4/n): Allow choosing "device" plugin type in "flipper-pkg init"
Summary: Added option to bootstrap device plugin in "flipper-pkg". Changelog: "flipper-pkg init" can now be used to bootstrap device plugins Reviewed By: mweststrate Differential Revision: D26389429 fbshipit-source-id: 90773011bd50289004cd747111e1787402840922
This commit is contained in:
committed by
Facebook GitHub Bot
parent
4cb40de3f5
commit
68248a7c63
@@ -4,7 +4,16 @@
|
|||||||
"outDir": "lib",
|
"outDir": "lib",
|
||||||
"rootDir": "src"
|
"rootDir": "src"
|
||||||
},
|
},
|
||||||
"references": [{"path": "../plugin-lib"}],
|
"references": [
|
||||||
"include": ["src"],
|
{
|
||||||
"exclude": ["node_modules"]
|
"path": "../plugin-lib"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/__tests__/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
"errorMessage": "should contain keyword \"flipper-plugin\""
|
"errorMessage": "should contain keyword \"flipper-plugin\""
|
||||||
},
|
},
|
||||||
"pluginType": {
|
"pluginType": {
|
||||||
"description": "Type of the plugin - client or device. If omitted, \"client\" type is used by default.",
|
"description": "Type of the plugin - \"client\" if the plugin connects to a specific client plugin running in a mobile app, or \"device\" if it connects to devices. If omitted, \"client\" type is assumed by default.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["client", "device"]
|
"enum": ["client", "device"]
|
||||||
},
|
},
|
||||||
@@ -84,6 +84,6 @@
|
|||||||
"id",
|
"id",
|
||||||
"main",
|
"main",
|
||||||
"flipperBundlerEntry",
|
"flipperBundlerEntry",
|
||||||
"keywords"
|
"keywords"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,14 @@ afterEach(() => {
|
|||||||
// fs.writeFile.mockRestore();
|
// fs.writeFile.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('It generates the correct files', async () => {
|
test('It generates the correct files for client plugin', async () => {
|
||||||
await initTemplate('my weird Package %name. etc', 'Nice title', '/dev/null');
|
await initTemplate(
|
||||||
|
'my weird Package %name. etc',
|
||||||
|
'Nice title',
|
||||||
|
'client',
|
||||||
|
undefined,
|
||||||
|
'/dev/null',
|
||||||
|
);
|
||||||
expect(files).toMatchInlineSnapshot(`
|
expect(files).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"/dev/null/.gitignore": "node_modules
|
"/dev/null/.gitignore": "node_modules
|
||||||
@@ -57,6 +63,7 @@ test('It generates the correct files', async () => {
|
|||||||
\\"name\\": \\"flipper-plugin-my-weird-package-name-etc\\",
|
\\"name\\": \\"flipper-plugin-my-weird-package-name-etc\\",
|
||||||
\\"id\\": \\"my weird Package %name. etc\\",
|
\\"id\\": \\"my weird Package %name. etc\\",
|
||||||
\\"version\\": \\"1.0.0\\",
|
\\"version\\": \\"1.0.0\\",
|
||||||
|
\\"pluginType\\": \\"client\\",
|
||||||
\\"main\\": \\"dist/bundle.js\\",
|
\\"main\\": \\"dist/bundle.js\\",
|
||||||
\\"flipperBundlerEntry\\": \\"src/index.tsx\\",
|
\\"flipperBundlerEntry\\": \\"src/index.tsx\\",
|
||||||
\\"license\\": \\"MIT\\",
|
\\"license\\": \\"MIT\\",
|
||||||
@@ -206,3 +213,202 @@ test('It generates the correct files', async () => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('It generates the correct files for device plugin', async () => {
|
||||||
|
await initTemplate(
|
||||||
|
'my weird Package %name. etc',
|
||||||
|
'Nice title',
|
||||||
|
'device',
|
||||||
|
['iOS', 'Android'],
|
||||||
|
'/dev/null',
|
||||||
|
);
|
||||||
|
expect(files).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"/dev/null/.gitignore": "node_modules
|
||||||
|
dist/
|
||||||
|
",
|
||||||
|
"/dev/null/babel.config.js": "module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@babel/preset-typescript',
|
||||||
|
'@babel/preset-react',
|
||||||
|
['@babel/preset-env', {targets: {node: 'current'}}]
|
||||||
|
],
|
||||||
|
};
|
||||||
|
",
|
||||||
|
"/dev/null/package.json": "{
|
||||||
|
\\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\",
|
||||||
|
\\"name\\": \\"flipper-plugin-my-weird-package-name-etc\\",
|
||||||
|
\\"id\\": \\"my weird Package %name. etc\\",
|
||||||
|
\\"version\\": \\"1.0.0\\",
|
||||||
|
\\"pluginType\\": \\"device\\",
|
||||||
|
\\"supportedDevices\\": [{\\"os\\":\\"iOS\\"},{\\"os\\":\\"Android\\"}],
|
||||||
|
\\"main\\": \\"dist/bundle.js\\",
|
||||||
|
\\"flipperBundlerEntry\\": \\"src/index.tsx\\",
|
||||||
|
\\"license\\": \\"MIT\\",
|
||||||
|
\\"keywords\\": [
|
||||||
|
\\"flipper-plugin\\"
|
||||||
|
],
|
||||||
|
\\"icon\\": \\"apps\\",
|
||||||
|
\\"title\\": \\"Nice title\\",
|
||||||
|
\\"scripts\\": {
|
||||||
|
\\"lint\\": \\"flipper-pkg lint\\",
|
||||||
|
\\"prepack\\": \\"flipper-pkg lint && flipper-pkg bundle\\",
|
||||||
|
\\"build\\": \\"flipper-pkg bundle\\",
|
||||||
|
\\"watch\\": \\"flipper-pkg bundle --watch\\",
|
||||||
|
\\"test\\": \\"jest --no-watchman\\"
|
||||||
|
},
|
||||||
|
\\"peerDependencies\\": {
|
||||||
|
\\"flipper\\": \\"latest\\",
|
||||||
|
\\"flipper-plugin\\": \\"latest\\",
|
||||||
|
\\"antd\\": \\"latest\\"
|
||||||
|
},
|
||||||
|
\\"devDependencies\\": {
|
||||||
|
\\"@babel/preset-react\\": \\"latest\\",
|
||||||
|
\\"@babel/preset-typescript\\": \\"latest\\",
|
||||||
|
\\"@testing-library/react\\": \\"latest\\",
|
||||||
|
\\"@types/jest\\": \\"latest\\",
|
||||||
|
\\"@types/react\\": \\"latest\\",
|
||||||
|
\\"@types/react-dom\\": \\"latest\\",
|
||||||
|
\\"antd\\": \\"latest\\",
|
||||||
|
\\"flipper\\": \\"latest\\",
|
||||||
|
\\"flipper-plugin\\": \\"latest\\",
|
||||||
|
\\"flipper-pkg\\": \\"latest\\",
|
||||||
|
\\"jest\\": \\"latest\\",
|
||||||
|
\\"typescript\\": \\"latest\\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"/dev/null/src/__tests__/test.spec.tsx": "import {TestUtils} from 'flipper-plugin';
|
||||||
|
import * as Plugin from '..';
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
// API: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
test('It can store data', () => {
|
||||||
|
const {instance, sendLogEntry} = TestUtils.startDevicePlugin(Plugin);
|
||||||
|
|
||||||
|
expect(instance.data.get()).toEqual([]);
|
||||||
|
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854112859),
|
||||||
|
message: 'test1',
|
||||||
|
pid: 0,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 1,
|
||||||
|
type: 'error',
|
||||||
|
app: 'X',
|
||||||
|
});
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854117859),
|
||||||
|
message: 'test2',
|
||||||
|
pid: 2,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 3,
|
||||||
|
type: 'warn',
|
||||||
|
app: 'Y',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(instance.data.get()).toMatchInlineSnapshot(\`
|
||||||
|
Array [
|
||||||
|
\\"test1\\",
|
||||||
|
\\"test2\\",
|
||||||
|
]
|
||||||
|
\`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
// API: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
test('It can render data', async () => {
|
||||||
|
const {instance, renderer, sendLogEntry} = TestUtils.renderDevicePlugin(
|
||||||
|
Plugin,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(instance.data.get()).toEqual([]);
|
||||||
|
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854112859),
|
||||||
|
message: 'test1',
|
||||||
|
pid: 0,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 1,
|
||||||
|
type: 'error',
|
||||||
|
app: 'X',
|
||||||
|
});
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854117859),
|
||||||
|
message: 'test2',
|
||||||
|
pid: 2,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 3,
|
||||||
|
type: 'warn',
|
||||||
|
app: 'Y',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await renderer.findByTestId('0')).not.toBeNull();
|
||||||
|
expect(await renderer.findByTestId('1')).toMatchInlineSnapshot();
|
||||||
|
});
|
||||||
|
",
|
||||||
|
"/dev/null/src/index.tsx": "import React from 'react';
|
||||||
|
import {
|
||||||
|
DevicePluginClient,
|
||||||
|
usePlugin,
|
||||||
|
createState,
|
||||||
|
useValue,
|
||||||
|
Layout,
|
||||||
|
} from 'flipper-plugin';
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#creating-a-first-plugin
|
||||||
|
// API: https://fbflipper.com/docs/extending/flipper-plugin#pluginclient
|
||||||
|
export function devicePlugin(client: DevicePluginClient) {
|
||||||
|
const data = createState<string[]>([]);
|
||||||
|
|
||||||
|
client.device.onLogEntry((entry) => {
|
||||||
|
data.update((draft) => {
|
||||||
|
draft.push(entry.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.addMenuEntry({
|
||||||
|
action: 'clear',
|
||||||
|
handler: async () => {
|
||||||
|
data.set([]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {data};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#building-a-user-interface-for-the-plugin
|
||||||
|
// API: https://fbflipper.com/docs/extending/flipper-plugin#react-hooks
|
||||||
|
export function Component() {
|
||||||
|
const instance = usePlugin(devicePlugin);
|
||||||
|
const data = useValue(instance.data);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.ScrollContainer>
|
||||||
|
{Object.entries(data).map(([id, d]) => (
|
||||||
|
<pre key={id} data-testid={id}>
|
||||||
|
{JSON.stringify(d)}
|
||||||
|
</pre>
|
||||||
|
))}
|
||||||
|
</Layout.ScrollContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"/dev/null/tsconfig.json": "{
|
||||||
|
\\"compilerOptions\\": {
|
||||||
|
\\"target\\": \\"ES2017\\",
|
||||||
|
\\"module\\": \\"ES6\\",
|
||||||
|
\\"jsx\\": \\"react\\",
|
||||||
|
\\"sourceMap\\": true,
|
||||||
|
\\"noEmit\\": true,
|
||||||
|
\\"strict\\": true,
|
||||||
|
\\"moduleResolution\\": \\"node\\",
|
||||||
|
\\"esModuleInterop\\": true,
|
||||||
|
\\"forceConsistentCasingInFileNames\\": true
|
||||||
|
},
|
||||||
|
\\"files\\": [\\"src/index.tsx\\"]
|
||||||
|
}
|
||||||
|
",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|||||||
@@ -16,10 +16,24 @@ import recursiveReaddirImport from 'recursive-readdir';
|
|||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import inquirer from 'inquirer';
|
import inquirer from 'inquirer';
|
||||||
import {homedir} from 'os';
|
import {homedir} from 'os';
|
||||||
|
import {PluginType} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
const recursiveReaddir = promisify<string, string[]>(recursiveReaddirImport);
|
const recursiveReaddir = promisify<string, string[]>(recursiveReaddirImport);
|
||||||
|
|
||||||
const templateDir = path.resolve(__dirname, '..', '..', 'templates', 'plugin');
|
const pluginTemplateDir = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'templates',
|
||||||
|
'plugin',
|
||||||
|
);
|
||||||
|
const devicePluginTemplateDir = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'templates',
|
||||||
|
'device-plugin',
|
||||||
|
);
|
||||||
const templateExt = '.template';
|
const templateExt = '.template';
|
||||||
|
|
||||||
export default class Init extends Command {
|
export default class Init extends Command {
|
||||||
@@ -43,6 +57,18 @@ export default class Init extends Command {
|
|||||||
const pluginDirectory: string = path.resolve(process.cwd(), args.directory);
|
const pluginDirectory: string = path.resolve(process.cwd(), args.directory);
|
||||||
await verifyFlipperSearchPath(pluginDirectory);
|
await verifyFlipperSearchPath(pluginDirectory);
|
||||||
|
|
||||||
|
const pluginTypeQuestion: inquirer.QuestionCollection = [
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'pluginType',
|
||||||
|
choices: ['client', 'device'],
|
||||||
|
message:
|
||||||
|
'Plugin Type ("client" if the plugin will work with a mobile app, "device" if the plugin will work with a mobile device):',
|
||||||
|
default: 'client',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const pluginType: PluginType = (await inquirer.prompt(pluginTypeQuestion))
|
||||||
|
.pluginType;
|
||||||
const idQuestion: inquirer.QuestionCollection = [
|
const idQuestion: inquirer.QuestionCollection = [
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -61,6 +87,24 @@ export default class Init extends Command {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
const title: string = (await inquirer.prompt(titleQuestion)).title;
|
const title: string = (await inquirer.prompt(titleQuestion)).title;
|
||||||
|
|
||||||
|
let supportedDevices: string[] | undefined;
|
||||||
|
|
||||||
|
if (pluginType === 'device') {
|
||||||
|
const supportedDevicesQuestion: inquirer.QuestionCollection = [
|
||||||
|
{
|
||||||
|
type: 'checkbox',
|
||||||
|
name: 'supportedDevices',
|
||||||
|
choices: ['iOS', 'Android', 'Metro'],
|
||||||
|
message:
|
||||||
|
'Supported Devices (iOS, Android or Metro (React Native bundler)):',
|
||||||
|
default: ['iOS', 'Android'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
supportedDevices = (await inquirer.prompt(supportedDevicesQuestion))
|
||||||
|
.supportedDevices;
|
||||||
|
}
|
||||||
|
|
||||||
const packageName = getPackageNameFromId(id);
|
const packageName = getPackageNameFromId(id);
|
||||||
const outputDirectory = path.join(pluginDirectory, packageName);
|
const outputDirectory = path.join(pluginDirectory, packageName);
|
||||||
|
|
||||||
@@ -72,7 +116,13 @@ export default class Init extends Command {
|
|||||||
`⚙️ Initializing Flipper desktop template in ${outputDirectory}`,
|
`⚙️ Initializing Flipper desktop template in ${outputDirectory}`,
|
||||||
);
|
);
|
||||||
await fs.ensureDir(outputDirectory);
|
await fs.ensureDir(outputDirectory);
|
||||||
await initTemplate(id, title, outputDirectory);
|
await initTemplate(
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
pluginType,
|
||||||
|
supportedDevices,
|
||||||
|
outputDirectory,
|
||||||
|
);
|
||||||
|
|
||||||
console.log(`⚙️ Installing dependencies`);
|
console.log(`⚙️ Installing dependencies`);
|
||||||
spawnSync('yarn', ['install'], {cwd: outputDirectory, stdio: [0, 1, 2]});
|
spawnSync('yarn', ['install'], {cwd: outputDirectory, stdio: [0, 1, 2]});
|
||||||
@@ -93,9 +143,13 @@ function getPackageNameFromId(id: string): string {
|
|||||||
export async function initTemplate(
|
export async function initTemplate(
|
||||||
id: string,
|
id: string,
|
||||||
title: string,
|
title: string,
|
||||||
|
pluginType: PluginType,
|
||||||
|
supportedDevices: string[] | undefined,
|
||||||
outputDirectory: string,
|
outputDirectory: string,
|
||||||
) {
|
) {
|
||||||
const packageName = getPackageNameFromId(id);
|
const packageName = getPackageNameFromId(id);
|
||||||
|
const templateDir =
|
||||||
|
pluginType === 'device' ? devicePluginTemplateDir : pluginTemplateDir;
|
||||||
const templateItems = await recursiveReaddir(templateDir);
|
const templateItems = await recursiveReaddir(templateDir);
|
||||||
|
|
||||||
for (const item of templateItems) {
|
for (const item of templateItems) {
|
||||||
@@ -115,6 +169,16 @@ export async function initTemplate(
|
|||||||
.toString()
|
.toString()
|
||||||
.replace('{{id}}', id)
|
.replace('{{id}}', id)
|
||||||
.replace('{{title}}', title)
|
.replace('{{title}}', title)
|
||||||
|
.replace(
|
||||||
|
'{{supported_devices}}',
|
||||||
|
JSON.stringify(
|
||||||
|
supportedDevices
|
||||||
|
? supportedDevices.map((d) => ({
|
||||||
|
os: d,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
)
|
||||||
.replace('{{package_name}}', packageName);
|
.replace('{{package_name}}', packageName);
|
||||||
await fs.writeFile(newFile, content);
|
await fs.writeFile(newFile, content);
|
||||||
}
|
}
|
||||||
|
|||||||
2
desktop/pkg/templates/device-plugin/.gitignore.template
Normal file
2
desktop/pkg/templates/device-plugin/.gitignore.template
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist/
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@babel/preset-typescript',
|
||||||
|
'@babel/preset-react',
|
||||||
|
['@babel/preset-env', {targets: {node: 'current'}}]
|
||||||
|
],
|
||||||
|
};
|
||||||
42
desktop/pkg/templates/device-plugin/package.json.template
Normal file
42
desktop/pkg/templates/device-plugin/package.json.template
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://fbflipper.com/schemas/plugin-package/v2.json",
|
||||||
|
"name": "{{package_name}}",
|
||||||
|
"id": "{{id}}",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"pluginType": "device",
|
||||||
|
"supportedDevices": {{supported_devices}},
|
||||||
|
"main": "dist/bundle.js",
|
||||||
|
"flipperBundlerEntry": "src/index.tsx",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"flipper-plugin"
|
||||||
|
],
|
||||||
|
"icon": "apps",
|
||||||
|
"title": "{{title}}",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "flipper-pkg lint",
|
||||||
|
"prepack": "flipper-pkg lint && flipper-pkg bundle",
|
||||||
|
"build": "flipper-pkg bundle",
|
||||||
|
"watch": "flipper-pkg bundle --watch",
|
||||||
|
"test": "jest --no-watchman"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"flipper": "latest",
|
||||||
|
"flipper-plugin": "latest",
|
||||||
|
"antd": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/preset-react": "latest",
|
||||||
|
"@babel/preset-typescript": "latest",
|
||||||
|
"@testing-library/react": "latest",
|
||||||
|
"@types/jest": "latest",
|
||||||
|
"@types/react": "latest",
|
||||||
|
"@types/react-dom": "latest",
|
||||||
|
"antd": "latest",
|
||||||
|
"flipper": "latest",
|
||||||
|
"flipper-plugin": "latest",
|
||||||
|
"flipper-pkg": "latest",
|
||||||
|
"jest": "latest",
|
||||||
|
"typescript": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import {TestUtils} from 'flipper-plugin';
|
||||||
|
import * as Plugin from '..';
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
// API: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
test('It can store data', () => {
|
||||||
|
const {instance, sendLogEntry} = TestUtils.startDevicePlugin(Plugin);
|
||||||
|
|
||||||
|
expect(instance.data.get()).toEqual([]);
|
||||||
|
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854112859),
|
||||||
|
message: 'test1',
|
||||||
|
pid: 0,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 1,
|
||||||
|
type: 'error',
|
||||||
|
app: 'X',
|
||||||
|
});
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854117859),
|
||||||
|
message: 'test2',
|
||||||
|
pid: 2,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 3,
|
||||||
|
type: 'warn',
|
||||||
|
app: 'Y',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(instance.data.get()).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
"test1",
|
||||||
|
"test2",
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
// API: https://fbflipper.com/docs/tutorial/js-custom#testing-plugin-logic
|
||||||
|
test('It can render data', async () => {
|
||||||
|
const {instance, renderer, sendLogEntry} = TestUtils.renderDevicePlugin(
|
||||||
|
Plugin,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(instance.data.get()).toEqual([]);
|
||||||
|
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854112859),
|
||||||
|
message: 'test1',
|
||||||
|
pid: 0,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 1,
|
||||||
|
type: 'error',
|
||||||
|
app: 'X',
|
||||||
|
});
|
||||||
|
sendLogEntry({
|
||||||
|
date: new Date(1611854117859),
|
||||||
|
message: 'test2',
|
||||||
|
pid: 2,
|
||||||
|
tag: 'test',
|
||||||
|
tid: 3,
|
||||||
|
type: 'warn',
|
||||||
|
app: 'Y',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await renderer.findByTestId('0')).not.toBeNull();
|
||||||
|
expect(await renderer.findByTestId('1')).toMatchInlineSnapshot();
|
||||||
|
});
|
||||||
46
desktop/pkg/templates/device-plugin/src/index.tsx.template
Normal file
46
desktop/pkg/templates/device-plugin/src/index.tsx.template
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
DevicePluginClient,
|
||||||
|
usePlugin,
|
||||||
|
createState,
|
||||||
|
useValue,
|
||||||
|
Layout,
|
||||||
|
} from 'flipper-plugin';
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#creating-a-first-plugin
|
||||||
|
// API: https://fbflipper.com/docs/extending/flipper-plugin#pluginclient
|
||||||
|
export function devicePlugin(client: DevicePluginClient) {
|
||||||
|
const data = createState<string[]>([]);
|
||||||
|
|
||||||
|
client.device.onLogEntry((entry) => {
|
||||||
|
data.update((draft) => {
|
||||||
|
draft.push(entry.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.addMenuEntry({
|
||||||
|
action: 'clear',
|
||||||
|
handler: async () => {
|
||||||
|
data.set([]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {data};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read more: https://fbflipper.com/docs/tutorial/js-custom#building-a-user-interface-for-the-plugin
|
||||||
|
// API: https://fbflipper.com/docs/extending/flipper-plugin#react-hooks
|
||||||
|
export function Component() {
|
||||||
|
const instance = usePlugin(devicePlugin);
|
||||||
|
const data = useValue(instance.data);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.ScrollContainer>
|
||||||
|
{Object.entries(data).map(([id, d]) => (
|
||||||
|
<pre key={id} data-testid={id}>
|
||||||
|
{JSON.stringify(d)}
|
||||||
|
</pre>
|
||||||
|
))}
|
||||||
|
</Layout.ScrollContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
14
desktop/pkg/templates/device-plugin/tsconfig.json.template
Normal file
14
desktop/pkg/templates/device-plugin/tsconfig.json.template
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"module": "ES6",
|
||||||
|
"jsx": "react",
|
||||||
|
"sourceMap": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"files": ["src/index.tsx"]
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
"name": "{{package_name}}",
|
"name": "{{package_name}}",
|
||||||
"id": "{{id}}",
|
"id": "{{id}}",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"pluginType": "client",
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"flipperBundlerEntry": "src/index.tsx",
|
"flipperBundlerEntry": "src/index.tsx",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
Reference in New Issue
Block a user