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 fd4925d5b..78c058204 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 817b16e3f..f73461493 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 000000000..7289b0f87 --- /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 23dc5f02f..44ad8a925 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 6ea171331..7714d383d 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 e8d181a27..3f5f33aee 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 d03fdb89a..fc7be66d0 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 da8653f84..f8c252a3f 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',