/**
* 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 strict-local
* @format
*/
'use strict';
const path = require('path');
import type Bundler from '../Bundler';
import type {TransformOptions} from '../DeltaBundler/Worker';
import type DeltaBundler, {TransformFn} from '../DeltaBundler';
import type {Type} from '../JSTransformer/worker';
import type {ConfigT} from 'metro-config/src/configTypes.flow';
type InlineRequiresRaw = {+blacklist: {[string]: true}} | boolean;
export type TransformInputOptions = $Diff<
TransformOptions,
{
inlineRequires: boolean,
},
>;
type TransformOptionsWithRawInlines = {|
...TransformOptions,
+inlineRequires: InlineRequiresRaw,
|};
async function calcTransformerOptions(
entryFiles: $ReadOnlyArray<string>,
bundler: Bundler,
deltaBundler: DeltaBundler<>,
config: ConfigT,
options: TransformInputOptions,
): Promise<TransformOptionsWithRawInlines> {
const baseOptions = {
customTransformOptions: options.customTransformOptions,
dev: options.dev,
hot: options.hot,
inlineRequires: false,
minify: options.minify,
platform: options.platform,
};
// When we're processing scripts, we don't need to calculate any
// inlineRequires information, since scripts by definition don't have
// requires().
if (options.type === 'script') {
return {
...baseOptions,
type: 'script',
};
}
const getDependencies = async path => {
const {dependencies} = await deltaBundler.buildGraph([path], {
resolve: await getResolveDependencyFn(bundler, options.platform),
transform: await getTransformFn([path], bundler, deltaBundler, config, {
...options,
minify: false,
}),
onProgress: null,
});
return Array.from(dependencies.keys());
};
const {transform} = await config.transformer.getTransformOptions(
entryFiles,
{dev: options.dev, hot: options.hot, platform: options.platform},
getDependencies,
);
return {
...baseOptions,
inlineRequires: transform.inlineRequires || false,
experimentalImportSupport: transform.experimentalImportSupport || false,
type: 'module',
};
}
function removeInlineRequiresBlacklistFromOptions(
path: string,
inlineRequires: InlineRequiresRaw,
): boolean {
if (typeof inlineRequires === 'object') {
return !(path in inlineRequires.blacklist);
}
return inlineRequires;
}
async function getTransformFn(
entryFiles: $ReadOnlyArray<string>,
bundler: Bundler,
deltaBundler: DeltaBundler<>,
config: ConfigT,
options: TransformInputOptions,
): Promise<TransformFn<>> {
const {inlineRequires, ...transformOptions} = await calcTransformerOptions(
entryFiles,
bundler,
deltaBundler,
config,
options,
);
return async (path: string) => {
return await bundler.transformFile(path, {
...transformOptions,
type: getType(transformOptions.type, path, config.resolver.assetExts),
inlineRequires: removeInlineRequiresBlacklistFromOptions(
path,
inlineRequires,
),
});
};
}
function getType(
type: string,
filePath: string,
assetExts: $ReadOnlyArray<string>,
): Type {
if (type === 'script') {
return type;
}
if (assetExts.indexOf(path.extname(filePath).slice(1)) !== -1) {
return 'asset';
}
return 'module';
}
async function getResolveDependencyFn(
bundler: Bundler,
platform: ?string,
): Promise<(from: string, to: string) => string> {
const dependencyGraph = await bundler.getDependencyGraph();
return (from: string, to: string) =>
dependencyGraph.resolveDependency(from, to, platform);
}
module.exports = {
getTransformFn,
getResolveDependencyFn,
};