mirror of
https://github.com/status-im/metro.git
synced 2025-01-19 15:41:26 +00:00
RN packager: transform-module.js: syncify everything
Reviewed By: davidaurelio Differential Revision: D5803768 fbshipit-source-id: 06c28e6c30fce347780d953312646a449d0026d9
This commit is contained in:
parent
7ccbcc5ebc
commit
69eb3430b6
@ -26,4 +26,4 @@ type Asyncify = <A, B, C>((A, B) => C) => (A, B, Callback<C>) => void;
|
||||
exports.optimizeModule =
|
||||
(wrapWorkerFn(asyncify(optimizeModule)): WorkerFnWithIO<OptimizationOptions>);
|
||||
exports.transformModule =
|
||||
(wrapWorkerFn(transformModule): WorkerFnWithIO<TransformOptions>);
|
||||
(wrapWorkerFn(asyncify(transformModule)): WorkerFnWithIO<TransformOptions>);
|
||||
|
@ -34,14 +34,9 @@ describe('optimizing JS modules', () => {
|
||||
}`;
|
||||
|
||||
let transformResult;
|
||||
beforeAll(done => {
|
||||
transformModule(originalCode, {filename, transformer}, (error, result) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
transformResult = JSON.stringify({type: 'code', details: result.details});
|
||||
done();
|
||||
});
|
||||
beforeAll(() => {
|
||||
const result = transformModule(originalCode, {filename, transformer});
|
||||
transformResult = JSON.stringify({type: 'code', details: result.details});
|
||||
});
|
||||
|
||||
it('copies everything from the transformed file, except for transform results', () => {
|
||||
|
@ -45,49 +45,35 @@ describe('transforming JS modules:', () => {
|
||||
ast: t.file(t.program(body)),
|
||||
});
|
||||
|
||||
it('passes through file name and code', done => {
|
||||
transformModule(sourceCode, options(), (error, result) => {
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(
|
||||
expect.objectContaining({
|
||||
code: sourceCode,
|
||||
file: filename,
|
||||
}),
|
||||
);
|
||||
done();
|
||||
});
|
||||
it('passes through file name and code', () => {
|
||||
const result = transformModule(sourceCode, options());
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(
|
||||
expect.objectContaining({
|
||||
code: sourceCode,
|
||||
file: filename,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('exposes a haste ID if present', done => {
|
||||
it('exposes a haste ID if present', () => {
|
||||
const hasteID = 'TheModule';
|
||||
const codeWithHasteID = `/** @providesModule ${hasteID} */`;
|
||||
transformModule(codeWithHasteID, options(), (error, result) => {
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(expect.objectContaining({hasteID}));
|
||||
done();
|
||||
});
|
||||
const result = transformModule(codeWithHasteID, options());
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(expect.objectContaining({hasteID}));
|
||||
});
|
||||
|
||||
it('sets `type` to `"module"` by default', done => {
|
||||
transformModule(sourceCode, options(), (error, result) => {
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(expect.objectContaining({type: 'module'}));
|
||||
done();
|
||||
});
|
||||
it('sets `type` to `"module"` by default', () => {
|
||||
const result = transformModule(sourceCode, options());
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(expect.objectContaining({type: 'module'}));
|
||||
});
|
||||
|
||||
it('sets `type` to `"script"` if the input is a polyfill', done => {
|
||||
transformModule(
|
||||
sourceCode,
|
||||
{...options(), polyfill: true},
|
||||
(error, result) => {
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(
|
||||
expect.objectContaining({type: 'script'}),
|
||||
);
|
||||
done();
|
||||
},
|
||||
);
|
||||
it('sets `type` to `"script"` if the input is a polyfill', () => {
|
||||
const result = transformModule(sourceCode, {...options(), polyfill: true});
|
||||
expect(result.type).toBe('code');
|
||||
expect(result.details).toEqual(expect.objectContaining({type: 'script'}));
|
||||
});
|
||||
|
||||
const defaults = {
|
||||
@ -102,95 +88,79 @@ describe('transforming JS modules:', () => {
|
||||
it(
|
||||
'calls the passed-in transform function with code, file name, and options ' +
|
||||
'for all passed in variants',
|
||||
done => {
|
||||
() => {
|
||||
const variants = {dev: {dev: true}, prod: {dev: false}};
|
||||
|
||||
transformModule(sourceCode, options(variants), () => {
|
||||
expect(transformer.transform).toBeCalledWith({
|
||||
filename,
|
||||
localPath: filename,
|
||||
options: {...defaults, ...variants.dev},
|
||||
src: sourceCode,
|
||||
});
|
||||
expect(transformer.transform).toBeCalledWith({
|
||||
filename,
|
||||
localPath: filename,
|
||||
options: {...defaults, ...variants.prod},
|
||||
src: sourceCode,
|
||||
});
|
||||
done();
|
||||
transformModule(sourceCode, options(variants));
|
||||
expect(transformer.transform).toBeCalledWith({
|
||||
filename,
|
||||
localPath: filename,
|
||||
options: {...defaults, ...variants.dev},
|
||||
src: sourceCode,
|
||||
});
|
||||
expect(transformer.transform).toBeCalledWith({
|
||||
filename,
|
||||
localPath: filename,
|
||||
options: {...defaults, ...variants.prod},
|
||||
src: sourceCode,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('calls back with any error yielded by the transform function', done => {
|
||||
it('calls back with any error yielded by the transform function', () => {
|
||||
const error = new Error();
|
||||
transformer.transform.stub.throws(error);
|
||||
|
||||
transformModule(sourceCode, options(), e => {
|
||||
try {
|
||||
transformModule(sourceCode, options());
|
||||
throw new Error('should not be reached');
|
||||
} catch (e) {
|
||||
expect(e).toBe(error);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('wraps the code produced by the transform function into a module factory', done => {
|
||||
transformModule(sourceCode, options(), (error, result) => {
|
||||
expect(error).toEqual(null);
|
||||
it('wraps the code produced by the transform function into a module factory', () => {
|
||||
const result = transformModule(sourceCode, options());
|
||||
|
||||
const {code, dependencyMapName} = result.details.transformed.default;
|
||||
expect(code.replace(/\s+/g, '')).toEqual(
|
||||
`__d(function(global,require,module,exports,${dependencyMapName}){${transformedCode}});`,
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('wraps the code produced by the transform function into an IIFE for polyfills', done => {
|
||||
transformModule(
|
||||
sourceCode,
|
||||
{...options(), polyfill: true},
|
||||
(error, result) => {
|
||||
expect(error).toEqual(null);
|
||||
|
||||
const {code} = result.details.transformed.default;
|
||||
expect(code.replace(/\s+/g, '')).toEqual(
|
||||
`(function(global){${transformedCode}})(this);`,
|
||||
);
|
||||
done();
|
||||
},
|
||||
const {code, dependencyMapName} = result.details.transformed.default;
|
||||
expect(code.replace(/\s+/g, '')).toEqual(
|
||||
`__d(function(global,require,module,exports,${dependencyMapName}){${transformedCode}});`,
|
||||
);
|
||||
});
|
||||
|
||||
it('creates source maps', done => {
|
||||
transformModule(sourceCode, options(), (error, result) => {
|
||||
const {code, map} = result.details.transformed.default;
|
||||
const position = findColumnAndLine(code, 'code');
|
||||
expect(position).not.toBeNull();
|
||||
|
||||
const consumer = new SourceMapConsumer(map);
|
||||
expect(consumer.originalPositionFor(position)).toEqual(
|
||||
expect.objectContaining({line: 1, column: sourceCode.indexOf('code')}),
|
||||
);
|
||||
done();
|
||||
});
|
||||
it('wraps the code produced by the transform function into an IIFE for polyfills', () => {
|
||||
const result = transformModule(sourceCode, {...options(), polyfill: true});
|
||||
const {code} = result.details.transformed.default;
|
||||
expect(code.replace(/\s+/g, '')).toEqual(
|
||||
`(function(global){${transformedCode}})(this);`,
|
||||
);
|
||||
});
|
||||
|
||||
it('extracts dependencies (require calls)', done => {
|
||||
it('creates source maps', () => {
|
||||
const result = transformModule(sourceCode, options());
|
||||
const {code, map} = result.details.transformed.default;
|
||||
const position = findColumnAndLine(code, 'code');
|
||||
expect(position).not.toBeNull();
|
||||
|
||||
const consumer = new SourceMapConsumer(map);
|
||||
expect(consumer.originalPositionFor(position)).toEqual(
|
||||
expect.objectContaining({line: 1, column: sourceCode.indexOf('code')}),
|
||||
);
|
||||
});
|
||||
|
||||
it('extracts dependencies (require calls)', () => {
|
||||
const dep1 = 'foo';
|
||||
const dep2 = 'bar';
|
||||
const code = `require('${dep1}'),require('${dep2}')`;
|
||||
const {body} = parse(code).program;
|
||||
transformer.transform.stub.returns(transformResult(body));
|
||||
|
||||
transformModule(code, options(), (error, result) => {
|
||||
expect(result.details.transformed.default).toEqual(
|
||||
expect.objectContaining({dependencies: [dep1, dep2]}),
|
||||
);
|
||||
done();
|
||||
});
|
||||
const result = transformModule(code, options());
|
||||
expect(result.details.transformed.default).toEqual(
|
||||
expect.objectContaining({dependencies: [dep1, dep2]}),
|
||||
);
|
||||
});
|
||||
|
||||
it('transforms for all variants', done => {
|
||||
it('transforms for all variants', () => {
|
||||
const variants = {dev: {dev: true}, prod: {dev: false}};
|
||||
transformer.transform.stub
|
||||
.withArgs(filename, sourceCode, variants.dev)
|
||||
@ -198,49 +168,35 @@ describe('transforming JS modules:', () => {
|
||||
.withArgs(filename, sourceCode, variants.prod)
|
||||
.returns(transformResult([]));
|
||||
|
||||
transformModule(sourceCode, options(variants), (error, result) => {
|
||||
const {dev, prod} = result.details.transformed;
|
||||
expect(dev.code.replace(/\s+/g, '')).toEqual(
|
||||
`__d(function(global,require,module,exports,${dev.dependencyMapName}){arbitrary(code);});`,
|
||||
);
|
||||
expect(prod.code.replace(/\s+/g, '')).toEqual(
|
||||
`__d(function(global,require,module,exports,${prod.dependencyMapName}){arbitrary(code);});`,
|
||||
);
|
||||
done();
|
||||
});
|
||||
const result = transformModule(sourceCode, options(variants));
|
||||
const {dev, prod} = result.details.transformed;
|
||||
expect(dev.code.replace(/\s+/g, '')).toEqual(
|
||||
`__d(function(global,require,module,exports,${dev.dependencyMapName}){arbitrary(code);});`,
|
||||
);
|
||||
expect(prod.code.replace(/\s+/g, '')).toEqual(
|
||||
`__d(function(global,require,module,exports,${prod.dependencyMapName}){arbitrary(code);});`,
|
||||
);
|
||||
});
|
||||
|
||||
it('prefixes JSON files with `module.exports = `', done => {
|
||||
it('prefixes JSON files with `module.exports = `', () => {
|
||||
const json = '{"foo":"bar"}';
|
||||
|
||||
transformModule(
|
||||
json,
|
||||
{...options(), filename: 'some.json'},
|
||||
(error, result) => {
|
||||
const {code} = result.details.transformed.default;
|
||||
expect(code.replace(/\s+/g, '')).toEqual(
|
||||
'__d(function(global,require,module,exports){' +
|
||||
`module.exports=${json}});`,
|
||||
);
|
||||
done();
|
||||
},
|
||||
const result = transformModule(json, {...options(), filename: 'some.json'});
|
||||
const {code} = result.details.transformed.default;
|
||||
expect(code.replace(/\s+/g, '')).toEqual(
|
||||
'__d(function(global,require,module,exports){' +
|
||||
`module.exports=${json}});`,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not create source maps for JSON files', done => {
|
||||
transformModule(
|
||||
'{}',
|
||||
{...options(), filename: 'some.json'},
|
||||
(error, result) => {
|
||||
expect(result.details.transformed.default).toEqual(
|
||||
expect.objectContaining({map: null}),
|
||||
);
|
||||
done();
|
||||
},
|
||||
it('does not create source maps for JSON files', () => {
|
||||
const result = transformModule('{}', {...options(), filename: 'some.json'});
|
||||
expect(result.details.transformed.default).toEqual(
|
||||
expect.objectContaining({map: null}),
|
||||
);
|
||||
});
|
||||
|
||||
it('adds package data for `package.json` files', done => {
|
||||
it('adds package data for `package.json` files', () => {
|
||||
const pkg = {
|
||||
name: 'package-name',
|
||||
main: 'package/main',
|
||||
@ -248,14 +204,11 @@ describe('transforming JS modules:', () => {
|
||||
'react-native': {'react-native': 'defs'},
|
||||
};
|
||||
|
||||
transformModule(
|
||||
JSON.stringify(pkg),
|
||||
{...options(), filename: 'arbitrary/package.json'},
|
||||
(error, result) => {
|
||||
expect(result.details.package).toEqual(pkg);
|
||||
done();
|
||||
},
|
||||
);
|
||||
const result = transformModule(JSON.stringify(pkg), {
|
||||
...options(),
|
||||
filename: 'arbitrary/package.json',
|
||||
});
|
||||
expect(result.details.package).toEqual(pkg);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -9,29 +9,28 @@
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const JsFileWrapping = require('./JsFileWrapping');
|
||||
|
||||
const asyncify = require('async/asyncify');
|
||||
const collectDependencies = require('./collect-dependencies');
|
||||
const defaults = require('../../defaults');
|
||||
const docblock = require('jest-docblock');
|
||||
const generate = require('./generate');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const path = require('path');
|
||||
const series = require('async/series');
|
||||
|
||||
const {basename} = require('path');
|
||||
|
||||
import type {
|
||||
Callback,
|
||||
TransformedCodeFile,
|
||||
TransformedSourceFile,
|
||||
Transformer,
|
||||
TransformerResult,
|
||||
TransformResult,
|
||||
TransformVariants,
|
||||
} from '../types.flow';
|
||||
import type {Ast} from 'babel-core';
|
||||
|
||||
export type TransformOptions = {|
|
||||
filename: string,
|
||||
@ -55,68 +54,46 @@ const ASSET_EXTENSIONS = new Set(defaults.assetExts);
|
||||
function transformModule(
|
||||
content: Buffer,
|
||||
options: TransformOptions,
|
||||
callback: Callback<TransformedSourceFile>,
|
||||
): void {
|
||||
): TransformedSourceFile {
|
||||
if (ASSET_EXTENSIONS.has(path.extname(options.filename).substr(1))) {
|
||||
transformAsset(content, options, callback);
|
||||
return;
|
||||
return transformAsset(content, options);
|
||||
}
|
||||
|
||||
const code = content.toString('utf8');
|
||||
if (options.filename.endsWith('.json')) {
|
||||
transformJSON(code, options, callback);
|
||||
return;
|
||||
return transformJSON(code, options);
|
||||
}
|
||||
|
||||
const {filename, transformer, variants = defaultVariants} = options;
|
||||
const tasks = {};
|
||||
Object.keys(variants).forEach(name => {
|
||||
tasks[name] = asyncify(() =>
|
||||
transformer.transform({
|
||||
filename,
|
||||
localPath: filename,
|
||||
options: {...defaultTransformOptions, ...variants[name]},
|
||||
src: code,
|
||||
}),
|
||||
);
|
||||
});
|
||||
const {filename, transformer, polyfill, variants = defaultVariants} = options;
|
||||
const transformed: {[key: string]: TransformResult} = {};
|
||||
|
||||
series(tasks, (error, results: {[key: string]: TransformerResult}) => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const transformed: {[key: string]: TransformResult} = {};
|
||||
|
||||
//$FlowIssue #14545724
|
||||
Object.entries(results).forEach(([key, value]: [*, TransformFnResult]) => {
|
||||
transformed[key] = makeResult(
|
||||
value.ast,
|
||||
filename,
|
||||
code,
|
||||
options.polyfill,
|
||||
);
|
||||
for (const variantName of Object.keys(variants)) {
|
||||
const {ast} = transformer.transform({
|
||||
filename,
|
||||
localPath: filename,
|
||||
options: {...defaultTransformOptions, ...variants[variantName]},
|
||||
src: code,
|
||||
});
|
||||
invariant(ast != null, 'ast required from the transform results');
|
||||
transformed[variantName] = makeResult(ast, filename, code, polyfill);
|
||||
}
|
||||
|
||||
const annotations = docblock.parse(docblock.extract(code));
|
||||
const annotations = docblock.parse(docblock.extract(code));
|
||||
|
||||
callback(null, {
|
||||
type: 'code',
|
||||
details: {
|
||||
assetContent: null,
|
||||
code,
|
||||
file: filename,
|
||||
hasteID: annotations.providesModule || null,
|
||||
transformed,
|
||||
type: options.polyfill ? 'script' : 'module',
|
||||
},
|
||||
});
|
||||
});
|
||||
return;
|
||||
return {
|
||||
details: {
|
||||
assetContent: null,
|
||||
code,
|
||||
file: filename,
|
||||
hasteID: annotations.providesModule || null,
|
||||
transformed,
|
||||
type: options.polyfill ? 'script' : 'module',
|
||||
},
|
||||
type: 'code',
|
||||
};
|
||||
}
|
||||
|
||||
function transformJSON(json, options, callback) {
|
||||
function transformJSON(json, options): TransformedSourceFile {
|
||||
const value = JSON.parse(json);
|
||||
const {filename} = options;
|
||||
const code = `__d(function(${JsFileWrapping.MODULE_FACTORY_PARAMETERS.join(
|
||||
@ -151,24 +128,23 @@ function transformJSON(json, options, callback) {
|
||||
'react-native': value['react-native'],
|
||||
};
|
||||
}
|
||||
callback(null, {type: 'code', details: result});
|
||||
return {type: 'code', details: result};
|
||||
}
|
||||
|
||||
function transformAsset(
|
||||
content: Buffer,
|
||||
options: TransformOptions,
|
||||
callback: Callback<TransformedSourceFile>,
|
||||
) {
|
||||
callback(null, {
|
||||
): TransformedSourceFile {
|
||||
return {
|
||||
details: {
|
||||
assetContentBase64: content.toString('base64'),
|
||||
filePath: options.filename,
|
||||
},
|
||||
type: 'asset',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function makeResult(ast, filename, sourceCode, isPolyfill = false) {
|
||||
function makeResult(ast: Ast, filename, sourceCode, isPolyfill = false) {
|
||||
let dependencies, dependencyMapName, file;
|
||||
if (isPolyfill) {
|
||||
dependencies = [];
|
||||
|
Loading…
x
Reference in New Issue
Block a user