From 81fb985335a8ee3657905177abe32380b6b5fdc0 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 3 Feb 2016 17:30:01 -0800 Subject: [PATCH] Support non-image assets in packager Summary: public The packager currently assumes that all assets that are not JSON or JS files must be images. Although it is possible to add other extension types, they crash the packager if you try to require them, because it attempts to get their dimensions, assuming that they are an image. This is a crude workaround for that problem, which skips the image-specific processing for non-image assets, but really it would be better if the packager was properly aware of different asset types and treated them differently (e.g. for sounds it could include the duration, for HTML pages it could parse and include linked CSS files, etc). I've also added an example of using `require('...')` to load a packager-managed HTML page in the UIExplorer WebView example. In future I anticipate that all static asset types (sounds, fonts, etc.) could be handled in this way, which allows them to be edited or added/removed on the fly instead of needing to restart the app. Reviewed By: martinbigio Differential Revision: D2895619 fb-gh-sync-id: cd93794ca66bad838621cd7df3ff3c62b5645e85 --- .flowconfig | 2 +- Examples/UIExplorer/WebViewExample.js | 60 ++++++++++++++++++- Examples/UIExplorer/helloworld.html | 25 ++++++++ .../Components/WebView/WebView.android.js | 7 ++- Libraries/Components/WebView/WebView.ios.js | 8 ++- local-cli/server/runServer.js | 7 ++- packager/react-packager/src/Bundler/index.js | 12 +++- packager/react-packager/src/Server/index.js | 7 ++- 8 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 Examples/UIExplorer/helloworld.html diff --git a/.flowconfig b/.flowconfig index fd4925d5b1b892..78c0582045e172 100644 --- a/.flowconfig +++ b/.flowconfig @@ -51,7 +51,7 @@ module.system=haste munge_underscores=true module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' -module.name_mapper='^[./a-zA-Z0-9$_-]+\.png$' -> 'RelativeImageStub' +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub' suppress_type=$FlowIssue suppress_type=$FlowFixMe diff --git a/Examples/UIExplorer/WebViewExample.js b/Examples/UIExplorer/WebViewExample.js index 817b16e3f2c44d..f7346149395cd8 100644 --- a/Examples/UIExplorer/WebViewExample.js +++ b/Examples/UIExplorer/WebViewExample.js @@ -50,7 +50,7 @@ var WebViewExample = React.createClass({ handleTextInputChange: function(event) { var url = event.nativeEvent.text; - if (!/^[a-zA-Z-_]:/.test(url)) { + if (!/^[a-zA-Z-_]+:/.test(url)) { url = 'http://' + url; } this.inputText = url; @@ -231,6 +231,34 @@ var styles = StyleSheet.create({ }, }); +const HTML = ` +\n + + + Hello Static World + + + + + +

Hello Static World

+ + +`; + exports.displayName = (undefined: ?string); exports.title = ''; exports.description = 'Base component to display web content'; @@ -239,6 +267,36 @@ exports.examples = [ title: 'Simple Browser', render(): ReactElement { return ; } }, + { + title: 'Bundled HTML', + render(): ReactElement { + return ( + + ); + } + }, + { + title: 'Static HTML', + render(): ReactElement { + return ( + + ); + } + }, { title: 'POST Test', render(): ReactElement { diff --git a/Examples/UIExplorer/helloworld.html b/Examples/UIExplorer/helloworld.html new file mode 100644 index 00000000000000..7289b0f87944cf --- /dev/null +++ b/Examples/UIExplorer/helloworld.html @@ -0,0 +1,25 @@ + + + + Hello Bundled World + + + + + +

Hello Bundled World

+ + diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js index 23dc5f02fb43b7..44ad8a9257216d 100644 --- a/Libraries/Components/WebView/WebView.android.js +++ b/Libraries/Components/WebView/WebView.android.js @@ -21,6 +21,7 @@ var deprecatedPropType = require('deprecatedPropType'); var keyMirror = require('keyMirror'); var merge = require('merge'); var requireNativeComponent = require('requireNativeComponent'); +var resolveAssetSource = require('resolveAssetSource'); var PropTypes = React.PropTypes; @@ -98,6 +99,10 @@ var WebView = React.createClass({ */ baseUrl: PropTypes.string, }), + /* + * Used internally by packager. + */ + PropTypes.number, ]), /** @@ -193,7 +198,7 @@ var WebView = React.createClass({ ref={RCT_WEBVIEW_REF} key="webViewKey" style={webViewStyles} - source={source} + source={resolveAssetSource(source)} injectedJavaScript={this.props.injectedJavaScript} userAgent={this.props.userAgent} javaScriptEnabled={javaScriptEnabled} diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 6ea171331b9a81..7714d383df8718 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -25,6 +25,7 @@ var invariant = require('invariant'); var keyMirror = require('keyMirror'); var processDecelerationRate = require('processDecelerationRate'); var requireNativeComponent = require('requireNativeComponent'); +var resolveAssetSource = require('resolveAssetSource'); var PropTypes = React.PropTypes; var RCTWebViewManager = require('NativeModules').WebViewManager; @@ -138,6 +139,10 @@ var WebView = React.createClass({ */ baseUrl: PropTypes.string, }), + /* + * Used internally by packager. + */ + PropTypes.number, ]), /** @@ -304,7 +309,7 @@ var WebView = React.createClass({ ref={RCT_WEBVIEW_REF} key="webViewKey" style={webViewStyles} - source={source} + source={resolveAssetSource(source)} injectedJavaScript={this.props.injectedJavaScript} bounces={this.props.bounces} scrollEnabled={this.props.scrollEnabled} @@ -432,6 +437,7 @@ var styles = StyleSheet.create({ flex: 1, justifyContent: 'center', alignItems: 'center', + height: 100, }, webView: { backgroundColor: '#ffffff', diff --git a/local-cli/server/runServer.js b/local-cli/server/runServer.js index e8d181a278e7c5..3f5f33aeeddf17 100644 --- a/local-cli/server/runServer.js +++ b/local-cli/server/runServer.js @@ -75,7 +75,12 @@ function getPackagerServer(args, config) { getTransformOptionsModulePath: config.getTransformOptionsModulePath, transformModulePath: transformerPath, assetRoots: args.assetRoots, - assetExts: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'], + assetExts: [ + 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'svg', 'webp', // Image formats + 'm4v', 'mov', 'mp4', 'mpeg', 'mpg', 'webm', // Video formats + 'aac', 'aiff', 'caf', 'm4a', 'mp3', 'wav', // Audio formats + 'html', // Document formats + ], resetCache: args.resetCache || args['reset-cache'], verbose: args.verbose, }); diff --git a/packager/react-packager/src/Bundler/index.js b/packager/react-packager/src/Bundler/index.js index d03fdb89a28e62..fc7be66d035723 100644 --- a/packager/react-packager/src/Bundler/index.js +++ b/packager/react-packager/src/Bundler/index.js @@ -554,8 +554,14 @@ class Bundler { assetUrlPath = assetUrlPath.replace(/\\/g, '/'); } + // Test extension against all types supported by image-size module. + // If it's not one of these, we won't treat it as an image. + let isImage = [ + 'png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff' + ].indexOf(path.extname(module.path).slice(1)) !== -1; + return Promise.all([ - sizeOf(module.path), + isImage ? sizeOf(module.path) : null, this._assetServer.getAssetData(relPath, platform), ]).then(function(res) { const dimensions = res[0]; @@ -564,8 +570,8 @@ class Bundler { __packager_asset: true, fileSystemLocation: path.dirname(module.path), httpServerLocation: assetUrlPath, - width: dimensions.width / module.resolution, - height: dimensions.height / module.resolution, + width: dimensions ? dimensions.width / module.resolution : undefined, + height: dimensions ? dimensions.height / module.resolution : undefined, scales: assetData.scales, files: assetData.files, hash: assetData.hash, diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index da8653f845b383..f8c252a3fd2313 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -58,7 +58,12 @@ const validateOpts = declareOpts({ }, assetExts: { type: 'array', - default: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'], + default: [ + 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'svg', 'webp', // Image formats + 'm4v', 'mov', 'mp4', 'mpeg', 'mpg', 'webm', // Video formats + 'aac', 'aiff', 'caf', 'm4a', 'mp3', 'wav', // Audio formats + 'html', // Document formats + ], }, transformTimeoutInterval: { type: 'number',