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:
committed by
Facebook GitHub Bot
parent
47dd675dc8
commit
5cdb7c952e
@@ -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
1
desktop/.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}...`);
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user