├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── RELEASING.md ├── SECURITY.md ├── SPECIFICATION.md ├── codecov.yml ├── examples ├── reactnative │ ├── .babelrc │ ├── .buckconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .watchmanconfig │ ├── App.js │ ├── README.md │ ├── __tests__ │ │ └── App.js │ ├── android │ │ ├── app │ │ │ ├── BUCK │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.pro │ │ │ └── src │ │ │ │ └── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── reactnative │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── keystores │ │ │ ├── BUCK │ │ │ └── debug.keystore.properties │ │ └── settings.gradle │ ├── app.json │ ├── index.js │ ├── ios │ │ ├── reactnative-tvOS │ │ │ └── Info.plist │ │ ├── reactnative-tvOSTests │ │ │ └── Info.plist │ │ ├── reactnative.xcodeproj │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ ├── reactnative-tvOS.xcscheme │ │ │ │ └── reactnative.xcscheme │ │ ├── reactnative │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── Base.lproj │ │ │ │ └── LaunchScreen.xib │ │ │ ├── Images.xcassets │ │ │ │ ├── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Info.plist │ │ │ └── main.m │ │ └── reactnativeTests │ │ │ ├── Info.plist │ │ │ └── reactnativeTests.m │ └── package.json ├── web │ ├── README.md │ ├── babel.js │ ├── index.html │ ├── index.js │ ├── package.json │ ├── server.js │ └── webpack.config.js ├── webpack.config.js └── whrs.toml ├── greenkeeper.json ├── package.json └── packages ├── bundle ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json ├── test │ ├── index.test.js │ ├── snapshots.test.js │ └── walker.test.js └── walker │ └── index.js ├── dimensions ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json ├── render.js └── test.js ├── list ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test │ └── index.test.js ├── parser ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── asset.js ├── index.js ├── package.json ├── packer.js ├── test │ ├── asset.test.js │ ├── index.test.js │ ├── packer.test.js │ └── transform.test.js └── transform.js ├── provider ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── asset │ └── index.js ├── context │ └── index.js ├── fallback │ └── index.js ├── index.js ├── package.json ├── provider │ ├── index.js │ └── remote.js ├── test │ ├── asset.test.js │ ├── fixtures │ │ └── index.js │ ├── index.test.js │ ├── nock.js │ ├── provider.test.js │ └── remote.test.js └── wrapper │ └── index.js ├── test ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── deeper │ └── homer.svg ├── entry.js ├── godaddy.json ├── godaddy.svg ├── homer-classnames.svg ├── homer-no-viewbox.json ├── homer-no-viewbox.svg ├── homer.json ├── homer.svg ├── package.json ├── src │ └── index.js ├── tiger.json └── tiger.svg └── webpack ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── loader.js ├── package.json └── test └── index.test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json binary 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | lib 85 | package-lock.json 86 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "10" 5 | - "12" 6 | sudo: required 7 | before_script: 8 | - sudo chown root /opt/google/chrome/chrome-sandbox 9 | - sudo chmod 4755 /opt/google/chrome/chrome-sandbox 10 | - export DISPLAY=:99.0 11 | - sh -e /etc/init.d/xvfb start 12 | addons: 13 | chrome: stable 14 | after_success: 15 | - bash <(curl -s https://codecov.io/bash) 16 | matrix: 17 | fast_finish: true 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | Some of the packages are tightly coupled and need to be released in a specific 4 | order. This file documents the order and release process of all the packages 5 | that are included in the Asset-System project. 6 | 7 | Releases are done using the `mono-repos` module: 8 | 9 | ``` 10 | npm install -g mono-repos 11 | ``` 12 | 13 | ## test 14 | 15 | This is the bundle that includes all test fixtures that are shared between 16 | the various of packages. This is probably the first package that you want to 17 | bump to latest and ensure that every sub-package receives the latest and 18 | greatest version: 19 | 20 | ``` 21 | mono --publish test --version x.x.x 22 | ``` 23 | 24 | Once released, bump version in _every_ package. 25 | 26 | ## parser 27 | 28 | If there are `asset-parser` changes, update the version in the `CHANGELOG.md` 29 | and release a new version: 30 | 31 | ``` 32 | mono --publish parser --version x.x.x 33 | ``` 34 | 35 | - Bump released version in `asset-provider` 36 | - Bump released version in `asset-bundle` 37 | 38 | ## provider 39 | 40 | If there are `asset-provider` changes, update the version in the `CHANGELOG.md` 41 | and release a new version: 42 | 43 | ``` 44 | mono --publish provider --version x.x.x 45 | ``` 46 | 47 | ## dimensions 48 | 49 | If there are `asset-dimensions` changes, update the version in the `CHANGELOG.md` 50 | and release a new version: 51 | 52 | ``` 53 | mono --publish dimensions --version x.x.x 54 | ``` 55 | 56 | - Bump released version in `asset-bundle` 57 | 58 | ## bundle 59 | 60 | If there are `asset-bundle` changes, update the version in the `CHANGELOG.md` 61 | and release a new version: 62 | 63 | ``` 64 | mono --publish bundle --version x.x.x 65 | ``` 66 | 67 | - Bump released version in `asset-webpack` 68 | 69 | ## webpack 70 | 71 | If there are `asset-webpack` changes, update the version in the `CHANGELOG.md` 72 | and release a new version: 73 | 74 | ``` 75 | mono --publish webpack --version x.x.x 76 | ``` 77 | 78 | ## list 79 | 80 | If there are `asset-list` changes, update the version in the `CHANGELOG.md` 81 | and release a new version: 82 | 83 | ``` 84 | mono --publish webpack --list x.x.x 85 | ``` 86 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | We take security very seriously at GoDaddy. We appreciate your efforts to 4 | responsibly disclose your findings, and will make every effort to acknowledge 5 | your contributions. 6 | 7 | ## Where should I report security issues? 8 | 9 | In order to give the community time to respond and upgrade, we strongly urge you 10 | report all security issues privately. 11 | 12 | To report a security issue in one of our Open Source projects email us directly 13 | at **oss@godaddy.com** and include the word "SECURITY" in the subject line. 14 | 15 | This mail is delivered to our Open Source Security team. 16 | 17 | After the initial reply to your report, the team will keep you informed of the 18 | progress being made towards a fix and announcement, and may ask for additional 19 | information or guidance. 20 | -------------------------------------------------------------------------------- /SPECIFICATION.md: -------------------------------------------------------------------------------- 1 | # Specification 2 | 3 | **Current spec version: 0.1.0** 4 | 5 | This documentation contains instructions how to transform SVG asset(s) in to 6 | readable format for this module. There are various of transformation steps 7 | that need to happen before an SVG can be consumed by this library. These exist 8 | for the following reason: 9 | 10 | - Reduce the data format so less bytes have to be send over the wire. 11 | - The client side code should be as small and dumb as possible. 12 | - Pre-transform SVG in easy format so less CPU cycles have to spend in the browser. 13 | 14 | ## Table of Contents 15 | 16 | - [Specification](#specification) 17 | - [Structure](#structure) 18 | - [Bundle](#bundle) 19 | - [Serialization](#serialization) 20 | 21 | ### Structure 22 | 23 | A SVG file needs to be transformed in a correct bundle file, in order for that 24 | you need to transform the SVG elements in to JavaScript compatible syntax. This 25 | should be done before uploading the asset so the computation for this happens 26 | during the build process on your own machine, instead of on the client devices 27 | that load these assets. If we imagine having a SVG with the following contents: 28 | 29 | ```html 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ``` 39 | 40 | We want to end up with the following "optimized" structure. 41 | 42 | ```json 43 | [ 44 | [ 45 | "Svg", 46 | { "viewBox": "0 0 300 600" }, 47 | [ 48 | [ 49 | "G", 50 | [ 51 | ["Path", { "fill": "#000", "d":"13.508 n0180-149 ..."}], 52 | ["Path", { "fill": "#FFF", "strokeOpacity":"0.1", "d":"13.508 n0180-149 ..."}] 53 | ] 54 | ] 55 | ] 56 | ] 57 | ] 58 | ``` 59 | 60 | You might notice that the following transformations has occurred: 61 | 62 | - The `svg` element has all it's properties removed except for `viewBox`. If no 63 | `viewBox` property is present, it should be calculated and added. 64 | - Tag names are transformed to be uppercase first, so they match `svgs` 65 | component names. 66 | - Attributes are transformed to be camelCase and then combined in to an objects 67 | which should be compatible with `react-native-svg` attributes. 68 | - Colors are optimized to small HEX codes if possible. 69 | - Child components are wrapped in an array []. 70 | 71 | Each element is transformed in to an array where the first item of the Array 72 | is the name of the tagName of the element which is transformed to be uppercase 73 | first to match the components from the `svgs` library. The second item of the 74 | array will either be: 75 | 76 | - An object with attributes that should be applied to the tag. 77 | - An array, that contains child elements for the given tag. 78 | - A string, which will be content for the element in the case of `Text` tags. 79 | - Nothing, no child elements or attributes are needed. 80 | 81 | See the `test/fixtures/index.js` file for more examples of the bundle structure 82 | and their resulting output. 83 | 84 | ### Bundle 85 | 86 | Creating a bundle of SVG assets is a straight forward process. The 87 | [structure](#structure) for assets is already known. The only thing it needs is 88 | an identifier for each asset. This should be a unique id as it will be used as 89 | `key` in the overall JSON structure and structure will be the value. A good 90 | practise is to use the filename of the asset key. So if you have a file with the 91 | name `example.svg` and `another.svg` you should end up with following bundle 92 | structure. 93 | 94 | ```json 95 | { 96 | "example": [ 97 | ["Text", "Generated structure"] 98 | ], 99 | "another": [ 100 | ["G", ["Text", { "stroke": 1 }, "Another"]] 101 | ] 102 | } 103 | ``` 104 | 105 | ### Serialization 106 | 107 | The current implementation assumes that JSON is used serialization format. Run 108 | the resulting bundle through your JSON encoder and upload it to the cloud. 109 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | parsers: 3 | javascript: 4 | enable_partials: yes 5 | status: 6 | project: 7 | default: 8 | target: "80%" 9 | patch: 10 | enabled: false 11 | -------------------------------------------------------------------------------- /examples/reactnative/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/reactnative/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /examples/reactnative/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /examples/reactnative/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | -------------------------------------------------------------------------------- /examples/reactnative/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/reactnative/App.js: -------------------------------------------------------------------------------- 1 | import Provider, { Asset } from 'asset-provider'; 2 | import React, { Component } from 'react'; 3 | import { 4 | Platform, 5 | StyleSheet, 6 | Text, 7 | View 8 | } from 'react-native'; 9 | 10 | const instructions = Platform.select({ 11 | ios: 'Press Cmd+R to reload,\n' + 12 | 'Cmd+D or shake for dev menu', 13 | android: 'Double tap R on your keyboard to reload,\n' + 14 | 'Shake or press menu button for dev menu', 15 | }); 16 | 17 | export default class App extends Component<{}> { 18 | render() { 19 | return ( 20 | 21 | 22 | {instructions} 23 | 24 | 25 | 26 | 27 | 28 | Loading Assets 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | } 40 | 41 | const styles = StyleSheet.create({ 42 | container: { 43 | flex: 1, 44 | justifyContent: 'center', 45 | alignItems: 'center', 46 | backgroundColor: '#F5FCFF', 47 | }, 48 | loading: { 49 | fontSize: 20, 50 | textAlign: 'center', 51 | margin: 10, 52 | }, 53 | instructions: { 54 | textAlign: 'center', 55 | color: '#333333', 56 | marginBottom: 5, 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /examples/reactnative/README.md: -------------------------------------------------------------------------------- 1 | # react-native 2 | 3 | A small demo that implements the `asset-provider` in a React-Native 4 | application. 5 | 6 | ## Starting the demo 7 | 8 | Make sure that all dependencies are installed (we do assume that you already 9 | have your machine setup for React-Native development): 10 | 11 | ``` 12 | npm install . 13 | ``` 14 | 15 | In addition to the dependencies of this repository you also need to install 16 | the dependencies of the `web` example as we will use that web server to 17 | serve the svg bundle to the React-Native application. 18 | 19 | ``` 20 | cd .. && cd web && npm install . 21 | ``` 22 | 23 | Once all dependencies are installed we need to start the web server of the `web` 24 | example. So navigate back to `web` folder and run: 25 | 26 | ``` 27 | npm start 28 | ``` 29 | 30 | Once the application is started we can boot up our React-Native application: 31 | 32 | ``` 33 | react-native run-ios 34 | react-native run-android 35 | ``` 36 | 37 | ## How is the demo setup 38 | 39 | ### App.js 40 | 41 | This is the entry point of the application and where we've implemented the 42 | `asset-provider`. So how is it build up? 43 | 44 | - The first and basically only notable change here is that we needed to install 45 | `react-native-svg` as a dependency of the project so we can render SVG 46 | on the devices. The rest is the same as on the web. 47 | - The `` and `` components are imported from the 48 | `asset-provider` module. 49 | - We create a `` component that downloads the `homer`, `godaddy` 50 | and `tiger` svg from the generated bundle. 51 | - We add ``'s as child components of the `` and reference the 52 | assets using the `name` property of the ``. 53 | - One of the ``s has a `` component as child node. This will 54 | be visible while we are download the asset from our server and will be 55 | replaced with the actual asset once the bundle is downloaded. 56 | 57 | ### Server 58 | 59 | The server aspect of the example is documented at the `web` example. 60 | -------------------------------------------------------------------------------- /examples/reactnative/__tests__/App.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import App from '../App'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.reactnative", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.reactnative", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion 23 98 | buildToolsVersion "23.0.1" 99 | 100 | defaultConfig { 101 | applicationId "com.reactnative" 102 | minSdkVersion 16 103 | targetSdkVersion 22 104 | versionCode 1 105 | versionName "1.0" 106 | ndk { 107 | abiFilters "armeabi-v7a", "x86" 108 | } 109 | } 110 | splits { 111 | abi { 112 | reset() 113 | enable enableSeparateBuildPerCPUArchitecture 114 | universalApk false // If true, also generate a universal APK 115 | include "armeabi-v7a", "x86" 116 | } 117 | } 118 | buildTypes { 119 | release { 120 | minifyEnabled enableProguardInReleaseBuilds 121 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 122 | } 123 | } 124 | // applicationVariants are e.g. debug, release 125 | applicationVariants.all { variant -> 126 | variant.outputs.each { output -> 127 | // For each separate APK per architecture, set a unique version code as described here: 128 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 129 | def versionCodes = ["armeabi-v7a":1, "x86":2] 130 | def abi = output.getFilter(OutputFile.ABI) 131 | if (abi != null) { // null for the universal-debug, universal-release variants 132 | output.versionCodeOverride = 133 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 134 | } 135 | } 136 | } 137 | } 138 | 139 | dependencies { 140 | compile project(':react-native-svg') 141 | compile fileTree(dir: "libs", include: ["*.jar"]) 142 | compile "com.android.support:appcompat-v7:23.0.1" 143 | compile "com.facebook.react:react-native:+" // From node_modules 144 | } 145 | 146 | // Run this once to be able to run the application with BUCK 147 | // puts all compile dependencies into folder libs for BUCK to use 148 | task copyDownloadableDepsToLibs(type: Copy) { 149 | from configurations.compile 150 | into 'libs' 151 | } 152 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/java/com/reactnative/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactnative; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "reactnative"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/java/com/reactnative/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.reactnative; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.horcrux.svg.SvgPackage; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.shell.MainReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage(), 27 | new SvgPackage() 28 | ); 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | }; 36 | 37 | @Override 38 | public ReactNativeHost getReactNativeHost() { 39 | return mReactNativeHost; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | SoLoader.init(this, /* native exopackage */ false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godaddy/asset-system/1d5380cd1aef38795aa86330a4365c1c4a2a03de/examples/reactnative/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godaddy/asset-system/1d5380cd1aef38795aa86330a4365c1c4a2a03de/examples/reactnative/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godaddy/asset-system/1d5380cd1aef38795aa86330a4365c1c4a2a03de/examples/reactnative/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godaddy/asset-system/1d5380cd1aef38795aa86330a4365c1c4a2a03de/examples/reactnative/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | reactnative 3 | 4 | -------------------------------------------------------------------------------- /examples/reactnative/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/reactnative/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/reactnative/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /examples/reactnative/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godaddy/asset-system/1d5380cd1aef38795aa86330a4365c1c4a2a03de/examples/reactnative/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/reactnative/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 6 | -------------------------------------------------------------------------------- /examples/reactnative/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /examples/reactnative/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /examples/reactnative/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /examples/reactnative/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /examples/reactnative/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'reactnative' 2 | include ':react-native-svg' 3 | project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /examples/reactnative/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactnative", 3 | "displayName": "reactnative" 4 | } -------------------------------------------------------------------------------- /examples/reactnative/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './App'; 3 | 4 | AppRegistry.registerComponent('reactnative', () => App); 5 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative.xcodeproj/xcshareddata/xcschemes/reactnative-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative.xcodeproj/xcshareddata/xcschemes/reactnative.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"reactnative" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | reactnative 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | NSLocationWhenInUseUsageDescription 42 | 43 | NSAppTransportSecurity 44 | 45 | NSAllowsLocalNetworking 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnative/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnativeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/reactnative/ios/reactnativeTests/reactnativeTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface reactnativeTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation reactnativeTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /examples/reactnative/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-system-react-native", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "asset-provider": "latest", 11 | "react": "16.2.0", 12 | "react-native": "0.52.1", 13 | "react-native-svg": "^6.0.1-rc.3" 14 | }, 15 | "devDependencies": { 16 | "babel-jest": "22.1.0", 17 | "babel-preset-react-native": "4.0.0", 18 | "jest": "22.1.4", 19 | "react-test-renderer": "16.2.0" 20 | }, 21 | "jest": { 22 | "preset": "react-native" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/web/README.md: -------------------------------------------------------------------------------- 1 | # Web 2 | 3 | A small demo that illustrates how you could use the asset provider in your 4 | own web application. 5 | 6 | ## Starting the demo 7 | 8 | Make sure you have the dependencies installed for this demo: 9 | 10 | ``` 11 | npm install . 12 | ``` 13 | 14 | Once all dependencies are installed you can run the server using: 15 | 16 | ``` 17 | npm start 18 | ``` 19 | 20 | Once the server is started you can navigate your browser to: 21 | 22 | - [`http://localhost:8080`](http://localhost:8080) 23 | 24 | ## How is the demo setup 25 | 26 | There are 3 core files that drive this demo: 27 | 28 | ### index.js 29 | 30 | This is the actual file that implements the `assets-provider`. This file 31 | is converted to ES5 by the `server.js`. The application it self is rather 32 | simple: 33 | 34 | - Imports `asset-provider`, and it's `` and `` components. 35 | - Specify a modification hook for the `pants` property, this will be used to 36 | change the color of homer's pants on the fly. 37 | - Creates a wrapping `` that will load our `godaddy.svg` bundle. This 38 | bundle will only contain a single asset which is the GoDaddy logo. 39 | - Render the asset by adding `` as child node of the 40 | `` 41 | - To make things interesting we've added another `` as child which 42 | will load another bundle that contains the `homer` and `tiger` svgs. 43 | - Add 3 more `` as child elements of the 44 | extra ``. While `godaddy` is not available in this bundle, it 45 | will still render as the `` is aware of it's own context and 46 | will request the asset from the wrapping ``. 47 | - Set the property `pants=red` on of the `homer` assets to change the color of 48 | his pants from light blue to red. 49 | - Renders the Example component in the `example` container. 50 | 51 | ### server.js 52 | 53 | The server is in charge of serving the `html`, `js` and the `svgs` asset bundle. 54 | 55 | It comes with a dedicated API for generating bundles using the `asset-bundle` 56 | library. The URL structure is a following: 57 | 58 | - `http://localhost:8080/[lowercase names of the asset, separated by -].svgs` 59 | 60 | It uses the assets that are available in the `asset` folder that is in the 61 | root of this repository. So if you wish to generate a bundle containing just 62 | the GoDaddy.svg logo you would request: 63 | 64 | - http://localhost:8080/godaddy.svgs 65 | 66 | And if you wanted to include 3 svgs in your bundle you could request: 67 | 68 | - http://localhost:8080/godaddy-homer-tiger.svgs 69 | 70 | This gives you some insight on the bundles, and what they contain. 71 | 72 | ### index.html 73 | 74 | This is the default HTML page that gets loaded when you visit the URL. It's only 75 | use is to load the `index.js` so it starts the React application. 76 | -------------------------------------------------------------------------------- /examples/web/babel.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('./server'); 3 | -------------------------------------------------------------------------------- /examples/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Asset Provider 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/web/index.js: -------------------------------------------------------------------------------- 1 | import Provider, { Asset, parser } from 'asset-provider'; 2 | import React, { Component } from 'react'; 3 | import { render } from 'react-dom'; 4 | 5 | // 6 | // Add a small modifier for the assets, each time we see the `pants` property 7 | // on an this parser will be called for all paths, circles and what 8 | // not that was used to create the svg. 9 | // 10 | // We know that homer's pants is #669BC7 so we can target that fill color 11 | // and override it with the given value. 12 | // 13 | parser.modify('pants', function (attr, prop, child) { 14 | if (attr.fill !== '#669BC7') return; 15 | 16 | attr.fill = prop.pants; 17 | }); 18 | 19 | class Example extends Component { 20 | render() { 21 | return ( 22 | 23 |
24 |

Rendering a godaddy asset

25 | 26 | 27 | Loading text 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 |
38 | ); 39 | } 40 | } 41 | 42 | render(, document.getElementById('example')) 43 | -------------------------------------------------------------------------------- /examples/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-system-react", 3 | "version": "1.0.0", 4 | "description": "Examples of how the asset-provider works.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node babel.js" 8 | }, 9 | "private": true, 10 | "license": "MIT", 11 | "browserify": { 12 | "transform": [ 13 | "babelify" 14 | ] 15 | }, 16 | "babel": { 17 | "plugins": [ 18 | "transform-object-rest-spread" 19 | ], 20 | "presets": [ 21 | "babel-preset-es2015", 22 | "babel-preset-react" 23 | ] 24 | }, 25 | "dependencies": { 26 | "asset-bundle": "latest", 27 | "asset-provider": "latest", 28 | "babel-loader": "^7.1.2", 29 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 30 | "babel-preset-es2015": "^6.24.1", 31 | "babel-preset-react": "^6.24.1", 32 | "babel-register": "^6.26.0", 33 | "babelify": "^8.0.0", 34 | "browserify": "^15.2.0", 35 | "memory-fs": "^0.4.1", 36 | "react": "^16.2.0", 37 | "react-dom": "^16.2.0", 38 | "webpack": "^4.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/web/server.js: -------------------------------------------------------------------------------- 1 | import assets from '../../test/fixtures/index.js'; 2 | import config from './webpack.config.js'; 3 | import Bundle from 'asset-bundle'; 4 | import MemoryFS from 'memory-fs'; 5 | import webpack from 'webpack'; 6 | import http from 'http'; 7 | import path from 'path'; 8 | import fs from 'fs'; 9 | 10 | // 11 | // Setup fake file system for WebPack for the client code. 12 | // 13 | const fsys = new MemoryFS(); 14 | 15 | /** 16 | * Serve the bundle.svgs. 17 | * 18 | * @param {Request} req HTTP request. 19 | * @param {Response} res HTTP response. 20 | * @private 21 | */ 22 | function svgs(req, res) { 23 | const bundle = new Bundle( 24 | req.url.slice(1, -5).split('-').map(function map(name) { 25 | return assets[name]; 26 | }) 27 | ); 28 | 29 | bundle.run(function (err, output) { 30 | if (err) throw err; 31 | 32 | res.setHeader('Content-Length', Buffer(output).length); 33 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 34 | 35 | res.end(output); 36 | }); 37 | } 38 | 39 | /** 40 | * Serve the index.html 41 | * 42 | * @param {Request} req HTTP request. 43 | * @param {Response} res HTTP response. 44 | * @private 45 | */ 46 | function html(req, res) { 47 | fs.readFile(path.join(__dirname, 'index.html'), function read(err, file) { 48 | if (err) throw err; 49 | 50 | res.setHeader('Content-Length', file.length); 51 | res.writeHead(200, { 'Content-Type': 'text/html' }); 52 | 53 | res.end(file); 54 | }); 55 | } 56 | 57 | /** 58 | * Serve the index.js client bundle. 59 | * 60 | * @param {Request} req HTTP request. 61 | * @param {Response} res HTTP response. 62 | * @private 63 | */ 64 | function client(req, res) { 65 | const compiler = webpack(config); 66 | compiler.outputFileSystem = fsys; 67 | 68 | compiler.run((err, stats) => { 69 | const file = fsys.readFileSync(path.join(__dirname, 'dist', 'client.js')); 70 | 71 | res.setHeader('Content-Length', file.length); 72 | res.writeHead(200, { 'Content-Type': 'text/javascript' }); 73 | 74 | res.end(file); 75 | }); 76 | } 77 | 78 | // 79 | // Poor or lazy man's HTTP file server. Don't do this, it's bad. 80 | // 81 | const server = http.createServer((req, res) => { 82 | console.log('received request for url', req.url); 83 | const ext = path.extname(req.url); 84 | 85 | switch(ext) { 86 | case '.svgs': 87 | svgs(req, res); 88 | break; 89 | 90 | case '.js': 91 | client(req, res); 92 | break; 93 | 94 | default: 95 | html(req, res); 96 | } 97 | }).listen(8080); 98 | 99 | // 100 | // Output some information that we're live, ready to go. 101 | // 102 | console.log('Example server is running on http://localhost:8080'); 103 | -------------------------------------------------------------------------------- /examples/web/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | name: 'JS', 5 | entry: [ 6 | path.join(__dirname, 'index.js') 7 | ], 8 | output: { 9 | path: path.join(__dirname, 'dist'), 10 | filename: 'client.js' 11 | }, 12 | module: { 13 | rules: [{ 14 | test: /\.jsx?$/, 15 | use: [{ 16 | loader: 'babel-loader', 17 | options: { 18 | presets: [ 19 | 'babel-preset-es2015', 20 | 'babel-preset-react' 21 | ] 22 | } 23 | }] 24 | }] 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /examples/webpack.config.js: -------------------------------------------------------------------------------- 1 | const Pipeline = require('asset-webpack'); 2 | const List = require('asset-list'); 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | entry: path.join(__dirname, 'index.js'), 7 | output: { 8 | path: path.resolve(__dirname, 'dist'), 9 | filename: 'output.js' 10 | }, 11 | 12 | module: { 13 | loaders: [ 14 | { test: /\.svg$/, loaders: ['file-loader'] } 15 | ] 16 | }, 17 | 18 | plugins: [ 19 | new Pipeline('bundle.svgs', { 20 | // 21 | // Set to `true` if you want to use the folder structure of the files as 22 | // basic namespacing. 23 | // 24 | namespace: true, 25 | 26 | plugins: [ 27 | [List, { 28 | // 29 | // `asset-list` options here. 30 | // 31 | }] 32 | ] 33 | }) 34 | ] 35 | }; 36 | -------------------------------------------------------------------------------- /examples/whrs.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | dev = ['dist/bundle.svgs'] 3 | test = ['dist/bundle.svgs'] 4 | prod = ['dist/bundle.svgs'] 5 | -------------------------------------------------------------------------------- /greenkeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "core": { 4 | "packages": [ 5 | "parser/package.json", 6 | "bundle/package.json", 7 | "provider/package.json" 8 | ] 9 | }, 10 | "tools": { 11 | "packages": [ 12 | "list/package.json", 13 | "test/package.json", 14 | "webpack/package.json" 15 | ] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-system", 3 | "version": "1.0.0", 4 | "description": "asset-system is a cross platform asset system for React and React-Native. This repo is the home for all asset- packages.", 5 | "main": "index.js", 6 | "scripts": { 7 | "publish:dimensions": "mono --publish dimensions", 8 | "publish:provider": "mono --publish provider", 9 | "publish:webpack": "mono --publish webpack", 10 | "publish:parser": "mono --publish parser", 11 | "publish:bundle": "mono --publish bundle", 12 | "publish:list": "mono --publish list", 13 | "test:dimensions": "mono --test dimensions", 14 | "test:provider": "mono --test provider", 15 | "test:webpack": "mono --test webpack", 16 | "test:parser": "mono --test parser", 17 | "test:bundle": "mono --test bundle", 18 | "test:list": "mono --test list", 19 | "test": "mono --test", 20 | "publish": "mono --publish", 21 | "install": "mono --install", 22 | "link": "mono --link" 23 | }, 24 | "private": true, 25 | "repository": { 26 | "type": "git", 27 | "url": "git@github.com:godaddy/asset-system.git" 28 | }, 29 | "keywords": [ 30 | "asset" 31 | ], 32 | "babel": { 33 | "presets": [ 34 | "@babel/preset-env", 35 | "@babel/preset-react" 36 | ] 37 | }, 38 | "author": "GoDaddy Operating Company, LLC", 39 | "license": "MIT", 40 | "contributors": [ 41 | "Arnout Kazemier " 42 | ], 43 | "devDependencies": { 44 | "@babel/core": "^7.1.2", 45 | "@babel/preset-env": "^7.1.0", 46 | "@babel/preset-react": "^7.0.0", 47 | "mono-repos": "^0.1.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/bundle/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | !lib 85 | -------------------------------------------------------------------------------- /packages/bundle/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | - Moved `dimensions` to it's own standalone project for re-use purposes. 4 | 5 | ### 4.1.0 6 | 7 | - Remove workaround to `await` page renders from puppeteer. 8 | 9 | ### 4.0.0 10 | 11 | - Major: Upgraded to use the latest `@babel/*` 7.0 packages. 12 | 13 | ### 3.0.1 14 | 15 | - Also lazy require the `puppeteer` library in our render method. 16 | 17 | ### 3.0.0 18 | 19 | - Removed `puppeteer` from the dependencies and made it an optional dependency 20 | instead as it needs to download 230mb+ files on installation each time for 21 | an edge case as documented in #13 22 | 23 | ### 2.1.0 24 | 25 | - Point main/browser to ES5 builds. 26 | 27 | ### 2.0.0 28 | 29 | - Bump to asset-parser@2.0.0 30 | 31 | ### 1.0.0 32 | 33 | - Initial public release. 34 | -------------------------------------------------------------------------------- /packages/bundle/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/bundle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-bundle", 3 | "version": "4.1.0", 4 | "description": "Build process that transforms (multiple) SVG assets into a asset-parser compatible bundle", 5 | "main": "./lib", 6 | "browser": "./lib", 7 | "module": "./index", 8 | "react-native": "./index", 9 | "scripts": { 10 | "test:web": "mocha --colors --require setup-env test/*.test.js", 11 | "test": "npm run build && nyc --reporter=text --reporter=lcov npm run test:web", 12 | "prepublishOnly": "npm run build", 13 | "build": "rm -rf ./lib && babel ./index.js -d ./lib && babel ./walker/*.js -d ./lib/walker", 14 | "lint": "eslint-godaddy *.js ./test/*.js" 15 | }, 16 | "dependencies": { 17 | "asset-dimensions": "^1.0.0", 18 | "asset-parser": "^3.0.0", 19 | "async": "^2.6.0", 20 | "camel-case": "^3.0.0", 21 | "cheerio": "^1.0.0-rc.2", 22 | "diagnostics": "^1.1.0", 23 | "react": "^16.2.0", 24 | "svgo": "^1.0.3" 25 | }, 26 | "devDependencies": { 27 | "@babel/cli": "^7.1.2", 28 | "@babel/core": "^7.1.2", 29 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 30 | "@babel/preset-env": "^7.1.0", 31 | "@babel/preset-react": "^7.0.0", 32 | "@babel/register": "^7.0.0", 33 | "asset-test": "^1.0.0", 34 | "assume": "^1.5.2", 35 | "eslint": "^4.14.0", 36 | "eslint-config-godaddy": "^2.1.0", 37 | "eslint-plugin-json": "^1.2.0", 38 | "eslint-plugin-mocha": "^4.11.0", 39 | "eslint-plugin-react": "^7.5.1", 40 | "mocha": "^5.0.1", 41 | "nyc": "^11.4.1", 42 | "puppeteer": "^1.1.0", 43 | "setup-env": "^1.2.1" 44 | }, 45 | "babel": { 46 | "plugins": [ 47 | "@babel/plugin-proposal-object-rest-spread" 48 | ], 49 | "presets": [ 50 | [ 51 | "@babel/preset-env", 52 | { 53 | "targets": { 54 | "node": 9 55 | } 56 | } 57 | ], 58 | "@babel/preset-react" 59 | ] 60 | }, 61 | "browserify": { 62 | "transform": [ 63 | "babelify" 64 | ] 65 | }, 66 | "nyc": { 67 | "exclude": [ 68 | "lib" 69 | ] 70 | }, 71 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/bundle", 72 | "repository": { 73 | "type": "git", 74 | "url": "git@github.com:godaddy/asset-system.git" 75 | }, 76 | "keywords": [ 77 | "SVG", 78 | "asset", 79 | "bundle" 80 | ], 81 | "author": "GoDaddy.com Operating Company, LLC", 82 | "contributors": [ 83 | "Arnout Kazemier " 84 | ], 85 | "license": "MIT" 86 | } 87 | -------------------------------------------------------------------------------- /packages/bundle/test/snapshots.test.js: -------------------------------------------------------------------------------- 1 | import { decode } from 'asset-parser'; 2 | import { fixtures } from 'asset-test'; 3 | import { describe, it } from 'mocha'; 4 | import Bundle from '../index'; 5 | import assume from 'assume'; 6 | import path from 'path'; 7 | import fs from 'fs'; 8 | 9 | /** 10 | * Small helper function to finding all the fixtures. 11 | * 12 | * @param {String} ext Extension name 13 | * @param {String} name File name 14 | * @returns {String} Absolute location 15 | * @private 16 | */ 17 | function find(ext, name) { 18 | return path.join(fixtures, name + '.' + ext); 19 | } 20 | 21 | /** 22 | * List of all the fixtures we're testing. 23 | * 24 | * @type {Array} 25 | * @private 26 | */ 27 | const tests = [ 28 | 'tiger', 29 | 'homer', 30 | 'homer-no-viewbox', 31 | 'godaddy' 32 | ]; 33 | 34 | describe('snapshots', function () { 35 | this.timeout(10 * 1000); 36 | 37 | let bundle; 38 | let svgs; 39 | 40 | before(function (next) { 41 | bundle = new Bundle(tests.map(find.bind(find, 'svg'))); 42 | 43 | bundle.run(function run(err, str) { 44 | if (err) return next(err); 45 | 46 | decode(str, function (fail, payload) { 47 | if (fail) return next(fail); 48 | 49 | svgs = payload.data; 50 | 51 | next(); 52 | }); 53 | }); 54 | }); 55 | 56 | tests.forEach(function each(name) { 57 | it(`correctly transforms the ${name} fixture`, function () { 58 | const item = svgs[name]; 59 | const output = JSON.parse(fs.readFileSync(find('json', name), 'utf-8')); 60 | 61 | assume(item).is.a('array'); 62 | assume(item).deep.equals(output); 63 | 64 | // 65 | // Uncomment the following line if you want to change the new snapshot: 66 | // 67 | // - fs.writeFileSync(find('json', name), JSON.stringify(item), 'utf-8'); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /packages/bundle/test/walker.test.js: -------------------------------------------------------------------------------- 1 | import { describe, it } from 'mocha'; 2 | import * as walker from '../walker'; 3 | import assume from 'assume'; 4 | 5 | describe('walker', function () { 6 | describe('#componentName', function () { 7 | it('transforms svg elements to svgs component names', function () { 8 | assume(walker.componentName('tspan')).equals('TSpan'); 9 | assume(walker.componentName('path')).equals('Path'); 10 | assume(walker.componentName('radialGradient')).equals('RadialGradient'); 11 | }); 12 | }); 13 | 14 | describe('#attributes', function () { 15 | it('camelCases the attributes', function () { 16 | const result = walker.attributes({ 17 | 'stroke-width': '10px' 18 | }); 19 | 20 | assume(result).has.length(1); 21 | assume(result.strokeWidth).equals('10px'); 22 | }); 23 | 24 | it('renames properties if needed', function () { 25 | const result = walker.attributes({ 26 | class: 'blue' 27 | }); 28 | 29 | assume(result).has.length(1); 30 | assume(result.className).equals('blue'); 31 | }); 32 | 33 | it('transforms values to number when possible', function () { 34 | const result = walker.attributes({ 35 | height: '10' 36 | }); 37 | 38 | assume(result).has.length(1); 39 | assume(result.height).equals(10); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/bundle/walker/index.js: -------------------------------------------------------------------------------- 1 | import diagnostics from 'diagnostics'; 2 | import camelCase from 'camel-case'; 3 | 4 | // 5 | // Setup our debug util. 6 | // 7 | const debug = diagnostics('asset:bundle:walker'); 8 | 9 | /** 10 | * Weird component names from svgs/react-native-svg that can't really be 11 | * transformed automatically using the componentName function. 12 | * 13 | * Naming things is hard.. 14 | * 15 | * @type {Object} 16 | * @private 17 | */ 18 | const names = { 19 | tspan: 'TSpan' // Not judging here but LOL WAT ARE YOU DOING. 20 | }; 21 | 22 | /** 23 | * Transforms a given tagName to a `svgs` and `react-native-svg` compatible 24 | * component name. 25 | * 26 | * @param {String} tagName The node's tagName. 27 | * @returns {String} The transformed string. 28 | * @private 29 | */ 30 | export function componentName(tagName) { 31 | if (names[tagName]) return names[tagName]; 32 | 33 | const first = tagName.charAt(0).toUpperCase(); 34 | return first + tagName.slice(1); 35 | } 36 | 37 | /** 38 | * List of properties that need to be renamed in order to be compatible in 39 | * React. 40 | * 41 | * @type {Object} 42 | * @private 43 | */ 44 | const rename = { 45 | 'class': 'className' 46 | }; 47 | 48 | /** 49 | * Transform attribute names to their correct JavaScript equivs. 50 | * 51 | * @param {Object} attr Attributes that needs to be transformed 52 | * @returns {Object} Transformed attributes. 53 | * @private 54 | */ 55 | export function attributes(attr) { 56 | return Object.keys(attr).reduce(function reduce(memo, key) { 57 | const name = rename[key] ? rename[key] : camelCase(key); 58 | let value = attr[key]; 59 | 60 | if (!isNaN(+value)) value = +value; 61 | 62 | memo[name] = value; 63 | return memo; 64 | }, {}); 65 | } 66 | 67 | /** 68 | * Walk the DOM tree and generate the required structures. 69 | * 70 | * @param {Object} svg Information of a single SVG asset. 71 | * @param {Object} modify Additional modification hooks. 72 | * @param {Function} fn Error first completion callback. 73 | * @private 74 | */ 75 | export default function walker(svg, modify, fn) { 76 | const { tree } = svg; 77 | 78 | /** 79 | * Iterate over all the children and transform them into the required 80 | * structure. 81 | * 82 | * @param {Object} root DOM root node. 83 | * @returns {Array} Parsed structure. 84 | * @private 85 | */ 86 | function iterate(root) { 87 | const node = []; 88 | const element = tree(root); 89 | const attr = element.attr(); 90 | const children = element.children(); 91 | 92 | // 93 | // Transform the name of the tag to be a component. 94 | // 95 | let name = componentName(root.tagName); 96 | let attrs = {}; 97 | 98 | // 99 | // Transform attributes to actual DOM properties. 100 | // 101 | if (attr && Object.keys(attr).length) { 102 | attrs = attributes(attr); 103 | } 104 | 105 | // 106 | // Edge case for the svg element, we want to NUKE all the Attributes 107 | // and introduce our new viewBox property if it exists. 108 | // 109 | if (name === 'Svg' && svg.viewBox) { 110 | debug(`introducing viewBox(${svg.viewBox}) on svg(${svg.name})`); 111 | attrs = { viewBox: svg.viewBox }; 112 | } 113 | 114 | // 115 | // Process the modification hooks. So they can alter attributes or even 116 | // complete components. 117 | // 118 | Object.keys(modify).forEach((key) => { 119 | name = modify[key](attrs, element, name) || name; 120 | }); 121 | 122 | // 123 | // All transformation have been completed, we can start assembling the 124 | // required structure: 125 | // 126 | // [ComponentName, { optional attributes }, [ optional children ]] 127 | // 128 | node.push(name); 129 | if (Object.keys(attrs).length) node.push(attrs) 130 | if (children.length) node.push(children.get().map(iterate)); 131 | 132 | return node; 133 | } 134 | 135 | svg.struc = tree('svg').get().map(iterate)[0]; 136 | fn(null, svg); 137 | } 138 | -------------------------------------------------------------------------------- /packages/dimensions/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | !lib 85 | -------------------------------------------------------------------------------- /packages/dimensions/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 1.0.0 4 | 5 | - Initial public release, extracted from `asset-bundle` 6 | -------------------------------------------------------------------------------- /packages/dimensions/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/dimensions/README.md: -------------------------------------------------------------------------------- 1 | # asset-dimensions 2 | 3 | Extract the dimensions of a given SVG image. When no information can be 4 | extracted from the `viewBox` or `width` and `height` properties it will be 5 | rendered in a headless browser and the Bounding Box will be extracted from 6 | the rendered image and returned instead for guaranteed sizing information. 7 | 8 | ## Installation 9 | 10 | ``` 11 | npm install --save asset-dimensions 12 | ``` 13 | 14 | The module has an **optional** dependency on [`puppeteer`][puppeteer]. This 15 | module is used to resolve the `viewBox` of a given `svg` asset if there is no 16 | `viewBox` or `height/width` combination available on the asset. 17 | 18 | If these properties are missing on some of your assets you will print a warning 19 | and can decide to either manually update them, or install `puppeteer` and it 20 | will automatically extract the correct viewBox from your svg. 21 | 22 | ``` 23 | npm install --save puppeteer 24 | ``` 25 | 26 | This dependency is not listed as `optionalDependency` in the package.json so 27 | it needs to be manually installed. The reason for this is so we do not download 28 | the headless chrome browser by default when it's not needed for our users. 29 | 30 | ## Usage 31 | 32 | The module exposes as `async function` as default interface. This function 33 | expects an object as first argument with the following properties: 34 | 35 | - `file` The path to the svg that we're extracting information from. This will 36 | be used in the debug output when we fail to extract the dimensions so you know 37 | which file needs manual updating, or why `puppeteer` is required. 38 | - `source` The contents of the file as plain text. 39 | - `tree` DOM (`cheerio`) of the SVG. 40 | 41 | ```js 42 | import dimensions from 'asset-dimensions'; 43 | import { readFileSync } from 'fs'; 44 | import { load } from 'cheerio'; 45 | import { join } from 'path'; 46 | 47 | const file = join(__dirname, 'assets', 'file.svg'); 48 | const source = readFileSync(file, 'utf-8'); 49 | 50 | const result = await dimensions({ 51 | tree: load(content, { decodeEntities: false }), 52 | content, 53 | file, 54 | }); 55 | 56 | console.log(result); 57 | ``` 58 | 59 | The returned `result` is an `object` that will have the following keys: 60 | 61 | - `width` Width of the asset. 62 | - `height` Height of the asset. 63 | - `x` y of the asset. 64 | - `y` x of the asset. 65 | - `viewBox` The viewBox constructed with the `height`, `width`, `x`, `y` props. 66 | 67 | ## License 68 | 69 | [MIT](LICENSE) 70 | 71 | [puppeteer]: https://github.com/GoogleChrome/puppeteer 72 | -------------------------------------------------------------------------------- /packages/dimensions/index.js: -------------------------------------------------------------------------------- 1 | import render from './render'; 2 | 3 | /** 4 | * Really stupid simple warning output. 5 | * 6 | * @param {Array} lines The messages that needs to be spammed. 7 | * @private 8 | */ 9 | function warning(lines) { 10 | lines.unshift(''); // Extra whitespace at the start. 11 | lines.push(''); // Extra whitespace at the end. 12 | 13 | lines.forEach(function each(line) { 14 | console.error('asset-dimensions:warning', line); 15 | }); 16 | } 17 | 18 | /** 19 | * Compile a viewBox from the given. 20 | * 21 | * @param {Object} details The width/height for the viewBox. 22 | * @returns {Object} Dimensions, and viewbox of the svg asset. 23 | * @private 24 | */ 25 | function viewBox({ width, height, x = 0, y = 0 }) { 26 | return { 27 | viewBox: `${x} ${y} ${width} ${height}`, 28 | height: +height, 29 | width: +width, 30 | x: +x, 31 | y: +y 32 | }; 33 | } 34 | 35 | /** 36 | * Ensure that we have dimension information from the read SVG so we can generate 37 | * a valid viewBox for the SVG. 38 | * 39 | * @param {Object} svg Our internal SVG object. 40 | * @returns {Object} Bounding box, and viewBox information of the asset. 41 | * @public 42 | */ 43 | export default async function dimensions({ file, source, tree }) { 44 | const root = tree('svg'); 45 | 46 | // 47 | // If we already have a valid viewBox attribute, we can just continue and 48 | // mind our own business.. This should satisfy 99% of the cases when designers 49 | // provide us with SVG exports from applications such as Illustrator, Sketch. 50 | // 51 | if (root.attr('viewBox')) { 52 | const parsed = root.attr('viewBox').split(/[ ,]+/g); 53 | 54 | return viewBox({ 55 | x: parseFloat(parsed[0], 10), 56 | y: parseFloat(parsed[1], 10), 57 | width: parseFloat(parsed[2], 10), 58 | height: parseFloat(parsed[3], 10) 59 | }); 60 | } 61 | 62 | // 63 | // While we have `svg.info` that can contain height and width, these values 64 | // are extracted directly from the svg width/height attributes are should 65 | // not be considered valid. For example, our homer.svg in fixtures is 66 | // actually: width: 457.2821960449219, height: 490.2278747558594 but the 67 | // info object (that originates from svgo) said 500x500. 68 | // 69 | // In all these cases, the best thing we can do is extract the bounding box 70 | // from the browser. 71 | // 72 | // In order to get the best render result we need to strip the whole 73 | // tag from the data and replace it with an empty once so the size is 74 | // determined based on the actual size of the svg, not by any other prop. 75 | // 76 | // The only problem here is that in order todo this we actually need to 77 | // download a full browser on the users device in order to get the correct 78 | // information. The `puppeteer` dependency can be up to 230mb that needs 79 | // to be downloaded on each installation. If the user doesn't manually install 80 | // this dependency, we will output a warning when we encounter an svg that 81 | // has `width` and `height` set as we cannot accurately measure it. And 82 | // in case of no width/height combo we will do a hard fail, forcing people 83 | // to install it. 84 | // 85 | let puppeteer = true; 86 | try { require.resolve('puppeteer'); } catch (e) { puppeteer = false; } 87 | 88 | if (!puppeteer && (root.attr('width') && root.attr('height'))) { 89 | warning([ 90 | 'file: ' + file, 91 | '', 92 | 'One of the svgs did not have a viewBox property, in order to correctly', 93 | 'calculate this, we need use `puppeteer` for browser based detection.', 94 | 'Please run the following command:', 95 | '', 96 | 'npm install --save puppeteer', 97 | '', 98 | 'The bundle process will still continue but the results might be inaccurate' 99 | ]); 100 | 101 | return viewBox({ 102 | width: root.attr('width'), 103 | height: root.attr('height'), 104 | x: 0, 105 | y: 0 106 | }); 107 | } 108 | 109 | if (!puppeteer) throw new Error([ 110 | 'file: ' + file, 111 | '', 112 | 'The supplied svg image does not have a `viewBox` and `width/height` combination.', 113 | 'We are unable to extract or create a valid viewBox for this asset without the', 114 | 'installation of `puppeteer`. Please run the following command:', 115 | '', 116 | 'npm install --save puppeteer', 117 | '', 118 | 'Or manually fix the svg, and run the bundle command again.' 119 | ].join('\n')); 120 | 121 | const boundingbox = await render(source.replace(//g, '')); 122 | return viewBox(boundingbox); 123 | } 124 | -------------------------------------------------------------------------------- /packages/dimensions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-dimensions", 3 | "version": "1.0.0", 4 | "description": "Get the size of your asset in width and height", 5 | "main": "./lib", 6 | "module": "./index", 7 | "scripts": { 8 | "build": "rm -rf ./lib && babel ./index.js -d ./lib && babel ./render.js -d ./lib", 9 | "test:runner": "mocha --colors --require setup-env test.js", 10 | "test": "npm run build && nyc --reporter=text --reporter=lcov npm run test:runner", 11 | "lint": "eslint-godaddy *.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+ssh://git@github.com/godaddy/asset-system.git" 16 | }, 17 | "keywords": [ 18 | "asset", 19 | "size", 20 | "svg", 21 | "dimensions", 22 | "width", 23 | "height", 24 | "calculate", 25 | "parse" 26 | ], 27 | "babel": { 28 | "plugins": [ 29 | "@babel/plugin-proposal-object-rest-spread" 30 | ], 31 | "presets": [ 32 | [ 33 | "@babel/preset-env", 34 | { 35 | "targets": { 36 | "node": 9 37 | } 38 | } 39 | ], 40 | "@babel/preset-react" 41 | ] 42 | }, 43 | "author": "GoDaddy.com Operating Company, LLC", 44 | "contributors": [ 45 | "Arnout Kazemier " 46 | ], 47 | "license": "MIT", 48 | "bugs": { 49 | "url": "https://github.com/godaddy/asset-system/issues" 50 | }, 51 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/dimensions", 52 | "devDependencies": { 53 | "@babel/cli": "^7.1.2", 54 | "@babel/core": "^7.1.2", 55 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 56 | "@babel/preset-env": "^7.1.0", 57 | "@babel/preset-react": "^7.0.0", 58 | "@babel/register": "^7.0.0", 59 | "asset-test": "^1.0.0", 60 | "assume": "^1.5.2", 61 | "cheerio": "^1.0.0-rc.3", 62 | "eslint": "^4.14.0", 63 | "eslint-config-godaddy": "^2.1.0", 64 | "eslint-plugin-json": "^1.2.0", 65 | "eslint-plugin-mocha": "^4.11.0", 66 | "eslint-plugin-react": "^7.5.1", 67 | "mocha": "^5.0.1", 68 | "nyc": "^11.4.1", 69 | "puppeteer": "^1.1.0", 70 | "setup-env": "^1.2.1" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/dimensions/render.js: -------------------------------------------------------------------------------- 1 | let puppeteer; 2 | try { 3 | puppeteer = require('puppeteer'); 4 | } catch (ex) { 5 | // 6 | // Ignore errors here since the consumer is notified 7 | // at run-time only in ./index.js 8 | // 9 | } 10 | 11 | /** 12 | * Extract height and width from an SVG by rendering it and extracting 13 | * the bounding box information. 14 | * 15 | * @param {String} svg The svg we want to render. 16 | * @returns {Object} Calculated dimensions for the boundingbox. 17 | * @public 18 | */ 19 | export default async function render(svg) { 20 | const html = `${svg}`; 21 | const browser = await puppeteer.launch({ headless: true }); 22 | const page = await browser.newPage(); 23 | const loaded = page.waitForNavigation({ waitUntil: 'load' }); 24 | 25 | await page.setContent(html); 26 | await loaded; 27 | 28 | // 29 | // Extract the bounding box information from the loaded SVG. The istanbul 30 | // ignore is of vital importance because the `evaluate` code is executed 31 | // in the headless chrome, not in this node process so it cannot access the 32 | // injected `cov_2feitg0dis` references. 33 | // 34 | /* istanbul ignore next */ 35 | const dimensions = await page.evaluate(() => { 36 | // 37 | // Hacks part II, the sequel. 38 | // 39 | // Apparently you cannot directly return the bouding box information, no, 40 | // you need to extract the information from the resulting boundingBox 41 | // object, and then return it again. Because, serialization of data is hard 42 | // I guess. 43 | // 44 | const { width, height, x, y } = document.getElementsByTagName('svg')[0].getBBox(); 45 | return { width, height, x, y }; 46 | }); 47 | 48 | await browser.close(); 49 | 50 | return dimensions; 51 | } 52 | -------------------------------------------------------------------------------- /packages/dimensions/test.js: -------------------------------------------------------------------------------- 1 | import { fixtures } from 'asset-test'; 2 | import { describe, it } from 'mocha'; 3 | import dimensions from './index.js'; 4 | import { load } from 'cheerio'; 5 | import assume from 'assume'; 6 | import path from 'path'; 7 | import fs from 'fs'; 8 | 9 | describe('dimensions', function () { 10 | function prepare(file) { 11 | 12 | // eslint-disable-next-line 13 | const source = fs.readFileSync(file, 'utf-8'); 14 | const tree = load(source, { decodeEntities: false }); 15 | 16 | return { file, source, tree }; 17 | } 18 | 19 | it('extracts the viewBox from the svg', async function () { 20 | const godaddy = path.join(fixtures, 'godaddy.svg'); 21 | const item = prepare(godaddy); 22 | 23 | const result = await dimensions(item); 24 | 25 | assume(result).is.a('object'); 26 | assume(result.viewBox).equals('0 0 127 55.2'); 27 | assume(result.x).equals(0); 28 | assume(result.y).equals(0); 29 | assume(result.width).equals(127); 30 | assume(result.height).equals(55.2); 31 | }); 32 | 33 | it('extracts the full viewBox by rendering headless', async function () { 34 | const tiger = path.join(fixtures, 'tiger.svg'); 35 | const item = prepare(tiger); 36 | 37 | const result = await dimensions(item); 38 | 39 | assume(result.viewBox).equals('16.072998046875 55.626800537109375 493.87451171875 509.1741638183594'); 40 | assume(result.x).equals(16.072998046875); 41 | assume(result.y).equals(55.626800537109375); 42 | assume(result.width).equals(493.87451171875); 43 | assume(result.height).equals(509.1741638183594); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/list/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | !lib 85 | -------------------------------------------------------------------------------- /packages/list/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 2.0.0 4 | 5 | - Major: Upgraded to use the latest `@babel/*` 7.0 packages. 6 | 7 | ### 1.1.0 8 | 9 | - Point main/browser to ES5 builds. 10 | 11 | ### 1.0.1 12 | 13 | - [#3] Verifying `dist/` dir exists prior to writing `bundle.md` 14 | 15 | ### 1.0.0 16 | 17 | - Initial public release. 18 | 19 | 20 | [#3]: https://github.com/godaddy/asset-system/issues/3 21 | -------------------------------------------------------------------------------- /packages/list/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/list/README.md: -------------------------------------------------------------------------------- 1 | # asset-list 2 | 3 | Automatically generate markdown documentation of your generated 4 | `asset-bundle` bundles. 5 | 6 | ## Table of contents 7 | 8 | - [Installation](#installation) 9 | - [Usage](#usage) 10 | - [Options](#options) 11 | - [asset-webpack](#asset-webpack) 12 | - [asset-bundle](#asset-bundle) 13 | - [output](#output) 14 | - [License](#license) 15 | 16 | ## Installation 17 | 18 | ``` 19 | npm install --save asset-list 20 | ``` 21 | 22 | ## Usage 23 | 24 | There are 2 different usage, directly with the `asset-bundle` module or as 25 | part of the `asset-webpack`. Both use the same options, the only difference 26 | is how you enable the plugin. 27 | 28 | This documentation generation is a **synchronous** process, it will happen 29 | **before** the completion callback is called of `asset-bundle`. And we will 30 | automatically write the file to the same output directory that contains the 31 | generated bundle. 32 | 33 | So when you generate an `./dist/assets.svgs` this module will also add an 34 | `./dist/assets.md` which contains the generated documentation. 35 | 36 | ### Options 37 | 38 | The options should be supplied as an object, the following properties are 39 | supported: 40 | 41 | - `file` Path of where the bundle and documentation should be stored. When you 42 | using the `asset-webpack` this will be set automatically to the output 43 | path. 44 | - `hosted` Indication that the SVG assets should reference a hosted server 45 | instead of a local, relative file path to the asset. 46 | - `separator` The separator that is used to indicate namespaces/categories in 47 | asset names. Defaults to `require('path').sep` so it's a folder separator. 48 | 49 | ### `asset-webpack` 50 | 51 | When using `asset-webpack` you can use the `plugins` option to supply a list 52 | of plugins. The notable difference here is that you **do not** need to supply 53 | the `file` option. This is set automatically by the WebPack plugin to the specified 54 | output file. 55 | 56 | ```js 57 | import Pipeline from 'asset-webpack'; 58 | import List from 'asset-list'; 59 | import path from 'path'; 60 | 61 | module.exports = { 62 | entry: path.join(__dirname, 'index.js'), 63 | output: { 64 | path: path.resolve(__dirname, 'dist'), 65 | filename: 'output.js' 66 | }, 67 | 68 | module: { 69 | loaders: [ 70 | { test: /\.svg$/, loaders: ['file-loader'] } 71 | ] 72 | }, 73 | 74 | plugins: [ 75 | new Pipeline('bundle.svgs', { 76 | namespace: true, 77 | plugins: [ 78 | [List, { /* asset-list options here */ }] 79 | ] 80 | }) 81 | ] 82 | }; 83 | ``` 84 | 85 | ### `asset-bundle` 86 | 87 | The `asset-bundle` provides a `plugin` method that is used to register 88 | plugins. The first argument should be the constructor, and the second argument 89 | the options that this library accepts. The `file` option is required. 90 | 91 | ```js 92 | import Bundle from 'asset-bundle'; 93 | import List from 'asset-list'; 94 | import path from 'path'; 95 | import fs from 'fs'; 96 | 97 | const bundle = new Bundle([ path.join(__dirname, 'test', 'fixtures', 'godaddy.svg'); ]); 98 | const output = path.join(__dirname, 'bundle.svg'); 99 | 100 | // 101 | // Add the plugin to the bundle process using the `.plugin` module. 102 | // 103 | bundle.plugin(List, { file: output }); 104 | 105 | // 106 | // Once the bundle is done, it will generate all the things. 107 | // 108 | bundle.run((err, str) => { 109 | fs.writeFileSync(output, str); 110 | }); 111 | ``` 112 | 113 | ## Output 114 | 115 | Below is an example output of the documentation that is generated about our 3 116 | example assets: 117 | 118 | # bundle.svgs 119 | 120 | ## Table of Contents 121 | 122 | - [Bundle details](#bundle-details) 123 | - [Assets](#assets) 124 | - [godaddy](#godaddy) 125 | - [tiger](#tiger) 126 | - [homer](#homer) 127 | 128 | ## Bundle details 129 | 130 | - **31.03KB** compressed(gzip). 131 | - **81.26KB** uncompressed. 132 | - Contains **3** assets. 133 | - Build according to specification: **0.1.0** 134 | 135 | The bundle can be included using the `asset-provider` components: 136 | 137 | ```js 138 | import Provider, { Asset } from "asset-parser"; 139 | 140 | 141 | 142 | 143 | ``` 144 | 145 | Learn more about [asset-provider](https://github.com/godaddy/asset-system) components. 146 | 147 | ## Assets 148 | 149 | ### godaddy 150 | 151 | | name | width | height | preview | 152 | | ------- | ----- | ------ | ------------------------------------------ | 153 | | godaddy | 127 | 55.2 | | 154 | 155 | ```js 156 | 157 | ``` 158 | 159 | ### tiger 160 | 161 | | name | width | height | preview | 162 | | ----- | ------------------ | ------------------ | ---------------------------------------- | 163 | | tiger | 493.87457275390625 | 509.17413330078125 | | 164 | 165 | ```js 166 | 167 | ``` 168 | 169 | ### homer 170 | 171 | | name | width | height | preview | 172 | | ----- | ----------------- | ----------------- | ---------------------------------------- | 173 | | homer | 457.2821960449219 | 490.2279052734375 | | 174 | 175 | ```js 176 | 177 | ``` 178 | 179 | ## License 180 | 181 | [MIT](LICENSE) 182 | -------------------------------------------------------------------------------- /packages/list/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-list", 3 | "version": "2.0.0", 4 | "description": "Automatically generate documentation for your generated asset-bundle's", 5 | "main": "./lib", 6 | "browser": "./lib", 7 | "module": "./index", 8 | "react-native": "./index", 9 | "scripts": { 10 | "test:web": "mocha --colors --require setup-env test/*.test.js", 11 | "test": "nyc --reporter=text --reporter=lcov npm run test:web", 12 | "prepublishOnly": "npm run build", 13 | "build": "rm -rf ./lib && babel ./index.js -d ./lib", 14 | "lint": "eslint-godaddy *.js ./test/*.js" 15 | }, 16 | "dependencies": { 17 | "bytes": "^3.0.0", 18 | "diagnostics": "^1.1.0", 19 | "markdown-table": "^1.1.1", 20 | "mkdirp": "^0.5.1" 21 | }, 22 | "devDependencies": { 23 | "@babel/cli": "^7.1.2", 24 | "@babel/core": "^7.1.2", 25 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 26 | "@babel/preset-env": "^7.1.0", 27 | "@babel/register": "^7.0.0", 28 | "asset-bundle": "latest", 29 | "asset-test": "latest", 30 | "assume": "^1.5.2", 31 | "clone": "^2.1.1", 32 | "eslint": "^4.14.0", 33 | "eslint-config-godaddy": "^2.1.0", 34 | "eslint-plugin-json": "^1.2.0", 35 | "eslint-plugin-mocha": "^4.11.0", 36 | "mocha": "^4.1.0", 37 | "nyc": "^11.4.1", 38 | "puppeteer": "^1.9.0", 39 | "setup-env": "^1.2.1" 40 | }, 41 | "babel": { 42 | "plugins": [ 43 | "@babel/plugin-proposal-object-rest-spread" 44 | ], 45 | "presets": [ 46 | "@babel/preset-env" 47 | ] 48 | }, 49 | "browserify": { 50 | "transform": [ 51 | "babelify" 52 | ] 53 | }, 54 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/list", 55 | "repository": { 56 | "type": "git", 57 | "url": "git@github.com:godaddy/asset-system.git" 58 | }, 59 | "author": "GoDaddy.com Operating Company, LLC", 60 | "contributors": [ 61 | "Arnout Kazemier " 62 | ], 63 | "license": "MIT" 64 | } 65 | -------------------------------------------------------------------------------- /packages/list/test/index.test.js: -------------------------------------------------------------------------------- 1 | import { fixtures } from 'asset-test'; 2 | import Bundle from 'asset-bundle'; 3 | import List from '../index.js'; 4 | import assume from 'assume'; 5 | import clone from 'clone'; 6 | import path from 'path'; 7 | 8 | describe('List', function () { 9 | const godaddy = path.join(fixtures, 'godaddy.svg'); 10 | const tiger = path.join(fixtures, 'tiger.svg'); 11 | const homer = path.join(fixtures, 'homer.svg'); 12 | 13 | let output; 14 | let bundle; 15 | let svgs; 16 | let svg; 17 | let list; 18 | 19 | before(function (next) { 20 | this.timeout(20000); 21 | 22 | bundle = new Bundle([ godaddy, tiger, homer ]); 23 | bundle.once('done', (err, out, data) => { 24 | output = out; 25 | svgs = data; 26 | svg = data[0]; 27 | 28 | next(); 29 | }); 30 | 31 | bundle.run(function nope(err) { 32 | if (err) next(err); 33 | }); 34 | 35 | list = new List(bundle, { 36 | file: path.join(__dirname, '..', 'dist', 'bundle.svgs') 37 | }); 38 | }); 39 | 40 | describe('#link', function () { 41 | it('is a function', function () { 42 | assume(list.link).is.a('function'); 43 | }); 44 | 45 | it('removes slashes', function () { 46 | assume(list.link('hello/world')).equals('helloworld'); 47 | }); 48 | 49 | it('lowercases', function () { 50 | assume(list.link('WHATDOING')).equals('whatdoing'); 51 | }); 52 | 53 | it('removes spaces', function () { 54 | assume(list.link('Long title here')).equals('long-title-here'); 55 | }); 56 | 57 | it('removes - suffix', function () { 58 | assume(list.link('Long title here-')).equals('long-title-here'); 59 | }); 60 | }); 61 | 62 | describe('#contents', function () { 63 | it('is a function', function () { 64 | assume(list.contents).is.a('function'); 65 | }); 66 | 67 | it('generates a table of contents', function () { 68 | const res = list.contents(clone(svgs)); 69 | 70 | assume(res).is.a('string'); 71 | assume(res).contains('## Table of Contents'); 72 | assume(res).contains('- [godaddy](#godaddy)'); 73 | }); 74 | 75 | it('generates deep indentation for namespaces', function () { 76 | const res = list.contents([ 77 | { name: 'foo' }, 78 | { name: 'foo/bar' }, 79 | { name: 'foo/bar/baz' }, 80 | { name: 'foo/bar/world' }, 81 | { name: 'hello' }, 82 | { name: 'unknown/namespace' } 83 | ]); 84 | 85 | assume(res).contains(' - [foo](#foo)'); 86 | assume(res).contains(' - [bar](#foobar)') 87 | assume(res).contains(' - [world](#foobarworld)'); 88 | 89 | assume(res).contains(' - unknown'); 90 | assume(res).contains(' - [namespace](#unknownnamespace)'); 91 | }); 92 | }); 93 | 94 | describe('#details', function () { 95 | it('is a function', function () { 96 | assume(list.details).is.a('function'); 97 | }); 98 | 99 | it('includes the details of the bundle', function () { 100 | const res = list.details(output, clone(svgs)); 101 | 102 | assume(res).contains('- **81.2KB** uncompressed'); 103 | assume(res).contains('- **30.99KB** compressed(gzip)'); 104 | assume(res).contains('- Contains **3** asset'); 105 | assume(res).contains('- Build according to specification **0.1.0**'); 106 | }); 107 | }); 108 | 109 | describe('#categories', function () { 110 | it('is a function', function () { 111 | assume(list.categories).is.a('function'); 112 | }); 113 | 114 | it('returns an object', function () { 115 | const res = list.categories([ 116 | { name: 'foo' }, 117 | { name: 'foo/bar' }, 118 | { name: 'foo/bar/baz' }, 119 | { name: 'foo/bar/world' }, 120 | { name: 'hello' } 121 | ]); 122 | 123 | assume(res).is.a('object'); 124 | }); 125 | }); 126 | 127 | describe('#asset', function () { 128 | it('is a function', function () { 129 | assume(list.asset).is.a('function'); 130 | }); 131 | 132 | it('includes the name of the Asset as header', function () { 133 | const res = list.asset(svg); 134 | 135 | assume(res).contains('### godaddy'); 136 | }); 137 | 138 | it('includes an example', function () { 139 | const res = list.asset(svg); 140 | 141 | assume(res).contains(''); 142 | }); 143 | 144 | it('includes with width and heigh', function () { 145 | const res = list.asset(svg); 146 | 147 | assume(res).contains('| 127'); 148 | assume(res).contains('| 55.2'); 149 | }); 150 | 151 | it('includes an example', function () { 152 | const res = list.asset(svg); 153 | 154 | assume(res).contains(' 48 | { this.parsed.svg } 49 | 50 | ) 51 | }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/parser/index.js: -------------------------------------------------------------------------------- 1 | import { encode, decode } from './packer'; 2 | import diagnostics from 'diagnostics'; 3 | import Asset from './asset'; 4 | 5 | // 6 | // Setup our debug util. 7 | // 8 | const debug = diagnostics('asset:parser:transform'); 9 | 10 | /** 11 | * The version of the specification that we currently follow. 12 | * 13 | * @type {String} 14 | * @public 15 | */ 16 | export const version = '0.1.0'; 17 | 18 | /** 19 | * Defines the parser function. 20 | * 21 | * @constructor 22 | * @public 23 | */ 24 | export default class Parser { 25 | constructor() { 26 | this.version = version; // Version number of the spec we follow. 27 | this.hooks = {}; // Stores our modifer functions. 28 | } 29 | 30 | /** 31 | * Register a new modification hook. 32 | * 33 | * @param {String} name Name property the transformer should trigger on. 34 | * @param {Function} fn Callback that transforms. 35 | * @public 36 | */ 37 | modify(name, fn) { 38 | debug(`registered a new tranform hook(${name})`); 39 | 40 | if (!(name in this.hooks)) this.hooks[name] = []; 41 | if (!~this.hooks[name].indexOf(fn)) this.hooks[name].push(fn); 42 | } 43 | 44 | /** 45 | * Returns an array of properties that we have modifiers registered for. 46 | * 47 | * @returns {Array} List of properties. 48 | * @public 49 | */ 50 | modifiers() { 51 | return Object.keys(this.hooks); 52 | } 53 | 54 | /** 55 | * Parse the received data. 56 | * 57 | * @param {String} format How is the data supplied. 58 | * @param {String} str Input that needs to be parsed to individual assets. 59 | * @param {Function} fn Error first completion callback. 60 | * @public 61 | */ 62 | parse(format, str, fn) { 63 | /** 64 | * Transforms the data structure into a format that can be used by the 65 | * React SVG Asset Provider. 66 | * 67 | * @param {Object} data Object where key is name, and value is an array. 68 | * @param {String} spec Specification version in which the data is encoded. 69 | * @private 70 | */ 71 | const transform = (data, spec) => { 72 | const assets = Object.keys(data).reduce((memo, key) => { 73 | if (Array.isArray(data[key])) { 74 | debug(`transforming ${key} in to an Asset`); 75 | memo[key] = new Asset(data[key], this.hooks, spec); 76 | } 77 | 78 | return memo; 79 | }, {}); 80 | 81 | fn(null, assets); 82 | }; 83 | 84 | /** 85 | * Handle the decoded data and process it into Assets. 86 | * 87 | * @param {Error} err Optional error that happend during decoding. 88 | * @param {Object} payload Data structure containing the svg names, data. 89 | * @returns {Undefined} Nope. 90 | * @private 91 | */ 92 | const decoded = (err, payload) => { 93 | if (err) { 94 | debug('failed to decode the supplied string, most likely invalid JSON', err); 95 | return fn(err, {}); 96 | } 97 | 98 | const data = payload.data; 99 | const spec = payload.version; 100 | 101 | if (typeof data === 'object' && !Array.isArray(data)) { 102 | debug(`received correct payload, encoded in spec(${spec})`); 103 | return transform(data, spec); 104 | } 105 | 106 | debug('incorrect payload structure', data, spec); 107 | fn(new Error(`Failed to decode payload, spec(${spec})`), {}); 108 | }; 109 | 110 | decode(str, decoded); 111 | } 112 | } 113 | 114 | // 115 | // Export all the components, parsers and utilities. 116 | // 117 | export { 118 | encode, 119 | decode, 120 | Asset 121 | }; 122 | -------------------------------------------------------------------------------- /packages/parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-parser", 3 | "version": "3.0.0", 4 | "description": "Parse SVG asset bundles", 5 | "main": "./lib", 6 | "browser": "./lib", 7 | "module": "./index", 8 | "react-native": "./index", 9 | "scripts": { 10 | "test:web": "mocha --colors --require setup-env test/*.test.js", 11 | "test": "nyc --reporter=text --reporter=lcov npm run test:web", 12 | "prepublishOnly": "npm run build", 13 | "build": "rm -rf ./lib && babel ./asset.js ./index.js ./packer.js ./transform.js -d ./lib", 14 | "lint": "eslint-godaddy-react *.js packer/*.js passthrough/*.js ./test/*.js" 15 | }, 16 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/parser", 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:godaddy/asset-system.git" 20 | }, 21 | "keywords": [ 22 | "Asset", 23 | "SVG", 24 | "parser" 25 | ], 26 | "babel": { 27 | "plugins": [ 28 | "@babel/plugin-proposal-object-rest-spread" 29 | ], 30 | "presets": [ 31 | "@babel/preset-env", 32 | "@babel/preset-react" 33 | ] 34 | }, 35 | "browserify": { 36 | "transform": [ 37 | "babelify" 38 | ] 39 | }, 40 | "author": "GoDaddy.com Operating Company, LLC", 41 | "contributors": [ 42 | "Arnout Kazemier " 43 | ], 44 | "license": "MIT", 45 | "devDependencies": { 46 | "@babel/cli": "^7.1.2", 47 | "@babel/core": "^7.1.2", 48 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 49 | "@babel/preset-env": "^7.1.0", 50 | "@babel/preset-react": "^7.0.0", 51 | "@babel/register": "^7.0.0", 52 | "asset-test": "^1.0.0", 53 | "assume": "^1.5.2", 54 | "assume-enzyme": "^0.0.1", 55 | "enzyme": "^3.9.0", 56 | "enzyme-adapter-react-16": "^1.13.2", 57 | "eslint": "^4.14.0", 58 | "eslint-config-godaddy-react": "^2.1.0", 59 | "eslint-plugin-json": "^1.2.0", 60 | "eslint-plugin-mocha": "^4.11.0", 61 | "eslint-plugin-react": "^7.5.1", 62 | "jsdom": "^11.5.1", 63 | "mocha": "^5.0.1", 64 | "nyc": "^11.4.1", 65 | "prop-types": "^15.6.0", 66 | "react": "^16.8.6", 67 | "react-dom": "^16.8.6", 68 | "react-test-renderer": "^16.2.0", 69 | "setup-env": "^1.2.1" 70 | }, 71 | "dependencies": { 72 | "diagnostics": "^1.1.0", 73 | "passing-through": "^1.0.0", 74 | "rip-out": "^1.0.0", 75 | "svgs": "^4.0.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/parser/packer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Our "unique" indentifier that signals the end of the specification version 3 | * number and the start of the actual encoded payload for that given version 4 | * number. 5 | * 6 | * @type {String} 7 | * @private 8 | */ 9 | const identifier = '§'; 10 | 11 | /** 12 | * Decode the data structure. 13 | * 14 | * @param {String} str String that needs to be decoded. 15 | * @param {Function} fn Error first completion callback. 16 | * @returns {undefined} Nope. 17 | * @public 18 | */ 19 | export function decode(str, fn) { 20 | let data; 21 | 22 | // 23 | // The bundle will be prefixed with the version of the bundle specification 24 | // that we follow, so we need to extract that version number before we can 25 | // parse the payload. 26 | // 27 | const end = str.indexOf(identifier); 28 | const version = str.slice(0, end); 29 | const payload = str.slice(end + identifier.length); 30 | 31 | try { data = JSON.parse(payload); } catch (e) { return fn(e); } 32 | 33 | fn(null, { data, version }); 34 | } 35 | 36 | /** 37 | * Pack the data structure. 38 | * 39 | * @param {String} spec Specification number. 40 | * @param {Object} data Object structurethat needs to be encoded. 41 | * @param {Function} fn Error first completion callback. 42 | * @returns {undefined} Nope. 43 | * @public 44 | */ 45 | export function encode(spec, data, fn) { 46 | let result; 47 | 48 | try { result = JSON.stringify(data); } catch (e) { return fn(e); } 49 | 50 | fn(null, `${spec}${identifier}${result}`); 51 | } 52 | -------------------------------------------------------------------------------- /packages/parser/test/asset.test.js: -------------------------------------------------------------------------------- 1 | import { create } from 'asset-test'; 2 | import { describe, it } from 'mocha'; 3 | import { mount } from 'enzyme'; 4 | import * as svgs from 'svgs'; 5 | import Asset from '../asset'; 6 | import assume from 'assume'; 7 | import React from 'react'; 8 | 9 | const fixture = create(svgs, React); 10 | const Svg = svgs.Svg; 11 | 12 | describe('Asset', function () { 13 | let asset; 14 | 15 | function setup(name, hooks) { 16 | asset = new Asset(fixture[name].structure, hooks); 17 | } 18 | 19 | it('is exported as a function', function () { 20 | assume(Asset).is.a('function'); 21 | }); 22 | 23 | it('does not transform the asset by default', function () { 24 | setup('complex'); 25 | 26 | assume(asset.data).equals(fixture.complex.structure); 27 | assume(asset.parsed).is.a('null'); 28 | }); 29 | 30 | describe('#render', function () { 31 | it('parses the data if its not parsed before', function () { 32 | setup('complex'); 33 | 34 | asset.render(); 35 | const parsed = asset.parsed; 36 | 37 | assume(parsed).is.not.a('null'); 38 | 39 | asset.data = fixture.g.structure; 40 | asset.render(); 41 | 42 | assume(asset.parsed).equals(parsed); 43 | }); 44 | 45 | it('returns a component', function () { 46 | setup('childs'); 47 | 48 | const result = asset.render(); 49 | const wrapper = mount({ result.svg }); 50 | 51 | assume(mount(fixture.childs.output).html()).contains(wrapper.html()); 52 | }); 53 | 54 | it('passes the changes to the component', function () { 55 | setup('green', { 56 | 'color': [function modify(attributes, props) { 57 | if (!attributes.fill) return; 58 | 59 | attributes.fill = props.color; 60 | }] 61 | }); 62 | 63 | const result = asset.render({ color: 'red' }); 64 | const wrapper = mount({ result.svg }); 65 | 66 | assume(wrapper.html()).to.contain(''); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /packages/parser/test/index.test.js: -------------------------------------------------------------------------------- 1 | import AssetParser, { encode, decode, Asset, version } from '../index.js'; 2 | import { describe, it, before } from 'mocha'; 3 | import { create } from 'asset-test'; 4 | import * as svgs from 'svgs'; 5 | import assume from 'assume'; 6 | import React from 'react'; 7 | 8 | const { complex } = create(svgs, React); 9 | 10 | describe('asset-parser', function () { 11 | it('exports all utilties', function () { 12 | assume(version).is.a('string'); 13 | assume(encode).is.a('function'); 14 | assume(decode).is.a('function'); 15 | assume(Asset).is.a('function'); 16 | assume(AssetParser).is.a('function'); 17 | }); 18 | 19 | describe('AssetParser', function () { 20 | let asset; 21 | 22 | function setup() { 23 | asset = new AssetParser(); 24 | } 25 | 26 | describe('#modify', function () { 27 | it('registers a new hook', function () { 28 | setup(); 29 | 30 | assume(asset.hooks).to.have.length(0); 31 | 32 | const what = () => {}; 33 | asset.modify('what', what); 34 | 35 | assume(asset.hooks).to.have.length(1); 36 | assume(asset.hooks.what).is.a('array'); 37 | assume(asset.hooks.what).to.have.length(1); 38 | assume(asset.hooks.what[0]).equals(what); 39 | }); 40 | 41 | it('can register multiple modifiers for a given property', function () { 42 | setup(); 43 | 44 | const one = () => {}; 45 | const two = () => {}; 46 | 47 | asset.modify('color', one); 48 | asset.modify('color', two); 49 | 50 | assume(asset.hooks).to.have.length(1); 51 | assume(asset.hooks.color).is.a('array'); 52 | assume(asset.hooks.color).to.have.length(2); 53 | assume(asset.hooks.color[0]).equals(one); 54 | assume(asset.hooks.color[1]).equals(two); 55 | }); 56 | 57 | it('ignores duplicates for a given prop', function () { 58 | setup(); 59 | 60 | const one = () => {}; 61 | const two = () => {}; 62 | 63 | asset.modify('color', one); 64 | asset.modify('color', two); 65 | asset.modify('color', two); 66 | 67 | assume(asset.hooks).to.have.length(1); 68 | assume(asset.hooks.color).is.a('array'); 69 | assume(asset.hooks.color).to.have.length(2); 70 | assume(asset.hooks.color[0]).equals(one); 71 | assume(asset.hooks.color[1]).equals(two); 72 | }); 73 | }); 74 | 75 | describe('#modifiers', function () { 76 | it('returns an array with the names of the modifiers', function () { 77 | setup(); 78 | 79 | asset.modify('foo', () => {}); 80 | asset.modify('bar', () => {}); 81 | asset.modify('bar', () => {}); 82 | 83 | assume(asset.modifiers()).is.a('array'); 84 | assume(asset.modifiers()).deep.equals(['foo', 'bar']); 85 | }); 86 | }); 87 | 88 | describe('#parse', function () { 89 | let str; 90 | 91 | before((next) => { 92 | setup(); 93 | 94 | encode(version, { complex: complex.structure }, (err, data) => { 95 | if (err) return next(err); 96 | 97 | str = data; 98 | next(); 99 | }); 100 | }); 101 | 102 | it('decodes the playload and returns a list of Asset instances', function (next) { 103 | asset.parse('bundle', str, (err, svgs) => { 104 | if (err) return next(err); 105 | 106 | assume(err).is.a('null'); 107 | assume(svgs).is.a('object'); 108 | 109 | assume(svgs.complex).is.instanceOf(Asset); 110 | next(); 111 | }); 112 | }); 113 | 114 | it('passes pass errors in to callback', function (next) { 115 | asset.parse('bundle', `${version}§l{}`, (err, svgs) => { 116 | assume(err).is.a('error'); 117 | assume(err.message).to.contain('Unexpected token'); 118 | 119 | assume(svgs).is.a('object'); 120 | assume(svgs).to.have.length(0); 121 | 122 | next(); 123 | }); 124 | }); 125 | 126 | it('returns error on invalid data structure', function (next) { 127 | asset.parse('bundle', `${version}§[{"foo":"bar"}]`, (err, svgs) => { 128 | assume(err).is.a('error'); 129 | assume(err.message).equals(`Failed to decode payload, spec(${version})`); 130 | 131 | assume(svgs).is.a('object'); 132 | assume(svgs).to.have.length(0); 133 | 134 | next(); 135 | }); 136 | }); 137 | }); 138 | }); 139 | }); 140 | -------------------------------------------------------------------------------- /packages/parser/test/packer.test.js: -------------------------------------------------------------------------------- 1 | /* eslint max-nested-callbacks: ["error", 10]*/ 2 | import { encode, decode, version as specification } from '../index.js'; 3 | import { describe, it } from 'mocha'; 4 | import assume from 'assume'; 5 | 6 | describe('packer', function () { 7 | describe('#encode', function () { 8 | it('packs an object into a string that can be decoded again', function (next) { 9 | const example = { foo: 'bar' }; 10 | 11 | encode(specification, example, function (err, str) { 12 | assume(err).is.a('null'); 13 | assume(str).is.a('string'); 14 | 15 | decode(str, function (error, payload) { 16 | assume(error).is.a('null'); 17 | assume(payload).is.a('object'); 18 | 19 | const { version, data } = payload; 20 | 21 | assume(data).deep.equals(example); 22 | assume(version).equals(specification); 23 | 24 | next(); 25 | }); 26 | }); 27 | }); 28 | 29 | it('will call the error first on encode error', function (next) { 30 | const example = { foo: 'bar' }; 31 | example.example = example; // Circular ref, explode. 32 | 33 | encode(specification, example, function (err, str) { 34 | assume(err).is.a('error'); 35 | assume(str).is.a('undefined'); 36 | 37 | next(); 38 | }); 39 | }); 40 | }); 41 | 42 | describe('#decode', function () { 43 | it('will call the callback with error first on decode error', function (next) { 44 | decode(`${specification}§{ not really: "data" }`, function (err, data) { 45 | assume(err).is.a('error'); 46 | assume(data).is.a('undefined'); 47 | 48 | next(); 49 | }); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/parser/test/transform.test.js: -------------------------------------------------------------------------------- 1 | import { Pass } from 'passing-through'; 2 | import transform from '../transform'; 3 | import { describe, it } from 'mocha'; 4 | import { create } from 'asset-test'; 5 | import { mount } from 'enzyme'; 6 | import * as svgs from 'svgs'; 7 | import assume from 'assume'; 8 | import React from 'react'; 9 | 10 | const fixture = create(svgs, React); 11 | const Text = svgs.Text; 12 | const Svg = svgs.Svg; 13 | 14 | describe('transform', function () { 15 | let wrapper; 16 | let output; 17 | 18 | function setup(data, hooks) { 19 | const { svg, props } = transform(data, hooks); 20 | wrapper = mount({ svg }); 21 | output = wrapper.html(); 22 | } 23 | 24 | it('is exported as a function', function () { 25 | assume(transform).is.a('function'); 26 | }); 27 | 28 | it('transforms the first element of an array to component', function () { 29 | setup(fixture.g.structure); 30 | 31 | assume(wrapper).to.contain(fixture.g.output); 32 | assume(wrapper).to.not.contain(); // sanity check 33 | }); 34 | 35 | it('applies the second element of the array as properties', function () { 36 | setup(fixture.green.structure); 37 | 38 | assume(wrapper).to.contain(fixture.green.output); 39 | }); 40 | 41 | it('adds child components when an array is encountered', function () { 42 | setup(fixture.childs.structure); 43 | 44 | assume(output).to.contain(mount(fixture.childs.output).html()); 45 | }); 46 | 47 | it('complex deeply tested svgs', function () { 48 | setup(fixture.complex.structure); 49 | 50 | assume(output).to.contain(mount(fixture.complex.output).html()); 51 | }); 52 | 53 | it('returns props even when no svg with props is found in structure', function () { 54 | const result = transform([['Text', ['Hello']]]); 55 | 56 | assume(result.props).is.a('object'); 57 | assume(result.props).has.length(0); 58 | }); 59 | 60 | it('returns text when a component cannot be found', function () { 61 | const result = transform([['what']]).svg; 62 | assume(result).deep.equals(['what']); 63 | }); 64 | 65 | it('extracts the Svg element from the structure and adds its props to props', function () { 66 | const result = transform(fixture.json.godaddy); 67 | 68 | assume(result.props).is.a('object'); 69 | assume(result.props).has.length(1); 70 | assume(result.props.viewBox).equals('0 0 127 55.2'); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/parser/transform.js: -------------------------------------------------------------------------------- 1 | import { Through } from 'passing-through'; 2 | import diagnostics from 'diagnostics'; 3 | import * as svgs from 'svgs'; 4 | import React from 'react'; 5 | 6 | // 7 | // Setup our debug util. 8 | // 9 | const debug = diagnostics('asset:parser:transform'); 10 | 11 | /** 12 | * Figure out the type of what. 13 | * 14 | * @param {Mixed} what Thing we need figure the type of. 15 | * @returns {String} The type of what. 16 | * @private 17 | */ 18 | function type(what) { 19 | return Object.prototype.toString.call(what).slice(8, -1).toLowerCase(); 20 | } 21 | 22 | /** 23 | * Transforms the SVG data in to actual Svgs SVG elements. 24 | * 25 | * @param {Object} data The SVG data for a single asset. 26 | * @param {String} version Version number of the specification. 27 | * @returns {Array} Array containing the new React elements. 28 | * @public 29 | */ 30 | export default function transform(data, version) { 31 | let props = {}; 32 | let key = 0; 33 | 34 | /** 35 | * Transform a row in to an actual component. 36 | * 37 | * @param {Array} row Row that needs to be transformed. 38 | * @returns {React.Element|null} Actual component. 39 | * @private 40 | */ 41 | function create(row = []) { 42 | const Component = svgs[row[0]]; 43 | let attributes = {}; 44 | let children; 45 | 46 | if (!Component) return row[0]; 47 | 48 | // 49 | // Figure out if the 2nd row is attributes. If not it should be 50 | // considered as children instead. 51 | // 52 | if (row[1]) { 53 | switch (type(row[1])) { 54 | case 'array': 55 | children = row[1].map(create); 56 | break; 57 | 58 | case 'object': 59 | attributes = row[1]; 60 | break; 61 | 62 | default: 63 | children = row[1]; 64 | } 65 | } 66 | 67 | if (!children && row[2]) { 68 | if (Array.isArray(row[2])) children = row[2].map(create); 69 | else children = row[2]; 70 | } 71 | 72 | // 73 | // Note: The attributes that we spread on the Component are **not** 74 | // used as the Through component will clone the child component and 75 | // override the properties. If we do not spread the properties we can 76 | // get prop-type warnings because required props as missing when we 77 | // **create** the component initially, but before it's rendered it does 78 | // receive all props. 79 | // 80 | return ( 81 | 82 | 83 | { children } 84 | 85 | 86 | ); 87 | } 88 | 89 | // 90 | // Optimization: If we're given an SVG element, we need to extract it and 91 | // 92 | if (data[0] === 'Svg') { 93 | props = data[1]; 94 | 95 | if (Array.isArray(props)) { 96 | data = props; 97 | props = {}; 98 | } else { 99 | data = data[2]; 100 | } 101 | 102 | debug('extracted props for Svg', props); 103 | } 104 | 105 | debug(`transforming payload according to specification(${version})`); 106 | return { 107 | svg: data.map(create), 108 | props 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /packages/provider/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | !lib 85 | -------------------------------------------------------------------------------- /packages/provider/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 4.2.1 4 | 5 | - Simplify array spread usage to avoid babel@7 issue [#7597](https://github.com/babel/babel/issues/7597) 6 | 7 | ### 4.2.0 8 | 9 | - Provide default `viewBox` to fallback image. 10 | 11 | ### 4.1.0 12 | 13 | - Allow for a custom asset fallback at the provider-level 14 | 15 | ### 4.0.0 16 | 17 | - Major: Upgraded to use the latest `@babel/*` 7.0 packages. 18 | 19 | ### 3.1.0 20 | 21 | - Fixed main/browser exports to point to ES5 builds 22 | - Various of fixes that caused usage with Next.js to fail. 23 | 24 | ### 2.0.0 - 3.0.0 (failed publish) 25 | 26 | - Fixed a race condition where fetching would still trigger setState when the 27 | component was unmounted already. 28 | - Removed React 15 support. 29 | 30 | ### 1.1.1 31 | 32 | - Allowed the `url` function that was added in 1.1.0 to access the provider 33 | instance through `this`. 34 | 35 | ### 1.1.0 36 | 37 | - Added support for specifying the `uri` as a function for dynamic URL 38 | generation. 39 | 40 | ### 1.0.0 41 | 42 | - Initial public release. 43 | -------------------------------------------------------------------------------- /packages/provider/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/provider/context/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component, createContext } from 'react'; 2 | import Fallback from '../fallback'; 3 | 4 | const { Provider, Consumer } = createContext({ 5 | modifiers: () => ([]), 6 | getItem: [], 7 | Fallback 8 | }); 9 | 10 | /** 11 | * Flatten the consumer and receiver data into a single array. 12 | * 13 | * @param {Array|Function} consumer 14 | * @param {Array|Function} receiver 15 | * @returns {Array} 16 | * @public 17 | */ 18 | function flatten(consumer, receiver) { 19 | return [].concat(consumer).concat(receiver).filter((value, index, all) => { 20 | return value && all.indexOf(value) === index; 21 | }); 22 | } 23 | 24 | /** 25 | * Prosumer is a provider combined with a consumer, so you can inherit from 26 | * for your provider. 27 | * 28 | * @param {Object} props The props. 29 | * @public 30 | */ 31 | function ProSumer({ children, fallback, getItem, modifiers }) { 32 | return ( 33 | 34 | { 35 | (context) => ( 36 | 41 | { children } 42 | 43 | ) 44 | } 45 | 46 | ); 47 | } 48 | 49 | /** 50 | * Wraps the a given component with the Consumer and applies the Context as 51 | * props. 52 | * 53 | * @param {Component} Component The component that needs to receive the context 54 | * @returns {Component} The Consumer wrapped component. 55 | * @public 56 | */ 57 | function withContext(Component) { 58 | const forward = React.forwardRef((props, ref) => ( 59 | 60 | { 61 | (context) => ( 62 | 63 | ) 64 | } 65 | 66 | )); 67 | 68 | forward.displayName = `${Component.name}(consumer)`; 69 | return forward; 70 | } 71 | 72 | export { 73 | withContext, 74 | ProSumer, 75 | Provider, 76 | Consumer 77 | } 78 | -------------------------------------------------------------------------------- /packages/provider/fallback/index.js: -------------------------------------------------------------------------------- 1 | import diagnostics from 'diagnostics'; 2 | import Svg, { G, Path } from 'svgs'; 3 | import React from 'react'; 4 | 5 | // 6 | // Setup our debug util. 7 | // 8 | const debug = diagnostics('asset:provider:wrapper'); 9 | 10 | /** 11 | * Renders a default fallback SVG. 12 | * 13 | * @returns {React.Element} Fallback SVG. 14 | * @public 15 | */ 16 | export default function Fallback() { 17 | debug('rendering the fallback'); 18 | 19 | /*eslint-disable */ 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | /* eslint-enable */ 29 | } 30 | -------------------------------------------------------------------------------- /packages/provider/index.js: -------------------------------------------------------------------------------- 1 | import Provider, { parser, READYSTATES } from './provider'; 2 | import * as Context from './context'; 3 | import Fallback from './fallback'; 4 | import Asset from './asset'; 5 | 6 | export default Provider; 7 | export { Asset, Fallback, Context, parser, READYSTATES }; 8 | -------------------------------------------------------------------------------- /packages/provider/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-provider", 3 | "version": "4.2.1", 4 | "description": "Provides the SVG assets through React Context", 5 | "main": "./lib", 6 | "browser": "./lib", 7 | "module": "./index", 8 | "react-native": "./index", 9 | "scripts": { 10 | "test:web": "mocha --colors --require setup-env --require test/nock.js --require cross-fetch/polyfill test/*.test.js", 11 | "test": "nyc --reporter=text --reporter=lcov npm run test:web", 12 | "prepublish": "npm run build", 13 | "build": "rm -rf ./lib && babel ./index.js -d ./lib && babel ./asset/*.js -d ./lib/asset && babel ./fallback/*.js -d ./lib/fallback && babel ./provider/*.js -d ./lib/provider && babel ./wrapper/*.js -d ./lib/wrapper", 14 | "lint": "eslint-godaddy-react *.js ./provider/*.js ./fallback/*.js ./asset/*.js ./test/*.js" 15 | }, 16 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/provider", 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:godaddy/asset-system.git" 20 | }, 21 | "keywords": [ 22 | "svg", 23 | "asset", 24 | "react", 25 | "react-native", 26 | "assets", 27 | "provider" 28 | ], 29 | "babel": { 30 | "plugins": [ 31 | "@babel/plugin-proposal-object-rest-spread" 32 | ], 33 | "presets": [ 34 | "@babel/preset-env", 35 | "@babel/preset-react" 36 | ] 37 | }, 38 | "browserify": { 39 | "transform": [ 40 | "babelify" 41 | ] 42 | }, 43 | "author": "GoDaddy.com Operating Company, LLC", 44 | "contributors": [ 45 | "Arnout Kazemier " 46 | ], 47 | "license": "MIT", 48 | "devDependencies": { 49 | "@babel/cli": "^7.1.2", 50 | "@babel/core": "^7.1.2", 51 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 52 | "@babel/preset-env": "^7.1.0", 53 | "@babel/preset-react": "^7.0.0", 54 | "@babel/register": "^7.0.0", 55 | "assume": "^1.5.2", 56 | "cross-fetch": "^3.0.3", 57 | "enzyme": "^3.10.0", 58 | "enzyme-adapter-react-16": "^1.14.0", 59 | "eslint": "^4.14.0", 60 | "eslint-config-godaddy-react": "^2.1.0", 61 | "eslint-plugin-json": "^1.2.0", 62 | "eslint-plugin-mocha": "^4.11.0", 63 | "eslint-plugin-react": "^7.5.1", 64 | "jsdom": "^11.5.1", 65 | "mocha": "^4.1.0", 66 | "nock": "^9.1.6", 67 | "nock-knock": "^1.1.0", 68 | "nyc": "^14.1.1", 69 | "prop-types": "^15.6.0", 70 | "react": "^16.8.6", 71 | "react-dom": "^16.8.6", 72 | "react-test-renderer": "^16.8.6", 73 | "setup-env": "^1.2.1" 74 | }, 75 | "dependencies": { 76 | "asset-parser": "^3.0.0", 77 | "diagnostics": "^1.1.0", 78 | "queueback": "^1.2.0", 79 | "rip-out": "^1.0.0", 80 | "svgs": "^3.2.0" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/provider/provider/remote.js: -------------------------------------------------------------------------------- 1 | import diagnostics from 'diagnostics'; 2 | import Queue from 'queueback'; 3 | 4 | // 5 | // Setup our debug util. 6 | // 7 | const debug = diagnostics('asset:provider:remote'); 8 | 9 | // 10 | // Cache and Queue setup to prevent duplicate HTTP requests. 11 | // 12 | const queue = new Queue(); 13 | const cache = {}; 14 | 15 | /** 16 | * Fetch a given resource and parse it's outcome, store it, and send it to 17 | * all other requests that were waiting for the same response. 18 | * 19 | * @param {Object} options Request options. 20 | * @param {AssetParser} parser AssetParser instance 21 | * @param {Function} fn Error first completion callback 22 | * @returns {Undefined|Boolean} Indication of cached, queued or loaded response. 23 | * @public 24 | */ 25 | function remote(options, parser, fn) { 26 | const { url, format } = options; 27 | const id = queue.id('GET', url); 28 | const item = cache[id]; 29 | 30 | // 31 | // Fastest option first, it's already cached, or we already fetching the 32 | // exact same URI. 33 | // 34 | if (item) { 35 | debug(`the requested url(${url}) was already requested and cached`); 36 | return fn(null, item); 37 | } 38 | 39 | if (queue.add('GET', url, fn)) { 40 | debug(`the requested url(${url}) was already requested, waiting for callback`); 41 | return true; 42 | } 43 | 44 | /** 45 | * Generic processing function for parser and loader responses. 46 | * 47 | * @param {Error} err Optional error. 48 | * @param {Object} data Parsed data. 49 | * @private 50 | */ 51 | const loaded = (err, data) => { 52 | const error = err || null; 53 | const svgs = data || {}; 54 | 55 | // 56 | // Worth noting, we don't want to cache errors even if it means that 57 | // multiple requests will be send. In unexpected slowness or downtime of 58 | // a CDN we might be able to fetch a resource in the end. Especially 59 | // important for native devices! 60 | // 61 | if (!error) cache[id] = svgs; 62 | queue.run('GET', url, error, svgs); 63 | }; 64 | 65 | debug(`first time requesting url(${url})`); 66 | fetch(url) 67 | .then(function status(response) { 68 | if (response.status >= 200 && response.status < 300) { 69 | return Promise.resolve(response); 70 | } else { 71 | return Promise.reject(new Error(response.statusText)); 72 | } 73 | }) 74 | .then(function parse(response) { 75 | return response.text(); 76 | }) 77 | .then(function fetched(data) { 78 | parser.parse(format, data, loaded); 79 | }) 80 | .catch(loaded); 81 | 82 | return false; 83 | } 84 | 85 | export { 86 | remote as default, 87 | remote, 88 | queue, 89 | cache 90 | } 91 | -------------------------------------------------------------------------------- /packages/provider/test/index.test.js: -------------------------------------------------------------------------------- 1 | import Provider, { Asset, Fallback, Context, parser } from '../index'; 2 | import { describe, it } from 'mocha'; 3 | import Parser from 'asset-parser'; 4 | import { mount } from 'enzyme'; 5 | import assume from 'assume'; 6 | import React from 'react'; 7 | 8 | describe('Asset Provider', function () { 9 | /** 10 | * Assert that the complex fixture is present in the output. 11 | * 12 | * @param {String} html HTML output 13 | * @private 14 | */ 15 | function complex(html) { 16 | /*eslint-disable */ 17 | assume(html).to.contain('tspan line 1tspan line 2tspan line 3'); 18 | assume(html).to.contain('123456789a'); 19 | assume(html).to.contain('delta on text'); 20 | /* eslint-enable */ 21 | } 22 | 23 | /** 24 | * Assert that the godaddy fixture is present in the output. 25 | * 26 | * @param {String} html HTML output 27 | * @private 28 | */ 29 | function godaddy(html) { 30 | assume(html).to.contain('viewBox="0 0 127 55.2"'); 31 | assume(html).to.contain('viewBox="0 0 127 55.2"'); 32 | assume(html).to.contain(''); 33 | } 34 | 35 | it('exposes all core components', function () { 36 | assume(parser).is.instanceOf(Parser); 37 | 38 | assume(Asset).exists(); 39 | assume(Provider).exists(); 40 | assume(Fallback).is.a('function'); 41 | 42 | assume(Context).is.a('object'); 43 | assume(Context.withContext).is.a('function'); 44 | assume(Context.Provider).exists(); 45 | assume(Context.Consumer).exists(); 46 | }); 47 | 48 | it('renders assets', function (next) { 49 | const wrapper = mount( 50 | 51 | 52 | 53 | ); 54 | 55 | setTimeout(function () { 56 | const html = wrapper.html(); 57 | complex(html); 58 | 59 | next(); 60 | }, 100); 61 | }); 62 | 63 | it('correctly resolves the `uri` function', function (next) { 64 | function uri(resolved) { 65 | setTimeout(function () { 66 | resolved(null, 'http://example.com/complex/bundle.svgs'); 67 | }, 10); 68 | } 69 | 70 | const wrapper = mount( 71 | 72 | 73 | 74 | ); 75 | 76 | setTimeout(function () { 77 | const html = wrapper.html(); 78 | complex(html); 79 | 80 | next(); 81 | }, 100); 82 | }) 83 | 84 | it('renders through nested context', function (next) { 85 | const wrapper = mount( 86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 |
94 | ); 95 | 96 | setTimeout(function () { 97 | const html = wrapper.html(); 98 | 99 | complex(html); 100 | godaddy(html); 101 | 102 | next(); 103 | }, 100); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /packages/provider/test/nock.js: -------------------------------------------------------------------------------- 1 | import { encode, version } from 'asset-parser'; 2 | import * as fixture from './fixtures'; 3 | import route from 'nock-knock'; 4 | import nock from 'nock'; 5 | 6 | // 7 | // Setup Nock for our asset loading tests. 8 | // 9 | nock('http://example.com') 10 | .get('/500') 11 | .times(Infinity) 12 | .reply(500) 13 | .get('/404') 14 | .times(Infinity) 15 | .reply(404) 16 | .get(route('/:fixture/bundle.svgs')) 17 | .times(Infinity) 18 | .delay(10) 19 | .reply(200, function (uri, body, fn) { 20 | const name = uri.split('/')[1]; 21 | const data = fixture[name]; 22 | 23 | encode(version || '0.1.0', data, fn); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/provider/test/remote.test.js: -------------------------------------------------------------------------------- 1 | import { describe, it, beforeEach } from 'mocha'; 2 | import { remote, cache, queue } from '../provider/remote'; 3 | import { decode } from 'asset-parser'; 4 | import { complex } from './fixtures'; 5 | import assume from 'assume'; 6 | 7 | describe('remote', function () { 8 | const url = 'http://example.com/complex/bundle.svg'; 9 | const format = 'bundle'; 10 | const method = 'GET'; 11 | 12 | function parse(form, data, fn) { 13 | if (typeof data === 'string') { 14 | return decode(data, function (err, payload) { 15 | if (err) return fn(err); 16 | 17 | return fn(null, payload.data); 18 | 19 | }); 20 | } 21 | 22 | fn(null, {}); 23 | } 24 | 25 | function nukeCache() { 26 | for (let key in cache) delete cache[key]; 27 | } 28 | 29 | beforeEach(nukeCache); 30 | afterEach(nukeCache); 31 | 32 | it('returns items from cache', function (next) { 33 | const item = queue.id(method, url); 34 | const mock = { hello: 'world' }; 35 | 36 | cache[item] = mock; 37 | 38 | remote({ format, url }, { parse }, (err, data) => { 39 | assume(err).is.a('null'); 40 | assume(data).equals(mock); 41 | 42 | next(); 43 | }); 44 | }); 45 | 46 | it('returns `undefined` when a response is cached', function () { 47 | const item = queue.id(method, url); 48 | const mock = { hello: 'world' }; 49 | 50 | cache[item] = mock; 51 | 52 | assume(remote({ format, url }, { parse }, () => {})).is.a('undefined'); 53 | }); 54 | 55 | it('returns `true` when a request for the same URL is progress', function () { 56 | assume(remote({ format, url }, { parse }, () => {})).is.false(); 57 | assume(remote({ format, url }, { parse }, () => {})).is.true(); 58 | assume(remote({ format, url }, { parse }, () => {})).is.true(); 59 | assume(remote({ format, url }, { parse }, () => {})).is.true(); 60 | }); 61 | 62 | [404, 500].forEach(function each(status) { 63 | it(`does not store items in cache on ${status} error`, function (next) { 64 | assume(cache).is.length(0); 65 | 66 | /*eslint-disable */ 67 | remote({ 68 | url: `http://example.com/${status}`, 69 | format 70 | }, { parse }, (err, data) => { 71 | assume(err.message).is.a('string'); 72 | assume(data).is.a('object'); 73 | 74 | assume(cache).is.length(0); 75 | 76 | next(); 77 | }); 78 | /* eslint-enable */ 79 | }); 80 | }); 81 | 82 | it(`does not store items in cache on error`, function (next) { 83 | assume(cache).is.length(0); 84 | remote({ 85 | url: `http://example-non-existing-nobody-buy-this-domain-plx.lol/`, 86 | format 87 | }, { parse }, (err, data) => { 88 | assume(err.message).is.a('string'); 89 | assume(data).is.a('object'); 90 | assume(cache['GET:http://example-non-existing-nobody-buy-this-domain-plx.lol/']).is.a('undefined'); 91 | 92 | next(); 93 | }); 94 | }); 95 | 96 | it('calls the parser when data is received correctly', function (next) { 97 | remote({ format, url }, { parse: (form, data, fn) => { 98 | assume(form).equals(format); 99 | assume(data).is.a('string'); 100 | assume(fn).is.a('function'); 101 | 102 | decode(data, function (err, payload) { 103 | if (err) return fn(err); 104 | 105 | assume(payload.version).is.a('string'); 106 | assume(payload.data).is.a('object'); 107 | assume(payload.data).deep.equals(complex); 108 | 109 | fn(null, { what: 'lol' }); 110 | }); 111 | } }, (err, data) => { 112 | assume(data).deep.equals({ what: 'lol' }); 113 | 114 | next(err); 115 | }); 116 | }); 117 | 118 | it('caches the correctly parsed data', function (next) { 119 | const item = queue.id(method, url); 120 | const mock = { hello: 'world' }; 121 | 122 | assume(cache).is.length(0); 123 | 124 | remote({ format, url }, { parse: (form, data, fn) => { 125 | fn(null, mock); 126 | } }, (err, data) => { 127 | assume(data).equals(mock); 128 | assume(cache).is.length(1); 129 | assume(cache[item]).equals(mock); 130 | 131 | next(err); 132 | }); 133 | }); 134 | 135 | it('broadcasts the parsed and cached data to all queued fns', function (next) { 136 | next = assume.wait(3, next); 137 | 138 | ['green', 'green', 'green'].forEach(function (name) { 139 | remote({ 140 | url: `http://example.com/${name}/bundle.svgs`, 141 | format 142 | }, { parse }, (err, svgs) => { 143 | assume(svgs).is.a('object'); 144 | next(err); 145 | }); 146 | }); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /packages/provider/wrapper/index.js: -------------------------------------------------------------------------------- 1 | import diagnostics from 'diagnostics'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | import Svg from 'svgs'; 5 | 6 | // 7 | // Setup our debug util. 8 | // 9 | const debug = diagnostics('asset:provider:wrapper'); 10 | 11 | /** 12 | * Wraps svg content with some default Svg properties. 13 | * 14 | * @param {Object} props Properties. 15 | * @returns {Svg} The wrapped Svg. 16 | * @private 17 | */ 18 | export default function SvgWrapper(props) { 19 | const attributes = { ...props }; 20 | 21 | // 22 | // If we're given a viewBox we want to make sure that we preserveAspectRatio 23 | // correctly so set it to `meet`. 24 | // 25 | if (props.viewBox) { 26 | debug('props have viewBox, adding preserveAspectRatio'); 27 | attributes.viewBox = props.viewBox; 28 | attributes.preserveAspectRatio = 'meet'; 29 | } 30 | 31 | return ( 32 | 33 | { props.children } 34 | 35 | ); 36 | } 37 | 38 | /** 39 | * We don't want a pure number, a string value that represents a number 40 | * is fine as well 41 | * 42 | * @type {PropTypes} 43 | * @private 44 | */ 45 | const numeric = PropTypes.oneOfType([ 46 | PropTypes.string, 47 | PropTypes.number 48 | ]).isRequired; 49 | 50 | /** 51 | * Force validation for some default properties that will be required to render 52 | * assets. 53 | * 54 | * Width and Height are required because we to prevent layout changes, so a pre 55 | * existing height/width needs to be known. 56 | * 57 | * @type {Object} 58 | * @private 59 | */ 60 | SvgWrapper.propTypes = { 61 | width: numeric, 62 | height: numeric, 63 | viewBox: PropTypes.string, 64 | children: PropTypes.element, 65 | title: PropTypes.string 66 | }; 67 | -------------------------------------------------------------------------------- /packages/test/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | !lib 85 | -------------------------------------------------------------------------------- /packages/test/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 1.0.0 4 | 5 | - Initial public release. 6 | -------------------------------------------------------------------------------- /packages/test/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/test/README.md: -------------------------------------------------------------------------------- 1 | # asset-test 2 | 3 | This is not the module you are looking for, this simply holds our test fixtures 4 | so they can be easily shared between all packages in this mono repo. 5 | 6 | ## Installation 7 | 8 | Nope, not gonna tell you, because you don't need this package. 9 | 10 | ## License 11 | 12 | [MIT](LICENSE) 13 | -------------------------------------------------------------------------------- /packages/test/entry.js: -------------------------------------------------------------------------------- 1 | global.godaddy = require('./godaddy.svg'); 2 | global.tiger = require('./tiger.svg'); 3 | global.homer = require('./deeper/homer.svg'); 4 | -------------------------------------------------------------------------------- /packages/test/godaddy.json: -------------------------------------------------------------------------------- 1 | ["Svg",{"viewBox":"0 0 127 55.2"},[["Path",{"fill":"#FFF","d":"M8.3 32.5c-3.1 1.1-3.6 2.3-3.4 4.1.2 1.8 2.3 2.6 4.4 2.4"}],["Path",{"fill":"none","stroke":"#231F20","strokeWidth":1.101,"d":"M8.3 32.5c-3.1 1.1-3.6 2.3-3.4 4.1.2 1.8 2.3 2.6 4.4 2.4"}],["Path",{"fill":"#FFF","d":"M4.1 17.6c6.4-2.9 14.4-3.6 19.9.7 5.5 4.3 7.4 15 7.4 17.6 0 .6-4.4 1.1-5.7 2.1-1.6 1.4 2.8 6.7 2.8 9.2 0 0 .5 3.5-2.8 4.3-3.7.8-6.3-2.1-9.3 0-.5.4-2 1.4-3.7 2.3L4.1 17.6"}],["Path",{"fill":"none","stroke":"#231F20","strokeLinecap":"round","strokeWidth":1.101,"d":"M4.1 17.6c6.4-2.9 14.4-3.6 19.9.7 5.5 4.3 7.4 15 7.4 17.6 0 .6-4.4 1.1-5.7 2.1-1.6 1.4 2.8 6.7 2.8 9.2 0 0 .5 3.5-2.8 4.3-3.7.8-6.3-2.1-9.3 0-.5.4-2 1.4-3.7 2.3L4.1 17.6z"}],["Path",{"fill":"#F48120","d":"M15.6 10.3c.3.8.1 1.5-.4 2-.3.2-.8.2-1 .6-.1.5.2.8.7 1 1.1.4 2.1-.3 2.5-1 1.4-2.4-1.3-4.6-1.6-7-.2-1.6 1.2-3.2 1.2-4.7 0-.5-1.6-.6-2.1.3-.8 1.7-2.1 3.6-1.2 5.2.6 1.1 1.4 2.2 1.9 3.6"}],["Path",{"fill":"#F48120","d":"M20.5 10.3c.2.9 0 1.6-.3 2.4-.4 1.1.9 1.5 1.5.9 2.5-2.7 1.3-4.2.3-5.6-1-1.5-1.2-1.8-1.7-2.9-.2-.4-.9-1.5.3-2.7.5-.4.4-1.6-.1-2.1-1.7-1.2-2.2 1-2.8 2.9-.8 2.5 2.1 4.5 2.8 7.1M3.7 12.5c.6.7.6 1.4.3 2-.2.3-.6 1-.7 1.4.1.5.4.1.9.1 1.1.1 1.9-.9 2.1-1.8.5-2.7-2.7-4-3.7-6.2-.7-1.4.2-3.3-.3-4.7-.2-.5-1.7 0-1.9 1-.3 1.9-.9 4 .5 5.3.8 1 1.9 1.8 2.8 2.9M8.3 9.7c.3.8.1 1.6-.1 2.4-.3 1.1 1.3 1.1 1.9.4 1.3-1.7.5-3.8-.3-5.3-.9-1.6-2.2-3-2.3-4.7 0-.5 1-.5 1.5-.7.6-.2 1.8-.8 1.2-1.2C8.3-.5 5.6.8 5.1 2.8c-.6 2.5 2.4 4.4 3.2 6.9"}],["Path",{"fill":"none","stroke":"#231F20","strokeLinecap":"round","strokeWidth":1.101,"d":"M17.6 39.7c2.8 4 4.4 6.4 10.1 7.2"}],["Path",{"fill":"#FFD420","d":"M6 19.1c.4-.3 1.5-3.8 1.5-4-.1-.3 1.5 3.4 1.6 3.6.1.1 3.2.3 3.4.6.2.3-1.9.9-2.2 1.3-.3.3 3.1 4.3 2.9 4.6-.2.3-4.5-2.7-4.7-2.5-.3.2-3.3 3.3-3.4 3.3s.9-4 .7-4.3c-.1-.3-2.3.2-2.5-.2-.1-.4 2.7-2.4 2.7-2.4"}],["Path",{"fill":"#231F20","d":"M7.2 23c-.6.5-.9 1.2-1.5 1.8-.1.1-.1-.1-.1-.2.2-.8.3-1.5.4-2.3.1-.3.2-.5.3-.8.1-.2.1-.4 0-.6-.2-.2-.5 0-.7.1-.6.3-1.2.3-1.8.1.2-.3.5-.4.8-.6.3-.2.7-.3 1-.6.2-.1.4-.1.4-.2.2-.2.4-.4.6-.7.6-.9.6-2.1 1-3.1.5 1 .7 2.2 1.2 3.4 0 .2.3-.1.4.2.4-.3.9-.2 1.3-.2.2 0 .6-.1.8.2-.5.3-1 .6-1.6.8-.2.1-.4.5-.2.8.1.2.3.3.4.4.8.7 1.4 1.6 2 2.5-.2.2-.4 0-.5-.2-.9-.7-1.8-1-2.7-1.8-.4-.5-.9.6-1.5 1m0 1.2c.3-.4.7-1 1.1-.9 1.2.1 2.2 1.1 3.2 1.8.6.4 1.1 1 1.8.9.2 0 .2-.3.4-.5.1-.2-.1-.4-.1-.4-.6-.5-.7-1.3-1.1-1.8-.6-.8-1.1-1.5-1.7-2.3.7-.6 1.5-.7 2.3-1 .2-.1.3-.4.2-.6 0-.2-.2-.3-.2-.3-.6-.1-1.2-.2-1.7-.4-.7-.1-1.4.1-2.1-.3-.3-.1-.3-.6-.4-.9-.3-1.2-.7-2.4-1.2-3.4-.1-.2-.4-.3-.6-.1-.1-.2-.2.1-.1.5.2 1.2-.3 2.4-.6 3.5-.1.2-.2.3-.4.4v.2c-.6.5-1.1.8-1.8 1.2-.6.3-1.1.9-1.6 1.4-.3.1-.4.8-.1.8 1 .3 1.9.2 2.9.1 0 1.2-.3 2.3-.7 3.3l-.3.6c-.2.4.2.8.4.7 1-.6 1.7-1.5 2.4-2.5z"}],["Path",{"fill":"none","stroke":"#231F20","strokeWidth":1.101,"d":"M12.6 28.9c-5.3.2-6.9 1.9-7.8 3-.9 1.1-.8 3.4.2 3.6"}],["Path",{"fill":"#BED63D","d":"M17.9 32.5c2.6 0 4.8-1.7 4.8-3.9 0-2.1-2.1-3.9-4.8-3.9s-4.8 1.7-4.8 3.9 2.1 3.9 4.8 3.9"}],["Ellipse",{"cx":17.9,"cy":28.6,"fill":"none","stroke":"#231F20","strokeWidth":1.101,"rx":4.8,"ry":3.9}],["Path",{"fill":"#BED63D","d":"M30.3 31.6c3 0 5.4-1.2 5.4-2.8 0-1.5-2.4-2.8-5.4-2.8s-5.4 1.1-5.4 2.8c0 1.8 2.4 2.8 5.4 2.8"}],["Path",{"fill":"none","stroke":"#231F20","strokeWidth":1.101,"d":"M30.3 31.6c3 0 5.4-1.2 5.4-2.8 0-1.5-2.4-2.8-5.4-2.8s-5.4 1.1-5.4 2.8c0 1.8 2.4 2.8 5.4 2.8z"}],["Path",{"fill":"#BED63D","d":"M22.7 28.6s2.2-.7 2.2.2"}],["Path",{"fill":"none","stroke":"#231F20","strokeWidth":1.101,"d":"M22.7 28.6s2.2-.7 2.2.2"}],["Path",{"fill":"#231F20","d":"M42 24.5c2.2-2.1 5-1.9 7.1 0 .7.6.3 1.5-.3 2-1.5 1-2.9-.5-4.4 0-1.4.4-2 2.5-2.4 4-1 3.8-1.7 9.3 1.5 11.2.7.4 1.6.2 1.9-.6.5-1.2 1.2-2.8.3-3.6-.3-.2-.9-.6-.9-.8-.7-1.6.7-2.4 1.6-2.7 1.6-.7 4 .6 3.1 3.1 0 .2-.6.4-.6.6 0 1.4-.4 2.5-.6 3.9-.1 1-.9 1.4-1.3 2.2-.6.6-1.3.4-1.9.8-2.8 1.6-6.1-.8-6.6-4.9-.9-5.6-.3-11.7 3.5-15.2M53.9 35.7c-3.2-1 .1-6.6.8-5.6 1.6 2.4 1.4 6.3-.8 5.6m-1 4.1c.8.6 2.4.6 3.1-.4 1.6-2.3 2.8-5 2.1-8.2-.3-1.4-.7-2.8-1.5-4-1.2-1.7-3.3-2.4-4-.2-.4 1.1-1.5 2.3-2 4.6-.7 3.1.2 6.5 2.3 8.2zM68.8 39.7c-.3 0-.6-13-.6-14.3 2.4.4 4.1 3.2 4.8 5.8.6 2.2-.4 4.5-1.3 6.4-.5 1-1.7 2.4-2.9 2.1m-2.5 3.4c1.4.7 2.9.2 4.1-.7 3.9-2.6 6.9-9.5 3.9-15-1.2-2.1-2.4-3.6-3.9-5-1.2-1-2.6-1.4-3.7-1.4-.7-.2-1.8 0-2.1 1.4 0 .2-.1.7 0 .9.2.5.8 1.1.8 1.6.4 4.9.8 9.6.7 14.3 0 .7-.8 1-1 1.5-.3.8.5 2 1.2 2.4zM83 41.1c-.2.8-4.6-.1-4.6-1.9.1-3.5 3.2-2.8 5-3.7.7 2.1-.1 3.5-.4 5.6m.6 4.9c1.7 1.5 1.9-3.9 2-6.4.2-3.8 1.7-7.3.5-10.7-1.4-3.8-6-2.7-8.2-.7-.3.4 0 3.7 1.2 3.1 1.6-.7 2.8-1.8 4.6-1.3.7.2.5 2 .2 2.6-.1.1-4.4-1.3-6.9 3.2-.8 1.3-1.1 4.3-.3 5.7 1.2 2.7 3.7 2.6 5.9 3.1.4-.1.4 1 1 1.4zM90 35.4c-.3-2.5 2.2-3.7 3.8-3.7 0 2.3-.6 4.5-.3 6.9-1.6.7-3.3-1.2-3.5-3.2m-3.1.6c0 3 1.9 4.4 3.4 5.7 1.1 1 2.3.6 3.5.6.7.2.7 2.8 2 2.6.5 0 1.2-.6 1.2-1-.9-6.9-1-14 .7-20.4.1-.8-.6-1.4-.9-1.6-.7-.4-1.6.4-2 1.4-.5 1.4-.2 3.5-.9 4.3-2.3 2.5-7 3.3-7 8.4zM101.4 35.4c-.3-2.5 2.2-3.7 3.8-3.7 0 2.3-.6 4.5-.3 6.9-1.6.7-3.3-1.2-3.5-3.2m-3.1.6c0 3 1.9 4.4 3.4 5.7 1.1 1 2.3.6 3.5.6.7.2.7 2.8 2 2.6.5 0 1.2-.6 1.2-1-.9-6.9-1-14 .7-20.4.1-.8-.6-1.4-.9-1.6-.7-.4-1.6.4-2 1.4-.5 1.4-.2 3.5-.9 4.3-2.3 2.5-7 3.3-7 8.4z"}],["Path",{"fill":"#231F20","d":"M110.5 26.1c.6-1.2 2.2-1.4 2.7 0 .7 2 .2 4.5.5 6.9.2.6.5 0 .6-.2.9-2.6 1.5-5.6 3.3-7.1.8-.6 2.7-.2 2 1.6-1.6 4-3.8 7.9-4.1 12.7-.2 2.9-2.3 11.2-3.8 13.8-1.1 1.8-3.8 2-4.6 0-.5-1.2-.9-3 .2-3.8.8-.6 1.8.4 2 1.6 2.1-2.2 2.4-6.6 2.9-10.1.3-2.2-1.1-4-1.2-6-.2-2.2-.5-4.3-.6-6.5-.1-1.1-.3-2.3.1-2.9M127 25.8c0 .4-.1.9-.3 1.2-.2.4-.5.7-.9.9-.4.2-.8.3-1.2.3-.4 0-.9-.1-1.2-.3-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.4.1-.9.3-1.2.2-.4.5-.7.9-.9.4-.2.8-.3 1.2-.3.4 0 .9.1 1.2.3.4.2.7.5.9.9.2.3.3.7.3 1.2m-.5 0c0-.5-.2-1-.6-1.4-.4-.4-.9-.6-1.4-.6-.5 0-1 .2-1.4.6-.4.4-.6.9-.6 1.4s.2 1 .6 1.4c.4.4.9.6 1.4.6.5 0 1-.2 1.4-.6.4-.4.6-.9.6-1.4zm-3.1-1.4h1.2c.3 0 .6.1.7.2.2.1.2.3.2.5s-.1.3-.2.5c-.1.1-.3.2-.5.3.1 0 .2.1.2.1.1.1.1.2.2.3 0 0 .1.3.4.8h-.8c-.3-.5-.4-.8-.5-.9-.1-.1-.2-.2-.3-.2h-.1v1.1h-.7v-2.7zm.7 1.2h.3c.2 0 .3 0 .4-.1.1-.1.1-.1.1-.2s0-.2-.1-.2c-.1-.1-.2-.1-.4-.1h-.3v.6z"}]]] -------------------------------------------------------------------------------- /packages/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-test", 3 | "version": "1.0.0", 4 | "description": "Test fixtures for the asset-system", 5 | "main": "./lib", 6 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/test", 7 | "repository": { 8 | "type": "git", 9 | "url": "git@github.com:godaddy/asset-system.git" 10 | }, 11 | "scripts": { 12 | "prepublishOnly": "npm run build", 13 | "build": "rm -rf ./lib && babel ./src/*.js -d ./lib" 14 | }, 15 | "keywords": [ 16 | "Asset", 17 | "SVG", 18 | "test", 19 | "tests" 20 | ], 21 | "babel": { 22 | "plugins": [ 23 | "@babel/plugin-proposal-object-rest-spread" 24 | ], 25 | "presets": [ 26 | "@babel/preset-env", 27 | "@babel/preset-react" 28 | ] 29 | }, 30 | "browserify": { 31 | "transform": [ 32 | "babelify" 33 | ] 34 | }, 35 | "author": "GoDaddy.com Operating Company, LLC", 36 | "contributors": [ 37 | "Arnout Kazemier " 38 | ], 39 | "license": "MIT", 40 | "devDependencies": { 41 | "@babel/cli": "^7.1.2", 42 | "@babel/core": "^7.1.2", 43 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 44 | "@babel/preset-env": "^7.1.0", 45 | "@babel/preset-react": "^7.0.0", 46 | "@babel/register": "^7.0.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/test/src/index.js: -------------------------------------------------------------------------------- 1 | import godaddy from '../godaddy.json'; 2 | import path from 'path'; 3 | 4 | /** 5 | * Location of the svgs. 6 | * 7 | * @type {Object} 8 | * @public 9 | */ 10 | const svgs = { 11 | godaddy: path.join(__dirname, '..', 'godaddy.svg'), 12 | homer: path.join(__dirname, '..', 'homer.svg'), 13 | tiger: path.join(__dirname, '..', 'tiger.svg'), 14 | }; 15 | 16 | /** 17 | * Location of the fixtures. 18 | * 19 | * @type {String} 20 | * @public 21 | */ 22 | const fixtures = path.join(__dirname, '..'); 23 | 24 | /** 25 | * Create fixtures, because we really don't different dependencies for 26 | * React and Svgs as that will make `instanceOf` checks unreliable as they 27 | * might be imported from different locations. 28 | * 29 | * @param {Object} svgs The svgs library. 30 | * @param {React} React React 31 | * @returns {Object} Fixtures. 32 | * @public 33 | */ 34 | function create(svgs, React) { 35 | const { Svg, G, Text, Rect, Circle, TSpan } = svgs; 36 | 37 | return { 38 | json: { godaddy }, 39 | g: { 40 | structure: [ 41 | ['G'] 42 | ], 43 | output: ( 44 | 45 | ) 46 | }, 47 | green: { 48 | structure: [ 49 | ['G', { fill: 'green' }] 50 | ], 51 | output: ( 52 | 53 | ) 54 | }, 55 | childs: { 56 | structure: [ 57 | ['G', [ 58 | ['Rect', { dy: 0, dx: 0 }], 59 | ['Circle', { dy: 0, dx: 0, radius: 0 }] 60 | ] 61 | ]], 62 | output: ( 63 | 64 | 65 | 66 | 67 | 68 | 69 | ) 70 | }, 71 | complex: { 72 | structure: [ 73 | ['G', 74 | [ 75 | ['Text', { y: 20, dx: '5 5' }, [ 76 | ['TSpan', { x: 10 }, 'tspan line 1'], 77 | ['TSpan', { x: 10, dy: 15 }, 'tspan line 2'], 78 | ['TSpan', { x: 10, dy: 15, dx: 10 }, 'tspan line 3'] 79 | ]], 80 | ['Text', { x: 10, y: 60, fill: 'red', fontSize: 14 }, [ 81 | ['TSpan', { dy: '5 10 20' }, '12345'], 82 | ['TSpan', { fill: 'blue', dy: 15, dx: '0 5 5' }, [ 83 | ['TSpan', '6'], 84 | ['TSpan', '7'] 85 | ]], 86 | ['TSpan', { dx: '0 10 20', dy: '0 20', fontWeight: 'bold', fontSize: 12 }, '89a'] 87 | ]], 88 | ['Text', { y: 140, dx: '0 5 5', dy: '0 -5 -5' }, 'delta on text'] 89 | ] 90 | ] 91 | ], 92 | output: ( 93 | 94 | 95 | 96 | tspan line 1 97 | tspan line 2 98 | tspan line 3 99 | 100 | 101 | 12345 102 | 103 | 6 104 | 7 105 | 106 | 89a 107 | 108 | delta on text 109 | 110 | 111 | ) 112 | } 113 | }; 114 | } 115 | 116 | export { 117 | create, 118 | fixtures, 119 | svgs as default 120 | } 121 | -------------------------------------------------------------------------------- /packages/webpack/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OSX 6 | .DS_Store 7 | 8 | # Xcode 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | 34 | # BUCK 35 | buck-out/ 36 | \.buckd/ 37 | *.keystore 38 | 39 | # fastlane 40 | # 41 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 42 | # screenshots whenever they are needed. 43 | # For more information about the recommended setup visit: 44 | # https://docs.fastlane.tools/best-practices/source-control/ 45 | 46 | */fastlane/report.xml 47 | */fastlane/Preview.html 48 | */fastlane/screenshots 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | 55 | # Directory for instrumented libs generated by jscoverage/JSCover 56 | lib-cov 57 | 58 | # Coverage directory used by tools like istanbul/nyc 59 | coverage 60 | .nyc_output 61 | 62 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 63 | .grunt 64 | 65 | # node-waf configuration 66 | .lock-wscript 67 | 68 | # Compiled binary addons (http://nodejs.org/api/addons.html) 69 | build/Release 70 | 71 | # Dependency directory and Node.js specific files 72 | node_modules 73 | npm-debug.log 74 | yarn-error.log 75 | 76 | # IDE 77 | .idea 78 | *.iml 79 | 80 | # Ignore build directories 81 | dist 82 | 83 | # Compiled source 84 | !lib 85 | -------------------------------------------------------------------------------- /packages/webpack/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 6.0.0 4 | 5 | - Major: Upgraded to latest `asset-bundle`. 6 | 7 | ### 5.0.0 8 | 9 | - Major: Upgraded to use the latest `@babel/*` 7.0 packages. 10 | - Major: Upgraded to `Webpack@4` syntax. 11 | 12 | ### 4.0.0 13 | 14 | - Major: Introduced a new `loader` method that will take care of the file 15 | content renaming so we can leverage the build-in functionality of `file-loader`. 16 | 17 | ### 3.0.0 18 | 19 | - Bump `asset-bundle` to `3.0.0` which moved `puppeteer` to an optional 20 | dependency. 21 | 22 | ### 2.0.0 23 | 24 | - Point main/browser to ES5 builds. 25 | - Various of fixes to make it work with Next.js based webpacks. 26 | 27 | ### 1.0.0 28 | 29 | - Initial public release. 30 | -------------------------------------------------------------------------------- /packages/webpack/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 GoDaddy Operating Company, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/webpack/README.md: -------------------------------------------------------------------------------- 1 | # asset-webpack 2 | 3 | A WebPack plugin that combines all your SVG assets into a `asset-provider` 4 | compatible bundle. 5 | 6 | ## Table of Contents 7 | 8 | - [Install](#install) 9 | - [Usage](#usage) 10 | - [Specify the `file-loader` for .svg extensions](#specify-the-file-loader-for-svg-extensions) 11 | - [Configure plugin](#configure-plugin) 12 | - [Producing a bundle](#producing-a-bundle) 13 | - [License](#license) 14 | 15 | ## Install 16 | 17 | ``` 18 | npm install --save asset-webpack 19 | ``` 20 | 21 | ## Usage 22 | 23 | Import the loader in to your `webpack.config.js`: 24 | 25 | ```js 26 | import SVGBundler from 'asset-webpack'; 27 | 28 | const bundle = new SVGBundler('bundle.svgs', { /* opts */ }); 29 | ``` 30 | 31 | The first argument is the name the bundle. It should be single file name, and 32 | it will be placed in the output directory that you specified in the WebPack 33 | configuration. The filename supports the following (template) replacements: 34 | 35 | - `[hash]` Will be replaced with a MD5 hash of the resulting bundle. 36 | 37 | The second argument is an **options** object that allows you to further 38 | configure the plugin, and the bundle system. The following options are 39 | supported: 40 | 41 | - `namespace` Should we use the folder structure of the assets as namespaces, 42 | defaults to `false`. 43 | - `root` Location of the root directory that used to generate the namespaces. 44 | - `bundler` Options that will be passed in to the `asset-bundle` constructor. 45 | - `modify` An object where the key is the name of the modifer and the value the 46 | function that does the modification. This will be passed in to `bundle#modify`. 47 | - `plugins` Plugins that need to be assigned to the `asset-bundle` instance. 48 | The plugins option should be an `Array` of arrays which is spread on the 49 | `bundle#plugin` method. 50 | 51 | ```js 52 | ... 53 | namespace: true, 54 | bundler: { 55 | multipass: true, 56 | }, 57 | modify: { 58 | color: function () { .. } 59 | }, 60 | plugins: [ 61 | [Constructor, { options }, args, etc], 62 | [Constructor, { options }, args, etc] 63 | ] 64 | ... 65 | ``` 66 | 67 | ### Configure the loader 68 | 69 | First we need to ensure that `.svg` files are handled by the bundle. Update the 70 | module rules/loaders to contain the following: 71 | 72 | ```js 73 | module: { 74 | rules: [ 75 | { test: /\.svg$/, use: bundle.loader() } 76 | ] 77 | } 78 | ``` 79 | 80 | The `bundle.loader()` will return a correctly pre-configured loader so we can 81 | replace the contents of the file, and introduce it into the svgs bundle. 82 | 83 | ### Insert the bundle instance as `plugin` 84 | 85 | Last but not least, pass the created `bundle` instance into the `plugin` array 86 | of your `webpack.config.js`: 87 | 88 | ```js 89 | { 90 | ..., 91 | plugins: [ bundle ] 92 | } 93 | ``` 94 | 95 | And done, you've completed the following steps: 96 | 97 | - Created a new Bundle instance 98 | - Used it's loader method to configure the correct loader 99 | - Passed the instance into the `plugin` array 100 | 101 | ### Producing a bundle 102 | 103 | Create the `entry` file that requires the SVG assets: 104 | 105 | ```js 106 | require('./file.svg'); 107 | require('./another.svg'); 108 | require('./more.svg'); 109 | ``` 110 | 111 | In addition to producing the bundled SVG, we've also rewritten the contents of 112 | the required file. The file will now return the `name` of your SVG how it's 113 | stored inside the bundle. 114 | 115 | ```js 116 | import Provider, { Asset } from 'asset-provider'; 117 | import React, { Component } from 'react'; 118 | import upload from './upload.svg'; 119 | import file from './file.svg'; 120 | 121 | export default class Example extends Component { 122 | render() { 123 | return ( 124 | 125 |
126 |

Upload

127 | 128 | 131 | 132 | 133 |
134 |
135 | ); 136 | } 137 | } 138 | ``` 139 | 140 | ## License 141 | 142 | [MIT](LICENSE) 143 | -------------------------------------------------------------------------------- /packages/webpack/index.js: -------------------------------------------------------------------------------- 1 | import { createHash } from 'crypto'; 2 | import Bundle from 'asset-bundle'; 3 | import async from 'async'; 4 | import path from 'path'; 5 | 6 | /** 7 | * Get the name of the file that required this module, so we can attempt to 8 | * resolve a sensible default root directory for the plugin. 9 | * 10 | * @type {String} 11 | * @private 12 | */ 13 | var requiredRoot = module.parent && module.parent.filename; 14 | delete require.cache[__filename]; 15 | 16 | if (!requiredRoot || !~requiredRoot.indexOf('webpack.config.js')) { 17 | requiredRoot = process.cwd(); 18 | } 19 | 20 | class WebPack { 21 | constructor(name = 'bundle.svgs', options = {}) { 22 | const { plugins, namespace, bundler, modify, root } = options; 23 | 24 | this.name = name; // Name of the file that needs to be written to disk. 25 | this.output = ''; // Output directly that we receive from webpack. 26 | 27 | // 28 | // Create the asset-bundle, but instead of passing it with paths 29 | // we need to pass it an empty array. We're going to call the processing 30 | // steps ourselves as we don't have the assets yet. 31 | // 32 | this.bundle = new Bundle([], { 33 | root: namespace ? path.dirname(root || requiredRoot) : null, 34 | ...(bundler || {}) 35 | }); 36 | 37 | // 38 | // Assign plugins to the provider if we have them. 39 | // 40 | if (Array.isArray(plugins)) plugins.forEach((args) => { 41 | const Constructor = args.shift(); 42 | const opts = args.shift() || {}; 43 | 44 | Object.defineProperty(opts, 'file', { 45 | get: () => { 46 | return path.join(this.output, name); 47 | } 48 | }); 49 | 50 | this.bundle.plugin(Constructor, opts); 51 | }); 52 | 53 | // 54 | // Assign modify functions to the bundler. 55 | // 56 | if (typeof modify === 'object') Object.keys(modify).forEach((key) => { 57 | this.bundle.modify(key, modify[key]); 58 | }); 59 | } 60 | 61 | /** 62 | * Generate the filename. 63 | * 64 | * @param {String} contents The bundle that was produced. 65 | * @returns {String} The filename. 66 | * @private 67 | */ 68 | hash(contents) { 69 | if (!~this.name.indexOf('[')) return this.name; 70 | 71 | const hash = createHash('md5'); 72 | hash.update(contents); 73 | 74 | return this.name.replace('[hash]', hash.digest('hex')); 75 | } 76 | 77 | /** 78 | * Returns the loader logic for a given WebPack Rule. This ensures that the 79 | * loader will be correctly configured. 80 | * 81 | * @returns {Object} Use information for WebPack 82 | * @public 83 | */ 84 | loader() { 85 | return { 86 | loader: require.resolve('./loader'), 87 | options: { 88 | external: '[path][name].[ext]', 89 | internal: (file) => this.bundle.name(file) 90 | } 91 | }; 92 | } 93 | 94 | /** 95 | * Apply the plugin. 96 | * 97 | * @param {Object} compiler The WebPack compiler 98 | * @private 99 | */ 100 | apply(compiler) { 101 | compiler.hooks.thisCompilation.tap('asset-webpack', (compilation) => { 102 | const options = compilation.options; 103 | const output = options.output.path; 104 | const bundle = this.bundle; 105 | 106 | this.output = output; 107 | 108 | compilation.hooks.optimizeAssets.tapAsync('asset-webpack', (assets, next) => { 109 | const files = []; 110 | 111 | compilation.modules.forEach((module) => { 112 | const loc = module.userRequest; 113 | 114 | if (!loc || !loc.match(/\.svg$/)) return; 115 | 116 | const buildInfo = module.buildInfo; 117 | const hash = Object.keys(buildInfo.assets)[0]; 118 | const data = buildInfo.assets[hash].source(); 119 | const name = bundle.name(loc); 120 | 121 | // 122 | // We want to avoid addition reads in our bundle process. So 123 | // we're going to mimic the output of the `bundle#parse` and 124 | // create the resulting structure our selfs. 125 | // 126 | files.push({ name, loc, data }); 127 | 128 | // 129 | // Now we don't want this file to end up in the bundle so we need 130 | // to remove it. In addition to that we need to rewrite the source. 131 | // So the `require(svgfile)` will just return the `name` of which 132 | // it's known in the bundle 133 | // 134 | delete assets[hash]; 135 | }); 136 | 137 | // 138 | // Because we've already read the paths, we need to manually 139 | // execute the processing waterfall. 140 | // 141 | async.waterfall([ 142 | bundle.optimize.bind(bundle, files), 143 | bundle.parse.bind(bundle), 144 | bundle.viewBox.bind(bundle), 145 | bundle.traverse.bind(bundle), 146 | bundle.encode.bind(bundle) 147 | ], (err, contents) => { 148 | if (err) throw err; 149 | 150 | const hash = this.hash(contents); 151 | const filename = compilation.getPath(hash, {}); 152 | 153 | // 154 | // Register the new asset. 155 | // 156 | assets[filename] = { 157 | source: () => contents, 158 | size: () => contents.length 159 | }; 160 | 161 | next(); 162 | }); 163 | }); 164 | }); 165 | } 166 | } 167 | 168 | // 169 | // Use the Node.js exports so we can require the code without .default 170 | // 171 | module.exports = WebPack; 172 | -------------------------------------------------------------------------------- /packages/webpack/loader.js: -------------------------------------------------------------------------------- 1 | import utils from 'loader-utils'; 2 | 3 | /** 4 | * The smallest loader we could get away with. 5 | * 6 | * @param {Content} content The content that needs to be loaded. 7 | * @returns {String} Name of the file in the svg bundle. 8 | */ 9 | module.exports = function svgLoader(content) { 10 | const options = utils.getOptions(this) || {}; 11 | const context = options.context || this.rootContext || this.options && this.options.context; 12 | const opts = { context, content }; 13 | 14 | // 15 | // Create internal and external names: 16 | // 17 | // Internal: How it ends up in the bundle so can be used as name prop. 18 | // External: Simple name to identify it during the WebPack plugin phase. 19 | // 20 | const external = utils.interpolateName(this, options.external, opts); 21 | const internal = utils.interpolateName(this, options.internal, opts); 22 | 23 | // 24 | // Ensures that these custom files end-up in `optimize-assets` event of 25 | // WebPack so we can intercept them. 26 | // 27 | this.emitFile(external, content); 28 | return `module.exports = ${JSON.stringify(internal)};`; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-webpack", 3 | "version": "6.0.0", 4 | "description": "asset-bundle integration for WebPack", 5 | "main": "./lib", 6 | "browser": "./lib", 7 | "module": "./index", 8 | "react-native": "./index", 9 | "scripts": { 10 | "test:web": "mocha --colors --require setup-env test/*.test.js", 11 | "test": "npm run build && nyc --reporter=text --reporter=lcov npm run test:web", 12 | "prepublish": "npm run build", 13 | "build": "babel ./index.js ./loader.js -d ./lib", 14 | "lint": "eslint-godaddy *.js ./test/*.js" 15 | }, 16 | "homepage": "https://github.com/godaddy/asset-system/tree/master/packages/webpack", 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:godaddy/asset-system.git" 20 | }, 21 | "author": "GoDaddy.com Operating Company, LLC", 22 | "contributors": [ 23 | "Arnout Kazemier " 24 | ], 25 | "license": "MIT", 26 | "babel": { 27 | "plugins": [ 28 | "@babel/plugin-proposal-object-rest-spread" 29 | ], 30 | "presets": [ 31 | "@babel/preset-env", 32 | "@babel/preset-react" 33 | ] 34 | }, 35 | "browserify": { 36 | "transform": [ 37 | "babelify" 38 | ] 39 | }, 40 | "nyc": { 41 | "exclude": [ 42 | "lib" 43 | ] 44 | }, 45 | "devDependencies": { 46 | "@babel/cli": "^7.1.2", 47 | "@babel/core": "^7.1.2", 48 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 49 | "@babel/preset-env": "^7.1.0", 50 | "@babel/preset-react": "^7.0.0", 51 | "@babel/register": "^7.0.0", 52 | "asset-test": "^1.0.0", 53 | "assume": "^1.5.2", 54 | "eslint": "^4.14.0", 55 | "eslint-config-godaddy": "^2.1.0", 56 | "eslint-plugin-json": "^1.2.0", 57 | "eslint-plugin-mocha": "^4.11.0", 58 | "eslint-plugin-react": "^7.5.1", 59 | "mocha": "^4.1.0", 60 | "nyc": "^11.4.1", 61 | "puppeteer": "^1.9.0", 62 | "setup-env": "^1.2.1", 63 | "webpack": "^4.20.2" 64 | }, 65 | "dependencies": { 66 | "asset-bundle": "^4.1.0", 67 | "async": "^2.6.0", 68 | "loader-utils": "^1.1.0" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/webpack/test/index.test.js: -------------------------------------------------------------------------------- 1 | import { createContext, runInContext } from 'vm'; 2 | import { fixtures } from 'asset-test'; 3 | import { describe, it } from 'mocha'; 4 | import Pipeline from '../index.js'; 5 | import Bundle from 'asset-bundle'; 6 | import webpack from 'webpack'; 7 | import assume from 'assume'; 8 | import path from 'path'; 9 | import fs from 'fs'; 10 | 11 | const entry = path.join(fixtures, 'entry.js'); 12 | 13 | describe('Asset Pipeline', function () { 14 | let pipeline; 15 | 16 | function setup(filename, options) { 17 | pipeline = new Pipeline(filename, options); 18 | } 19 | 20 | it('exposes the constructor through module.exports', function () { 21 | const Packer = require('../'); 22 | 23 | assume(Packer).is.a('function'); 24 | 25 | const test = new Packer('example', {}); 26 | 27 | assume(test.hash).is.a('function'); 28 | assume(test.apply).is.a('function'); 29 | }); 30 | 31 | describe('#loader', function () { 32 | beforeEach(function each() { 33 | setup('filename.svgs'); 34 | }); 35 | 36 | it('has a loader method', function () { 37 | assume(pipeline.loader).is.a('function'); 38 | }); 39 | 40 | it('pre-configures the loader to our own loader', function () { 41 | const loader = pipeline.loader(); 42 | 43 | assume(loader).is.a('object'); 44 | assume(loader.loader).equals(require.resolve('../loader')); 45 | }); 46 | 47 | it('provides a internal override function that uses bundle#name', function () { 48 | const loader = pipeline.loader(); 49 | const name = loader.options.internal; 50 | 51 | [ 52 | path.join(__dirname, 'test.svg'), 53 | path.join(__dirname, 'test/bar.svg') 54 | ].forEach(function (filename) { 55 | assume(name(filename)).equals(pipeline.bundle.name(filename)); 56 | }); 57 | }); 58 | }); 59 | 60 | describe('#hash', function () { 61 | const content = '08080ad8fa0d98f0sd98fa0sd98fa0s lol what is this'; 62 | 63 | it('returns the filename if theres no replace tokens', function () { 64 | setup('example.svgs'); 65 | 66 | const name = pipeline.hash(content); 67 | assume(name).equals('example.svgs'); 68 | }); 69 | 70 | it('replaces [hash] with md5 hash', function () { 71 | setup('example.[hash].svgs'); 72 | 73 | const name = pipeline.hash(content); 74 | assume(name).equals('example.a09246d44e397b3903a4fc5efd6b9566.svgs'); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('WebPack Integration', function () { 80 | this.timeout(20000); 81 | 82 | function clonepack(merge, fn) { 83 | const pipeline = new Pipeline('bundle.svgs', { 84 | root: entry, 85 | namespace: true 86 | }); 87 | 88 | const config = { 89 | entry: entry, 90 | output: { 91 | path: path.resolve(__dirname, 'dist'), 92 | filename: 'output.js' 93 | }, 94 | 95 | module: { 96 | rules: [ 97 | { test: /\.svg$/, use: pipeline.loader() } 98 | ] 99 | }, 100 | 101 | plugins: [ 102 | pipeline 103 | ] 104 | }; 105 | 106 | webpack({ ...config, ...merge }, fn); 107 | } 108 | 109 | it('executes the plugin without errors', function (next) { 110 | clonepack({}, function (err, stats) { 111 | if (err) return next(err); 112 | 113 | if (stats.hasErrors()) { 114 | return next(stats.toString()); 115 | } 116 | 117 | const dist = path.join(__dirname, 'dist'); 118 | const bundle = fs.readFileSync(path.join(dist, 'bundle.svgs'), 'utf-8'); 119 | const output = fs.readFileSync(path.join(dist, 'output.js'), 'utf-8'); 120 | 121 | // 122 | // Ensure that the imports are rewritten 123 | // 124 | const sandbox = {}; 125 | createContext(sandbox); 126 | runInContext(output, sandbox); 127 | 128 | // 129 | // Validate that the require statements return the name of the asset. 130 | // 131 | assume(sandbox.godaddy).equals('godaddy'); 132 | assume(sandbox.homer).equals('deeper/homer'); 133 | assume(sandbox.tiger).equals('tiger'); 134 | 135 | // 136 | // Validate that these names are actually in the bundle. 137 | // 138 | assume(bundle).includes('"deeper/homer":'); 139 | assume(bundle).includes('"godaddy":'); 140 | assume(bundle).includes('"tiger":'); 141 | 142 | next(); 143 | }); 144 | }); 145 | 146 | it('allows plugin configuration through webpack', function (next) { 147 | clonepack({ 148 | plugins: [ 149 | new Pipeline('bungle.svgs', { 150 | plugins: [ 151 | [ 152 | function example(bundle, opts) { 153 | assume(bundle).is.instanceOf(Bundle); 154 | assume(opts).is.a('object'); 155 | assume(opts.file).is.a('string'); 156 | assume(opts.file).includes('bungle.svgs'); 157 | assume(opts.foo).equals('bar'); 158 | 159 | next(); 160 | }, 161 | { foo: 'bar' } 162 | ] 163 | ] 164 | }) 165 | ] 166 | }, function () { /* ignore me */}); 167 | }); 168 | 169 | it('allows modify configuration through webpack', function (next) { 170 | let called = false; 171 | clonepack({ 172 | plugins: [ 173 | new Pipeline('bungle.svgs', { 174 | modify: { 175 | foo: function () { 176 | if (!called) { 177 | called = true; 178 | next(); 179 | } 180 | } 181 | } 182 | }) 183 | ] 184 | }, function () { /* ignore me */}); 185 | }); 186 | }); 187 | --------------------------------------------------------------------------------