Update build scripts to support bundling server add-ons

Summary: Summary

Reviewed By: nikoant

Differential Revision: D34170565

fbshipit-source-id: be9904809bf33e85536a4c6ead0e753ef05209ff
This commit is contained in:
Andrey Goncharov
2022-02-28 03:50:34 -08:00
committed by Facebook GitHub Bot
parent 47dd675dc8
commit 5cdb7c952e
9 changed files with 165 additions and 51 deletions

View File

@@ -14,5 +14,6 @@ scripts/generate-changelog.js
static/index.js
static/defaultPlugins/*
app/src/defaultPlugins/index.tsx
flipper-server-core/src/defaultPlugins/index.tsx
generated
flipper-server/static

1
desktop/.gitignore vendored
View File

@@ -6,6 +6,7 @@ node_modules/
/static/defaultPlugins/
/app/src/defaultPlugins/index.tsx
/flipper-ui-browser/src/defaultPlugins/index.tsx
/flipper-server-core/src/defaultPlugins/index.tsx
/coverage
.env
tsc-error.log

View File

@@ -13,6 +13,7 @@ export interface PluginDetails {
version: string;
source: string;
main: string;
serverAddOnSource?: string;
serverAddOn?: string;
id: string;
gatekeeper?: string;
@@ -84,6 +85,7 @@ export interface InstalledPluginDetails extends ConcretePluginDetails {
isActivatable: true;
dir: string;
entry: string;
serverAddOnEntry?: string;
}
// Describes plugin physically available for activation in Flipper.
@@ -162,6 +164,7 @@ function getPluginDetailsV2(packageJson: any): PluginDetails {
main: packageJson.main,
serverAddOn: packageJson.serverAddOn,
source: packageJson.flipperBundlerEntry,
serverAddOnSource: packageJson.flipperBundlerEntryServerAddOn,
id: packageJson.id || packageJson.name,
gatekeeper: packageJson.gatekeeper,
icon: packageJson.icon,

View File

@@ -34,31 +34,25 @@ async function getMetroDir() {
return __dirname;
}
type Options = {
sourceMapPath?: string | undefined;
};
export default async function bundlePlugin(
pluginDir: string,
dev: boolean,
options?: Options,
) {
const stat = await fs.lstat(pluginDir);
if (!stat.isDirectory()) {
throw new Error(`Plugin source ${pluginDir} is not a directory.`);
interface RunMetroConfig {
pluginDir: string;
baseConfig: any;
entry: string;
out: string;
dev: boolean;
sourceMapPath?: string;
babelTransformerPath: string;
}
const packageJsonPath = path.join(pluginDir, 'package.json');
if (!(await fs.pathExists(packageJsonPath))) {
throw new Error(
`package.json is not found in plugin source directory ${pluginDir}.`,
);
}
const plugin = await getInstalledPluginDetails(pluginDir);
const entry = plugin.source;
const out = path.resolve(pluginDir, plugin.main);
await fs.ensureDir(path.dirname(out));
const baseConfig = await Metro.loadConfig();
async function runMetro({
pluginDir,
baseConfig,
entry,
out,
dev,
sourceMapPath,
babelTransformerPath,
}: RunMetroConfig) {
const config = Object.assign({}, baseConfig, {
reporter: {update: () => {}},
projectRoot: pluginDir,
@@ -72,7 +66,7 @@ export default async function bundlePlugin(
},
transformer: {
...baseConfig.transformer,
babelTransformerPath: require.resolve('flipper-babel-transformer'),
babelTransformerPath,
minifierPath: require.resolve('metro-minify-terser'),
minifierConfig: {
// see: https://www.npmjs.com/package/terser
@@ -98,7 +92,7 @@ export default async function bundlePlugin(
],
});
const sourceMapUrl = out.replace(/\.js$/, '.map');
const sourceMap = dev || !!options?.sourceMapPath;
const sourceMap = dev || !!sourceMapPath;
await Metro.runBuild(config, {
dev,
sourceMap,
@@ -113,11 +107,71 @@ export default async function bundlePlugin(
await stripSourceMapComment(out);
}
if (
options?.sourceMapPath &&
path.resolve(options.sourceMapPath) !== path.resolve(sourceMapUrl)
sourceMapPath &&
path.resolve(sourceMapPath) !== path.resolve(sourceMapUrl)
) {
console.log(`Moving plugin sourcemap to ${options.sourceMapPath}`);
await fs.ensureDir(path.dirname(options.sourceMapPath));
await fs.move(sourceMapUrl, options.sourceMapPath, {overwrite: true});
console.log(`Moving plugin sourcemap to ${sourceMapPath}`);
await fs.ensureDir(path.dirname(sourceMapPath));
await fs.move(sourceMapUrl, sourceMapPath, {overwrite: true});
}
}
type Options = {
sourceMapPath?: string | undefined;
sourceMapPathServerAddOn?: string | undefined;
};
export default async function bundlePlugin(
pluginDir: string,
dev: boolean,
options?: Options,
) {
const stat = await fs.lstat(pluginDir);
if (!stat.isDirectory()) {
throw new Error(`Plugin source ${pluginDir} is not a directory.`);
}
const packageJsonPath = path.join(pluginDir, 'package.json');
if (!(await fs.pathExists(packageJsonPath))) {
throw new Error(
`package.json is not found in plugin source directory ${pluginDir}.`,
);
}
const plugin = await getInstalledPluginDetails(pluginDir);
const baseConfig = await Metro.loadConfig();
const bundleConfigs: RunMetroConfig[] = [];
await fs.ensureDir(path.dirname(plugin.entry));
bundleConfigs.push({
pluginDir,
baseConfig,
entry: plugin.source,
out: plugin.entry,
dev,
sourceMapPath: options?.sourceMapPath,
babelTransformerPath: require.resolve('flipper-babel-transformer'),
});
if (
plugin.serverAddOnSource &&
plugin.serverAddOn &&
plugin.serverAddOnEntry
) {
await fs.ensureDir(path.dirname(plugin.serverAddOnEntry));
bundleConfigs.push({
pluginDir,
baseConfig,
entry: plugin.serverAddOnSource,
out: plugin.serverAddOnEntry,
dev,
sourceMapPath: options?.sourceMapPathServerAddOn,
babelTransformerPath: require.resolve(
`flipper-babel-transformer/${
dev ? 'lib/transform-server-dev' : 'lib/transform-server-prod'
}`,
),
});
}
await Promise.all(bundleConfigs.map((config) => runMetro(config)));
}

View File

@@ -53,6 +53,7 @@ test('getPluginDetailsV1', async () => {
"main": "dist/bundle.js",
"name": "flipper-plugin-test",
"pluginType": undefined,
"serverAddOnEntry": undefined,
"source": "src/index.tsx",
"specVersion": 1,
"supportedApps": undefined,
@@ -97,6 +98,8 @@ test('getPluginDetailsV2', async () => {
"pluginType": undefined,
"publishedDocs": undefined,
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": undefined,
@@ -141,6 +144,8 @@ test('id used as title if the latter omited', async () => {
"pluginType": undefined,
"publishedDocs": undefined,
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": undefined,
@@ -184,6 +189,8 @@ test('name without "flipper-plugin-" prefix is used as title if the latter omite
"pluginType": undefined,
"publishedDocs": undefined,
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": undefined,
@@ -230,6 +237,8 @@ test('flipper-plugin-version is parsed', async () => {
"pluginType": undefined,
"publishedDocs": undefined,
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": undefined,
@@ -280,6 +289,8 @@ test('plugin type and supported devices parsed', async () => {
"pluginType": "device",
"publishedDocs": undefined,
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": undefined,
@@ -346,6 +357,8 @@ test('plugin type and supported apps parsed', async () => {
"pluginType": "client",
"publishedDocs": undefined,
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": Array [
@@ -435,6 +448,8 @@ test('can merge two package.json files', async () => {
"setup": true,
},
"serverAddOn": undefined,
"serverAddOnEntry": undefined,
"serverAddOnSource": undefined,
"source": "src/index.tsx",
"specVersion": 2,
"supportedApps": undefined,

View File

@@ -63,11 +63,15 @@ export async function getInstalledPluginDetails(
`${packageJson.name}@${packageJson.version || '0.0.0'}.js`,
)
: path.resolve(dir, packageJson.main);
const serverAddOnEntry = packageJson.serverAddOn
? path.resolve(dir, packageJson.serverAddOn)
: undefined;
return {
...pluginDetails,
isBundled: false,
isActivatable: true,
dir,
entry,
serverAddOnEntry,
};
}

View File

@@ -59,6 +59,12 @@ const argv = yargs
type: 'string',
alias: 'os',
},
'output-sourcemap-server-addon': {
description:
'File path for the server add-on sourcemap to be written. Optional.',
type: 'string',
alias: 'os',
},
})
.help()
.strict()
@@ -72,9 +78,13 @@ async function buildPlugin() {
const outputUnpackedArg = argv['output-unpacked'];
const minFlipperVersion = argv['min-flipper-version'];
const outputSourcemapArg = argv['output-sourcemap'];
const outputSourcemapServerAddOnArg = argv['output-sourcemap-server-addon'];
const packageJsonPath = path.join(pluginDir, 'package.json');
const packageJsonOverridePath = path.join(pluginDir, 'fb', 'package.json');
await runBuild(pluginDir, false, {sourceMapPath: outputSourcemapArg});
await runBuild(pluginDir, false, {
sourceMapPath: outputSourcemapArg,
sourceMapPathServerAddOn: outputSourcemapServerAddOnArg,
});
const checksum = await computePackageChecksum(pluginDir);
if (previousChecksum !== checksum && argv.version) {
console.log(`Plugin changed. Packaging new version ${argv.version}...`);

View File

@@ -36,6 +36,7 @@ import {
serverDir,
rootDir,
browserUiDir,
serverCoreDir,
} from './paths';
import pFilter from 'p-filter';
import child from 'child_process';
@@ -108,6 +109,27 @@ export async function prepareDefaultPlugins(isInsidersBuild: boolean = false) {
console.log('✅ Prepared default plugins.');
}
function getGeneratedIndex(pluginRequires: string) {
return `
/* eslint-disable */
// THIS FILE IS AUTO-GENERATED by function "generateDefaultPluginEntryPoints" in "build-utils.ts".
declare const require: any;
// This function exists to make sure that if one require fails in its module initialisation, not everything fails
function tryRequire(module: string, fn: () => any): any {
try {
return fn();
} catch (e) {
console.error(\`Could not require \${module}: \`, e)
return {};
}
}
export default {\n${pluginRequires}\n} as any
`;
}
async function generateDefaultPluginEntryPoints(
defaultPlugins: InstalledPluginDetails[],
) {
@@ -124,36 +146,20 @@ async function generateDefaultPluginEntryPoints(
p.flipperSDKVersion === '0.0.0' ? version : p.flipperSDKVersion,
dir: undefined,
entry: undefined,
serverAddOnEntry: undefined,
} as BundledPluginDetails),
);
await fs.writeJSON(
path.join(defaultPluginsDir, 'bundled.json'),
bundledPlugins,
);
const pluginRequres = bundledPlugins
const pluginRequires = bundledPlugins
.map(
(x) =>
` '${x.name}': tryRequire('${x.name}', () => require('${x.name}'))`,
)
.join(',\n');
const generatedIndex = `
/* eslint-disable */
// THIS FILE IS AUTO-GENERATED by function "generateDefaultPluginEntryPoints" in "build-utils.ts".
declare const require: any;
// This function exists to make sure that if one require fails in its module initialisation, not everything fails
function tryRequire(module: string, fn: () => any): any {
try {
return fn();
} catch (e) {
console.error(\`Could not require \${module}: \`, e)
return {};
}
}
export default {\n${pluginRequres}\n} as any
`;
const generatedIndex = getGeneratedIndex(pluginRequires);
await fs.ensureDir(path.join(appDir, 'src', 'defaultPlugins'));
await fs.writeFile(
path.join(appDir, 'src', 'defaultPlugins', 'index.tsx'),
@@ -164,6 +170,25 @@ async function generateDefaultPluginEntryPoints(
path.join(browserUiDir, 'src', 'defaultPlugins', 'index.tsx'),
generatedIndex,
);
const serverAddOns = defaultPlugins.filter(
({serverAddOnSource}) => !!serverAddOnSource,
);
if (serverAddOns.length) {
const serverAddOnRequires = serverAddOns
.map(
(x) =>
` '${x.name}': tryRequire('${x.serverAddOnSource}', () => require('${x.serverAddOnSource}'))`,
)
.join(',\n');
const generatedIndexServerAddOns = getGeneratedIndex(serverAddOnRequires);
await fs.ensureDir(path.join(serverCoreDir, 'src', 'defaultPlugins'));
await fs.writeFile(
path.join(serverCoreDir, 'src', 'defaultPlugins', 'index.tsx'),
generatedIndexServerAddOns,
);
}
console.log('✅ Generated bundled plugin entry points.');
}

View File

@@ -14,6 +14,7 @@ export const appDir = path.join(rootDir, 'app');
export const browserUiDir = path.join(rootDir, 'flipper-ui-browser');
export const staticDir = path.join(rootDir, 'static');
export const serverDir = path.join(rootDir, 'flipper-server');
export const serverCoreDir = path.join(rootDir, 'flipper-server-core');
export const defaultPluginsDir = path.join(staticDir, 'defaultPlugins');
export const pluginsDir = path.join(rootDir, 'plugins');
export const fbPluginsDir = path.join(pluginsDir, 'fb');