Skip to content

Commit

Permalink
Remove workerWrapper and make Metro compatible with instantiable tran…
Browse files Browse the repository at this point in the history
…sformers

Reviewed By: mjesun

Differential Revision: D9996619

fbshipit-source-id: 2bef03c6296faf4aa5be176237137045e6e8b8d2
  • Loading branch information
rafeca authored and facebook-github-bot committed Sep 24, 2018
1 parent f8cfe20 commit bba48f0
Show file tree
Hide file tree
Showing 12 changed files with 216 additions and 278 deletions.
2 changes: 1 addition & 1 deletion packages/metro-config/src/defaults/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const getDefaultValues = (projectRoot: ?string): IntermediateConfigT => ({
projectRoot: projectRoot || path.resolve(__dirname, '../../..'),
watchFolders: [],
watch: false,
transformerPath: require.resolve('metro/src/JSTransformer/workerWrapper.js'),
transformerPath: require.resolve('metro/src/JSTransformer/worker.js'),
maxWorkers: getMaxWorkers(),
resetCache: false,
reporter: new TerminalReporter(new Terminal(process.stdout)),
Expand Down
12 changes: 6 additions & 6 deletions packages/metro/src/Bundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
const DependencyGraph = require('./node-haste/DependencyGraph');
const Transformer = require('./DeltaBundler/Transformer');

import type {WorkerOptions} from './DeltaBundler/Worker';
import type {TransformOptions} from './DeltaBundler/Worker';
import type {TransformResult} from './DeltaBundler';
import type {ConfigT} from 'metro-config/src/configTypes.flow';

Expand All @@ -33,10 +33,10 @@ class Bundler {
}

async end() {
const dependencyGraph = await this._depGraphPromise;

this._transformer.end();
await this._depGraphPromise.then(dependencyGraph =>
dependencyGraph.getWatcher().end(),
);
dependencyGraph.getWatcher().end();
}

getDependencyGraph(): Promise<DependencyGraph> {
Expand All @@ -45,13 +45,13 @@ class Bundler {

async transformFile(
filePath: string,
workerOptions: WorkerOptions,
transformOptions: TransformOptions,
): Promise<TransformResult<>> {
// We need to be sure that the DependencyGraph has been initialized.
// TODO: Remove this ugly hack!
await this._depGraphPromise;

return this._transformer.transformFile(filePath, workerOptions);
return this._transformer.transformFile(filePath, transformOptions);
}
}

Expand Down
71 changes: 30 additions & 41 deletions packages/metro/src/DeltaBundler/Transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ const WorkerFarm = require('./WorkerFarm');

const assert = require('assert');
const fs = require('fs');
const getTransformCacheKeyFn = require('./Transformer/getTransformCacheKeyFn');
const getTransformCacheKey = require('./Transformer/getTransformCacheKey');
const path = require('path');

const {Cache, stableHash} = require('metro-cache');

import type {TransformResult} from '../DeltaBundler';
import type {WorkerOptions} from './Worker';
import type {TransformOptions, TransformerConfig} from './Worker';
import type {ConfigT} from 'metro-config/src/configTypes.flow';

class Transformer {
Expand All @@ -36,43 +36,45 @@ class Transformer {
this._config.watchFolders.forEach(verifyRootExists);
this._cache = new Cache(config.cacheStores);
this._getSha1 = getSha1Fn;
this._workerFarm = new WorkerFarm(config);

const getTransformCacheKey = getTransformCacheKeyFn({
babelTransformerPath: this._config.transformer.babelTransformerPath,
const transformerConfig: TransformerConfig = {
transformerPath: this._config.transformerPath,
transformerConfig: {
assetPlugins: this._config.transformer.assetPlugins,
assetRegistryPath: this._config.transformer.assetRegistryPath,
asyncRequireModulePath: this._config.transformer.asyncRequireModulePath,
babelTransformerPath: this._config.transformer.babelTransformerPath,
dynamicDepsInPackages: this._config.transformer.dynamicDepsInPackages,
minifierPath: this._config.transformer.minifierPath,
optimizationSizeLimit: this._config.transformer.optimizationSizeLimit,
},
};

this._workerFarm = new WorkerFarm(config, transformerConfig);

const globalCacheKey = getTransformCacheKey({
cacheVersion: this._config.cacheVersion,
projectRoot: this._config.projectRoot,
transformerPath: this._config.transformerPath,
transformerConfig,
});

this._baseHash = stableHash([getTransformCacheKey()]).toString('binary');
this._baseHash = stableHash([globalCacheKey]).toString('binary');
}

async transformFile(
filePath: string,
transformerOptions: WorkerOptions,
transformerOptions: TransformOptions,
): Promise<TransformResult<>> {
const cache = this._cache;

const {
assetPlugins,
assetRegistryPath,
asyncRequireModulePath,
// Already in the global cache key.
babelTransformerPath: _babelTransformerPath,
dynamicDepsInPackages,
minifierPath,
optimizationSizeLimit,
transformOptions: {
customTransformOptions,
enableBabelRCLookup,
dev,
hot,
inlineRequires,
minify,
platform,
projectRoot: _projectRoot, // Blacklisted property.
},
customTransformOptions,
dev,
experimentalImportSupport,
hot,
inlineRequires,
minify,
platform,
type,
...extra
} = transformerOptions;
Expand All @@ -94,20 +96,12 @@ class Transformer {
// Path.
localPath,

// We cannot include "transformCodeOptions" because of "projectRoot".
assetPlugins,
assetRegistryPath,
asyncRequireModulePath,
dynamicDepsInPackages,
minifierPath,

customTransformOptions,
enableBabelRCLookup,
dev,
experimentalImportSupport,
hot,
inlineRequires,
minify,
optimizationSizeLimit,
platform,
type,
]);
Expand All @@ -120,12 +114,7 @@ class Transformer {
// the transformer to computed the corresponding result.
const data = result
? {result, sha1}
: await this._workerFarm.transform(
localPath,
_projectRoot,
this._config.transformerPath,
transformerOptions,
);
: await this._workerFarm.transform(localPath, transformerOptions);

// Only re-compute the full key if the SHA-1 changed. This is because
// references are used by the cache implementation in a weak map to keep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jest
.setMock('jest-worker', () => ({}))
.mock('fs', () => new (require('metro-memory-fs'))())
.mock('assert')
.mock('../getTransformCacheKeyFn', () => () => () => 'hash')
.mock('../getTransformCacheKey', () => () => 'hash')
.mock('../../WorkerFarm')
.mock('/path/to/transformer.js', () => ({}), {virtual: true});

Expand Down Expand Up @@ -78,9 +78,7 @@ describe('Transformer', function() {
result: {},
});

await transformerInstance.transformFile('./foo.js', {
transformOptions: {},
});
await transformerInstance.transformFile('./foo.js', {});

// We got the SHA-1 of the file from the dependency graph.
expect(getSha1).toBeCalledWith('./foo.js');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,50 @@
'use strict';

const crypto = require('crypto');
const fs = require('fs');
const getKeyFromFiles = require('../../lib/getKeyFromFiles');
const path = require('path');

const VERSION = require('../../../package.json').version;

import type {TransformerConfig} from '../Worker';

/**
* Returns a function that will return the transform cache key based on some
* passed transform options.
*/
function getTransformCacheKeyFn(opts: {|
+babelTransformerPath: string,
function getTransformCacheKey(opts: {|
+cacheVersion: string,
+projectRoot: string,
+transformerPath: string,
|}): (options: mixed) => string {
const transformModuleHash = getKeyFromFile(opts.transformerPath);
+transformerConfig: TransformerConfig,
|}): string {
const {transformerPath, transformerConfig} = opts.transformerConfig;

// eslint-disable-next-line lint/flow-no-fixme
/* $FlowFixMe: dynamic requires prevent static typing :'( */
const transformer = require(opts.transformerPath);

const cacheFiles =
typeof transformer.getTransformDependencies !== 'undefined'
? transformer.getTransformDependencies()
: [];
const Transformer = require(transformerPath);
const transformerInstance = new Transformer(
opts.projectRoot,
transformerConfig,
);

const babelTransformerModuleHash = getKeyFromFile(opts.babelTransformerPath);
const transformerKey =
typeof transformerInstance.getCacheKey !== 'undefined'
? transformerInstance.getCacheKey()
: '';

const cacheKeyParts = [
'metro-cache',
VERSION,
opts.cacheVersion,
path.relative(path.join(__dirname, '../../../..'), opts.projectRoot),
transformModuleHash,
babelTransformerModuleHash,
...cacheFiles.map(getKeyFromFile),
getKeyFromFiles([transformerPath]),
transformerKey,
];

const transformCacheKey = crypto
.createHash('sha1')
.update(cacheKeyParts.join('$'))
.digest('hex');

return function(options: mixed): string {
return transformCacheKey;
};
}

function getKeyFromFile(filePath: string) {
return crypto
.createHash('sha1')
.update(fs.readFileSync(filePath))
.update(cacheKeyParts.join('$'))
.digest('hex');
}

module.exports = getTransformCacheKeyFn;
module.exports = getTransformCacheKey;
57 changes: 43 additions & 14 deletions packages/metro/src/DeltaBundler/Worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,33 @@ const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

import type {WorkerOptions as JsWorkerOptions} from '../JSTransformer/workerWrapper';
import type {
JsTransformOptions,
JsTransformerConfig,
} from '../JSTransformer/worker';
import type {MixedOutput, TransformResultDependency} from './types.flow';
import type {LogEntry} from 'metro-core/src/Logger';

export type WorkerOptions = JsWorkerOptions;
export type WorkerFn = typeof transform;
export type {
JsTransformOptions as TransformOptions,
} from '../JSTransformer/worker';

export type Worker = {
transform: typeof transform,
setup: typeof setup,
};

export type TransformerFn<T: MixedOutput> = (
string,
Buffer,
WorkerOptions,
JsTransformOptions,
) => Promise<Result<T>>;

export type TransformerConfig = {
transformerPath: string,
transformerConfig: JsTransformerConfig,
};

type Result<T: MixedOutput> = {|
output: $ReadOnlyArray<T>,
dependencies: $ReadOnlyArray<TransformResultDependency>,
Expand All @@ -38,12 +53,31 @@ type Data<T: MixedOutput> = {
transformFileEndLogEntry: LogEntry,
};

let transformer;
let projectRoot;

function setup(
projectRootArg: string,
{transformerPath, transformerConfig}: TransformerConfig,
) {
// eslint-disable-next-line lint/flow-no-fixme
// $FlowFixMe Transforming fixed types to generic types during refactor.
const Transformer = require(transformerPath);

projectRoot = projectRootArg;
transformer = new Transformer(projectRoot, transformerConfig);
}

async function transform<T: MixedOutput>(
filename: string,
projectRoot: string,
transformerPath: string,
transformerOptions: WorkerOptions,
transformOptions: JsTransformOptions,
projectRootArg: string,
transformerConfig: TransformerConfig,
): Promise<Data<T>> {
if (!projectRoot) {
setup(projectRootArg, transformerConfig);
}

const transformFileStartLogEntry = {
action_name: 'Transforming file',
action_phase: 'start',
Expand All @@ -58,13 +92,7 @@ async function transform<T: MixedOutput>(
.update(data)
.digest('hex');

// eslint-disable-next-line lint/flow-no-fixme
// $FlowFixMe Transforming fixed types to generic types during refactor.
const {transform} = (require(transformerPath): {
transform: TransformerFn<T>,
});

const result = await transform(filename, data, transformerOptions);
const result = await transformer.transform(filename, data, transformOptions);

const transformFileEndLogEntry = getEndLogEntry(
transformFileStartLogEntry,
Expand Down Expand Up @@ -93,5 +121,6 @@ function getEndLogEntry(startLogEntry: LogEntry, filename: string): LogEntry {
}

module.exports = {
setup,
transform,
};
Loading

0 comments on commit bba48f0

Please sign in to comment.