Allow to pass custom transformer parameters via the request url

Reviewed By: davidaurelio

Differential Revision: D6869137

fbshipit-source-id: a985b2df7e4fca308e62adaededc2ad0038d5373
This commit is contained in:
Rafael Oleza 2018-02-04 09:14:30 -08:00 committed by Facebook Github Bot
parent cd23c579b5
commit 79fd0b341e
11 changed files with 129 additions and 20 deletions

View File

@ -160,6 +160,7 @@ class DeltaCalculator extends EventEmitter {
const transformOptionsForBlacklist = {
assetDataPlugins: this._options.assetPlugins,
customTransformOptions: this._options.customTransformOptions,
enableBabelRCLookup,
dev: this._options.dev,
hot: this._options.hot,

View File

@ -13,6 +13,7 @@
'use strict';
import type {Options as BundleOptions} from '../DeltaBundler';
import type {CustomTransformOptions} from '../JSTransformer/worker';
/**
* Module to easily create the needed configuration parameters needed for the
@ -21,6 +22,7 @@ import type {Options as BundleOptions} from '../DeltaBundler';
module.exports = function getBundlingOptionsForHmr(
entryFile: string,
platform: string,
customTransformOptions: CustomTransformOptions,
): BundleOptions {
// These are the really meaningful bundling options. The others below are
// not relevant for HMR.
@ -36,6 +38,7 @@ module.exports = function getBundlingOptionsForHmr(
...mainOptions,
assetPlugins: [],
bundleType: 'hmr',
customTransformOptions,
dev: true,
entryModuleOnly: false,
excludeSource: false,

View File

@ -15,6 +15,8 @@
const addParamsToDefineCall = require('../lib/addParamsToDefineCall');
const formatBundlingError = require('../lib/formatBundlingError');
const getBundlingOptionsForHmr = require('./getBundlingOptionsForHmr');
const nullthrows = require('fbjs/lib/nullthrows');
const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions');
const querystring = require('querystring');
const url = require('url');
@ -53,10 +55,10 @@ class HmrServer<TClient: Client> {
clientUrl: string,
sendFn: (data: string) => mixed,
): Promise<Client> {
const {bundleEntry, platform} = querystring.parse(
/* $FlowFixMe: url might be null */
url.parse(clientUrl).query,
);
const urlObj = nullthrows(url.parse(clientUrl, true));
const {bundleEntry, platform} = nullthrows(urlObj.query);
const customTransformOptions = parseCustomTransformOptions(urlObj);
// Create a new DeltaTransformer for each client. Once the clients are
// modified to support Delta Bundles, they'll be able to pass the
@ -64,7 +66,7 @@ class HmrServer<TClient: Client> {
// the same DeltaTransformer between the WS connection and the HTTP one.
const deltaBundler = this._packagerServer.getDeltaBundler();
const {deltaTransformer} = await deltaBundler.getDeltaTransformer(
getBundlingOptionsForHmr(bundleEntry, platform),
getBundlingOptionsForHmr(bundleEntry, platform, customTransformOptions),
);
// Trigger an initial build to start up the DeltaTransformer.

View File

@ -62,8 +62,11 @@ export type Transformer<ExtraOptions: {} = {}> = {
getCacheKey: () => string,
};
export type CustomTransformOptions = {[string]: mixed, __proto__: null};
export type TransformOptionsStrict = {|
+assetDataPlugins: $ReadOnlyArray<string>,
+customTransformOptions?: CustomTransformOptions,
+enableBabelRCLookup: boolean,
+dev: boolean,
+hot: boolean,
@ -75,6 +78,7 @@ export type TransformOptionsStrict = {|
export type TransformOptions = {
+assetDataPlugins: $ReadOnlyArray<string>,
+customTransformOptions?: CustomTransformOptions,
+enableBabelRCLookup?: boolean,
+dev?: boolean,
+hot?: boolean,

View File

@ -183,6 +183,7 @@ describe('processRequest', () => {
{
assetPlugins: [],
bundleType: 'bundle',
customTransformOptions: {},
deltaBundleId: expect.any(String),
dev: true,
entryFile: 'index.ios.js',
@ -214,6 +215,7 @@ describe('processRequest', () => {
{
assetPlugins: [],
bundleType: 'bundle',
customTransformOptions: {},
deltaBundleId: expect.any(String),
dev: true,
entryFile: 'index.js',
@ -245,6 +247,7 @@ describe('processRequest', () => {
expect(Serializers.fullBundle).toBeCalledWith(expect.any(DeltaBundler), {
assetPlugins: ['assetPlugin1', 'assetPlugin2'],
bundleType: 'bundle',
customTransformOptions: {},
deltaBundleId: expect.any(String),
dev: true,
entryFile: 'index.js',
@ -447,6 +450,7 @@ describe('processRequest', () => {
expect.any(DeltaBundler),
{
assetPlugins: [],
customTransformOptions: {},
deltaBundleId: null,
dev: true,
entryFile: 'foo file',

View File

@ -22,6 +22,8 @@ const formatBundlingError = require('../lib/formatBundlingError');
const getMaxWorkers = require('../lib/getMaxWorkers');
const getOrderedDependencyPaths = require('../lib/getOrderedDependencyPaths');
const mime = require('mime-types');
const nullthrows = require('fbjs/lib/nullthrows');
const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions');
const parsePlatformFilePath = require('../node-haste/lib/parsePlatformFilePath');
const path = require('path');
const symbolicate = require('./symbolicate');
@ -762,10 +764,10 @@ class Server {
_getOptionsFromUrl(reqUrl: string): BundleOptions & DeltaBundlerOptions {
// `true` to parse the query param as an object.
const urlObj = url.parse(reqUrl, true);
const urlObj = nullthrows(url.parse(reqUrl, true));
const urlQuery = nullthrows(urlObj.query);
/* $FlowFixMe: `pathname` could be empty for an invalid URL */
const pathname = decodeURIComponent(urlObj.pathname);
const pathname = urlObj.pathname ? decodeURIComponent(urlObj.pathname) : '';
let isMap = false;
@ -795,38 +797,38 @@ class Server {
// try to get the platform from the url
const platform =
/* $FlowFixMe: `query` could be empty for an invalid URL */
urlObj.query.platform ||
urlQuery.platform ||
parsePlatformFilePath(pathname, this._platforms).platform;
/* $FlowFixMe: `query` could be empty for an invalid URL */
const deltaBundleId = urlObj.query.deltaBundleId;
const deltaBundleId = urlQuery.deltaBundleId;
/* $FlowFixMe: `query` could be empty for an invalid URL */
const assetPlugin = urlObj.query.assetPlugin;
const assetPlugin = urlQuery.assetPlugin;
const assetPlugins = Array.isArray(assetPlugin)
? assetPlugin
: typeof assetPlugin === 'string' ? [assetPlugin] : [];
const dev = this._getBoolOptionFromQuery(urlObj.query, 'dev', true);
const minify = this._getBoolOptionFromQuery(urlObj.query, 'minify', false);
const dev = this._getBoolOptionFromQuery(urlQuery, 'dev', true);
const minify = this._getBoolOptionFromQuery(urlQuery, 'minify', false);
const excludeSource = this._getBoolOptionFromQuery(
urlObj.query,
urlQuery,
'excludeSource',
false,
);
const includeSource = this._getBoolOptionFromQuery(
urlObj.query,
urlQuery,
'inlineSourceMap',
false,
);
const customTransformOptions = parseCustomTransformOptions(urlObj);
return {
sourceMapUrl: url.format({
...urlObj,
pathname: pathname.replace(/\.(bundle|delta)$/, '.map'),
}),
bundleType: isMap ? 'map' : deltaBundleId ? 'delta' : 'bundle',
customTransformOptions,
entryFile,
deltaBundleId,
dev,
@ -873,6 +875,7 @@ class Server {
static DEFAULT_BUNDLE_OPTIONS = {
assetPlugins: [],
customTransformOptions: Object.create(null),
dev: true,
entryModuleOnly: false,
excludeSource: false,

View File

@ -24,6 +24,7 @@ const path = require('path');
const throat = require('throat');
import type {
CustomTransformOptions,
Options as TransformWorkerOptions,
TransformOptionsStrict,
} from '../JSTransformer/worker';
@ -440,6 +441,7 @@ class OptionsHasher {
): crypto$Hash {
const {
assetDataPlugins,
customTransformOptions,
enableBabelRCLookup,
dev,
hot,
@ -472,6 +474,9 @@ class OptionsHasher {
hash.update(JSON.stringify(assetDataPlugins));
hash.update(JSON.stringify(platform));
hash.update(JSON.stringify(this.toLocalPath(projectRoot)));
hash.update(
JSON.stringify(this.sortTransformOptions(customTransformOptions || {})),
);
return hash;
}
@ -483,6 +488,14 @@ class OptionsHasher {
toLocalPath(filePath: string): string {
return path.relative(this._rootPath, filePath);
}
sortTransformOptions(
options: CustomTransformOptions,
): Array<[string, mixed]> {
return Object.keys(options)
.sort()
.map(key => [key, options[key]]);
}
}
class CannotHashOptionsError extends Error {

View File

@ -19,12 +19,12 @@ Object {
exports[`GlobalTransformCache fetches results 1`] = `
Array [
Object {
"code": "/* code from http://globalcache.com/a00af5f7803566564fc1e5bd209552899c7354e1-foo.js */",
"code": "/* code from http://globalcache.com/3b3b861b6b80dd038c51262ca8b8b9f76e353ada-foo.js */",
"dependencies": Array [],
"dependencyOffsets": Array [],
},
Object {
"code": "/* code from http://globalcache.com/50013dd319b49a40f1b3d325be62320a4c2a6c82-bar.js */",
"code": "/* code from http://globalcache.com/087b5bc3467f9a0670e49ce9118e6f6791aa12c6-bar.js */",
"dependencies": Array [],
"dependencyOffsets": Array [],
},

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+javascript_foundation
* @format
* @flow
*/
'use strict';
const parseCustomTransformOptions = require('../parseCustomTransformOptions');
const url = require('url');
it('should parse some custom options from a http url', () => {
const myUrl =
'http://localhost/my/bundle.bundle?dev=true&transform.foo=value&transform.bar=other';
expect(parseCustomTransformOptions(url.parse(myUrl, true))).toEqual({
foo: 'value',
bar: 'other',
});
});
it('should parse some custom options from a websocket url', () => {
const myUrl = 'ws://localhost/hot?transform.foo=value&transform.bar=other';
expect(parseCustomTransformOptions(url.parse(myUrl, true))).toEqual({
foo: 'value',
bar: 'other',
});
});
it('should return an empty object if there are no custom params', () => {
const myUrl = 'http://localhost/my/bundle.bundle?dev=true';
expect(parseCustomTransformOptions(url.parse(myUrl, true))).toEqual({});
});

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+javascript_foundation
* @format
* @flow
*/
'use strict';
const nullthrows = require('fbjs/lib/nullthrows');
import type {CustomTransformOptions} from '../JSTransformer/worker';
const PREFIX = 'transform.';
module.exports = function parseCustomTransformOptions(urlObj: {
query?: {[string]: string},
}): CustomTransformOptions {
const customTransformOptions = Object.create(null);
const query = nullthrows(urlObj.query);
Object.keys(query).forEach(key => {
if (key.startsWith(PREFIX)) {
customTransformOptions[key.substr(PREFIX.length)] = query[key];
}
});
return customTransformOptions;
};

View File

@ -17,6 +17,7 @@ import type {
PostProcessBundleSourcemap,
} from '../Bundler';
import type {PostProcessModules} from '../DeltaBundler';
import type {CustomTransformOptions} from '../JSTransformer/worker';
import type {DynamicRequiresBehavior} from '../ModuleGraph/worker/collectDependencies';
import type {GlobalTransformCache} from '../lib/GlobalTransformCache';
import type {TransformCache} from '../lib/TransformCaching';
@ -34,6 +35,7 @@ type MetroSourceMapOrMappings =
export type BundleOptions = {
+assetPlugins: Array<string>,
bundleType: BundleType,
customTransformOptions: CustomTransformOptions,
dev: boolean,
entryFile: string,
+entryModuleOnly: boolean,