From b08ce130fdd8bd455ec2f0528a597ac986866de4 Mon Sep 17 00:00:00 2001 From: Rafael Oleza Date: Thu, 23 Nov 2017 15:33:45 -0800 Subject: [PATCH] Move wrapModule logic for scripts, modules and JSON files to the transformer Reviewed By: davidaurelio Differential Revision: D6389116 fbshipit-source-id: fa43bd54669849c22b6b9d155ab07676b2455ef7 --- packages/metro-bundler/src/Bundler/index.js | 8 +- .../src/DeltaBundler/DeltaTransformer.js | 118 +++- .../__tests__/Transformer-test.js | 4 +- .../metro-bundler/src/JSTransformer/index.js | 2 + .../worker/__tests__/worker-test.js | 302 +++------- .../src/JSTransformer/worker/index.js | 96 +-- .../worker/collect-dependencies.js | 9 +- .../src/Resolver/__tests__/Resolver-test.js | 92 +-- packages/metro-bundler/src/Resolver/index.js | 35 +- .../__snapshots__/basic_bundle-test.js.snap | 546 +++++++++--------- .../src/lib/GlobalTransformCache.js | 4 +- .../metro-bundler/src/lib/TransformCaching.js | 12 +- .../lib/__tests__/TransformCaching-test.js | 2 - .../metro-bundler/src/node-haste/Module.js | 3 +- 14 files changed, 529 insertions(+), 704 deletions(-) diff --git a/packages/metro-bundler/src/Bundler/index.js b/packages/metro-bundler/src/Bundler/index.js index ad0ef9b8..8de17cec 100644 --- a/packages/metro-bundler/src/Bundler/index.js +++ b/packages/metro-bundler/src/Bundler/index.js @@ -213,6 +213,7 @@ class Bundler { module.path, module.localPath, code, + module.isPolyfill(), transformCodeOptions, ), transformCache: opts.transformCache, @@ -240,14 +241,11 @@ class Bundler { } async generateAssetObjAndCode( - module: Module, + path: string, assetPlugins: Array, platform: ?string = null, ) { - const assetData = await this._assetServer.getAssetData( - module.path, - platform, - ); + const assetData = await this._assetServer.getAssetData(path, platform); const asset = await this._applyAssetPlugins(assetPlugins, assetData); const { diff --git a/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js b/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js index c34d5b7d..a5effb21 100644 --- a/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js +++ b/packages/metro-bundler/src/DeltaBundler/DeltaTransformer.js @@ -14,6 +14,7 @@ const DeltaCalculator = require('./DeltaCalculator'); +const addParamsToDefineCall = require('../lib/addParamsToDefineCall'); const createModuleIdFactory = require('../lib/createModuleIdFactory'); const {EventEmitter} = require('events'); @@ -425,27 +426,37 @@ class DeltaTransformer extends EventEmitter { ): Promise<[number, ?DeltaEntry]> { const name = module.getName(); const metadata = await this._getMetadata(module, transformOptions); + const edge = dependencyEdges.get(module.path); const dependencyPairs = edge ? edge.dependencies : new Map(); - const wrapped = this._resolver.wrapModule({ - module, - getModuleId: this._getModuleId, - dependencyPairs, - dependencyOffsets: metadata.dependencyOffsets || [], - name, - code: metadata.code, - map: metadata.map, - dev: this._bundleOptions.dev, - }); + let wrappedCode; + + if (module.isAsset()) { + wrappedCode = await this._wrapAsset({ + code: metadata.code, + dependencyPairs, + name, + path: module.path, + }); + } else if (!module.isPolyfill()) { + wrappedCode = this._addDependencyMap({ + code: metadata.code, + dependencyPairs, + name, + path: module.path, + }); + } else { + wrappedCode = metadata.code; + } const {code, map} = transformOptions.minify ? await this._resolver.minifyModule( module.path, - wrapped.code, - wrapped.map, + wrappedCode, + metadata.map, ) - : wrapped; + : {code: wrappedCode, map: metadata.map}; const id = this._getModuleId(module.path); @@ -463,6 +474,70 @@ class DeltaTransformer extends EventEmitter { ]; } + /** + * Function to add the mapping object between local module ids and + * actual bundle module ids for dependencies. This way, we can do the path + * replacements on require() calls on transformers (since local ids do not + * change between bundles). + */ + _addDependencyMap({ + code, + dependencyPairs, + name, + path, + }: { + code: string, + dependencyPairs: Map, + name: string, + path: string, + }): string { + const moduleId = this._getModuleId(path); + const params = [ + moduleId, + Array.from(dependencyPairs.values()).map(this._getModuleId), + ]; + + // Add the module name as the last parameter (to make it easier to do + // requires by name when debugging). + if (this._bundleOptions.dev) { + params.push(name); + } + + return addParamsToDefineCall(code, ...params); + } + + /** + * Temporary function to wrap an asset. This logic will go away once we + * generate the needed JS code for assets in the transformer. + */ + async _wrapAsset({ + code, + dependencyPairs, + name, + path, + }: { + code: string, + dependencyPairs: Map, + name: string, + path: string, + }): Promise { + const asset = await this._bundler.generateAssetObjAndCode( + path, + this._bundleOptions.assetPlugins, + this._bundleOptions.platform, + ); + + return await this._resolver.wrapModule({ + path, + getModuleId: this._getModuleId, + dependencyPairs, + dependencyOffsets: asset.meta.dependencyOffsets, + name, + code: asset.code, + dev: this._bundleOptions.dev, + }); + } + _getModuleType(module: Module): DeltaEntryType { if (module.isAsset()) { return 'asset'; @@ -480,25 +555,10 @@ class DeltaTransformer extends EventEmitter { transformOptions: JSTransformerOptions, ): Promise<{ +code: string, - +dependencyOffsets: ?Array, + +dependencies: Array, +map: CompactRawMappings, +source: string, }> { - if (module.isAsset()) { - const asset = await this._bundler.generateAssetObjAndCode( - module, - this._bundleOptions.assetPlugins, - this._bundleOptions.platform, - ); - - return { - code: asset.code, - dependencyOffsets: asset.meta.dependencyOffsets, - map: [], - source: '', - }; - } - return await module.read(transformOptions); } diff --git a/packages/metro-bundler/src/JSTransformer/__tests__/Transformer-test.js b/packages/metro-bundler/src/JSTransformer/__tests__/Transformer-test.js index c093f32d..d66ab4da 100644 --- a/packages/metro-bundler/src/JSTransformer/__tests__/Transformer-test.js +++ b/packages/metro-bundler/src/JSTransformer/__tests__/Transformer-test.js @@ -59,6 +59,7 @@ describe('Transformer', function() { fileName, localPath, code, + false, transformOptions, ); @@ -67,6 +68,7 @@ describe('Transformer', function() { fileName, localPath, code, + false, transformOptions, ); }); @@ -92,7 +94,7 @@ describe('Transformer', function() { expect.assertions(6); return transformer - .transformFile(fileName, localPath, '', {}) + .transformFile(fileName, localPath, '', true, {}) .catch(function(error) { expect(error.type).toEqual('TransformError'); expect(error.message).toBe( diff --git a/packages/metro-bundler/src/JSTransformer/index.js b/packages/metro-bundler/src/JSTransformer/index.js index 6f44ed48..1dc02db3 100644 --- a/packages/metro-bundler/src/JSTransformer/index.js +++ b/packages/metro-bundler/src/JSTransformer/index.js @@ -87,6 +87,7 @@ module.exports = class Transformer { filename: string, localPath: LocalPath, code: string, + isScript: boolean, options: Options, ): Promise { try { @@ -97,6 +98,7 @@ module.exports = class Transformer { filename, localPath, code, + isScript, options, ); diff --git a/packages/metro-bundler/src/JSTransformer/worker/__tests__/worker-test.js b/packages/metro-bundler/src/JSTransformer/worker/__tests__/worker-test.js index 2e477a4c..a0641925 100644 --- a/packages/metro-bundler/src/JSTransformer/worker/__tests__/worker-test.js +++ b/packages/metro-bundler/src/JSTransformer/worker/__tests__/worker-test.js @@ -13,243 +13,95 @@ jest .mock('../constant-folding') - .mock('../extract-dependencies') .mock('../inline') - .mock('../minify') - .mock('babel-generator'); + .mock('../minify'); -const {objectContaining} = jasmine; +const path = require('path'); +const transformCode = require('..').transformAndExtractDependencies; describe('code transformation worker:', () => { - let transformCode; - let babelGenerator; - - let extractDependencies, transformer; - beforeEach(() => { - jest.resetModules(); - ({transformCode} = require('..')); - extractDependencies = require('../extract-dependencies').mockReturnValue( - {}, - ); - transformer = { - transform: jest.fn(({filename, options, src}) => ({ - code: src, - map: [], - })), - }; - - babelGenerator = require('babel-generator'); - - babelGenerator.default.mockReturnValue({ - code: '', - map: [], - }); - }); - - it('calls the transform with file name, source code, and transform options', function() { - const filename = 'arbitrary/file.js'; - const localPath = `local/${filename}`; - const sourceCode = 'arbitrary(code)'; - const transformOptions = {arbitrary: 'options'}; - transformCode(transformer, filename, localPath, sourceCode, { - dev: true, - transform: transformOptions, - }); - expect(transformer.transform).toBeCalledWith({ - filename, - localPath, - options: transformOptions, - plugins: [], - src: sourceCode, - }); - }); - - it('calls the transform with two plugins when not in dev mode', () => { - const filename = 'arbitrary/file.js'; - const localPath = `local/${filename}`; - const sourceCode = 'arbitrary(code)'; - const options = {dev: false, transform: {arbitrary: 'options'}}; - - transformCode(transformer, filename, localPath, sourceCode, options); - - const plugins = transformer.transform.mock.calls[0][0].plugins; - - expect(plugins[0]).toEqual([expect.any(Object), options]); - expect(plugins[1]).toEqual([expect.any(Object), options]); - }); - - it('prefixes JSON files with an assignment to module.exports to make the code valid', function() { - const filename = 'arbitrary/file.json'; - const localPath = `local/${filename}`; - const sourceCode = '{"arbitrary":"property"}'; - transformCode(transformer, filename, localPath, sourceCode, {dev: true}); - - expect(transformer.transform).toBeCalledWith({ - filename, - localPath, - options: undefined, - plugins: [], - src: `module.exports=${sourceCode}`, - }); - }); - - it('calls back with the result of the transform in the cache', async () => { - const result = { - code: 'some.other(code)', - map: [], - }; - - babelGenerator.default.mockReturnValue({ - code: 'some.other(code)', - map: [], - }); - - const data = await transformCode( - transformer, - 'filename', - 'local/filename', - result.code, - {}, + it('transforms a simple script', async () => { + const {result} = await transformCode( + path.join(__dirname, '../../../transformer.js'), + 'arbitrary/file.js', + `local/file.js`, + 'someReallyArbitrary(code)', + true, + { + dev: true, + transform: {}, + }, ); - expect(data.result).toEqual(objectContaining(result)); + expect(result.code).toBe( + [ + '(function (global) {', + ' someReallyArbitrary(code);', + '})(this);', + ].join('\n'), + ); + expect(result.map).toHaveLength(3); + expect(result.dependencies).toEqual([]); }); - it('removes the leading `module.exports` before returning if the file is a JSON file, even if minified', async () => { - const code = '{a:1,b:2}'; - const filePath = 'arbitrary/file.json'; - - babelGenerator.default.mockReturnValue({ - code: '{a:1,b:2}', - map: [], - }); - - const data = await transformCode(transformer, filePath, filePath, code, {}); - - expect(data.result.code).toEqual(code); - }); - - it('removes shebang when present', async () => { - const shebang = '#!/usr/bin/env node'; - const result = { - code: `${shebang} \n arbitrary(code)`, - }; - const filePath = 'arbitrary/file.js'; - - babelGenerator.default.mockReturnValue({ - code: `${shebang} \n arbitrary(code)`, - map: [], - }); - - const data = await transformCode( - transformer, - filePath, - filePath, - result.code, - {}, + it('transforms a simple module', async () => { + const {result} = await transformCode( + path.join(__dirname, '../../../transformer.js'), + 'arbitrary/file.js', + `local/file.js`, + 'arbitrary(code)', + false, + { + dev: true, + transform: {}, + }, ); - const {code} = data.result; - expect(code).not.toContain(shebang); - expect(code.split('\n').length).toEqual(result.code.split('\n').length); + expect(result.code).toBe( + [ + '__d(function (global, require, module, exports, _dependencyMap) {', + ' arbitrary(code);', + '});', + ].join('\n'), + ); + expect(result.map).toHaveLength(3); + expect(result.dependencies).toEqual([]); }); - it('calls back with any error yielded by the transform', async () => { - const message = 'SyntaxError: this code is broken.'; + it('transforms a module with dependencies', async () => { + const {result} = await transformCode( + path.join(__dirname, '../../../transformer.js'), + 'arbitrary/file.js', + `local/file.js`, + [ + 'require("./a");', + 'arbitrary(code);', + 'const b = require("b");', + 'import c from "./c";', + ].join('\n'), + false, + { + dev: true, + transform: {}, + }, + ); - transformer.transform.mockImplementation(() => { - throw new Error(message); - }); - - expect.assertions(1); - - try { - await transformCode( - transformer, - 'filename', - 'local/filename', - 'code', - {}, - ); - } catch (error) { - expect(error.message).toBe(message); - } - }); - - describe('dependency extraction', () => { - it('passes the transformed code the `extractDependencies`', async () => { - const code = 'arbitrary(code)'; - - babelGenerator.default.mockReturnValue({ - code: 'arbitrary(code)', - map: [], - }); - - await transformCode(transformer, 'filename', 'local/filename', code, {}); - - expect(extractDependencies).toBeCalledWith(code, 'filename'); - }); - - it('uses `dependencies` and `dependencyOffsets` provided by `extractDependencies` for the result', async () => { - const dependencyData = { - dependencies: ['arbitrary', 'list', 'of', 'dependencies'], - dependencyOffsets: [12, 119, 185, 328, 471], - }; - - extractDependencies.mockReturnValue(dependencyData); - - const data = await transformCode( - transformer, - 'filename', - 'local/filename', - 'code', - {}, - ); - - expect(data.result).toEqual(objectContaining(dependencyData)); - }); - - it('does not extract requires of JSON files', async () => { - const jsonStr = '{"arbitrary":"json"}'; - - babelGenerator.default.mockReturnValue({ - code: '{"arbitrary":"json"}', - map: [], - }); - - const data = await transformCode( - transformer, - 'arbitrary.json', - 'local/arbitrary.json', - jsonStr, - {}, - ); - - const {dependencies, dependencyOffsets} = data.result; - - expect(extractDependencies).not.toBeCalled(); - expect(dependencies).toEqual([]); - expect(dependencyOffsets).toEqual([]); - }); - - it('calls back with every error thrown by `extractDependencies`', async () => { - const error = new Error('arbitrary'); - - extractDependencies.mockImplementation(() => { - throw error; - }); - - try { - await transformCode( - transformer, - 'arbitrary.js', - 'local/arbitrary.js', - 'code', - {}, - ); - } catch (err) { - expect(err).toBe(error); - } - }); + expect(result.code).toBe( + [ + '__d(function (global, require, module, exports, _dependencyMap) {', + ' var _c = require(_dependencyMap[0], "./c");', + '', + ' var _c2 = babelHelpers.interopRequireDefault(_c);', + '', + ' require(_dependencyMap[1], "./a");', + '', + ' arbitrary(code);', + '', + ' var b = require(_dependencyMap[2], "b");', + '});', + ].join('\n'), + ); + expect(result.map).toHaveLength(13); + expect(result.dependencies).toEqual(['./c', './a', 'b']); }); }); diff --git a/packages/metro-bundler/src/JSTransformer/worker/index.js b/packages/metro-bundler/src/JSTransformer/worker/index.js index 9c5eb7af..f4fe15b9 100644 --- a/packages/metro-bundler/src/JSTransformer/worker/index.js +++ b/packages/metro-bundler/src/JSTransformer/worker/index.js @@ -12,28 +12,25 @@ 'use strict'; +const JsFileWrapping = require('../../ModuleGraph/worker/JsFileWrapping'); + +const collectDependencies = require('../../ModuleGraph/worker/collect-dependencies'); const constantFolding = require('./constant-folding'); -const extractDependencies = require('./extract-dependencies'); const generate = require('babel-generator').default; const inline = require('./inline'); const minify = require('./minify'); -const {compactMapping, toRawMappings} = require('../../Bundler/source-map'); +const {compactMapping} = require('../../Bundler/source-map'); import type {LogEntry} from '../../Logger/Types'; -import type { - CompactRawMappings, - MappingsMap, - RawMappings, -} from '../../lib/SourceMap'; +import type {CompactRawMappings, MappingsMap} from '../../lib/SourceMap'; import type {LocalPath} from '../../node-haste/lib/toLocalPath'; import type {ResultWithMap} from './minify'; import type {Ast, Plugins as BabelPlugins} from 'babel-core'; export type TransformedCode = { code: string, - dependencies: Array, - dependencyOffsets: Array, + dependencies: $ReadOnlyArray, map: CompactRawMappings, }; @@ -96,6 +93,7 @@ async function transformCode( filename: string, localPath: LocalPath, sourceCode: string, + isScript: boolean, options: Options, ): Promise { const isJson = filename.endsWith('.json'); @@ -116,7 +114,7 @@ async function transformCode( ? [] : [[inline.plugin, options], [constantFolding.plugin, options]]; - const result = await transformer.transform({ + const {ast} = await transformer.transform({ filename, localPath, options: options.transform, @@ -124,9 +122,45 @@ async function transformCode( src: sourceCode, }); - // Serialize the AST received from the transformer. - const transformed = generate( - result.ast, + const timeDelta = process.hrtime(transformFileStartLogEntry.start_timestamp); + const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6); + const transformFileEndLogEntry = { + action_name: 'Transforming file', + action_phase: 'end', + file_name: filename, + duration_ms, + log_entry_label: 'Transforming file', + }; + + let dependencies, wrappedAst; + + // If the module to transform is a script (meaning that is not part of the + // dependency graph and it code will just be prepended to the bundle modules), + // we need to wrap it differently than a commonJS module (also, scripts do + // not have dependencies). + if (isScript) { + dependencies = []; + wrappedAst = JsFileWrapping.wrapPolyfill(ast); + } else { + let dependencyData = collectDependencies(ast); + + if (!options.dev) { + dependencyData = collectDependencies.forOptimization( + ast, + dependencyData.dependencies, + dependencyData.dependencyMapName, + ); + } + + dependencies = dependencyData.dependencies.map(dep => dep.name); + wrappedAst = JsFileWrapping.wrapModule( + ast, + dependencyData.dependencyMapName, + ); + } + + const result = generate( + wrappedAst, { code: false, comments: false, @@ -139,40 +173,10 @@ async function transformCode( sourceCode, ); - // If the transformer returns standard sourcemaps, we need to transform them - // to rawMappings so we can process them correctly. - const rawMappings = - transformed.map && !Array.isArray(transformed.map) - ? toRawMappings(transformed.map) - : transformed.map; - - // Convert the sourcemaps to Compact Raw source maps. - const map = rawMappings ? rawMappings.map(compactMapping) : []; - - let code = transformed.code; - if (isJson) { - code = code.replace(/^\w+\.exports=/, ''); - } else { - // Remove shebang - code = code.replace(/^#!.*/, ''); - } - - const depsResult = isJson - ? {dependencies: [], dependencyOffsets: []} - : extractDependencies(code, filename); - - const timeDelta = process.hrtime(transformFileStartLogEntry.start_timestamp); - const duration_ms = Math.round((timeDelta[0] * 1e9 + timeDelta[1]) / 1e6); - const transformFileEndLogEntry = { - action_name: 'Transforming file', - action_phase: 'end', - file_name: filename, - duration_ms, - log_entry_label: 'Transforming file', - }; + const map = result.rawMappings ? result.rawMappings.map(compactMapping) : []; return { - result: {...depsResult, code, map}, + result: {dependencies, code: result.code, map}, transformFileStartLogEntry, transformFileEndLogEntry, }; @@ -201,6 +205,7 @@ exports.transformAndExtractDependencies = async function( filename: string, localPath: LocalPath, sourceCode: string, + isScript: boolean, options: Options, ): Promise { // $FlowFixMe: impossible to type a dynamic require. @@ -211,6 +216,7 @@ exports.transformAndExtractDependencies = async function( filename, localPath, sourceCode, + isScript, options, ); }; diff --git a/packages/metro-bundler/src/ModuleGraph/worker/collect-dependencies.js b/packages/metro-bundler/src/ModuleGraph/worker/collect-dependencies.js index db152d0a..fe8f9234 100644 --- a/packages/metro-bundler/src/ModuleGraph/worker/collect-dependencies.js +++ b/packages/metro-bundler/src/ModuleGraph/worker/collect-dependencies.js @@ -133,7 +133,14 @@ function createMapLookup(dependencyMapIdentifier, propertyIdentifier) { ); } -function collectDependencies(ast, replacement, dependencyMapIdentifier) { +function collectDependencies( + ast, + replacement, + dependencyMapIdentifier, +): { + dependencies: $ReadOnlyArray, + dependencyMapName: string, +} { const visited = new WeakSet(); const traversalState = {dependencyMapIdentifier}; traverse( diff --git a/packages/metro-bundler/src/Resolver/__tests__/Resolver-test.js b/packages/metro-bundler/src/Resolver/__tests__/Resolver-test.js index ca6bcc51..379d9166 100644 --- a/packages/metro-bundler/src/Resolver/__tests__/Resolver-test.js +++ b/packages/metro-bundler/src/Resolver/__tests__/Resolver-test.js @@ -161,8 +161,8 @@ describe('Resolver', function() { dependencyPairs.set(relativePath, dependencyModule.path); } - const {code: processedCode} = depResolver.wrapModule({ - module: module, + const processedCode = depResolver.wrapModule({ + path: module.path, getModuleId: resolutionResponse.getModuleId, dependencyPairs, name: 'test module', @@ -201,11 +201,11 @@ describe('Resolver', function() { mainModuleId: 'test module', }); - const {code: processedCode} = depResolver.wrapModule({ + const processedCode = depResolver.wrapModule({ getModuleId: resolutionResponse.getModuleId, dependencyPairs: resolutionResponse.getResolvedDependencyPairs(module), code, - module, + path: module.path, name: 'test module', dev: true, }); @@ -220,90 +220,6 @@ describe('Resolver', function() { ); }); - it('should pass through passed-in source maps', () => { - expect.assertions(1); - const module = createModule('test module'); - const resolutionResponse = new ResolutionResponseMock({ - dependencies: [module], - mainModuleId: 'test module', - }); - const inputMap = {version: 3, mappings: 'ARBITRARY'}; - - const {map} = depResolver.wrapModule({ - getModuleId: resolutionResponse.getModuleId, - dependencyPairs: resolutionResponse.getResolvedDependencyPairs(module), - module, - name: 'test module', - code: 'arbitrary(code)', - map: inputMap, - }); - expect(map).toBe(inputMap); - }); - - it('should resolve polyfills', async function() { - expect.assertions(1); - return Resolver.load({ - projectRoot: '/root', - }).then(depResolver => { - const polyfill = createPolyfill('test polyfill', []); - const code = ['global.fetch = () => 1;'].join(''); - - const {code: processedCode} = depResolver.wrapModule({ - module: polyfill, - code, - }); - - expect(processedCode).toEqual( - [ - '(function(global) {', - 'global.fetch = () => 1;', - '\n})' + - "(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);", - ].join(''), - ); - }); - }); - - describe('JSON files:', () => { - const code = JSON.stringify({arbitrary: 'data'}); - const id = 'arbitrary.json'; - let depResolver, module, resolutionResponse; - - beforeEach(() => { - return Resolver.load({projectRoot: '/root'}).then(r => { - depResolver = r; - module = createJsonModule(id); - resolutionResponse = new ResolutionResponseMock({ - dependencies: [module], - mainModuleId: id, - }); - }); - }); - - it('should prefix JSON files with `module.exports=`', () => { - expect.assertions(1); - const {code: processedCode} = depResolver.wrapModule({ - getModuleId: resolutionResponse.getModuleId, - dependencyPairs: resolutionResponse.getResolvedDependencyPairs( - module, - ), - module, - name: id, - code, - dev: false, - }); - - expect(processedCode).toEqual( - [ - `__d(/* ${id} */function(global, require, module, exports) {`, - `module.exports = ${code}\n}, ${resolutionResponse.getModuleId( - module.path, - )});`, - ].join(''), - ); - }); - }); - describe('minification:', () => { const code = 'arbitrary(code)'; const id = 'arbitrary.js'; diff --git a/packages/metro-bundler/src/Resolver/index.js b/packages/metro-bundler/src/Resolver/index.js index 9d207fca..86fecfd9 100644 --- a/packages/metro-bundler/src/Resolver/index.js +++ b/packages/metro-bundler/src/Resolver/index.js @@ -120,7 +120,6 @@ class Resolver { } resolveRequires( - module: Module, getModuleId: (path: string) => number, code: string, dependencyPairs: Map, @@ -154,44 +153,30 @@ class Resolver { } wrapModule({ - module, + path, getModuleId, dependencyPairs, dependencyOffsets, name, - map, code, dev = true, }: { - module: Module, + path: string, getModuleId: (path: string) => number, dependencyPairs: Map, dependencyOffsets: Array, name: string, - map: CompactRawMappings, code: string, dev?: boolean, - }): {code: string, map: CompactRawMappings} { - if (module.isJSON()) { - code = `module.exports = ${code}`; - } + }): string { + code = this.resolveRequires( + getModuleId, + code, + dependencyPairs, + dependencyOffsets, + ); - if (module.isPolyfill()) { - code = definePolyfillCode(code); - } else { - const moduleId = getModuleId(module.path); - - code = this.resolveRequires( - module, - getModuleId, - code, - dependencyPairs, - dependencyOffsets, - ); - code = defineModuleCode(moduleId, code, name, dev); - } - - return {code, map}; + return defineModuleCode(getModuleId(path), code, name, dev); } async minifyModule( diff --git a/packages/metro-bundler/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap b/packages/metro-bundler/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap index 9eadbbb5..22a156b7 100644 --- a/packages/metro-bundler/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap +++ b/packages/metro-bundler/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap @@ -1,325 +1,337 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`basic_bundle bundles package with polyfills 1`] = ` -"(function(global) { +"(function (global) { + global.__DEV__ = false; + global.__BUNDLE_START_TIME__ = global.nativePerformanceNow ? global.nativePerformanceNow() : Date.now(); +})(this); +(function (global) { + 'use strict'; -global.__DEV__ = false; + global.require = _require; + global.__d = define; + var modules = Object.create(null); -global.__BUNDLE_START_TIME__ = global.nativePerformanceNow ? global.nativePerformanceNow() : Date.now(); -})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this); -(function(global) { + function define(factory, moduleId, dependencyMap) { + if (moduleId in modules) { + return; + } -'use strict'; - -global.require = _require; -global.__d = define; - -var modules = Object.create(null); - - -function define(factory, moduleId, dependencyMap) { - if (moduleId in modules) { - return; + modules[moduleId] = { + dependencyMap: dependencyMap, + exports: undefined, + factory: factory, + hasError: false, + isInitialized: false + }; } - modules[moduleId] = { - dependencyMap: dependencyMap, - exports: undefined, - factory: factory, - hasError: false, - isInitialized: false - }; -} -function _require(moduleId) { - var moduleIdReallyIsNumber = moduleId; - var module = modules[moduleIdReallyIsNumber]; - return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module); -} + function _require(moduleId) { + var moduleIdReallyIsNumber = moduleId; + var module = modules[moduleIdReallyIsNumber]; + return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module); + } + + var inGuard = false; + + function guardedLoadModule(moduleId, module) { + if (!inGuard && global.ErrorUtils) { + inGuard = true; + var returnValue = void 0; + + try { + returnValue = loadModuleImplementation(moduleId, module); + } catch (e) { + global.ErrorUtils.reportFatalError(e); + } + + inGuard = false; + return returnValue; + } else { + return loadModuleImplementation(moduleId, module); + } + } + + var ID_MASK_SHIFT = 16; + var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT; + + function unpackModuleId(moduleId) { + var segmentId = moduleId >>> ID_MASK_SHIFT; + var localId = moduleId & LOCAL_ID_MASK; + return { + segmentId: segmentId, + localId: localId + }; + } + + _require.unpackModuleId = unpackModuleId; + + function loadModuleImplementation(moduleId, module) { + var nativeRequire = global.nativeRequire; + + if (!module && nativeRequire) { + var _unpackModuleId = unpackModuleId(moduleId), + segmentId = _unpackModuleId.segmentId, + localId = _unpackModuleId.localId; + + nativeRequire(localId, segmentId); + module = modules[moduleId]; + } + + if (!module) { + throw unknownModuleError(moduleId); + } + + if (module.hasError) { + throw moduleThrewError(moduleId, module.error); + } + + module.isInitialized = true; + var exports = module.exports = {}; + var _module = module, + factory = _module.factory, + dependencyMap = _module.dependencyMap; -var inGuard = false; -function guardedLoadModule(moduleId, module) { - if (!inGuard && global.ErrorUtils) { - inGuard = true; - var returnValue = void 0; try { - returnValue = loadModuleImplementation(moduleId, module); + var _moduleObject = { + exports: exports + }; + factory(global, _require, _moduleObject, exports, dependencyMap); + { + module.factory = undefined; + module.dependencyMap = undefined; + } + return module.exports = _moduleObject.exports; } catch (e) { - global.ErrorUtils.reportFatalError(e); + module.hasError = true; + module.error = e; + module.isInitialized = false; + module.exports = undefined; + throw e; } - inGuard = false; - return returnValue; - } else { - return loadModuleImplementation(moduleId, module); - } -} - -var ID_MASK_SHIFT = 16; -var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT; - -function unpackModuleId(moduleId) { - var segmentId = moduleId >>> ID_MASK_SHIFT; - var localId = moduleId & LOCAL_ID_MASK; - return { segmentId: segmentId, localId: localId }; -} -_require.unpackModuleId = unpackModuleId; - -function loadModuleImplementation(moduleId, module) { - var nativeRequire = global.nativeRequire; - if (!module && nativeRequire) { - var _unpackModuleId = unpackModuleId(moduleId), - segmentId = _unpackModuleId.segmentId, - localId = _unpackModuleId.localId; - - nativeRequire(localId, segmentId); - module = modules[moduleId]; } - if (!module) { - throw unknownModuleError(moduleId); + function unknownModuleError(id) { + var message = 'Requiring unknown module \\"' + id + '\\".'; + return Error(message); } - if (module.hasError) { - throw moduleThrewError(moduleId, module.error); + function moduleThrewError(id, error) { + var displayName = id; + return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error); } +})(this); +(function (global) { + 'use strict'; - module.isInitialized = true; - var exports = module.exports = {}; - var _module = module, - factory = _module.factory, - dependencyMap = _module.dependencyMap; - - try { - - var _moduleObject = { exports: exports }; - - factory(global, _require, _moduleObject, exports, dependencyMap); - - { - module.factory = undefined; - module.dependencyMap = undefined; - } - - return module.exports = _moduleObject.exports; - } catch (e) { - module.hasError = true; - module.error = e; - module.isInitialized = false; - module.exports = undefined; - throw e; + if (!Object.keys) { + Object.keys = function () {}; } -} +})(this); +(function (global) { + 'use strict'; -function unknownModuleError(id) { - var message = 'Requiring unknown module \\"' + id + '\\".'; + if (!String.prototype.repeat) { + String.prototype.repeat = function () {}; + } +})(this); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; - return Error(message); -} + var Bar = require(_dependencyMap[0]); -function moduleThrewError(id, error) { - var displayName = id; - return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error); -} -})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this); -(function(global) { + var Foo = require(_dependencyMap[1]); -'use strict'; + module.exports = { + Foo: Foo, + Bar: Bar + }; +},4,[5,6]); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; -if (!Object.keys) { - Object.keys = function () {}; -} -})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this); -(function(global) { + var Foo = require(_dependencyMap[0]); -'use strict'; + module.exports = { + type: 'bar', + foo: Foo.type + }; +},5,[6]); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; -if (!String.prototype.repeat) { - String.prototype.repeat = function () {}; -} -})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this); -__d(/* /TestBundle.js */function(global, require, module, exports) { + var asset = require(_dependencyMap[0]); -'use strict'; - -var Bar = require(5); // 5 = ./Bar -var Foo = require(6); // 6 = ./Foo - -module.exports = { Foo: Foo, Bar: Bar }; -}, 4); -__d(/* /Bar.js */function(global, require, module, exports) { - -'use strict'; - -var Foo = require(6); // 6 = ./Foo - -module.exports = { type: 'bar', foo: Foo.type }; -}, 5); -__d(/* /Foo.js */function(global, require, module, exports) { - -'use strict'; - -var asset = require(7); // 7 = ./test.png - -module.exports = { type: 'foo', asset: asset }; -}, 6); + module.exports = { + type: 'foo', + asset: asset + }; +},6,[7]); __d(/* /test.png */function(global, require, module, exports) {module.exports=require(8).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 8 = /AssetRegistry }, 7); -__d(/* /AssetRegistry.js */function(global, require, module, exports) { - -'use strict'; -}, 8); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; +},8,[]); require(4);" `; exports[`basic_bundle bundles package without polyfills 1`] = ` -"(function(global) { +"(function (global) { + global.__DEV__ = false; + global.__BUNDLE_START_TIME__ = global.nativePerformanceNow ? global.nativePerformanceNow() : Date.now(); +})(this); +(function (global) { + 'use strict'; -global.__DEV__ = false; + global.require = _require; + global.__d = define; + var modules = Object.create(null); -global.__BUNDLE_START_TIME__ = global.nativePerformanceNow ? global.nativePerformanceNow() : Date.now(); -})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this); -(function(global) { + function define(factory, moduleId, dependencyMap) { + if (moduleId in modules) { + return; + } -'use strict'; - -global.require = _require; -global.__d = define; - -var modules = Object.create(null); - - -function define(factory, moduleId, dependencyMap) { - if (moduleId in modules) { - return; + modules[moduleId] = { + dependencyMap: dependencyMap, + exports: undefined, + factory: factory, + hasError: false, + isInitialized: false + }; } - modules[moduleId] = { - dependencyMap: dependencyMap, - exports: undefined, - factory: factory, - hasError: false, - isInitialized: false - }; -} -function _require(moduleId) { - var moduleIdReallyIsNumber = moduleId; - var module = modules[moduleIdReallyIsNumber]; - return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module); -} + function _require(moduleId) { + var moduleIdReallyIsNumber = moduleId; + var module = modules[moduleIdReallyIsNumber]; + return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module); + } + + var inGuard = false; + + function guardedLoadModule(moduleId, module) { + if (!inGuard && global.ErrorUtils) { + inGuard = true; + var returnValue = void 0; + + try { + returnValue = loadModuleImplementation(moduleId, module); + } catch (e) { + global.ErrorUtils.reportFatalError(e); + } + + inGuard = false; + return returnValue; + } else { + return loadModuleImplementation(moduleId, module); + } + } + + var ID_MASK_SHIFT = 16; + var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT; + + function unpackModuleId(moduleId) { + var segmentId = moduleId >>> ID_MASK_SHIFT; + var localId = moduleId & LOCAL_ID_MASK; + return { + segmentId: segmentId, + localId: localId + }; + } + + _require.unpackModuleId = unpackModuleId; + + function loadModuleImplementation(moduleId, module) { + var nativeRequire = global.nativeRequire; + + if (!module && nativeRequire) { + var _unpackModuleId = unpackModuleId(moduleId), + segmentId = _unpackModuleId.segmentId, + localId = _unpackModuleId.localId; + + nativeRequire(localId, segmentId); + module = modules[moduleId]; + } + + if (!module) { + throw unknownModuleError(moduleId); + } + + if (module.hasError) { + throw moduleThrewError(moduleId, module.error); + } + + module.isInitialized = true; + var exports = module.exports = {}; + var _module = module, + factory = _module.factory, + dependencyMap = _module.dependencyMap; -var inGuard = false; -function guardedLoadModule(moduleId, module) { - if (!inGuard && global.ErrorUtils) { - inGuard = true; - var returnValue = void 0; try { - returnValue = loadModuleImplementation(moduleId, module); + var _moduleObject = { + exports: exports + }; + factory(global, _require, _moduleObject, exports, dependencyMap); + { + module.factory = undefined; + module.dependencyMap = undefined; + } + return module.exports = _moduleObject.exports; } catch (e) { - global.ErrorUtils.reportFatalError(e); + module.hasError = true; + module.error = e; + module.isInitialized = false; + module.exports = undefined; + throw e; } - inGuard = false; - return returnValue; - } else { - return loadModuleImplementation(moduleId, module); - } -} - -var ID_MASK_SHIFT = 16; -var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT; - -function unpackModuleId(moduleId) { - var segmentId = moduleId >>> ID_MASK_SHIFT; - var localId = moduleId & LOCAL_ID_MASK; - return { segmentId: segmentId, localId: localId }; -} -_require.unpackModuleId = unpackModuleId; - -function loadModuleImplementation(moduleId, module) { - var nativeRequire = global.nativeRequire; - if (!module && nativeRequire) { - var _unpackModuleId = unpackModuleId(moduleId), - segmentId = _unpackModuleId.segmentId, - localId = _unpackModuleId.localId; - - nativeRequire(localId, segmentId); - module = modules[moduleId]; } - if (!module) { - throw unknownModuleError(moduleId); + function unknownModuleError(id) { + var message = 'Requiring unknown module \\"' + id + '\\".'; + return Error(message); } - if (module.hasError) { - throw moduleThrewError(moduleId, module.error); + function moduleThrewError(id, error) { + var displayName = id; + return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error); } +})(this); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; - module.isInitialized = true; - var exports = module.exports = {}; - var _module = module, - factory = _module.factory, - dependencyMap = _module.dependencyMap; + var Bar = require(_dependencyMap[0]); - try { + var Foo = require(_dependencyMap[1]); - var _moduleObject = { exports: exports }; + module.exports = { + Foo: Foo, + Bar: Bar + }; +},2,[3,4]); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; - factory(global, _require, _moduleObject, exports, dependencyMap); + var Foo = require(_dependencyMap[0]); - { - module.factory = undefined; - module.dependencyMap = undefined; - } + module.exports = { + type: 'bar', + foo: Foo.type + }; +},3,[4]); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; - return module.exports = _moduleObject.exports; - } catch (e) { - module.hasError = true; - module.error = e; - module.isInitialized = false; - module.exports = undefined; - throw e; - } -} + var asset = require(_dependencyMap[0]); -function unknownModuleError(id) { - var message = 'Requiring unknown module \\"' + id + '\\".'; - - return Error(message); -} - -function moduleThrewError(id, error) { - var displayName = id; - return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error); -} -})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this); -__d(/* /TestBundle.js */function(global, require, module, exports) { - -'use strict'; - -var Bar = require(3); // 3 = ./Bar -var Foo = require(4); // 4 = ./Foo - -module.exports = { Foo: Foo, Bar: Bar }; -}, 2); -__d(/* /Bar.js */function(global, require, module, exports) { - -'use strict'; - -var Foo = require(4); // 4 = ./Foo - -module.exports = { type: 'bar', foo: Foo.type }; -}, 3); -__d(/* /Foo.js */function(global, require, module, exports) { - -'use strict'; - -var asset = require(5); // 5 = ./test.png - -module.exports = { type: 'foo', asset: asset }; -}, 4); + module.exports = { + type: 'foo', + asset: asset + }; +},4,[5]); __d(/* /test.png */function(global, require, module, exports) {module.exports=require(6).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 6 = /AssetRegistry }, 5); -__d(/* /AssetRegistry.js */function(global, require, module, exports) { - -'use strict'; -}, 6); +__d(function (global, require, module, exports, _dependencyMap) { + 'use strict'; +},6,[]); require(2);" `; diff --git a/packages/metro-bundler/src/lib/GlobalTransformCache.js b/packages/metro-bundler/src/lib/GlobalTransformCache.js index 614282cd..63d76de3 100644 --- a/packages/metro-bundler/src/lib/GlobalTransformCache.js +++ b/packages/metro-bundler/src/lib/GlobalTransformCache.js @@ -212,9 +212,7 @@ function validateCachedResult(cachedResult: mixed): ?CachedResult { typeof cachedResult === 'object' && typeof cachedResult.code === 'string' && Array.isArray(cachedResult.dependencies) && - cachedResult.dependencies.every(dep => typeof dep === 'string') && - Array.isArray(cachedResult.dependencyOffsets) && - cachedResult.dependencyOffsets.every(offset => typeof offset === 'number') + cachedResult.dependencies.every(dep => typeof dep === 'string') ) { return (cachedResult: any); } diff --git a/packages/metro-bundler/src/lib/TransformCaching.js b/packages/metro-bundler/src/lib/TransformCaching.js index bddf743f..ce5f944d 100644 --- a/packages/metro-bundler/src/lib/TransformCaching.js +++ b/packages/metro-bundler/src/lib/TransformCaching.js @@ -33,8 +33,7 @@ const CACHE_SUB_DIR = 'cache'; export type CachedResult = { code: string, - dependencies: Array, - dependencyOffsets: Array, + dependencies: $ReadOnlyArray, map: CompactRawMappings, }; @@ -143,7 +142,6 @@ class FileBasedCache { .digest('hex'), hashSourceCode(props), result.dependencies, - result.dependencyOffsets, result.map, ]), ); @@ -214,7 +212,6 @@ class FileBasedCache { result: { code: transformedCode, dependencies: metadata.dependencies, - dependencyOffsets: metadata.dependencyOffsets, map: metadata.sourceMap, }, outdatedDependencies: EMPTY_ARRAY, @@ -335,7 +332,6 @@ function readMetadataFileSync( cachedResultHash: string, cachedSourceHash: string, dependencies: Array, - dependencyOffsets: Array, sourceMap: CompactRawMappings, } { const metadataStr = fs.readFileSync(metadataFilePath, 'utf8'); @@ -347,7 +343,6 @@ function readMetadataFileSync( cachedResultHash, cachedSourceHash, dependencies, - dependencyOffsets, sourceMap, ] = metadata; if ( @@ -357,10 +352,6 @@ function readMetadataFileSync( Array.isArray(dependencies) && dependencies.every(dep => typeof dep === 'string') ) || - !( - Array.isArray(dependencyOffsets) && - dependencyOffsets.every(offset => typeof offset === 'number') - ) || !(sourceMap == null || typeof sourceMap === 'object') ) { return null; @@ -369,7 +360,6 @@ function readMetadataFileSync( cachedResultHash, cachedSourceHash, dependencies, - dependencyOffsets, sourceMap, }; } diff --git a/packages/metro-bundler/src/lib/__tests__/TransformCaching-test.js b/packages/metro-bundler/src/lib/__tests__/TransformCaching-test.js index da4bc655..27857540 100644 --- a/packages/metro-bundler/src/lib/__tests__/TransformCaching-test.js +++ b/packages/metro-bundler/src/lib/__tests__/TransformCaching-test.js @@ -71,7 +71,6 @@ describe('TransformCaching.FileBasedCache', () => { result: { code: `/* result for ${key} */`, dependencies: ['foo', `dep of ${key}`], - dependencyOffsets: [12, 34], map: {desc: `source map for ${key}`}, }, }; @@ -104,7 +103,6 @@ describe('TransformCaching.FileBasedCache', () => { result: { code: `/* result for ${key} */`, dependencies: ['foo', 'bar'], - dependencyOffsets: [12, 34], map: {desc: `source map for ${key}`}, }, }; diff --git a/packages/metro-bundler/src/node-haste/Module.js b/packages/metro-bundler/src/node-haste/Module.js index 22204b1f..ce85a706 100644 --- a/packages/metro-bundler/src/node-haste/Module.js +++ b/packages/metro-bundler/src/node-haste/Module.js @@ -39,8 +39,7 @@ import type {LocalPath} from './lib/toLocalPath'; export type ReadResult = { +code: string, - +dependencies: Array, - +dependencyOffsets?: ?Array, + +dependencies: $ReadOnlyArray, +map: CompactRawMappings, +source: string, };