Support plugins that contain a scope in their name (#1427)

Summary: Pull Request resolved: https://github.com/facebook/flipper/pull/1427

Reviewed By: mweststrate

Differential Revision: D22868784

fbshipit-source-id: c332e5b05e3fccb74cf5fdcdecf15b8f2e8c5006
This commit is contained in:
Anton Nikolaev
2020-08-04 10:50:45 -07:00
committed by Facebook GitHub Bot
parent 14e6b1078d
commit 7e84c8e880
3 changed files with 36 additions and 12 deletions

View File

@@ -8,7 +8,7 @@
"description": "The name of the package. Must start with \"flipper-plugin-\" prefix.",
"type": "string",
"maxLength": 214,
"pattern": "^flipper-plugin-[a-z0-9-._~]*$",
"pattern": "^(?:@[a-z0-9-*~][a-z0-9-*._~]*/)?flipper-plugin-[a-z0-9-._~]*$",
"errorMessage": "should start with \"flipper-plugin-\", e.g. \"flipper-plugin-example\""
},
"id": {

View File

@@ -48,6 +48,15 @@ test('valid package json', async () => {
expect(result).toBe(null);
});
test('valid scoped package json', async () => {
const testPackageJson = Object.assign({}, validPackageJson);
testPackageJson.name = '@test/flipper-plugin-package';
const json = JSON.stringify(testPackageJson);
fs.readFile = jest.fn().mockResolvedValue(new Buffer(json));
const result = await runLint('dir');
expect(result).toBe(null);
});
test('$schema field is required', async () => {
const testPackageJson = Object.assign({}, validPackageJson);
delete testPackageJson.$schema;

View File

@@ -45,11 +45,21 @@ function getPluginPendingInstallationDir(
}
function getPluginPendingInstallationsDir(name: string): string {
return path.join(pluginPendingInstallationDir, name);
return path.join(
pluginPendingInstallationDir,
replaceInvalidPathSegmentCharacters(name),
);
}
function getPluginInstallationDir(name: string): string {
return path.join(pluginInstallationDir, name);
return path.join(
pluginInstallationDir,
replaceInvalidPathSegmentCharacters(name),
);
}
function replaceInvalidPathSegmentCharacters(name: string) {
return name.replace('/', '__');
}
async function installPluginFromTempDir(
@@ -145,7 +155,10 @@ export async function installPluginFromNpm(name: string) {
const plugManNoDep = providePluginManagerNoDependencies();
plugManNoDep.options.pluginsPath = tmpDir;
await plugManNoDep.install(name);
const pluginTempDir = path.join(tmpDir, name);
const pluginTempDir = path.join(
tmpDir,
replaceInvalidPathSegmentCharacters(name),
);
await installPluginFromTempDir(pluginTempDir);
} finally {
await fs.remove(tmpDir);
@@ -178,14 +191,15 @@ export async function getInstalledPlugins(): Promise<PluginMap> {
const dirs = await fs.readdir(pluginInstallationDir);
const plugins = await Promise.all<[string, PluginDetails]>(
dirs.map(
(name) =>
(dirName) =>
new Promise(async (resolve, reject) => {
const pluginDir = path.join(pluginInstallationDir, name);
const pluginDir = path.join(pluginInstallationDir, dirName);
if (!(await fs.lstat(pluginDir)).isDirectory()) {
return resolve(undefined);
}
try {
resolve([name, await getPluginDetails(pluginDir)]);
const details = await getPluginDetails(pluginDir);
resolve([details.name, details]);
} catch (e) {
reject(e);
}
@@ -203,24 +217,25 @@ export async function getPendingInstallationPlugins(): Promise<PluginMap> {
const dirs = await fs.readdir(pluginPendingInstallationDir);
const plugins = await Promise.all<[string, PluginDetails]>(
dirs.map(
(name) =>
(dirName) =>
new Promise(async (resolve, reject) => {
const versions = (
await fs.readdir(path.join(pluginPendingInstallationDir, name))
await fs.readdir(path.join(pluginPendingInstallationDir, dirName))
).sort((v1, v2) => semver.compare(v2, v1, true));
if (versions.length === 0) {
return resolve(undefined);
}
const pluginDir = path.join(
pluginPendingInstallationDir,
name,
dirName,
versions[0],
);
if (!(await fs.lstat(pluginDir)).isDirectory()) {
return resolve(undefined);
}
try {
resolve([name, await getPluginDetails(pluginDir)]);
const details = await getPluginDetails(pluginDir);
resolve([details.name, details]);
} catch (e) {
reject(e);
}
@@ -244,7 +259,7 @@ export async function getPendingAndInstalledPlugins(): Promise<PluginMap> {
}
export async function removePlugin(name: string): Promise<void> {
await fs.remove(path.join(pluginInstallationDir, name));
await fs.remove(getPluginInstallationDir(name));
}
export async function finishPendingPluginInstallations() {