create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
160 lines (131 loc) • 4.41 kB
Flow
/**
* 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.
*
* @flow
* @format
*/
;
const WorkerFarm = require('./WorkerFarm');
const assert = require('assert');
const fs = require('fs');
const getTransformCacheKey = require('./getTransformCacheKey');
const path = require('path');
const {Cache, stableHash} = require('metro-cache');
import type {TransformResult, TransformResultWithSource} from '../DeltaBundler';
import type {TransformOptions, TransformerConfig} from './Worker';
import type {ConfigT} from 'metro-config/src/configTypes.flow';
class Transformer {
_config: ConfigT;
_cache: Cache<TransformResult<>>;
_baseHash: string;
_getSha1: string => string;
_workerFarm: WorkerFarm;
constructor(config: ConfigT, getSha1Fn: string => string) {
this._config = config;
this._config.watchFolders.forEach(verifyRootExists);
this._cache = new Cache(config.cacheStores);
this._getSha1 = getSha1Fn;
// Remove the transformer config params that we don't want to pass to the
// transformer. We should change the config object and move them away so we
// can treat the transformer config params as opaque.
const {
getTransformOptions: _getTransformOptions,
postMinifyProcess: _postMinifyProcess,
transformVariants: _transformVariants,
workerPath: _workerPath,
...transformerConfig
} = this._config.transformer;
const transformerOptions: TransformerConfig = {
transformerPath: this._config.transformerPath,
transformerConfig,
};
this._workerFarm = new WorkerFarm(config, transformerOptions);
const globalCacheKey = getTransformCacheKey({
cacheVersion: this._config.cacheVersion,
projectRoot: this._config.projectRoot,
transformerConfig: transformerOptions,
});
this._baseHash = stableHash([globalCacheKey]).toString('binary');
}
async transformFile(
filePath: string,
transformerOptions: TransformOptions,
): Promise<TransformResultWithSource<>> {
const cache = this._cache;
const {
customTransformOptions,
dev,
experimentalImportSupport,
hot,
inlinePlatform,
inlineRequires,
minify,
nonInlinedRequires,
platform,
runtimeBytecodeVersion,
type,
unstable_disableES6Transforms,
unstable_transformProfile,
...extra
} = transformerOptions;
for (const key in extra) {
if (hasOwnProperty.call(extra, key)) {
throw new Error(
'Extra keys detected: ' + Object.keys(extra).join(', '),
);
}
}
const localPath = path.relative(this._config.projectRoot, filePath);
const partialKey = stableHash([
// This is the hash related to the global Bundler config.
this._baseHash,
// Path.
localPath,
customTransformOptions,
dev,
experimentalImportSupport,
hot,
inlinePlatform,
inlineRequires,
minify,
nonInlinedRequires,
platform,
runtimeBytecodeVersion,
type,
unstable_disableES6Transforms,
unstable_transformProfile,
]);
const sha1 = this._getSha1(filePath);
let fullKey = Buffer.concat([partialKey, Buffer.from(sha1, 'hex')]);
const result = await cache.get(fullKey);
// A valid result from the cache is used directly; otherwise we call into
// the transformer to computed the corresponding result.
const data = result
? {result, sha1}
: 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
// track of the cache that returned the result.
if (sha1 !== data.sha1) {
fullKey = Buffer.concat([partialKey, Buffer.from(data.sha1, 'hex')]);
}
cache.set(fullKey, data.result);
return {
...data.result,
getSource(): Buffer {
return fs.readFileSync(filePath);
},
};
}
end(): void {
this._workerFarm.kill();
}
}
function verifyRootExists(root: string): void {
// Verify that the root exists.
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
}
module.exports = Transformer;