"init" command for flipper-pkg tool

Summary: Added command "init" to "flipper-pkg" which helps to quickly initialise Flipper desktop plugin.

Reviewed By: passy

Differential Revision: D21253819

fbshipit-source-id: 85a2fbde07ecb63737d180d2a7e5cc2846b4f533
This commit is contained in:
Anton Nikolaev
2020-04-27 17:31:39 -07:00
committed by Facebook GitHub Bot
parent 21c574ac80
commit d08dfee018
9 changed files with 225 additions and 18 deletions

View File

@@ -26,6 +26,7 @@ USAGE
<!-- commands --> <!-- commands -->
* [`flipper-pkg bundle [DIRECTORY]`](#flipper-pkg-bundle-directory) * [`flipper-pkg bundle [DIRECTORY]`](#flipper-pkg-bundle-directory)
* [`flipper-pkg help [COMMAND]`](#flipper-pkg-help-command) * [`flipper-pkg help [COMMAND]`](#flipper-pkg-help-command)
* [`flipper-pkg init [DIRECTORY]`](#flipper-pkg-init-directory)
* [`flipper-pkg lint [DIRECTORY]`](#flipper-pkg-lint-directory) * [`flipper-pkg lint [DIRECTORY]`](#flipper-pkg-lint-directory)
* [`flipper-pkg pack [DIRECTORY]`](#flipper-pkg-pack-directory) * [`flipper-pkg pack [DIRECTORY]`](#flipper-pkg-pack-directory)
@@ -63,6 +64,24 @@ OPTIONS
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.2.3/src/commands/help.ts)_ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.2.3/src/commands/help.ts)_
## `flipper-pkg init [DIRECTORY]`
initializes Flipper desktop plugin template in the provided directory
```
USAGE
$ flipper-pkg init [DIRECTORY]
ARGUMENTS
DIRECTORY [default: .] Path to directory where plugin package template should be initialized. Defaults to the current
working directory.
EXAMPLE
$ flipper-pkg init path/to/plugin
```
_See code: [src/commands/init.ts](https://github.com/facebook/flipper/blob/v0.39.0/src/commands/init.ts)_
## `flipper-pkg lint [DIRECTORY]` ## `flipper-pkg lint [DIRECTORY]`
validates a plugin package directory validates a plugin package directory

View File

@@ -22,7 +22,8 @@
"cli-ux": "^5.4.5", "cli-ux": "^5.4.5",
"flipper-pkg-lib": "0.39.0", "flipper-pkg-lib": "0.39.0",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"inquirer": "^7.0.5" "inquirer": "^7.1.0",
"recursive-readdir": "^2.2.2"
}, },
"devDependencies": { "devDependencies": {
"@oclif/dev-cli": "^1", "@oclif/dev-cli": "^1",

View File

@@ -0,0 +1,91 @@
/**
* 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 {Command} from '@oclif/command';
import {args} from '@oclif/parser';
import path from 'path';
import fs from 'fs-extra';
import recursiveReaddirImport from 'recursive-readdir';
import {promisify} from 'util';
import inquirer from 'inquirer';
const recursiveReaddir = promisify<string, string[]>(recursiveReaddirImport);
const templateDir = path.resolve(__dirname, '..', '..', 'templates', 'plugin');
const templateExt = '.template';
export default class Init extends Command {
public static description =
'initializes a Flipper desktop plugin template in the provided directory';
public static examples = [`$ flipper-pkg init path/to/plugin`];
public static args: args.IArg[] = [
{
name: 'directory',
required: false,
default: '.',
description:
'Path to the directory where the plugin package template should be initialized. Defaults to the current working directory.',
},
];
public async run() {
const {args} = this.parse(Init);
const outputDirectory: string = path.resolve(process.cwd(), args.directory);
console.log(
`⚙️ Initializing Flipper desktop template in ${outputDirectory}`,
);
const defaultID = path.basename(outputDirectory);
const idQuestion: inquirer.QuestionCollection = [
{
type: 'input',
name: 'id',
message:
'ID (must match native plugin ID, e.g. returned by getId() in Android plugin):',
default: defaultID,
},
];
const id: string = (await inquirer.prompt(idQuestion)).id;
const titleQuestion: inquirer.QuestionCollection = [
{
type: 'input',
name: 'title',
message: 'Title (will be shown in the Flipper main sidebar):',
default: id,
},
];
const title: string = (await inquirer.prompt(titleQuestion)).title;
const packageNameSuffix = id.toLowerCase().replace(' ', '-');
const templateItems = await recursiveReaddir(templateDir);
for (const item of templateItems) {
const lstat = await fs.lstat(item);
if (lstat.isFile()) {
const file = path.relative(templateDir, item);
const dir = path.dirname(file);
const newDir = path.join(outputDirectory, dir);
const newFile = file.endsWith('.template')
? path.join(
outputDirectory,
file.substring(0, file.length - templateExt.length),
)
: path.join(outputDirectory, file);
await fs.ensureDir(newDir);
const content = (await fs.readFile(item))
.toString()
.replace('{{id}}', id)
.replace('{{title}}', title)
.replace('{{package_name_suffix}}', packageNameSuffix);
await fs.writeFile(newFile, content);
}
}
console.log(
`✅ Plugin template initialized. Package name: flipper-plugin-${packageNameSuffix}.`,
);
}
}

View File

@@ -0,0 +1,25 @@
{
"$schema": "https://fbflipper.com/schemas/plugin-package/v2.json",
"name": "flipper-plugin-{{package_name_suffix}}",
"id": "{{id}}",
"version": "1.0.0",
"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"
},
"peerDependencies": {
"flipper": "latest"
},
"devDependencies": {
"flipper": "latest",
"flipper-pkg": "latest"
}
}

View File

@@ -0,0 +1,47 @@
import React from 'react';
import {FlipperPlugin, FlexColumn, KeyboardActions} from 'flipper';
type State = {};
type Data = {};
type PersistedState = {
data: Array<Data>;
};
export default class extends FlipperPlugin<State, any, PersistedState> {
static keyboardActions: KeyboardActions = ['clear'];
static defaultPersistedState: PersistedState = {
data: [],
};
static persistedStateReducer = (
persistedState: PersistedState,
method: string,
data: Data,
): PersistedState => {
return {
...persistedState,
data: persistedState.data.concat([data]),
};
};
state = {};
onKeyboardAction = (action: string) => {
if (action === 'clear') {
this.props.setPersistedState({data: []});
}
};
render() {
return (
<FlexColumn>
{this.props.persistedState.data.map((d) => (
<div>{d}</div>
))}
</FlexColumn>
);
}
}

View File

@@ -9,8 +9,9 @@
"expand-tilde": "^2.0.2", "expand-tilde": "^2.0.2",
"fb-watchman": "^2.0.0", "fb-watchman": "^2.0.0",
"fix-path": "^3.0.0", "fix-path": "^3.0.0",
"fs-extra": "^8.1.0",
"flipper-pkg-lib": "0.39.0", "flipper-pkg-lib": "0.39.0",
"fs-extra": "^8.1.0",
"ignore": "^5.1.4",
"mem": "^6.0.0", "mem": "^6.0.0",
"mkdirp": "^1.0.0", "mkdirp": "^1.0.0",
"p-filter": "^2.1.0", "p-filter": "^2.1.0",
@@ -19,7 +20,6 @@
"uuid": "^7.0.1", "uuid": "^7.0.1",
"ws": "^7.2.3", "ws": "^7.2.3",
"xdg-basedir": "^4.0.0", "xdg-basedir": "^4.0.0",
"ignore": "^5.1.4",
"yargs": "^15.3.1" "yargs": "^15.3.1"
} }
} }

View File

@@ -6243,7 +6243,7 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
inquirer@^7.0.0, inquirer@^7.0.5: inquirer@^7.0.0, inquirer@^7.1.0:
version "7.1.0" version "7.1.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==

View File

@@ -36,19 +36,28 @@ Plugin File example structure:
## Plugin Definition ## Plugin Definition
### flipper-pkg
CLI tool `flipper-pkg` helps to initialize, validate, and package Flipper desktop plugins.
The tool is published to npm and can be installed as a `devDependency` for the plugin package, or as a global CLI tool:
```
yarn global add flipper-pkg
```
or
```
npm install flipper-pkg --global
```
### Package Format
All Flipper Desktop plugins must be self-contained in a directory. This directory must contain at a minimum package.json and entry source file, e.g.: All Flipper Desktop plugins must be self-contained in a directory. This directory must contain at a minimum package.json and entry source file, e.g.:
* package.json * package.json
* src/index.tsx * src/index.tsx
The best way to initialize a JS plugin is to create a directory, and run `yarn init` inside it. By convention, the `name` of a Flipper plugin package should start with `flipper-plugin-`, e.g. `flipper-plugin-myplugin`. The best way to initialize a JS plugin is to create a directory, and run `flipper-pkg init` inside it ("flipper-pkg" should be installed globally before that). It will ask few questions and initialize the plugin for you.
Make sure that the `id` field in your package.json is the same as the identifier of the client plugin, e.g. if your Java plugin returns `myplugin` from its `getId()` method, the `id` field in your `package.json` should also be `myplugin`. After `flipper-pkg init` finished, you should have files `package.json` and `src/index.tsx` files in the directory. The first file is the plugin package manifest and the second is the entry point to your plugin. An example `package.json` file could look like this:
Flipper has [tooling for transpiling and bundling](#transpiling-and-bundling) which allows writing plugins in plain ES6 JavaScript, [Flow](https://flow.org/) or [TypeScript](https://www.typescriptlang.org/) but we recommend you use **TypeScript** for the best development experience. We also recommend you use the file extension `.tsx` when using TypeScript which adds support for inline React expressions.
After `yarn init` finishes, create an `src/index.tsx` file which will be the entry point to your plugin. An example `package.json` file could look like this:
Example `package.json`:
``` ```
{ {
"$schema": "https://fbflipper.com/schemas/plugin-package/v2.json", "$schema": "https://fbflipper.com/schemas/plugin-package/v2.json",
@@ -84,7 +93,7 @@ Important attributes of `package.json`:
- `name` Npm package name. Should start with `flipper-plugin-` by convention, so Flipper plugins can be easily found on npm. - `name` Npm package name. Should start with `flipper-plugin-` by convention, so Flipper plugins can be easily found on npm.
- `id` Used as the plugin native identifier and **must match the mobile plugin identifier**. - `id` Used as the plugin native identifier and **must match the mobile plugin identifier**, e.g. returned by `getId` method of your Java plugin.
- `main` Points to the plugin bundle which will be loaded by Flipper. The "flipper-pkg" utility uses this field to determine output location during plugin bundling. - `main` Points to the plugin bundle which will be loaded by Flipper. The "flipper-pkg" utility uses this field to determine output location during plugin bundling.
@@ -115,6 +124,10 @@ export default class extends FlipperPlugin {
Plugin definition can be validated using command `flipper-pkg lint`. The command shows all the mismatches which should be fixed to make plugin definition valid. Plugin definition can be validated using command `flipper-pkg lint`. The command shows all the mismatches which should be fixed to make plugin definition valid.
### Transpilation
Flipper has [tooling for transpiling and bundling](#transpiling-and-bundling) which allows writing plugins in plain ES6 JavaScript, [Flow](https://flow.org/) or [TypeScript](https://www.typescriptlang.org/) but we recommend you use **TypeScript** for the best development experience. We also recommend you use the file extension `.tsx` when using TypeScript which adds support for inline React expressions.
### npm dependencies ### npm dependencies
If you need any dependencies in your plugin, you can install them using `yarn add`. If you need any dependencies in your plugin, you can install them using `yarn add`.

View File

@@ -29,20 +29,31 @@ Your file will then look something like this:
} }
``` ```
## Installing flipper-pkg
`flipper-pkg` tool helps to define, validate and package Flipper desktop plugins. You can install it globally using:
```
yarn global add flipper-pkg
```
or
```
npm install flipper-pkg --global
```
## Creating the Plugin Package ## Creating the Plugin Package
With the loading part out of the way, we can create the new plugin. For that, first With the loading part out of the way, we can create the new plugin. For that, first create a new folder inside the custom plugins directory. Then use `flpper-pkg init` to initialise a new Flipper desktop plugin package:
create a new folder inside the custom plugins directory. Then use `yarn init` (`npm init` if that's more your style)
to initialise a new JavaScript package:
```bash ```bash
$ cd ~/Flipper/custom-plugins/ $ cd ~/Flipper/custom-plugins/
$ mkdir sea-mammals $ mkdir sea-mammals
$ cd sea-mammals $ cd sea-mammals
$ yarn init $ flipper-pkg init
``` ```
Open the `package.json` and edit it. There are a few important things: The tool will ask you to provide "id" and "title" for your plugin. Use "sea-mammals" as "id" and "Sea Mammals" as "title". After that the tool will create two files in the directory: `package.json` and `src/index.tsx`.
Open the `package.json` to check the fields:
1) "$schema" must contain URI identifying scheme according to which the plugin is defined. Currently, Flipper supports plugins defined by the specification version 2 (https://fbflipper.com/schemas/plugin-package/v2.json), while version 1 is being deprecated. 1) "$schema" must contain URI identifying scheme according to which the plugin is defined. Currently, Flipper supports plugins defined by the specification version 2 (https://fbflipper.com/schemas/plugin-package/v2.json), while version 1 is being deprecated.
2) "name" must start with "flipper-plugin-" 2) "name" must start with "flipper-plugin-"
3) "keywords" must contain "flipper-plugin" 3) "keywords" must contain "flipper-plugin"