"lint" command for flipper-pkg tool
Summary: Implemented json schema for flipper plugin package.json and used it for validation in "flipper-pkg lint" command. Nice thing about json schema is that it not only allows to validate json, but also can be referenced using "$schema" property in json so IDEs like VSCode can find it and use for code completion, validation and to show properties documentation. I'm going to deploy the schema as a part of documentation website so it can be referenced as https://fbflipper.com/schemas/plugin-package/v2.json. Also the "$schema" field can be used instead of "specVersion" to determine the specification according to which the plugin is defined. E.g., if specification version 3 would be created, it will be described in schema https://fbflipper.com/schemas/plugin-package/v3.json, etc. Reviewed By: passy Differential Revision: D21228294 fbshipit-source-id: f21351e584ef936a7d6b314436448489691f83a6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
01f8d80402
commit
21c574ac80
96
desktop/pkg/src/utils/runLint.ts
Normal file
96
desktop/pkg/src/utils/runLint.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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 Ajv from 'ajv';
|
||||
import filePathExists from './validation/filePathExists';
|
||||
|
||||
const pluginPackageJsonSchemaUrl =
|
||||
'https://fbflipper.com/schemas/plugin-package/v2.json';
|
||||
|
||||
const packageJsonSchemaUrl =
|
||||
'https://schemastore.azurewebsites.net/schemas/json/package.json';
|
||||
|
||||
const schemasDir = path.resolve(__dirname, '..', '..', 'schemas');
|
||||
const packageJsonSchemaPath = path.join(schemasDir, 'package.json');
|
||||
const pluginPackageJsonSchemaPath = path.join(
|
||||
schemasDir,
|
||||
'plugin-package-v2.json',
|
||||
);
|
||||
|
||||
export default async function runLint(
|
||||
inputDirectory: string,
|
||||
): Promise<null | string[]> {
|
||||
const packageJsonPath = path.join(inputDirectory, 'package.json');
|
||||
if (!(await fs.pathExists(packageJsonPath))) {
|
||||
return [
|
||||
`package.json not found in plugin source directory ${inputDirectory}.`,
|
||||
];
|
||||
}
|
||||
const packageJsonString = (await fs.readFile(packageJsonPath)).toString();
|
||||
const packageJson = JSON.parse(packageJsonString);
|
||||
if (!packageJson.$schema) {
|
||||
return [
|
||||
[
|
||||
`. should have required property "$schema" pointing to a supported schema URI, e.g.:`,
|
||||
`{`,
|
||||
` "$schema": "${pluginPackageJsonSchemaUrl}",`,
|
||||
` "name": "flipper-plugin-example",`,
|
||||
` ...`,
|
||||
`}`,
|
||||
].join('\n'),
|
||||
];
|
||||
}
|
||||
|
||||
if (packageJson.$schema != pluginPackageJsonSchemaUrl) {
|
||||
return [
|
||||
[
|
||||
`.$schema should point to a supported schema. Currently supported schemas:`,
|
||||
`- ${pluginPackageJsonSchemaUrl}`,
|
||||
].join('\n'),
|
||||
];
|
||||
}
|
||||
|
||||
const packageJsonSchema = await fs.readJson(packageJsonSchemaPath);
|
||||
const pluginPackageJsonSchema = await fs.readJson(
|
||||
pluginPackageJsonSchemaPath,
|
||||
);
|
||||
const ajv = new Ajv({
|
||||
allErrors: true,
|
||||
loadSchema,
|
||||
schemaId: 'auto',
|
||||
meta: true,
|
||||
jsonPointers: true,
|
||||
});
|
||||
require('ajv-errors')(ajv);
|
||||
ajv
|
||||
.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'))
|
||||
.addSchema(packageJsonSchema, packageJsonSchemaUrl)
|
||||
.addSchema(pluginPackageJsonSchema, pluginPackageJsonSchemaUrl)
|
||||
.addKeyword('filePathExists', filePathExists(inputDirectory));
|
||||
|
||||
const validate = await ajv.compileAsync(pluginPackageJsonSchema);
|
||||
const valid = await validate(packageJson);
|
||||
if (!valid) {
|
||||
return validate.errors
|
||||
? validate.errors.map(
|
||||
(error) =>
|
||||
`${error.dataPath === '' ? '.' : error.dataPath} ${
|
||||
error.message || 'unspecified error'
|
||||
}`,
|
||||
)
|
||||
: [];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function loadSchema(_uri: string) {
|
||||
return false;
|
||||
}
|
||||
43
desktop/pkg/src/utils/validation/filePathExists.ts
Normal file
43
desktop/pkg/src/utils/validation/filePathExists.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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 ajv from 'ajv';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
export default function (inputDirectory: string) {
|
||||
const filePathExists: ajv.KeywordDefinition = {
|
||||
errors: true,
|
||||
compile: function validatePathExists(schema: any) {
|
||||
function filePathExistsValidator(value: any, dataPath: any) {
|
||||
const it = filePathExistsValidator as ajv.SchemaValidateFunction;
|
||||
if (!schema) {
|
||||
return true;
|
||||
}
|
||||
it.errors = [];
|
||||
const tpl = {
|
||||
keyword: 'filePathExists',
|
||||
dataPath,
|
||||
schemaPath: '',
|
||||
params: [],
|
||||
};
|
||||
const fullPath = path.resolve(inputDirectory, value);
|
||||
if (!fs.pathExistsSync(fullPath) || !fs.lstatSync(fullPath).isFile()) {
|
||||
it.errors.push({
|
||||
...tpl,
|
||||
message: `should point to a valid file`,
|
||||
});
|
||||
}
|
||||
return it.errors.length === 0;
|
||||
}
|
||||
return filePathExistsValidator;
|
||||
},
|
||||
};
|
||||
return filePathExists;
|
||||
}
|
||||
Reference in New Issue
Block a user