Re-use pkg-lib for runtime plugin compilation
Summary: Re-use the same function for plugin building both at publishing time by "flipper-pkg" and in runtime by Flipper itself. Reviewed By: jknoxville Differential Revision: D21129685 fbshipit-source-id: b7bff6737310352d28a14223128f127ac4d2eebf
This commit is contained in:
committed by
Facebook GitHub Bot
parent
ca2d04a5da
commit
8a7470556e
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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 {transform} from '@babel/core';
|
||||
import electronRequiresMainPlugin = require('../electron-requires-main');
|
||||
|
||||
const babelOptions = {
|
||||
ast: true,
|
||||
plugins: [electronRequiresMainPlugin],
|
||||
filename: 'index.js',
|
||||
};
|
||||
|
||||
const testCase1 = "const module = require('module');";
|
||||
test(testCase1, () => {
|
||||
const src = testCase1;
|
||||
const code = transform(src, babelOptions)!.code;
|
||||
expect(code).toMatchInlineSnapshot(
|
||||
`"const module = electronRequire('module');"`,
|
||||
);
|
||||
});
|
||||
|
||||
const testCase2 = "const module = require('./module');";
|
||||
test(testCase2, () => {
|
||||
const src = testCase2;
|
||||
const code = transform(src, babelOptions)!.code;
|
||||
expect(code).toMatchInlineSnapshot(`"const module = require('./module');"`);
|
||||
});
|
||||
|
||||
const testCase3 = "const module = require('../module');";
|
||||
test(testCase3, () => {
|
||||
const src = testCase3;
|
||||
const code = transform(src, babelOptions)!.code;
|
||||
expect(code).toMatchInlineSnapshot(`"const module = require('../module');"`);
|
||||
});
|
||||
|
||||
const testCase4 = "const path = require.resolve('module');";
|
||||
test(testCase4, () => {
|
||||
const src = testCase4;
|
||||
const code = transform(src, babelOptions)!.code;
|
||||
expect(code).toMatchInlineSnapshot(
|
||||
`"const path = electronRequire.resolve('module');"`,
|
||||
);
|
||||
});
|
||||
@@ -23,10 +23,20 @@ module.exports = () => ({
|
||||
node.arguments[0].type === 'StringLiteral'
|
||||
) {
|
||||
const source = node.arguments[0].value;
|
||||
if (!source.startsWith('./')) {
|
||||
if (!source.startsWith('./') && !source.startsWith('../')) {
|
||||
node.callee.name = 'electronRequire';
|
||||
}
|
||||
}
|
||||
if (
|
||||
node.callee.type === 'MemberExpression' &&
|
||||
node.callee.object.type === 'Identifier' &&
|
||||
node.callee.object.name === 'require' &&
|
||||
node.callee.property.name === 'resolve' &&
|
||||
node.arguments.length === 1 &&
|
||||
node.arguments[0].type == 'StringLiteral'
|
||||
) {
|
||||
node.callee.object.name = 'electronRequire';
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,6 +9,25 @@
|
||||
|
||||
import Metro from 'metro';
|
||||
import getWatchFolders from './getWatchFolders';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
let metroDir: string | undefined;
|
||||
const metroDirPromise = getMetroDir().then((dir) => (metroDir = dir));
|
||||
|
||||
async function getMetroDir() {
|
||||
let dir = __dirname;
|
||||
while (true) {
|
||||
const dirToCheck = path.join(dir, 'node_modules', 'metro');
|
||||
if (await fs.pathExists(dirToCheck)) return dirToCheck;
|
||||
const nextDir = path.dirname(dir);
|
||||
if (!nextDir || nextDir === '' || nextDir === dir) {
|
||||
break;
|
||||
}
|
||||
dir = nextDir;
|
||||
}
|
||||
return __dirname;
|
||||
}
|
||||
|
||||
function hash(string: string) {
|
||||
let hash = 0;
|
||||
@@ -41,11 +60,14 @@ export default async function runBuild(
|
||||
entry: string,
|
||||
out: string,
|
||||
) {
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
const baseConfig = await Metro.loadConfig();
|
||||
const config = Object.assign({}, baseConfig, {
|
||||
reporter: {update: () => {}},
|
||||
projectRoot: inputDirectory,
|
||||
watchFolders: [inputDirectory, ...(await getWatchFolders(inputDirectory))],
|
||||
watchFolders: [metroDir || (await metroDirPromise)].concat(
|
||||
await getWatchFolders(inputDirectory),
|
||||
),
|
||||
serializer: {
|
||||
...baseConfig.serializer,
|
||||
getRunModuleStatement: (moduleID: string) =>
|
||||
@@ -56,11 +78,17 @@ export default async function runBuild(
|
||||
...baseConfig.transformer,
|
||||
babelTransformerPath: require.resolve('flipper-babel-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
...baseConfig.resolver,
|
||||
resolverMainFields: ['flipperBundlerEntry', 'module', 'main'],
|
||||
sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs'],
|
||||
blacklistRE: /\.native\.js$/,
|
||||
},
|
||||
});
|
||||
await Metro.runBuild(config, {
|
||||
dev: false,
|
||||
dev,
|
||||
minify: false,
|
||||
resetCache: false,
|
||||
resetCache: !dev,
|
||||
sourceMap: true,
|
||||
entry,
|
||||
out,
|
||||
|
||||
@@ -9,20 +9,16 @@
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import Metro from 'metro';
|
||||
import util from 'util';
|
||||
import recursiveReaddir from 'recursive-readdir';
|
||||
import pMap from 'p-map';
|
||||
import {homedir} from 'os';
|
||||
import {getWatchFolders, PluginDetails} from 'flipper-pkg-lib';
|
||||
import {runBuild, PluginDetails} from 'flipper-pkg-lib';
|
||||
import getPlugins from './getPlugins';
|
||||
import startWatchPlugins from './startWatchPlugins';
|
||||
|
||||
const HOME_DIR = homedir();
|
||||
|
||||
let metroDir: string | undefined;
|
||||
const metroDirPromise = getMetroDir().then((dir) => (metroDir = dir));
|
||||
|
||||
const DEFAULT_COMPILE_OPTIONS: CompileOptions = {
|
||||
force: false,
|
||||
failSilently: true,
|
||||
@@ -97,57 +93,18 @@ async function startWatchChanges(
|
||||
),
|
||||
);
|
||||
}
|
||||
function hash(string: string) {
|
||||
let hash = 0;
|
||||
if (string.length === 0) {
|
||||
return hash;
|
||||
}
|
||||
let chr;
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
chr = string.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
const fileToIdMap = new Map();
|
||||
const createModuleIdFactory = () => (filePath: string) => {
|
||||
if (filePath === '__prelude__') {
|
||||
return 0;
|
||||
}
|
||||
let id = fileToIdMap.get(filePath);
|
||||
if (typeof id !== 'number') {
|
||||
id = hash(filePath);
|
||||
fileToIdMap.set(filePath, id);
|
||||
}
|
||||
return id;
|
||||
};
|
||||
async function mostRecentlyChanged(dir: string) {
|
||||
const files = await util.promisify<string, string[]>(recursiveReaddir)(dir);
|
||||
return files
|
||||
.map((f) => fs.lstatSync(f).ctime)
|
||||
.reduce((a, b) => (a > b ? a : b), new Date(0));
|
||||
}
|
||||
async function getMetroDir() {
|
||||
let dir = __dirname;
|
||||
while (true) {
|
||||
const dirToCheck = path.join(dir, 'node_modules', 'metro');
|
||||
if (await fs.pathExists(dirToCheck)) return dirToCheck;
|
||||
const nextDir = path.dirname(dir);
|
||||
if (!nextDir || nextDir === '' || nextDir === dir) {
|
||||
break;
|
||||
}
|
||||
dir = nextDir;
|
||||
}
|
||||
return __dirname;
|
||||
}
|
||||
async function compilePlugin(
|
||||
pluginDetails: PluginDetails,
|
||||
pluginCache: string,
|
||||
{force, failSilently}: CompileOptions,
|
||||
): Promise<CompiledPluginDetails | null> {
|
||||
const {dir, specVersion, version, main, source, name} = pluginDetails;
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
if (specVersion > 1) {
|
||||
// eslint-disable-next-line no-console
|
||||
const entry = path.join(dir, main);
|
||||
@@ -176,37 +133,7 @@ async function compilePlugin(
|
||||
// eslint-disable-line no-console
|
||||
console.log(`⚙️ Compiling ${name}...`);
|
||||
try {
|
||||
await Metro.runBuild(
|
||||
{
|
||||
reporter: {update: () => {}},
|
||||
projectRoot: dir,
|
||||
watchFolders: [metroDir || (await metroDirPromise)].concat(
|
||||
await getWatchFolders(dir),
|
||||
),
|
||||
serializer: {
|
||||
getRunModuleStatement: (moduleID: string) =>
|
||||
`module.exports = global.__r(${moduleID}).default;`,
|
||||
createModuleIdFactory,
|
||||
},
|
||||
transformer: {
|
||||
babelTransformerPath: global.electronResolve
|
||||
? global.electronResolve('flipper-babel-transformer') // when compilation is executing in Electron main process
|
||||
: require.resolve('flipper-babel-transformer'), // when compilation is is executing in Node.js script
|
||||
},
|
||||
resolver: {
|
||||
sourceExts: ['tsx', 'ts', 'js'],
|
||||
blacklistRE: /\.native\.js$/,
|
||||
},
|
||||
},
|
||||
{
|
||||
entry: source,
|
||||
out: entry,
|
||||
dev,
|
||||
sourceMap: true,
|
||||
minify: false,
|
||||
resetCache: !dev,
|
||||
},
|
||||
);
|
||||
await runBuild(dir, source, entry);
|
||||
} catch (e) {
|
||||
if (failSilently) {
|
||||
console.error(
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
global.electronRequire = require;
|
||||
global.electronResolve = require.resolve;
|
||||
global.electronProcess = process;
|
||||
|
||||
require('./main.bundle.js');
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
"fb-watchman": "^2.0.0",
|
||||
"fix-path": "^3.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"flipper-babel-transformer": "0.37.0",
|
||||
"flipper-pkg-lib": "0.37.0",
|
||||
"mem": "^6.0.0",
|
||||
"metro": "^0.59.0",
|
||||
"mkdirp": "^1.0.0",
|
||||
"p-map": "^4.0.0",
|
||||
"p-filter": "^2.1.0",
|
||||
|
||||
@@ -12,7 +12,6 @@ declare module NodeJS {
|
||||
__REVISION__: string | undefined;
|
||||
__VERSION__: string;
|
||||
electronRequire: (name: string) => any;
|
||||
electronResolve: (name: string) => string;
|
||||
window: Window | undefined;
|
||||
WebSocket: any;
|
||||
fetch: any;
|
||||
|
||||
Reference in New Issue
Block a user