Bundle headless plugins
Summary: Current temporary limitations: all headless plugins are bundled with Flipper. Reviewed By: antonk52 Differential Revision: D36098168 fbshipit-source-id: c58abe41776157258a5c39a80a5c1a33cf3f42c5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
1f2f799772
commit
a6d7f98cfd
@@ -18,10 +18,16 @@ const babelOptions = {
|
|||||||
filename: 'index.js',
|
filename: 'index.js',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const babelOptionsPlugin = {
|
||||||
|
...babelOptions,
|
||||||
|
root: 'plugins/randomPlugin/',
|
||||||
|
filename: 'plugins/randomPlugin/index.js',
|
||||||
|
};
|
||||||
|
|
||||||
test('transform react requires to global object', () => {
|
test('transform react requires to global object', () => {
|
||||||
const src = 'require("react")';
|
const src = 'require("react")';
|
||||||
const ast = parse(src);
|
const ast = parse(src);
|
||||||
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
|
||||||
const {code} = generate(transformed!);
|
const {code} = generate(transformed!);
|
||||||
expect(code).toBe('global.React;');
|
expect(code).toBe('global.React;');
|
||||||
});
|
});
|
||||||
@@ -29,7 +35,7 @@ test('transform react requires to global object', () => {
|
|||||||
test('transform react-dom requires to global object', () => {
|
test('transform react-dom requires to global object', () => {
|
||||||
const src = 'require("react-dom")';
|
const src = 'require("react-dom")';
|
||||||
const ast = parse(src);
|
const ast = parse(src);
|
||||||
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
|
||||||
const {code} = generate(transformed!);
|
const {code} = generate(transformed!);
|
||||||
expect(code).toBe('global.ReactDOM;');
|
expect(code).toBe('global.ReactDOM;');
|
||||||
});
|
});
|
||||||
@@ -37,7 +43,7 @@ test('transform react-dom requires to global object', () => {
|
|||||||
test('transform react-dom/client requires to global object', () => {
|
test('transform react-dom/client requires to global object', () => {
|
||||||
const src = 'require("react-dom/client")';
|
const src = 'require("react-dom/client")';
|
||||||
const ast = parse(src);
|
const ast = parse(src);
|
||||||
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
|
||||||
const {code} = generate(transformed!);
|
const {code} = generate(transformed!);
|
||||||
expect(code).toBe('global.ReactDOMClient;');
|
expect(code).toBe('global.ReactDOMClient;');
|
||||||
});
|
});
|
||||||
@@ -45,19 +51,35 @@ test('transform react-dom/client requires to global object', () => {
|
|||||||
test('transform flipper requires to global object', () => {
|
test('transform flipper requires to global object', () => {
|
||||||
const src = 'require("flipper")';
|
const src = 'require("flipper")';
|
||||||
const ast = parse(src);
|
const ast = parse(src);
|
||||||
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
|
||||||
const {code} = generate(transformed!);
|
const {code} = generate(transformed!);
|
||||||
expect(code).toBe('global.Flipper;');
|
expect(code).toBe('global.Flipper;');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('do NOT transform flipper requires outside of plugins folder', () => {
|
||||||
|
const src = 'require("flipper");';
|
||||||
|
const ast = parse(src);
|
||||||
|
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
||||||
|
const {code} = generate(transformed!);
|
||||||
|
expect(code).toBe('require("flipper");');
|
||||||
|
});
|
||||||
|
|
||||||
test('transform React identifier to global.React', () => {
|
test('transform React identifier to global.React', () => {
|
||||||
const src = 'React;';
|
const src = 'React;';
|
||||||
const ast = parse(src);
|
const ast = parse(src);
|
||||||
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
|
||||||
const {code} = generate(transformed!);
|
const {code} = generate(transformed!);
|
||||||
expect(code).toBe('global.React;');
|
expect(code).toBe('global.React;');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('do NOT transform React identifier to global.React outside of plugins folder', () => {
|
||||||
|
const src = 'React;';
|
||||||
|
const ast = parse(src);
|
||||||
|
const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
|
||||||
|
const {code} = generate(transformed!);
|
||||||
|
expect(code).toBe('React;');
|
||||||
|
});
|
||||||
|
|
||||||
test('do NOT transform local React namespace import to global.React', () => {
|
test('do NOT transform local React namespace import to global.React', () => {
|
||||||
const src = `import * as React from 'react';`;
|
const src = `import * as React from 'react';`;
|
||||||
const ast = parse(src, {sourceType: 'module'});
|
const ast = parse(src, {sourceType: 'module'});
|
||||||
@@ -70,7 +92,7 @@ test('throw error when requiring outside the plugin', () => {
|
|||||||
const src = 'require("../test.js")';
|
const src = 'require("../test.js")';
|
||||||
const ast = parse(src);
|
const ast = parse(src);
|
||||||
expect(() => {
|
expect(() => {
|
||||||
transformFromAstSync(ast, src, babelOptions);
|
transformFromAstSync(ast, src, babelOptionsPlugin);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,16 @@ import {
|
|||||||
tryReplaceGlobalReactUsage,
|
tryReplaceGlobalReactUsage,
|
||||||
} from './replace-flipper-requires';
|
} from './replace-flipper-requires';
|
||||||
|
|
||||||
|
const sourceRootDir = resolve(__dirname, '../..');
|
||||||
|
const pluginRootDir = resolve(__dirname, '../../plugin');
|
||||||
|
|
||||||
// do not apply this transform for these paths
|
// do not apply this transform for these paths
|
||||||
const EXCLUDE_PATHS = ['relay-devtools/DevtoolsUI'];
|
const EXCLUDE_PATHS = ['relay-devtools/DevtoolsUI'];
|
||||||
function isExcludedPath(path: string) {
|
function isExcludedPath(path: string) {
|
||||||
|
// Replace requires and React for plugins, but not for the Flipper core code which can access bundled React and other Flipper packages
|
||||||
|
if (path.startsWith(sourceRootDir) && !path.startsWith(pluginRootDir)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
for (const epath of EXCLUDE_PATHS) {
|
for (const epath of EXCLUDE_PATHS) {
|
||||||
if (path.indexOf(epath) > -1) {
|
if (path.indexOf(epath) > -1) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export function tryReplaceGlobalReactUsage(path: NodePath<Identifier>) {
|
|||||||
if (
|
if (
|
||||||
path.node.name === 'React' &&
|
path.node.name === 'React' &&
|
||||||
(path.parentPath.node as any).id !== path.node &&
|
(path.parentPath.node as any).id !== path.node &&
|
||||||
|
path.parent.type !== 'ObjectProperty' &&
|
||||||
!isReactImportIdentifier(path)
|
!isReactImportIdentifier(path)
|
||||||
) {
|
) {
|
||||||
path.replaceWith(identifier('global.React'));
|
path.replaceWith(identifier('global.React'));
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {default as doTransform} from './transform';
|
|||||||
import {default as getCacheKey} from './get-cache-key';
|
import {default as getCacheKey} from './get-cache-key';
|
||||||
|
|
||||||
const presets = [
|
const presets = [
|
||||||
|
'@babel/preset-react',
|
||||||
[
|
[
|
||||||
'@babel/preset-env',
|
'@babel/preset-env',
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {default as doTransform} from './transform';
|
|||||||
import {default as getCacheKey} from './get-cache-key';
|
import {default as getCacheKey} from './get-cache-key';
|
||||||
|
|
||||||
const presets = [
|
const presets = [
|
||||||
|
'@babel/preset-react',
|
||||||
[
|
[
|
||||||
'@babel/preset-env',
|
'@babel/preset-env',
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
genMercurialRevision,
|
genMercurialRevision,
|
||||||
getVersionNumber,
|
getVersionNumber,
|
||||||
prepareDefaultPlugins,
|
prepareDefaultPlugins,
|
||||||
|
prepareHeadlessPlugins,
|
||||||
} from './build-utils';
|
} from './build-utils';
|
||||||
import {defaultPluginsDir, distDir, serverDir, staticDir} from './paths';
|
import {defaultPluginsDir, distDir, serverDir, staticDir} from './paths';
|
||||||
import isFB from './isFB';
|
import isFB from './isFB';
|
||||||
@@ -330,6 +331,7 @@ async function buildServerRelease() {
|
|||||||
|
|
||||||
await compileServerMain(false);
|
await compileServerMain(false);
|
||||||
await prepareDefaultPlugins(argv.channel === 'insiders');
|
await prepareDefaultPlugins(argv.channel === 'insiders');
|
||||||
|
await prepareHeadlessPlugins();
|
||||||
await copyStaticResources(dir, versionNumber);
|
await copyStaticResources(dir, versionNumber);
|
||||||
await downloadIcons(path.join(dir, 'static'));
|
await downloadIcons(path.join(dir, 'static'));
|
||||||
await buildBrowserBundle(path.join(dir, 'static'), false);
|
await buildBrowserBundle(path.join(dir, 'static'), false);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
rootDir,
|
rootDir,
|
||||||
browserUiDir,
|
browserUiDir,
|
||||||
serverCoreDir,
|
serverCoreDir,
|
||||||
|
serverCompanionDir,
|
||||||
} from './paths';
|
} from './paths';
|
||||||
import pFilter from 'p-filter';
|
import pFilter from 'p-filter';
|
||||||
import child from 'child_process';
|
import child from 'child_process';
|
||||||
@@ -110,6 +111,14 @@ export async function prepareDefaultPlugins(isInsidersBuild: boolean = false) {
|
|||||||
console.log('✅ Prepared default plugins.');
|
console.log('✅ Prepared default plugins.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function prepareHeadlessPlugins() {
|
||||||
|
console.log(`⚙️ Preparing headless plugins...`);
|
||||||
|
const sourcePlugins = await getSourcePlugins();
|
||||||
|
const headlessPlugins = sourcePlugins.filter((p) => p.headless);
|
||||||
|
await generateHeadlessPluginEntryPoints(headlessPlugins);
|
||||||
|
console.log('✅ Prepared headless plugins.');
|
||||||
|
}
|
||||||
|
|
||||||
function getGeneratedIndex(pluginRequires: string) {
|
function getGeneratedIndex(pluginRequires: string) {
|
||||||
return `
|
return `
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
@@ -191,6 +200,28 @@ async function generateDefaultPluginEntryPoints(
|
|||||||
console.log('✅ Generated bundled plugin entry points.');
|
console.log('✅ Generated bundled plugin entry points.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function generateHeadlessPluginEntryPoints(
|
||||||
|
headlessPlugins: InstalledPluginDetails[],
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`⚙️ Generating entry points for ${headlessPlugins.length} headless plugins...`,
|
||||||
|
);
|
||||||
|
const headlessRequires = headlessPlugins
|
||||||
|
.map(
|
||||||
|
(x) =>
|
||||||
|
` '${x.name}': tryRequire('${x.name}', () => require('${x.name}'))`,
|
||||||
|
)
|
||||||
|
.join(',\n');
|
||||||
|
const generatedIndexHeadless = getGeneratedIndex(headlessRequires);
|
||||||
|
await fs.ensureDir(path.join(serverCompanionDir, 'src', 'defaultPlugins'));
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(serverCompanionDir, 'src', 'defaultPlugins', 'index.tsx'),
|
||||||
|
generatedIndexHeadless,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Generated headless plugin entry points.');
|
||||||
|
}
|
||||||
|
|
||||||
async function buildDefaultPlugins(defaultPlugins: InstalledPluginDetails[]) {
|
async function buildDefaultPlugins(defaultPlugins: InstalledPluginDetails[]) {
|
||||||
if (process.env.FLIPPER_NO_REBUILD_PLUGINS) {
|
if (process.env.FLIPPER_NO_REBUILD_PLUGINS) {
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
|
|
||||||
/* eslint-disable flipper/no-console-error-without-context */
|
/* eslint-disable flipper/no-console-error-without-context */
|
||||||
|
|
||||||
import {prepareDefaultPlugins} from './build-utils';
|
import {prepareDefaultPlugins, prepareHeadlessPlugins} from './build-utils';
|
||||||
|
|
||||||
prepareDefaultPlugins().catch((err) => {
|
Promise.all([prepareDefaultPlugins(), prepareHeadlessPlugins()]).catch(
|
||||||
console.error(err);
|
(err) => {
|
||||||
process.exit(1);
|
console.error(err);
|
||||||
});
|
process.exit(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export const browserUiDir = path.join(rootDir, 'flipper-ui-browser');
|
|||||||
export const staticDir = path.join(rootDir, 'static');
|
export const staticDir = path.join(rootDir, 'static');
|
||||||
export const serverDir = path.join(rootDir, 'flipper-server');
|
export const serverDir = path.join(rootDir, 'flipper-server');
|
||||||
export const serverCoreDir = path.join(rootDir, 'flipper-server-core');
|
export const serverCoreDir = path.join(rootDir, 'flipper-server-core');
|
||||||
|
export const serverCompanionDir = path.join(
|
||||||
|
rootDir,
|
||||||
|
'flipper-server-companion',
|
||||||
|
);
|
||||||
export const defaultPluginsDir = path.join(staticDir, 'defaultPlugins');
|
export const defaultPluginsDir = path.join(staticDir, 'defaultPlugins');
|
||||||
export const pluginsDir = path.join(rootDir, 'plugins');
|
export const pluginsDir = path.join(rootDir, 'plugins');
|
||||||
export const fbPluginsDir = path.join(pluginsDir, 'fb');
|
export const fbPluginsDir = path.join(pluginsDir, 'fb');
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
compileServerMain,
|
compileServerMain,
|
||||||
launchServer,
|
launchServer,
|
||||||
prepareDefaultPlugins,
|
prepareDefaultPlugins,
|
||||||
|
prepareHeadlessPlugins,
|
||||||
} from './build-utils';
|
} from './build-utils';
|
||||||
import Watchman from './watchman';
|
import Watchman from './watchman';
|
||||||
import isFB from './isFB';
|
import isFB from './isFB';
|
||||||
@@ -185,6 +186,7 @@ async function startWatchChanges() {
|
|||||||
await prepareDefaultPlugins(
|
await prepareDefaultPlugins(
|
||||||
process.env.FLIPPER_RELEASE_CHANNEL === 'insiders',
|
process.env.FLIPPER_RELEASE_CHANNEL === 'insiders',
|
||||||
);
|
);
|
||||||
|
await prepareHeadlessPlugins();
|
||||||
|
|
||||||
// watch
|
// watch
|
||||||
await startWatchChanges();
|
await startWatchChanges();
|
||||||
|
|||||||
Reference in New Issue
Block a user