Remove baseTransformer

Reviewed By: jeanlauliac

Differential Revision: D4506124

fbshipit-source-id: 642f06dffe4ea710113e8e8426915bf1b40d4611
This commit is contained in:
Christoph Pojer 2017-02-09 03:59:30 -08:00 committed by Facebook Github Bot
parent 5f99af011a
commit 2517d18535
7 changed files with 143 additions and 161 deletions

View File

@ -14,35 +14,46 @@ jest.mock('../extract-dependencies');
jest.mock('../inline'); jest.mock('../inline');
jest.mock('../minify'); jest.mock('../minify');
const {any, objectContaining} = jasmine; const {objectContaining} = jasmine;
describe('code transformation worker:', () => { describe('code transformation worker:', () => {
let transformCode; let transformCode;
let extractDependencies, transform; let extractDependencies, transformer;
beforeEach(() => { beforeEach(() => {
jest.resetModules(); jest.resetModules();
({transformCode} = require('..')); ({transformCode} = require('..'));
extractDependencies = extractDependencies =
require('../extract-dependencies').mockReturnValue({}); require('../extract-dependencies').mockReturnValue({});
transform = jest.fn(); transformer = {
transform: jest.fn((src, filename, options) => ({
code: src,
map: {},
})),
};
}); });
it('calls the transform with file name, source code, and transform options', function() { it('calls the transform with file name, source code, and transform options', function() {
const filename = 'arbitrary/file.js'; const filename = 'arbitrary/file.js';
const sourceCode = 'arbitrary(code)'; const sourceCode = 'arbitrary(code)';
const transformOptions = {arbitrary: 'options'}; const transformOptions = {arbitrary: 'options'};
transformCode(transform, filename, sourceCode, {transform: transformOptions}); transformCode(transformer, filename, sourceCode, {transform: transformOptions}, () => {});
expect(transform).toBeCalledWith( expect(transformer.transform).toBeCalledWith(
{filename, sourceCode, options: transformOptions}, any(Function)); sourceCode,
filename,
transformOptions,
);
}); });
it('prefixes JSON files with an assignment to module.exports to make the code valid', function() { it('prefixes JSON files with an assignment to module.exports to make the code valid', function() {
const filename = 'arbitrary/file.json'; const filename = 'arbitrary/file.json';
const sourceCode = '{"arbitrary":"property"}'; const sourceCode = '{"arbitrary":"property"}';
transformCode(transform, filename, sourceCode, {}); transformCode(transformer, filename, sourceCode, {}, () => {});
expect(transform).toBeCalledWith( expect(transformer.transform).toBeCalledWith(
{filename, sourceCode: `module.exports=${sourceCode}`}, any(Function)); `module.exports=${sourceCode}`,
filename,
undefined,
);
}); });
it('calls back with the result of the transform in the cache', done => { it('calls back with the result of the transform in the cache', done => {
@ -50,10 +61,8 @@ describe('code transformation worker:', () => {
code: 'some.other(code)', code: 'some.other(code)',
map: {} map: {}
}; };
transform.mockImplementation((_, callback) =>
callback(null, result));
transformCode(transform, 'filename', 'code', {}, (error, data) => { transformCode(transformer, 'filename', result.code, {}, (error, data) => {
expect(error).toBeNull(); expect(error).toBeNull();
expect(data.result).toEqual(objectContaining(result)); expect(data.result).toEqual(objectContaining(result));
done(); done();
@ -64,14 +73,11 @@ describe('code transformation worker:', () => {
'removes the leading assignment to `module.exports` before passing ' + 'removes the leading assignment to `module.exports` before passing ' +
'on the result if the file is a JSON file, even if minified', 'on the result if the file is a JSON file, even if minified',
done => { done => {
const result = { const code = '{a:1,b:2}';
code: 'p.exports={a:1,b:2}',
};
transform.mockImplementation((_, callback) => callback(null, result));
const filePath = 'arbitrary/file.json'; const filePath = 'arbitrary/file.json';
transformCode(transform, filePath, 'b', {}, (error, data) => { transformCode(transformer, filePath, code, {}, (error, data) => {
expect(error).toBeNull(); expect(error).toBeNull();
expect(data.result.code).toEqual('{a:1,b:2}'); expect(data.result.code).toEqual(code);
done(); done();
}, },
); );
@ -83,9 +89,8 @@ describe('code transformation worker:', () => {
const result = { const result = {
code: `${shebang} \n arbitrary(code)`, code: `${shebang} \n arbitrary(code)`,
}; };
transform.mockImplementation((_, callback) => callback(null, result));
const filePath = 'arbitrary/file.js'; const filePath = 'arbitrary/file.js';
transformCode(transform, filePath, 'b', {}, (error, data) => { transformCode(transformer, filePath, result.code, {}, (error, data) => {
expect(error).toBeNull(); expect(error).toBeNull();
const {code} = data.result; const {code} = data.result;
expect(code).not.toContain(shebang); expect(code).not.toContain(shebang);
@ -95,26 +100,22 @@ describe('code transformation worker:', () => {
}); });
it('calls back with any error yielded by the transform', done => { it('calls back with any error yielded by the transform', done => {
const error = Error('arbitrary error'); const message = 'SyntaxError: this code is broken.';
transform.mockImplementation((_, callback) => callback(error)); transformer.transform.mockImplementation(() => {
transformCode(transform, 'filename', 'code', {}, e => { throw new Error(message);
expect(e).toBe(error); });
transformCode(transformer, 'filename', 'code', {}, error => {
expect(error.message).toBe(message);
done(); done();
}); });
}); });
describe('dependency extraction:', () => { describe('dependency extraction', () => {
let code;
beforeEach(() => {
transform.mockImplementation(
(_, callback) => callback(null, {code}));
});
it('passes the transformed code the `extractDependencies`', done => { it('passes the transformed code the `extractDependencies`', done => {
code = 'arbitrary(code)'; const code = 'arbitrary(code)';
transformCode(transform, 'filename', 'code', {}, (error) => { transformCode(transformer, 'filename', code, {}, (error) => {
expect(error).toBeNull(); expect(error).toBeNull();
expect(extractDependencies).toBeCalledWith(code); expect(extractDependencies).toBeCalledWith(code);
done(); done();
@ -131,7 +132,7 @@ describe('code transformation worker:', () => {
}; };
extractDependencies.mockReturnValue(dependencyData); extractDependencies.mockReturnValue(dependencyData);
transformCode(transform, 'filename', 'code', {}, (error, data) => { transformCode(transformer, 'filename', 'code', {}, (error, data) => {
expect(error).toBeNull(); expect(error).toBeNull();
expect(data.result).toEqual(objectContaining(dependencyData)); expect(data.result).toEqual(objectContaining(dependencyData));
done(); done();
@ -141,7 +142,7 @@ describe('code transformation worker:', () => {
it('does not extract requires if files are marked as "extern"', done => { it('does not extract requires if files are marked as "extern"', done => {
const opts = {extern: true}; const opts = {extern: true};
transformCode(transform, 'filename', 'code', opts, (error, data) => { transformCode(transformer, 'filename', 'code', opts, (error, data) => {
expect(error).toBeNull(); expect(error).toBeNull();
const {dependencies, dependencyOffsets} = data.result; const {dependencies, dependencyOffsets} = data.result;
expect(extractDependencies).not.toBeCalled(); expect(extractDependencies).not.toBeCalled();
@ -153,7 +154,7 @@ describe('code transformation worker:', () => {
it('does not extract requires of JSON files', done => { it('does not extract requires of JSON files', done => {
const jsonStr = '{"arbitrary":"json"}'; const jsonStr = '{"arbitrary":"json"}';
transformCode(transform, 'arbitrary.json', jsonStr, {}, (error, data) => { transformCode(transformer, 'arbitrary.json', jsonStr, {}, (error, data) => {
expect(error).toBeNull(); expect(error).toBeNull();
const {dependencies, dependencyOffsets} = data.result; const {dependencies, dependencyOffsets} = data.result;
expect(extractDependencies).not.toBeCalled(); expect(extractDependencies).not.toBeCalled();
@ -187,13 +188,12 @@ describe('code transformation worker:', () => {
extractDependencies.mockImplementation( extractDependencies.mockImplementation(
code => code === foldedCode ? dependencyData : {}); code => code === foldedCode ? dependencyData : {});
transform.mockImplementation( transformer.transform.mockImplementation((src, fileName, _) => transformResult);
(_, callback) => callback(null, transformResult));
}); });
it('passes the transform result to `inline` for constant inlining', done => { it('passes the transform result to `inline` for constant inlining', done => {
transformResult = {map: {version: 3}, code: 'arbitrary(code)'}; transformResult = {map: {version: 3}, code: 'arbitrary(code)'};
transformCode(transform, filename, 'code', options, () => { transformCode(transformer, filename, 'code', options, () => {
expect(inline).toBeCalledWith(filename, transformResult, options); expect(inline).toBeCalledWith(filename, transformResult, options);
done(); done();
}); });
@ -202,21 +202,21 @@ describe('code transformation worker:', () => {
it('passes the result obtained from `inline` on to `constant-folding`', done => { it('passes the result obtained from `inline` on to `constant-folding`', done => {
const inlineResult = {map: {version: 3, sources: []}, ast: {}}; const inlineResult = {map: {version: 3, sources: []}, ast: {}};
inline.mockReturnValue(inlineResult); inline.mockReturnValue(inlineResult);
transformCode(transform, filename, 'code', options, () => { transformCode(transformer, filename, 'code', options, () => {
expect(constantFolding).toBeCalledWith(filename, inlineResult); expect(constantFolding).toBeCalledWith(filename, inlineResult);
done(); done();
}); });
}); });
it('Uses the code obtained from `constant-folding` to extract dependencies', done => { it('Uses the code obtained from `constant-folding` to extract dependencies', done => {
transformCode(transform, filename, 'code', options, () => { transformCode(transformer, filename, 'code', options, () => {
expect(extractDependencies).toBeCalledWith(foldedCode); expect(extractDependencies).toBeCalledWith(foldedCode);
done(); done();
}); });
}); });
it('uses the dependencies obtained from the optimized result', done => { it('uses the dependencies obtained from the optimized result', done => {
transformCode(transform, filename, 'code', options, (_, data) => { transformCode(transformer, filename, 'code', options, (_, data) => {
const result = data.result; const result = data.result;
expect(result.dependencies).toEqual(dependencyData.dependencies); expect(result.dependencies).toEqual(dependencyData.dependencies);
done(); done();
@ -224,7 +224,7 @@ describe('code transformation worker:', () => {
}); });
it('uses data produced by `constant-folding` for the result', done => { it('uses data produced by `constant-folding` for the result', done => {
transformCode(transform, 'filename', 'code', options, (_, data) => { transformCode(transformer, 'filename', 'code', options, (_, data) => {
expect(data.result) expect(data.result)
.toEqual(objectContaining({code: foldedCode, map: foldedMap})); .toEqual(objectContaining({code: foldedCode, map: foldedMap}));
done(); done();

View File

@ -20,20 +20,6 @@ const minify = require('./minify');
import type {LogEntry} from '../../Logger/Types'; import type {LogEntry} from '../../Logger/Types';
import type {Ast, SourceMap, TransformOptions as BabelTransformOptions} from 'babel-core'; import type {Ast, SourceMap, TransformOptions as BabelTransformOptions} from 'babel-core';
function makeTransformParams(filename, sourceCode, options, willMinify) {
invariant(
!willMinify || options.generateSourceMaps,
'Minifying source code requires the `generateSourceMaps` option to be `true`',
);
if (filename.endsWith('.json')) {
sourceCode = 'module.exports=' + sourceCode;
}
return {filename, sourceCode, options};
}
export type TransformedCode = { export type TransformedCode = {
code: string, code: string,
dependencies: Array<string>, dependencies: Array<string>,
@ -41,17 +27,13 @@ export type TransformedCode = {
map?: ?SourceMap, map?: ?SourceMap,
}; };
type Transform = ( type Transformer = {
params: { transform: (
filename: string, filename: string,
sourceCode: string, sourceCode: string,
options: ?{}, options: ?{},
}, ) => {ast: ?Ast, code: string, map: ?SourceMap}
callback: ( };
error?: Error,
tranformed?: {ast: ?Ast, code: string, map: ?SourceMap},
) => mixed,
) => void;
export type TransformOptions = { export type TransformOptions = {
generateSourceMaps: boolean, generateSourceMaps: boolean,
@ -80,19 +62,21 @@ type Callback = (
) => mixed; ) => mixed;
function transformCode( function transformCode(
transform: Transform, transformer: Transformer,
filename: string, filename: string,
sourceCode: string, sourceCode: string,
options: Options, options: Options,
callback: Callback, callback: Callback,
) { ) {
const params = makeTransformParams( invariant(
filename, !options.minify || options.transform.generateSourceMaps,
sourceCode, 'Minifying source code requires the `generateSourceMaps` option to be `true`',
options.transform,
options.minify,
); );
const isJson = filename.endsWith('.json'); const isJson = filename.endsWith('.json');
if (isJson) {
sourceCode = 'module.exports=' + sourceCode;
}
const transformFileStartLogEntry = { const transformFileStartLogEntry = {
action_name: 'Transforming file', action_name: 'Transforming file',
@ -102,52 +86,53 @@ function transformCode(
start_timestamp: process.hrtime(), start_timestamp: process.hrtime(),
}; };
transform(params, (error, transformed) => { let transformed;
if (error) { try {
callback(error); transformed = transformer.transform(sourceCode, filename, options.transform);
return; } catch (error) {
} callback(error);
return;
}
invariant( invariant(
transformed != null, transformed != null,
'Missing transform results despite having no error.', 'Missing transform results despite having no error.',
); );
var code, map; var code, map;
if (options.minify) { if (options.minify) {
({code, map} = ({code, map} =
constantFolding(filename, inline(filename, transformed, options))); constantFolding(filename, inline(filename, transformed, options)));
invariant(code != null, 'Missing code from constant-folding transform.'); invariant(code != null, 'Missing code from constant-folding transform.');
} else { } else {
({code, map} = transformed); ({code, map} = transformed);
} }
if (isJson) { if (isJson) {
code = code.replace(/^\w+\.exports=/, ''); code = code.replace(/^\w+\.exports=/, '');
} else { } else {
// Remove shebang // Remove shebang
code = code.replace(/^#!.*/, ''); code = code.replace(/^#!.*/, '');
} }
const depsResult = isJson || options.extern const depsResult = isJson || options.extern
? {dependencies: [], dependencyOffsets: []} ? {dependencies: [], dependencyOffsets: []}
: extractDependencies(code); : extractDependencies(code);
const timeDelta = process.hrtime(transformFileStartLogEntry.start_timestamp); const timeDelta = process.hrtime(transformFileStartLogEntry.start_timestamp);
const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6); const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6);
const transformFileEndLogEntry = { const transformFileEndLogEntry = {
action_name: 'Transforming file', action_name: 'Transforming file',
action_phase: 'end', action_phase: 'end',
file_name: filename, file_name: filename,
duration_ms: duration_ms, duration_ms: duration_ms,
log_entry_label: 'Transforming file', log_entry_label: 'Transforming file',
}; };
return callback(null, { callback(null, {
result: {...depsResult, code, map}, result: {...depsResult, code, map},
transformFileStartLogEntry, transformFileStartLogEntry,
transformFileEndLogEntry, transformFileEndLogEntry,
});
}); });
} }

View File

@ -11,6 +11,7 @@
'use strict'; 'use strict';
import type {SourceMap} from './output/source-map'; import type {SourceMap} from './output/source-map';
import type {Ast} from 'babel-core';
import type {Console} from 'console'; import type {Console} from 'console';
export type Callback<A = void, B = void> export type Callback<A = void, B = void>
@ -99,18 +100,19 @@ type ResolveOptions = {
log?: Console, log?: Console,
}; };
export type TransformFn = ( export type TransformerResult = {
data: {| ast: ?Ast,
filename: string, code: string,
options?: Object, map: ?SourceMap,
plugins?: Array<string | Object | [string | Object, any]>, };
sourceCode: string,
|},
callback: Callback<TransformFnResult>
) => void;
export type TransformFnResult = { export type Transformer = {
ast: Object, transform: (
sourceCode: string,
filename: string,
options: ?{},
plugins?: Array<string | Object | [string | Object, any]>,
) => {ast: ?Ast, code: string, map: ?SourceMap}
}; };
export type TransformResult = {| export type TransformResult = {|

View File

@ -12,7 +12,7 @@ jest.disableAutomock();
const optimizeModule = require('../optimize-module'); const optimizeModule = require('../optimize-module');
const transformModule = require('../transform-module'); const transformModule = require('../transform-module');
const transform = require('../../../../transformer.js'); const transformer = require('../../../../transformer.js');
const {SourceMapConsumer} = require('source-map'); const {SourceMapConsumer} = require('source-map');
const {objectContaining} = jasmine; const {objectContaining} = jasmine;
@ -32,7 +32,7 @@ describe('optimizing JS modules', () => {
let transformResult; let transformResult;
beforeAll(done => { beforeAll(done => {
transformModule(originalCode, {filename, transform}, (error, result) => { transformModule(originalCode, {filename, transformer}, (error, result) => {
if (error) { if (error) {
throw error; throw error;
} }

View File

@ -24,18 +24,20 @@ const {any, objectContaining} = jasmine;
describe('transforming JS modules:', () => { describe('transforming JS modules:', () => {
const filename = 'arbitrary'; const filename = 'arbitrary';
let transform; let transformer;
beforeEach(() => { beforeEach(() => {
transform = fn(); transformer = {
transform.stub.yields(null, transformResult()); transform: fn(),
};
transformer.transform.stub.returns(transformResult());
}); });
const {bodyAst, sourceCode, transformedCode} = createTestData(); const {bodyAst, sourceCode, transformedCode} = createTestData();
const options = variants => ({ const options = variants => ({
filename, filename,
transform, transformer,
variants, variants,
}); });
@ -80,17 +82,17 @@ describe('transforming JS modules:', () => {
const variants = {dev: {dev: true}, prod: {dev: false}}; const variants = {dev: {dev: true}, prod: {dev: false}};
transformModule(sourceCode, options(variants), () => { transformModule(sourceCode, options(variants), () => {
expect(transform) expect(transformer.transform)
.toBeCalledWith({filename, sourceCode, options: variants.dev}, any(Function)); .toBeCalledWith(sourceCode, filename, variants.dev);
expect(transform) expect(transformer.transform)
.toBeCalledWith({filename, sourceCode, options: variants.prod}, any(Function)); .toBeCalledWith(sourceCode, filename, variants.prod);
done(); done();
}); });
}); });
it('calls back with any error yielded by the transform function', done => { it('calls back with any error yielded by the transform function', done => {
const error = new Error(); const error = new Error();
transform.stub.yields(error); transformer.transform.stub.throws(error);
transformModule(sourceCode, options(), e => { transformModule(sourceCode, options(), e => {
expect(e).toBe(error); expect(e).toBe(error);
@ -138,7 +140,7 @@ describe('transforming JS modules:', () => {
const dep1 = 'foo', dep2 = 'bar'; const dep1 = 'foo', dep2 = 'bar';
const code = `require('${dep1}'),require('${dep2}')`; const code = `require('${dep1}'),require('${dep2}')`;
const {body} = parse(code).program; const {body} = parse(code).program;
transform.stub.yields(null, transformResult(body)); transformer.transform.stub.returns(transformResult(body));
transformModule(code, options(), (error, result) => { transformModule(code, options(), (error, result) => {
expect(result.transformed.default) expect(result.transformed.default)
@ -149,11 +151,11 @@ describe('transforming JS modules:', () => {
it('transforms for all variants', done => { it('transforms for all variants', done => {
const variants = {dev: {dev: true}, prod: {dev: false}}; const variants = {dev: {dev: true}, prod: {dev: false}};
transform.stub transformer.transform.stub
.withArgs(filename, sourceCode, variants.dev) .withArgs(filename, sourceCode, variants.dev)
.yields(null, transformResult(bodyAst)) .returns(transformResult(bodyAst))
.withArgs(filename, sourceCode, variants.prod) .withArgs(filename, sourceCode, variants.prod)
.yields(null, transformResult([])); .returns(transformResult([]));
transformModule(sourceCode, options(variants), (error, result) => { transformModule(sourceCode, options(variants), (error, result) => {
const {dev, prod} = result.transformed; const {dev, prod} = result.transformed;

View File

@ -21,8 +21,8 @@ const {basename} = require('path');
import type { import type {
Callback, Callback,
TransformedFile, TransformedFile,
TransformFn, Transformer,
TransformFnResult, TransformerResult,
TransformResult, TransformResult,
TransformVariants, TransformVariants,
} from '../types.flow'; } from '../types.flow';
@ -30,7 +30,7 @@ import type {
export type TransformOptions = {| export type TransformOptions = {|
filename: string, filename: string,
polyfill?: boolean, polyfill?: boolean,
transform: TransformFn, transformer: Transformer,
variants?: TransformVariants, variants?: TransformVariants,
|}; |};
@ -47,17 +47,23 @@ function transformModule(
return transformJSON(code, options, callback); return transformJSON(code, options, callback);
} }
const {filename, transform, variants = defaultVariants} = options; const {filename, transformer, variants = defaultVariants} = options;
const tasks = {}; const tasks = {};
Object.keys(variants).forEach(name => { Object.keys(variants).forEach(name => {
tasks[name] = cb => transform({ tasks[name] = cb => {
filename, try {
sourceCode: code, cb(null, transformer.transform(
options: variants[name], code,
}, cb); filename,
variants[name],
));
} catch (error) {
cb(error, null);
}
};
}); });
series(tasks, (error, results: {[key: string]: TransformFnResult}) => { series(tasks, (error, results: {[key: string]: TransformerResult}) => {
if (error) { if (error) {
callback(error); callback(error);
return; return;

View File

@ -126,17 +126,4 @@ function transform(src, filename, options) {
} }
} }
module.exports = function(data, callback) {
let result;
try {
result = transform(data.sourceCode, data.filename, data.options);
} catch (e) {
callback(e);
return;
}
callback(null, result);
};
// export for use in jest
module.exports.transform = transform; module.exports.transform = transform;