"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:
committed by
Facebook GitHub Bot
parent
21c574ac80
commit
d08dfee018
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
91
desktop/pkg/src/commands/init.ts
Normal file
91
desktop/pkg/src/commands/init.ts
Normal 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}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
desktop/pkg/templates/plugin/package.json.template
Normal file
25
desktop/pkg/templates/plugin/package.json.template
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
47
desktop/pkg/templates/plugin/src/index.tsx.template
Normal file
47
desktop/pkg/templates/plugin/src/index.tsx.template
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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==
|
||||||
|
|||||||
@@ -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`.
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user