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'
|
node.arguments[0].type === 'StringLiteral'
|
||||||
) {
|
) {
|
||||||
const source = node.arguments[0].value;
|
const source = node.arguments[0].value;
|
||||||
if (!source.startsWith('./')) {
|
if (!source.startsWith('./') && !source.startsWith('../')) {
|
||||||
node.callee.name = 'electronRequire';
|
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 Metro from 'metro';
|
||||||
import getWatchFolders from './getWatchFolders';
|
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) {
|
function hash(string: string) {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
@@ -41,11 +60,14 @@ export default async function runBuild(
|
|||||||
entry: string,
|
entry: string,
|
||||||
out: string,
|
out: string,
|
||||||
) {
|
) {
|
||||||
|
const dev = process.env.NODE_ENV !== 'production';
|
||||||
const baseConfig = await Metro.loadConfig();
|
const baseConfig = await Metro.loadConfig();
|
||||||
const config = Object.assign({}, baseConfig, {
|
const config = Object.assign({}, baseConfig, {
|
||||||
reporter: {update: () => {}},
|
reporter: {update: () => {}},
|
||||||
projectRoot: inputDirectory,
|
projectRoot: inputDirectory,
|
||||||
watchFolders: [inputDirectory, ...(await getWatchFolders(inputDirectory))],
|
watchFolders: [metroDir || (await metroDirPromise)].concat(
|
||||||
|
await getWatchFolders(inputDirectory),
|
||||||
|
),
|
||||||
serializer: {
|
serializer: {
|
||||||
...baseConfig.serializer,
|
...baseConfig.serializer,
|
||||||
getRunModuleStatement: (moduleID: string) =>
|
getRunModuleStatement: (moduleID: string) =>
|
||||||
@@ -56,11 +78,17 @@ export default async function runBuild(
|
|||||||
...baseConfig.transformer,
|
...baseConfig.transformer,
|
||||||
babelTransformerPath: require.resolve('flipper-babel-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, {
|
await Metro.runBuild(config, {
|
||||||
dev: false,
|
dev,
|
||||||
minify: false,
|
minify: false,
|
||||||
resetCache: false,
|
resetCache: !dev,
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
entry,
|
entry,
|
||||||
out,
|
out,
|
||||||
|
|||||||
@@ -9,20 +9,16 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import Metro from 'metro';
|
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import recursiveReaddir from 'recursive-readdir';
|
import recursiveReaddir from 'recursive-readdir';
|
||||||
import pMap from 'p-map';
|
import pMap from 'p-map';
|
||||||
import {homedir} from 'os';
|
import {homedir} from 'os';
|
||||||
import {getWatchFolders, PluginDetails} from 'flipper-pkg-lib';
|
import {runBuild, PluginDetails} from 'flipper-pkg-lib';
|
||||||
import getPlugins from './getPlugins';
|
import getPlugins from './getPlugins';
|
||||||
import startWatchPlugins from './startWatchPlugins';
|
import startWatchPlugins from './startWatchPlugins';
|
||||||
|
|
||||||
const HOME_DIR = homedir();
|
const HOME_DIR = homedir();
|
||||||
|
|
||||||
let metroDir: string | undefined;
|
|
||||||
const metroDirPromise = getMetroDir().then((dir) => (metroDir = dir));
|
|
||||||
|
|
||||||
const DEFAULT_COMPILE_OPTIONS: CompileOptions = {
|
const DEFAULT_COMPILE_OPTIONS: CompileOptions = {
|
||||||
force: false,
|
force: false,
|
||||||
failSilently: true,
|
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) {
|
async function mostRecentlyChanged(dir: string) {
|
||||||
const files = await util.promisify<string, string[]>(recursiveReaddir)(dir);
|
const files = await util.promisify<string, string[]>(recursiveReaddir)(dir);
|
||||||
return files
|
return files
|
||||||
.map((f) => fs.lstatSync(f).ctime)
|
.map((f) => fs.lstatSync(f).ctime)
|
||||||
.reduce((a, b) => (a > b ? a : b), new Date(0));
|
.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(
|
async function compilePlugin(
|
||||||
pluginDetails: PluginDetails,
|
pluginDetails: PluginDetails,
|
||||||
pluginCache: string,
|
pluginCache: string,
|
||||||
{force, failSilently}: CompileOptions,
|
{force, failSilently}: CompileOptions,
|
||||||
): Promise<CompiledPluginDetails | null> {
|
): Promise<CompiledPluginDetails | null> {
|
||||||
const {dir, specVersion, version, main, source, name} = pluginDetails;
|
const {dir, specVersion, version, main, source, name} = pluginDetails;
|
||||||
const dev = process.env.NODE_ENV !== 'production';
|
|
||||||
if (specVersion > 1) {
|
if (specVersion > 1) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
const entry = path.join(dir, main);
|
const entry = path.join(dir, main);
|
||||||
@@ -176,37 +133,7 @@ async function compilePlugin(
|
|||||||
// eslint-disable-line no-console
|
// eslint-disable-line no-console
|
||||||
console.log(`⚙️ Compiling ${name}...`);
|
console.log(`⚙️ Compiling ${name}...`);
|
||||||
try {
|
try {
|
||||||
await Metro.runBuild(
|
await runBuild(dir, source, entry);
|
||||||
{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (failSilently) {
|
if (failSilently) {
|
||||||
console.error(
|
console.error(
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
global.electronRequire = require;
|
global.electronRequire = require;
|
||||||
global.electronResolve = require.resolve;
|
|
||||||
global.electronProcess = process;
|
global.electronProcess = process;
|
||||||
|
|
||||||
require('./main.bundle.js');
|
require('./main.bundle.js');
|
||||||
|
|||||||
@@ -10,10 +10,8 @@
|
|||||||
"fb-watchman": "^2.0.0",
|
"fb-watchman": "^2.0.0",
|
||||||
"fix-path": "^3.0.0",
|
"fix-path": "^3.0.0",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"flipper-babel-transformer": "0.37.0",
|
|
||||||
"flipper-pkg-lib": "0.37.0",
|
"flipper-pkg-lib": "0.37.0",
|
||||||
"mem": "^6.0.0",
|
"mem": "^6.0.0",
|
||||||
"metro": "^0.59.0",
|
|
||||||
"mkdirp": "^1.0.0",
|
"mkdirp": "^1.0.0",
|
||||||
"p-map": "^4.0.0",
|
"p-map": "^4.0.0",
|
||||||
"p-filter": "^2.1.0",
|
"p-filter": "^2.1.0",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ declare module NodeJS {
|
|||||||
__REVISION__: string | undefined;
|
__REVISION__: string | undefined;
|
||||||
__VERSION__: string;
|
__VERSION__: string;
|
||||||
electronRequire: (name: string) => any;
|
electronRequire: (name: string) => any;
|
||||||
electronResolve: (name: string) => string;
|
|
||||||
window: Window | undefined;
|
window: Window | undefined;
|
||||||
WebSocket: any;
|
WebSocket: any;
|
||||||
fetch: any;
|
fetch: any;
|
||||||
|
|||||||
Reference in New Issue
Block a user