├── app ├── utils │ └── .gitkeep ├── app.icns ├── constants │ └── routes.json ├── Routes.js ├── reducers │ ├── index.js │ ├── types.js │ └── counter.js ├── store │ ├── configureStore.js │ ├── configureStore.prod.js │ └── configureStore.dev.js ├── Root.js ├── containers │ └── Home │ │ ├── styles.scss │ │ └── index.jsx ├── index.js ├── actions │ └── counter.js ├── globalStyles.scss ├── app.html ├── main.dev.js └── menu.js ├── internals ├── mocks │ └── fileMock.js ├── flow │ ├── WebpackAsset.js.flow │ └── CSSModule.js.flow ├── img │ ├── js.png │ ├── npm.png │ ├── eslint.png │ ├── flow.png │ ├── jest.png │ ├── react.png │ ├── redux.png │ ├── yarn.png │ ├── webpack.png │ ├── flow-padded.png │ ├── jest-padded.png │ ├── js-padded.png │ ├── yarn-padded.png │ ├── eslint-padded.png │ ├── react-padded.png │ ├── react-router.png │ ├── redux-padded.png │ ├── eslint-padded-90.png │ ├── flow-padded-90.png │ ├── jest-padded-90.png │ ├── react-padded-90.png │ ├── redux-padded-90.png │ ├── webpack-padded.png │ ├── yarn-padded-90.png │ ├── webpack-padded-90.png │ ├── react-router-padded.png │ └── react-router-padded-90.png └── scripts │ ├── CheckNodeEnv.js │ ├── CheckPortInUse.js │ └── CheckBuiltsExist.js ├── resources ├── icon.icns ├── icon.ico ├── icon.png └── icons │ ├── 16x16.png │ ├── 24x24.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 64x64.png │ ├── 96x96.png │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 256x256.png │ └── 512x512.png ├── .testcafe-electron-rc ├── flow-typed └── module_vx.x.x.js ├── .stylelintrc ├── .gitattributes ├── test ├── example.js ├── e2e │ ├── helpers.js │ └── HomePage.e2e.js ├── .eslintrc ├── actions │ ├── __snapshots__ │ │ └── counter.spec.js.snap │ └── counter.spec.js ├── reducers │ ├── __snapshots__ │ │ └── counter.spec.js.snap │ └── counter.spec.js ├── components │ ├── __snapshots__ │ │ └── Counter.spec.js.snap │ └── Counter.spec.js └── containers │ └── CounterPage.spec.js ├── renovate.json ├── configs ├── webpack.config.eslint.js ├── webpack.config.base.js ├── webpack.config.renderer.dev.dll.babel.js ├── webpack.config.main.prod.babel.js ├── webpack.config.renderer.prod.babel.js └── webpack.config.renderer.dev.babel.js ├── .prettierrc ├── .editorconfig ├── appveyor.yml ├── README.md ├── .gitignore ├── .flowconfig ├── .dockerignore ├── .eslintignore ├── LICENSE ├── .travis.yml ├── babel.config.js ├── .eslintrc ├── package.json └── CHANGELOG.md /app/utils/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /internals/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /app/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/app/app.icns -------------------------------------------------------------------------------- /app/constants/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "HOME": "/", 3 | "COUNTER": "/counter" 4 | } -------------------------------------------------------------------------------- /internals/flow/WebpackAsset.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | declare export default string 3 | -------------------------------------------------------------------------------- /resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icon.icns -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icon.ico -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icon.png -------------------------------------------------------------------------------- /.testcafe-electron-rc: -------------------------------------------------------------------------------- 1 | { 2 | "mainWindowUrl": "./app/app.html", 3 | "appPath": "." 4 | } 5 | -------------------------------------------------------------------------------- /flow-typed/module_vx.x.x.js: -------------------------------------------------------------------------------- 1 | declare module 'module' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /internals/flow/CSSModule.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare export default { [key: string]: string } -------------------------------------------------------------------------------- /internals/img/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/js.png -------------------------------------------------------------------------------- /internals/img/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/npm.png -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard", "stylelint-config-prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /internals/img/eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/eslint.png -------------------------------------------------------------------------------- /internals/img/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/flow.png -------------------------------------------------------------------------------- /internals/img/jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/jest.png -------------------------------------------------------------------------------- /internals/img/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/react.png -------------------------------------------------------------------------------- /internals/img/redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/redux.png -------------------------------------------------------------------------------- /internals/img/yarn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/yarn.png -------------------------------------------------------------------------------- /internals/img/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/webpack.png -------------------------------------------------------------------------------- /resources/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/16x16.png -------------------------------------------------------------------------------- /resources/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/24x24.png -------------------------------------------------------------------------------- /resources/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/32x32.png -------------------------------------------------------------------------------- /resources/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/48x48.png -------------------------------------------------------------------------------- /resources/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/64x64.png -------------------------------------------------------------------------------- /resources/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/96x96.png -------------------------------------------------------------------------------- /internals/img/flow-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/flow-padded.png -------------------------------------------------------------------------------- /internals/img/jest-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/jest-padded.png -------------------------------------------------------------------------------- /internals/img/js-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/js-padded.png -------------------------------------------------------------------------------- /internals/img/yarn-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/yarn-padded.png -------------------------------------------------------------------------------- /resources/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/1024x1024.png -------------------------------------------------------------------------------- /resources/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/128x128.png -------------------------------------------------------------------------------- /resources/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/256x256.png -------------------------------------------------------------------------------- /resources/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/resources/icons/512x512.png -------------------------------------------------------------------------------- /internals/img/eslint-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/eslint-padded.png -------------------------------------------------------------------------------- /internals/img/react-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/react-padded.png -------------------------------------------------------------------------------- /internals/img/react-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/react-router.png -------------------------------------------------------------------------------- /internals/img/redux-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/redux-padded.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary 3 | *.jpg binary 4 | *.jpeg binary 5 | *.ico binary 6 | *.icns binary 7 | -------------------------------------------------------------------------------- /internals/img/eslint-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/eslint-padded-90.png -------------------------------------------------------------------------------- /internals/img/flow-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/flow-padded-90.png -------------------------------------------------------------------------------- /internals/img/jest-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/jest-padded-90.png -------------------------------------------------------------------------------- /internals/img/react-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/react-padded-90.png -------------------------------------------------------------------------------- /internals/img/redux-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/redux-padded-90.png -------------------------------------------------------------------------------- /internals/img/webpack-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/webpack-padded.png -------------------------------------------------------------------------------- /internals/img/yarn-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/yarn-padded-90.png -------------------------------------------------------------------------------- /internals/img/webpack-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/webpack-padded-90.png -------------------------------------------------------------------------------- /internals/img/react-router-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/react-router-padded.png -------------------------------------------------------------------------------- /internals/img/react-router-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soroushchehresa/VIF/HEAD/internals/img/react-router-padded-90.png -------------------------------------------------------------------------------- /test/example.js: -------------------------------------------------------------------------------- 1 | describe('description', () => { 2 | it('should have description', () => { 3 | expect(1 + 2).toBe(3); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /test/e2e/helpers.js: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: off */ 2 | import { ClientFunction } from 'testcafe'; 3 | 4 | export const getPageUrl = ClientFunction(() => window.location.href); 5 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "rangeStrategy": "bump", 4 | "baseBranches": ["next"], 5 | "automerge": true, 6 | "major": { 7 | "automerge": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /configs/webpack.config.eslint.js: -------------------------------------------------------------------------------- 1 | /* eslint import/no-unresolved: off, import/no-self-import: off */ 2 | require('@babel/register'); 3 | 4 | module.exports = require('./webpack.config.renderer.dev.babel').default; 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": [".prettierrc", ".babelrc", ".eslintrc", ".stylelintrc"], 5 | "options": { 6 | "parser": "json" 7 | } 8 | } 9 | ], 10 | "singleQuote": true 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /app/Routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Switch, Route } from 'react-router'; 3 | import routes from './constants/routes'; 4 | import HomePage from './containers/Home'; 5 | 6 | export default () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:testcafe/recommended", 3 | "env": { 4 | "jest/globals": true 5 | }, 6 | "plugins": ["jest", "testcafe"], 7 | "rules": { 8 | "jest/no-disabled-tests": "warn", 9 | "jest/no-focused-tests": "error", 10 | "jest/no-identical-title": "error" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/reducers/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { combineReducers } from 'redux'; 3 | import { connectRouter } from 'connected-react-router'; 4 | import counter from './counter'; 5 | 6 | export default function createRootReducer(history: History) { 7 | return combineReducers({ 8 | router: connectRouter(history), 9 | counter 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /test/actions/__snapshots__/counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`actions should decrement should create decrement action 1`] = ` 4 | Object { 5 | "type": "DECREMENT_COUNTER", 6 | } 7 | `; 8 | 9 | exports[`actions should increment should create increment action 1`] = ` 10 | Object { 11 | "type": "INCREMENT_COUNTER", 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /test/reducers/__snapshots__/counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`; 4 | 5 | exports[`reducers counter should handle INCREMENT_COUNTER 1`] = `2`; 6 | 7 | exports[`reducers counter should handle initial state 1`] = `0`; 8 | 9 | exports[`reducers counter should handle unknown action type 1`] = `1`; 10 | -------------------------------------------------------------------------------- /app/reducers/types.js: -------------------------------------------------------------------------------- 1 | import type { Dispatch as ReduxDispatch, Store as ReduxStore } from 'redux'; 2 | 3 | export type counterStateType = { 4 | +counter: number 5 | }; 6 | 7 | export type Action = { 8 | +type: string 9 | }; 10 | 11 | export type GetState = () => counterStateType; 12 | 13 | export type Dispatch = ReduxDispatch; 14 | 15 | export type Store = ReduxStore; 16 | -------------------------------------------------------------------------------- /app/store/configureStore.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import configureStoreDev from './configureStore.dev'; 3 | import configureStoreProd from './configureStore.prod'; 4 | 5 | const selectedConfigureStore = 6 | process.env.NODE_ENV === 'production' 7 | ? configureStoreProd 8 | : configureStoreDev; 9 | 10 | export const { configureStore } = selectedConfigureStore; 11 | 12 | export const { history } = selectedConfigureStore; 13 | -------------------------------------------------------------------------------- /app/reducers/counter.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter'; 3 | import type { Action } from './types'; 4 | 5 | export default function counter(state: number = 0, action: Action) { 6 | switch (action.type) { 7 | case INCREMENT_COUNTER: 8 | return state + 1; 9 | case DECREMENT_COUNTER: 10 | return state - 1; 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internals/scripts/CheckNodeEnv.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | 4 | export default function CheckNodeEnv(expectedEnv: string) { 5 | if (!expectedEnv) { 6 | throw new Error('"expectedEnv" not set'); 7 | } 8 | 9 | if (process.env.NODE_ENV !== expectedEnv) { 10 | console.log( 11 | chalk.whiteBright.bgRed.bold( 12 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` 13 | ) 14 | ); 15 | process.exit(2); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internals/scripts/CheckPortInUse.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | import detectPort from 'detect-port'; 4 | 5 | (function CheckPortInUse() { 6 | const port: string = process.env.PORT || '1212'; 7 | 8 | detectPort(port, (err: ?Error, availablePort: number) => { 9 | if (port !== String(availablePort)) { 10 | throw new Error( 11 | chalk.whiteBright.bgRed.bold( 12 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 yarn dev` 13 | ) 14 | ); 15 | } else { 16 | process.exit(0); 17 | } 18 | }); 19 | })(); 20 | -------------------------------------------------------------------------------- /app/Root.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React, { Component } from 'react'; 3 | import { Provider } from 'react-redux'; 4 | import { ConnectedRouter } from 'connected-react-router'; 5 | import type { Store } from './reducers/types'; 6 | import Routes from './Routes'; 7 | 8 | type Props = { 9 | store: Store, 10 | history: {} 11 | }; 12 | 13 | export default class Root extends Component { 14 | render() { 15 | const { store, history } = this.props; 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | platform: 4 | - x64 5 | 6 | environment: 7 | matrix: 8 | - nodejs_version: 10 9 | 10 | cache: 11 | - '%LOCALAPPDATA%/Yarn' 12 | - node_modules 13 | - flow-typed 14 | - '%USERPROFILE%\.electron' 15 | 16 | matrix: 17 | fast_finish: true 18 | 19 | build: off 20 | 21 | version: '{build}' 22 | 23 | shallow_clone: true 24 | 25 | clone_depth: 1 26 | 27 | install: 28 | - ps: Install-Product node $env:nodejs_version x64 29 | - set CI=true 30 | - yarn 31 | 32 | test_script: 33 | - yarn package-ci 34 | - yarn lint 35 | # - yarn flow 36 | - yarn test 37 | - yarn build-e2e 38 | - yarn test-e2e 39 | -------------------------------------------------------------------------------- /app/store/configureStore.prod.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { createStore, applyMiddleware } from 'redux'; 3 | import thunk from 'redux-thunk'; 4 | import { createHashHistory } from 'history'; 5 | import { routerMiddleware } from 'connected-react-router'; 6 | import createRootReducer from '../reducers'; 7 | import type { counterStateType } from '../reducers/types'; 8 | 9 | const history = createHashHistory(); 10 | const rootReducer = createRootReducer(history); 11 | const router = routerMiddleware(history); 12 | const enhancer = applyMiddleware(thunk, router); 13 | 14 | function configureStore(initialState?: counterStateType) { 15 | return createStore(rootReducer, initialState, enhancer); 16 | } 17 | 18 | export default { configureStore, history }; 19 | -------------------------------------------------------------------------------- /app/containers/Home/styles.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | outline: none; 9 | flex-direction: column; 10 | box-sizing: border-box; 11 | 12 | & > .centerText { 13 | font-size: 14px; 14 | text-align: center; 15 | color: #dedede; 16 | animation: bounce 2s infinite; 17 | pointer-events: none; 18 | line-height: 0; 19 | } 20 | 21 | & > .progressbarWrapper { 22 | width: 150px; 23 | margin-top: 20px; 24 | } 25 | } 26 | 27 | @keyframes bounce { 28 | 0% { 29 | transform: scale(1.1); 30 | } 31 | 50% { 32 | transform: scale(1); 33 | } 34 | 100% { 35 | transform: scale(1.1); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { AppContainer } from 'react-hot-loader'; 4 | import Root from './Root'; 5 | import { configureStore, history } from './store/configureStore'; 6 | import './globalStyles.scss'; 7 | 8 | const store = configureStore(); 9 | 10 | render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | 17 | if (module.hot) { 18 | module.hot.accept('./Root', () => { 19 | // eslint-disable-next-line global-require 20 | const NextRoot = require('./Root').default; 21 | render( 22 | 23 | 24 | , 25 | document.getElementById('root') 26 | ); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /test/reducers/counter.spec.js: -------------------------------------------------------------------------------- 1 | import counter from '../../app/reducers/counter'; 2 | import { 3 | INCREMENT_COUNTER, 4 | DECREMENT_COUNTER 5 | } from '../../app/actions/counter'; 6 | 7 | describe('reducers', () => { 8 | describe('counter', () => { 9 | it('should handle initial state', () => { 10 | expect(counter(undefined, {})).toMatchSnapshot(); 11 | }); 12 | 13 | it('should handle INCREMENT_COUNTER', () => { 14 | expect(counter(1, { type: INCREMENT_COUNTER })).toMatchSnapshot(); 15 | }); 16 | 17 | it('should handle DECREMENT_COUNTER', () => { 18 | expect(counter(1, { type: DECREMENT_COUNTER })).toMatchSnapshot(); 19 | }); 20 | 21 | it('should handle unknown action type', () => { 22 | expect(counter(1, { type: 'unknown' })).toMatchSnapshot(); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /app/actions/counter.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { GetState, Dispatch } from '../reducers/types'; 3 | 4 | export const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; 5 | export const DECREMENT_COUNTER = 'DECREMENT_COUNTER'; 6 | 7 | export function increment() { 8 | return { 9 | type: INCREMENT_COUNTER 10 | }; 11 | } 12 | 13 | export function decrement() { 14 | return { 15 | type: DECREMENT_COUNTER 16 | }; 17 | } 18 | 19 | export function incrementIfOdd() { 20 | return (dispatch: Dispatch, getState: GetState) => { 21 | const { counter } = getState(); 22 | 23 | if (counter % 2 === 0) { 24 | return; 25 | } 26 | 27 | dispatch(increment()); 28 | }; 29 | } 30 | 31 | export function incrementAsync(delay: number = 1000) { 32 | return (dispatch: Dispatch) => { 33 | setTimeout(() => { 34 | dispatch(increment()); 35 | }, delay); 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /app/globalStyles.scss: -------------------------------------------------------------------------------- 1 | @import '~@fortawesome/fontawesome-free/css/all.css'; 2 | 3 | body { 4 | position: relative; 5 | color: white; 6 | height: 100vh; 7 | font-family: Arial, Helvetica, Helvetica Neue, serif; 8 | overflow-y: hidden; 9 | margin: 0; 10 | & > video { 11 | pointer-events: none; 12 | } 13 | } 14 | 15 | *, 16 | *::after, 17 | *::before { 18 | -webkit-user-select: none; 19 | -webkit-user-drag: none; 20 | cursor: default !important; 21 | -webkit-app-region: drag; 22 | } 23 | 24 | h2 { 25 | margin: 0; 26 | font-size: 2.25rem; 27 | font-weight: bold; 28 | letter-spacing: -0.025em; 29 | color: #fff; 30 | } 31 | 32 | p { 33 | font-size: 24px; 34 | } 35 | 36 | li { 37 | list-style: none; 38 | } 39 | 40 | a { 41 | color: white; 42 | opacity: 0.75; 43 | text-decoration: none; 44 | } 45 | 46 | a:hover { 47 | opacity: 1; 48 | text-decoration: none; 49 | cursor: pointer; 50 | } 51 | -------------------------------------------------------------------------------- /internals/scripts/CheckBuiltsExist.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // Check if the renderer and main bundles are built 3 | import path from 'path'; 4 | import chalk from 'chalk'; 5 | import fs from 'fs'; 6 | 7 | function CheckBuildsExist() { 8 | const mainPath = path.join(__dirname, '..', '..', 'app', 'main.prod.js'); 9 | const rendererPath = path.join( 10 | __dirname, 11 | '..', 12 | '..', 13 | 'app', 14 | 'dist', 15 | 'renderer.prod.js' 16 | ); 17 | 18 | if (!fs.existsSync(mainPath)) { 19 | throw new Error( 20 | chalk.whiteBright.bgRed.bold( 21 | 'The main process is not built yet. Build it by running "yarn build-main"' 22 | ) 23 | ); 24 | } 25 | 26 | if (!fs.existsSync(rendererPath)) { 27 | throw new Error( 28 | chalk.whiteBright.bgRed.bold( 29 | 'The renderer process is not built yet. Build it by running "yarn build-renderer"' 30 | ) 31 | ); 32 | } 33 | } 34 | 35 | CheckBuildsExist(); 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # 💨 VIF 6 | This is a simple cross-platform desktop application to convert videos to high-quality GIFs.
7 | Vif supports all the video formats that OS and HTML5 supports (mp4, mov, HEVC, mp4, ogg, ogv, webm and etc). 8 | 9 |
10 | 11 | 12 | 13 | 14 | ## Download and Install: 15 | [The latest release](https://github.com/soroushchehresa/Vif/releases/latest) 16 | 17 | 18 | ## This project implemented by the following technologies: 19 | * [Electron](https://github.com/electron) 20 | * [React](https://github.com/facebook/react) 21 | * [Sass](https://sass-lang.com) 22 | * [gifshot](https://github.com/yahoo/gifshot) 23 | 24 | 25 | ## Support: 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # OSX 31 | .DS_Store 32 | 33 | # flow-typed 34 | flow-typed/npm/* 35 | !flow-typed/npm/module_vx.x.x.js 36 | 37 | # App packaged 38 | release 39 | app/main.prod.js 40 | app/main.prod.js.map 41 | app/renderer.prod.js 42 | app/renderer.prod.js.map 43 | app/style.css 44 | app/style.css.map 45 | dist 46 | dll 47 | main.js 48 | main.js.map 49 | 50 | .idea 51 | npm-debug.log.* 52 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /app/main.prod.js 3 | /app/main.prod.js.map 4 | /app/dist/.* 5 | /resources/.* 6 | /node_modules/webpack-cli 7 | /release/.* 8 | /dll/.* 9 | /release/.* 10 | /git/.* 11 | 12 | [include] 13 | 14 | [libs] 15 | 16 | [options] 17 | esproposal.class_static_fields=enable 18 | esproposal.class_instance_fields=enable 19 | esproposal.export_star_as=enable 20 | module.name_mapper.extension='css' -> '/internals/flow/CSSModule.js.flow' 21 | module.name_mapper.extension='styl' -> '/internals/flow/CSSModule.js.flow' 22 | module.name_mapper.extension='scss' -> '/internals/flow/CSSModule.js.flow' 23 | module.name_mapper.extension='png' -> '/internals/flow/WebpackAsset.js.flow' 24 | module.name_mapper.extension='jpg' -> '/internals/flow/WebpackAsset.js.flow' 25 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe 26 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue 27 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # OSX 31 | .DS_Store 32 | 33 | # flow-typed 34 | flow-typed/npm/* 35 | !flow-typed/npm/module_vx.x.x.js 36 | 37 | # App packaged 38 | release 39 | app/main.prod.js 40 | app/main.prod.js.map 41 | app/renderer.prod.js 42 | app/renderer.prod.js.map 43 | app/style.css 44 | app/style.css.map 45 | dist 46 | dll 47 | main.js 48 | main.js.map 49 | 50 | .idea 51 | npm-debug.log.* 52 | .*.dockerfile -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # OSX 31 | .DS_Store 32 | 33 | # flow-typed 34 | flow-typed/npm/* 35 | !flow-typed/npm/module_vx.x.x.js 36 | 37 | # App packaged 38 | release 39 | app/main.prod.js 40 | app/main.prod.js.map 41 | app/renderer.prod.js 42 | app/renderer.prod.js.map 43 | app/style.css 44 | app/style.css.map 45 | dist 46 | dll 47 | main.js 48 | main.js.map 49 | 50 | .idea 51 | npm-debug.log.* 52 | __snapshots__ 53 | 54 | # Package.json 55 | package.json 56 | .travis.yml 57 | -------------------------------------------------------------------------------- /configs/webpack.config.base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { dependencies } from '../package.json'; 8 | 9 | export default { 10 | externals: [...Object.keys(dependencies || {})], 11 | 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.jsx?$/, 16 | exclude: /node_modules/, 17 | use: { 18 | loader: 'babel-loader', 19 | options: { 20 | cacheDirectory: true 21 | } 22 | } 23 | } 24 | ] 25 | }, 26 | 27 | output: { 28 | path: path.join(__dirname, '..', 'app'), 29 | // https://github.com/webpack/webpack/issues/1114 30 | libraryTarget: 'commonjs2' 31 | }, 32 | 33 | /** 34 | * Determine the array of extensions that should be used to resolve modules. 35 | */ 36 | resolve: { 37 | extensions: ['.js', '.jsx', '.json'] 38 | }, 39 | 40 | plugins: [ 41 | new webpack.EnvironmentPlugin({ 42 | NODE_ENV: 'production' 43 | }), 44 | 45 | new webpack.NamedModulesPlugin() 46 | ] 47 | }; 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present C. T. Lin 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 | 23 | -------------------------------------------------------------------------------- /app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Electron React! 6 | 17 | 18 | 19 |
20 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/components/__snapshots__/Counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Counter component should match exact snapshot 1`] = ` 4 |
5 |
6 |
10 | 14 | 17 | 18 |
19 |
23 | 1 24 |
25 |
28 | 38 | 48 | 56 | 64 |
65 |
66 |
67 | `; 68 | -------------------------------------------------------------------------------- /test/actions/counter.spec.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | import * as actions from '../../app/actions/counter'; 3 | 4 | describe('actions', () => { 5 | it('should increment should create increment action', () => { 6 | expect(actions.increment()).toMatchSnapshot(); 7 | }); 8 | 9 | it('should decrement should create decrement action', () => { 10 | expect(actions.decrement()).toMatchSnapshot(); 11 | }); 12 | 13 | it('should incrementIfOdd should create increment action', () => { 14 | const fn = actions.incrementIfOdd(); 15 | expect(fn).toBeInstanceOf(Function); 16 | const dispatch = spy(); 17 | const getState = () => ({ counter: 1 }); 18 | fn(dispatch, getState); 19 | expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true); 20 | }); 21 | 22 | it('should incrementIfOdd shouldnt create increment action if counter is even', () => { 23 | const fn = actions.incrementIfOdd(); 24 | const dispatch = spy(); 25 | const getState = () => ({ counter: 2 }); 26 | fn(dispatch, getState); 27 | expect(dispatch.called).toBe(false); 28 | }); 29 | 30 | // There's no nice way to test this at the moment... 31 | it('should incrementAsync', done => { 32 | const fn = actions.incrementAsync(1); 33 | expect(fn).toBeInstanceOf(Function); 34 | const dispatch = spy(); 35 | fn(dispatch); 36 | setTimeout(() => { 37 | expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe( 38 | true 39 | ); 40 | done(); 41 | }, 5); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /configs/webpack.config.renderer.dev.dll.babel.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: off, import/no-dynamic-require: off */ 2 | 3 | /** 4 | * Builds the DLL for development electron renderer process 5 | */ 6 | 7 | import webpack from 'webpack'; 8 | import path from 'path'; 9 | import merge from 'webpack-merge'; 10 | import baseConfig from './webpack.config.base'; 11 | import { dependencies } from '../package.json'; 12 | import CheckNodeEnv from '../internals/scripts/CheckNodeEnv'; 13 | 14 | CheckNodeEnv('development'); 15 | 16 | const dist = path.join(__dirname, '..', 'dll'); 17 | 18 | export default merge.smart(baseConfig, { 19 | context: path.join(__dirname, '..'), 20 | 21 | devtool: 'eval', 22 | 23 | mode: 'development', 24 | 25 | target: 'electron-renderer', 26 | 27 | externals: ['fsevents', 'crypto-browserify'], 28 | 29 | /** 30 | * Use `module` from `webpack.config.renderer.dev.js` 31 | */ 32 | module: require('./webpack.config.renderer.dev.babel').default.module, 33 | 34 | entry: { 35 | renderer: Object.keys(dependencies || {}) 36 | }, 37 | 38 | output: { 39 | library: 'renderer', 40 | path: dist, 41 | filename: '[name].dev.dll.js', 42 | libraryTarget: 'var' 43 | }, 44 | 45 | plugins: [ 46 | new webpack.DllPlugin({ 47 | path: path.join(dist, '[name].json'), 48 | name: '[name]' 49 | }), 50 | 51 | /** 52 | * Create global constants which can be configured at compile time. 53 | * 54 | * Useful for allowing different behaviour between development builds and 55 | * release builds 56 | * 57 | * NODE_ENV should be production so that modules do not perform certain 58 | * development checks 59 | */ 60 | new webpack.EnvironmentPlugin({ 61 | NODE_ENV: 'development' 62 | }), 63 | 64 | new webpack.LoaderOptionsPlugin({ 65 | debug: true, 66 | options: { 67 | context: path.join(__dirname, '..', 'app'), 68 | output: { 69 | path: path.join(__dirname, '..', 'dll') 70 | } 71 | } 72 | }) 73 | ] 74 | }); 75 | -------------------------------------------------------------------------------- /test/containers/CounterPage.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Enzyme, { mount } from 'enzyme'; 3 | import Adapter from 'enzyme-adapter-react-16'; 4 | import { Provider } from 'react-redux'; 5 | import { createBrowserHistory } from 'history'; 6 | import { ConnectedRouter } from 'connected-react-router'; 7 | import CounterPage from '../../app/containers/CounterPage'; 8 | import { configureStore } from '../../app/store/configureStore'; 9 | 10 | Enzyme.configure({ adapter: new Adapter() }); 11 | 12 | function setup(initialState) { 13 | const store = configureStore(initialState); 14 | const history = createBrowserHistory(); 15 | const provider = ( 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | const app = mount(provider); 23 | return { 24 | app, 25 | buttons: app.find('button'), 26 | p: app.find('.counter') 27 | }; 28 | } 29 | 30 | describe('containers', () => { 31 | describe('App', () => { 32 | it('should display initial count', () => { 33 | const { p } = setup(); 34 | expect(p.text()).toMatch(/^0$/); 35 | }); 36 | 37 | it('should display updated count after increment button click', () => { 38 | const { buttons, p } = setup(); 39 | buttons.at(0).simulate('click'); 40 | expect(p.text()).toMatch(/^1$/); 41 | }); 42 | 43 | it('should display updated count after decrement button click', () => { 44 | const { buttons, p } = setup(); 45 | buttons.at(1).simulate('click'); 46 | expect(p.text()).toMatch(/^-1$/); 47 | }); 48 | 49 | it('shouldnt change if even and if odd button clicked', () => { 50 | const { buttons, p } = setup(); 51 | buttons.at(2).simulate('click'); 52 | expect(p.text()).toMatch(/^0$/); 53 | }); 54 | 55 | it('should change if odd and if odd button clicked', () => { 56 | const { buttons, p } = setup({ counter: 1 }); 57 | buttons.at(2).simulate('click'); 58 | expect(p.text()).toMatch(/^2$/); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /configs/webpack.config.main.prod.babel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import merge from 'webpack-merge'; 8 | import TerserPlugin from 'terser-webpack-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import baseConfig from './webpack.config.base'; 11 | import CheckNodeEnv from '../internals/scripts/CheckNodeEnv'; 12 | 13 | CheckNodeEnv('production'); 14 | 15 | export default merge.smart(baseConfig, { 16 | devtool: 'source-map', 17 | 18 | mode: 'production', 19 | 20 | target: 'electron-main', 21 | 22 | entry: './app/main.dev', 23 | 24 | output: { 25 | path: path.join(__dirname, '..'), 26 | filename: './app/main.prod.js' 27 | }, 28 | 29 | optimization: { 30 | minimizer: process.env.E2E_BUILD 31 | ? [] 32 | : [ 33 | new TerserPlugin({ 34 | parallel: true, 35 | sourceMap: true, 36 | cache: true 37 | }) 38 | ] 39 | }, 40 | 41 | plugins: [ 42 | new BundleAnalyzerPlugin({ 43 | analyzerMode: 44 | process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 45 | openAnalyzer: process.env.OPEN_ANALYZER === 'true' 46 | }), 47 | 48 | /** 49 | * Create global constants which can be configured at compile time. 50 | * 51 | * Useful for allowing different behaviour between development builds and 52 | * release builds 53 | * 54 | * NODE_ENV should be production so that modules do not perform certain 55 | * development checks 56 | */ 57 | new webpack.EnvironmentPlugin({ 58 | NODE_ENV: 'production', 59 | DEBUG_PROD: false, 60 | START_MINIMIZED: false 61 | }) 62 | ], 63 | 64 | /** 65 | * Disables webpack processing of __dirname and __filename. 66 | * If you run the bundle in node.js it falls back to these values of node.js. 67 | * https://github.com/webpack/webpack/issues/2010 68 | */ 69 | node: { 70 | __dirname: false, 71 | __filename: false 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /test/components/Counter.spec.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | import React from 'react'; 3 | import Enzyme, { shallow } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | import { BrowserRouter as Router } from 'react-router-dom'; 6 | import renderer from 'react-test-renderer'; 7 | import Counter from '../../app/components/Counter'; 8 | 9 | Enzyme.configure({ adapter: new Adapter() }); 10 | 11 | function setup() { 12 | const actions = { 13 | increment: spy(), 14 | incrementIfOdd: spy(), 15 | incrementAsync: spy(), 16 | decrement: spy() 17 | }; 18 | const component = shallow(); 19 | return { 20 | component, 21 | actions, 22 | buttons: component.find('button'), 23 | p: component.find('.counter') 24 | }; 25 | } 26 | 27 | describe('Counter component', () => { 28 | it('should should display count', () => { 29 | const { p } = setup(); 30 | expect(p.text()).toMatch(/^1$/); 31 | }); 32 | 33 | it('should first button should call increment', () => { 34 | const { buttons, actions } = setup(); 35 | buttons.at(0).simulate('click'); 36 | expect(actions.increment.called).toBe(true); 37 | }); 38 | 39 | it('should match exact snapshot', () => { 40 | const { actions } = setup(); 41 | const counter = ( 42 |
43 | 44 | 45 | 46 |
47 | ); 48 | const tree = renderer.create(counter).toJSON(); 49 | 50 | expect(tree).toMatchSnapshot(); 51 | }); 52 | 53 | it('should second button should call decrement', () => { 54 | const { buttons, actions } = setup(); 55 | buttons.at(1).simulate('click'); 56 | expect(actions.decrement.called).toBe(true); 57 | }); 58 | 59 | it('should third button should call incrementIfOdd', () => { 60 | const { buttons, actions } = setup(); 61 | buttons.at(2).simulate('click'); 62 | expect(actions.incrementIfOdd.called).toBe(true); 63 | }); 64 | 65 | it('should fourth button should call incrementAsync', () => { 66 | const { buttons, actions } = setup(); 67 | buttons.at(3).simulate('click'); 68 | expect(actions.incrementAsync.called).toBe(true); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /app/store/configureStore.dev.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { createHashHistory } from 'history'; 4 | import { routerMiddleware, routerActions } from 'connected-react-router'; 5 | import { createLogger } from 'redux-logger'; 6 | import createRootReducer from '../reducers'; 7 | import * as counterActions from '../actions/counter'; 8 | import type { counterStateType } from '../reducers/types'; 9 | 10 | const history = createHashHistory(); 11 | 12 | const rootReducer = createRootReducer(history); 13 | 14 | const configureStore = (initialState?: counterStateType) => { 15 | // Redux Configuration 16 | const middleware = []; 17 | const enhancers = []; 18 | 19 | // Thunk Middleware 20 | middleware.push(thunk); 21 | 22 | // Logging Middleware 23 | const logger = createLogger({ 24 | level: 'info', 25 | collapsed: true 26 | }); 27 | 28 | // Skip redux logs in console during the tests 29 | if (process.env.NODE_ENV !== 'test') { 30 | middleware.push(logger); 31 | } 32 | 33 | // Router Middleware 34 | const router = routerMiddleware(history); 35 | middleware.push(router); 36 | 37 | // Redux DevTools Configuration 38 | const actionCreators = { 39 | ...counterActions, 40 | ...routerActions 41 | }; 42 | // If Redux DevTools Extension is installed use it, otherwise use Redux compose 43 | /* eslint-disable no-underscore-dangle */ 44 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ 45 | ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ 46 | // Options: http://extension.remotedev.io/docs/API/Arguments.html 47 | actionCreators 48 | }) 49 | : compose; 50 | /* eslint-enable no-underscore-dangle */ 51 | 52 | // Apply Middleware & Compose Enhancers 53 | enhancers.push(applyMiddleware(...middleware)); 54 | const enhancer = composeEnhancers(...enhancers); 55 | 56 | // Create Store 57 | const store = createStore(rootReducer, initialState, enhancer); 58 | 59 | if (module.hot) { 60 | module.hot.accept( 61 | '../reducers', 62 | // eslint-disable-next-line global-require 63 | () => store.replaceReducer(require('../reducers').default) 64 | ); 65 | } 66 | 67 | return store; 68 | }; 69 | 70 | export default { configureStore, history }; 71 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | allow_failures: 3 | - os: windows 4 | include: 5 | - os: osx 6 | language: node_js 7 | node_js: 8 | - node 9 | env: 10 | - ELECTRON_CACHE=$HOME/.cache/electron 11 | - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder 12 | 13 | - os: linux 14 | language: node_js 15 | node_js: 16 | - node 17 | addons: 18 | apt: 19 | sources: 20 | - ubuntu-toolchain-r-test 21 | packages: 22 | - gcc-multilib 23 | - g++-8 24 | - g++-multilib 25 | - icnsutils 26 | - graphicsmagick 27 | - xz-utils 28 | - xorriso 29 | - rpm 30 | 31 | - os: windows 32 | language: node_js 33 | node_js: 34 | - node 35 | env: 36 | - ELECTRON_CACHE=$HOME/.cache/electron 37 | - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder 38 | 39 | before_cache: 40 | - rm -rf $HOME/.cache/electron-builder/wine 41 | 42 | cache: 43 | yarn: true 44 | directories: 45 | - node_modules 46 | - $(npm config get prefix)/lib/node_modules 47 | - flow-typed 48 | - $HOME/.cache/electron 49 | - $HOME/.cache/electron-builder 50 | 51 | before_install: 52 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX="g++-8"; fi 53 | 54 | install: 55 | - yarn --ignore-engines 56 | # On Linux, initialize "virtual display". See before_script 57 | - | 58 | if [ "$TRAVIS_OS_NAME" == "linux" ]; then 59 | /sbin/start-stop-daemon \ 60 | --start \ 61 | --quiet \ 62 | --pidfile /tmp/custom_xvfb_99.pid \ 63 | --make-pidfile \ 64 | --background \ 65 | --exec /usr/bin/Xvfb \ 66 | -- :99 -ac -screen 0 1280x1024x16 67 | else 68 | : 69 | fi 70 | 71 | before_script: 72 | # On Linux, create a "virtual display". This allows browsers to work properly 73 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=:99.0; fi 74 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh -e /etc/init.d/xvfb start; fi 75 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sleep 3; fi 76 | 77 | script: 78 | - yarn package-ci 79 | - yarn lint 80 | - yarn flow 81 | # HACK: Temporarily ignore `yarn test` on linux 82 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then yarn test; fi 83 | - yarn build-e2e 84 | - yarn test-e2e 85 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: off */ 2 | 3 | const developmentEnvironments = ['development', 'test']; 4 | 5 | const developmentPlugins = [require('react-hot-loader/babel')]; 6 | 7 | const productionPlugins = [ 8 | require('babel-plugin-dev-expression'), 9 | 10 | // babel-preset-react-optimize 11 | require('@babel/plugin-transform-react-constant-elements'), 12 | require('@babel/plugin-transform-react-inline-elements'), 13 | require('babel-plugin-transform-react-remove-prop-types') 14 | ]; 15 | 16 | module.exports = api => { 17 | // see docs about api at https://babeljs.io/docs/en/config-files#apicache 18 | 19 | const development = api.env(developmentEnvironments); 20 | 21 | return { 22 | presets: [ 23 | [ 24 | require('@babel/preset-env'), 25 | { 26 | targets: { electron: require('electron/package.json').version }, 27 | useBuiltIns: 'usage' 28 | } 29 | ], 30 | require('@babel/preset-flow'), 31 | [require('@babel/preset-react'), { development }] 32 | ], 33 | plugins: [ 34 | // Stage 0 35 | require('@babel/plugin-proposal-function-bind'), 36 | 37 | // Stage 1 38 | require('@babel/plugin-proposal-export-default-from'), 39 | require('@babel/plugin-proposal-logical-assignment-operators'), 40 | [require('@babel/plugin-proposal-optional-chaining'), { loose: false }], 41 | [ 42 | require('@babel/plugin-proposal-pipeline-operator'), 43 | { proposal: 'minimal' } 44 | ], 45 | [ 46 | require('@babel/plugin-proposal-nullish-coalescing-operator'), 47 | { loose: false } 48 | ], 49 | require('@babel/plugin-proposal-do-expressions'), 50 | 51 | // Stage 2 52 | [require('@babel/plugin-proposal-decorators'), { legacy: true }], 53 | require('@babel/plugin-proposal-function-sent'), 54 | require('@babel/plugin-proposal-export-namespace-from'), 55 | require('@babel/plugin-proposal-numeric-separator'), 56 | require('@babel/plugin-proposal-throw-expressions'), 57 | 58 | // Stage 3 59 | require('@babel/plugin-syntax-dynamic-import'), 60 | require('@babel/plugin-syntax-import-meta'), 61 | [require('@babel/plugin-proposal-class-properties'), { loose: true }], 62 | require('@babel/plugin-proposal-json-strings'), 63 | 64 | ...(development ? developmentPlugins : productionPlugins) 65 | ] 66 | }; 67 | }; 68 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "sourceType": "module", 5 | "allowImportExportEverywhere": true 6 | }, 7 | "extends": ["airbnb", "prettier", "prettier/flowtype", "prettier/react"], 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "arrow-parens": ["off"], 14 | "compat/compat": "error", 15 | "consistent-return": "off", 16 | "comma-dangle": "off", 17 | "flowtype/boolean-style": ["error", "boolean"], 18 | "flowtype/define-flow-type": "warn", 19 | "flowtype/delimiter-dangle": ["error", "never"], 20 | "flowtype/generic-spacing": ["error", "never"], 21 | "flowtype/no-primitive-constructor-types": "error", 22 | "flowtype/no-weak-types": "warn", 23 | "flowtype/object-type-delimiter": ["error", "comma"], 24 | "flowtype/require-parameter-type": "off", 25 | "flowtype/require-return-type": "off", 26 | "flowtype/require-valid-file-annotation": "off", 27 | "flowtype/semi": ["error", "always"], 28 | "flowtype/space-after-type-colon": ["error", "always"], 29 | "flowtype/space-before-generic-bracket": ["error", "never"], 30 | "flowtype/space-before-type-colon": ["error", "never"], 31 | "flowtype/union-intersection-spacing": ["error", "always"], 32 | "flowtype/use-flow-type": "error", 33 | "flowtype/valid-syntax": "error", 34 | "generator-star-spacing": "off", 35 | "import/no-unresolved": "error", 36 | "import/no-extraneous-dependencies": "off", 37 | "jsx-a11y/anchor-is-valid": "off", 38 | "no-console": "off", 39 | "no-use-before-define": "off", 40 | "no-multi-assign": "off", 41 | "promise/param-names": "error", 42 | "promise/always-return": "error", 43 | "promise/catch-or-return": "error", 44 | "promise/no-native": "off", 45 | "react/sort-comp": [ 46 | "error", 47 | { 48 | "order": [ 49 | "type-annotations", 50 | "static-methods", 51 | "lifecycle", 52 | "everything-else", 53 | "render" 54 | ] 55 | } 56 | ], 57 | "react/jsx-no-bind": "off", 58 | "react/jsx-filename-extension": [ 59 | "error", 60 | { "extensions": [".js", ".jsx"] } 61 | ], 62 | "react/prefer-stateless-function": "off" 63 | }, 64 | "plugins": ["flowtype", "import", "promise", "compat", "react"], 65 | "settings": { 66 | "import/resolver": { 67 | "webpack": { 68 | "config": "configs/webpack.config.eslint.js" 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/main.dev.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint global-require: off */ 3 | 4 | import { app, BrowserWindow } from 'electron'; 5 | import { autoUpdater } from 'electron-updater'; 6 | import log from 'electron-log'; 7 | import is from 'electron-is'; 8 | 9 | export default class AppUpdater { 10 | constructor() { 11 | log.transports.file.level = 'info'; 12 | autoUpdater.logger = log; 13 | autoUpdater.checkForUpdatesAndNotify(); 14 | } 15 | } 16 | 17 | let mainWindow = null; 18 | 19 | if (process.env.NODE_ENV === 'production') { 20 | const sourceMapSupport = require('source-map-support'); 21 | sourceMapSupport.install(); 22 | } 23 | 24 | if ( 25 | process.env.NODE_ENV === 'development' || 26 | process.env.DEBUG_PROD === 'true' 27 | ) { 28 | require('electron-debug')(); 29 | } 30 | 31 | const installExtensions = async () => { 32 | const installer = require('electron-devtools-installer'); 33 | const forceDownload = !!process.env.UPGRADE_EXTENSIONS; 34 | const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS']; 35 | 36 | return Promise.all( 37 | extensions.map(name => installer.default(installer[name], forceDownload)) 38 | ) 39 | .catch(console.log); 40 | }; 41 | 42 | /** 43 | * Add event listeners... 44 | */ 45 | 46 | app.on('window-all-closed', () => { 47 | // Respect the OSX convention of having the application in memory even 48 | // after all windows have been closed 49 | if (process.platform !== 'darwin') { 50 | app.quit(); 51 | } 52 | }); 53 | 54 | app.on('browser-window-created', function(e, window) { 55 | window.setMenu(null); 56 | }); 57 | 58 | app.on('ready', async () => { 59 | if ( 60 | process.env.NODE_ENV === 'development' || 61 | process.env.DEBUG_PROD === 'true' 62 | ) { 63 | await installExtensions(); 64 | } 65 | 66 | let options = { 67 | show: false, 68 | width: 360, 69 | height: 240, 70 | backgroundColor: '#373834', 71 | vibrancy: 'dark', 72 | transparent: true, 73 | resizable: false, 74 | titleBarStyle: 'hiddenInset', 75 | maximizable: false, 76 | frame: false, 77 | webPreferences: { 78 | nodeIntegration: true 79 | } 80 | }; 81 | 82 | if (is.macOS()) { 83 | delete options.backgroundColor; 84 | } 85 | 86 | mainWindow = new BrowserWindow(options); 87 | 88 | mainWindow.loadURL(`file://${__dirname}/app.html`); 89 | 90 | // @TODO: Use 'ready-to-show' event 91 | // https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event 92 | mainWindow.webContents.on('did-finish-load', () => { 93 | if (!mainWindow) { 94 | throw new Error('"mainWindow" is not defined'); 95 | } 96 | if (process.env.START_MINIMIZED) { 97 | mainWindow.minimize(); 98 | } else { 99 | mainWindow.show(); 100 | mainWindow.focus(); 101 | } 102 | }); 103 | 104 | mainWindow.on('closed', () => { 105 | mainWindow = null; 106 | }); 107 | 108 | // Remove this if your app does not use auto updates 109 | // eslint-disable-next-line 110 | new AppUpdater(); 111 | }); 112 | -------------------------------------------------------------------------------- /test/e2e/HomePage.e2e.js: -------------------------------------------------------------------------------- 1 | import { ClientFunction, Selector } from 'testcafe'; 2 | import { ReactSelector, waitForReact } from 'testcafe-react-selectors'; 3 | import { getPageUrl } from './helpers'; 4 | 5 | const getPageTitle = ClientFunction(() => document.title); 6 | const counterSelector = Selector('[data-tid="counter"]'); 7 | const buttonsSelector = Selector('[data-tclass="btn"]'); 8 | const clickToCounterLink = t => 9 | t.click(Selector('a').withExactText('to Counter')); 10 | const incrementButton = buttonsSelector.nth(0); 11 | const decrementButton = buttonsSelector.nth(1); 12 | const oddButton = buttonsSelector.nth(2); 13 | const asyncButton = buttonsSelector.nth(3); 14 | const getCounterText = () => counterSelector().innerText; 15 | const assertNoConsoleErrors = async t => { 16 | const { error } = await t.getBrowserConsoleMessages(); 17 | await t.expect(error).eql([]); 18 | }; 19 | 20 | fixture`Home Page`.page('../../app/app.html').afterEach(assertNoConsoleErrors); 21 | 22 | test('e2e', async t => { 23 | await t.expect(getPageTitle()).eql('Hello Electron React!'); 24 | }); 25 | 26 | test('should open window', async t => { 27 | await t.expect(getPageTitle()).eql('Hello Electron React!'); 28 | }); 29 | 30 | test( 31 | "should haven't any logs in console of main window", 32 | assertNoConsoleErrors 33 | ); 34 | 35 | test('should to Counter with click "to Counter" link', async t => { 36 | await t 37 | .click('[data-tid=container] > a') 38 | .expect(getCounterText()) 39 | .eql('0'); 40 | }); 41 | 42 | test('should navgiate to /counter', async t => { 43 | await waitForReact(); 44 | await t 45 | .click( 46 | ReactSelector('Link').withProps({ 47 | to: '/counter' 48 | }) 49 | ) 50 | .expect(getPageUrl()) 51 | .contains('/counter'); 52 | }); 53 | 54 | fixture`Counter Tests` 55 | .page('../../app/app.html') 56 | .beforeEach(clickToCounterLink) 57 | .afterEach(assertNoConsoleErrors); 58 | 59 | test('should display updated count after increment button click', async t => { 60 | await t 61 | .click(incrementButton) 62 | .expect(getCounterText()) 63 | .eql('1'); 64 | }); 65 | 66 | test('should display updated count after descrement button click', async t => { 67 | await t 68 | .click(decrementButton) 69 | .expect(getCounterText()) 70 | .eql('-1'); 71 | }); 72 | 73 | test('should not change if even and if odd button clicked', async t => { 74 | await t 75 | .click(oddButton) 76 | .expect(getCounterText()) 77 | .eql('0'); 78 | }); 79 | 80 | test('should change if odd and if odd button clicked', async t => { 81 | await t 82 | .click(incrementButton) 83 | .click(oddButton) 84 | .expect(getCounterText()) 85 | .eql('2'); 86 | }); 87 | 88 | test('should change if async button clicked and a second later', async t => { 89 | await t 90 | .click(asyncButton) 91 | .expect(getCounterText()) 92 | .eql('0') 93 | .expect(getCounterText()) 94 | .eql('1'); 95 | }); 96 | 97 | test('should back to home if back button clicked', async t => { 98 | await t 99 | .click('[data-tid="backButton"] > a') 100 | .expect(Selector('[data-tid="container"]').visible) 101 | .ok(); 102 | }); 103 | -------------------------------------------------------------------------------- /app/containers/Home/index.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import fs from 'fs'; 5 | import base64ImageToFile from 'base64image-to-file'; 6 | import CircularProgressbar from 'react-circular-progressbar'; 7 | import getDimensions from 'get-video-dimensions'; 8 | import { getVideoDurationInSeconds } from 'get-video-duration'; 9 | import gifshot from 'gifshot'; 10 | import path from 'path'; 11 | import styles from './styles.scss'; 12 | 13 | export default class Index extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | activeDropZone: false, 18 | captureProgress: null, 19 | saving: false 20 | }; 21 | } 22 | 23 | ondragover = e => { 24 | const { activeDropZone } = this.state; 25 | if (e.dataTransfer.items.length === 1) { 26 | e.preventDefault(); 27 | if (!activeDropZone) { 28 | this.setState({ activeDropZone: true }); 29 | } 30 | } 31 | return false; 32 | }; 33 | 34 | ondrop = e => { 35 | e.preventDefault(); 36 | const { activeDropZone, saving } = this.state; 37 | if (activeDropZone) { 38 | this.setState({ activeDropZone: false }); 39 | } 40 | const filePath = e.dataTransfer.files[0].path; 41 | const outputPath = path.join(filePath.split('/') 42 | .reverse() 43 | .slice(1) 44 | .reverse() 45 | .join('/')); 46 | getDimensions(filePath) 47 | .then(({ width, height }) => { 48 | const streamFile = fs.createReadStream(filePath); 49 | getVideoDurationInSeconds(streamFile) 50 | .then(videoDuration => { 51 | gifshot.createGIF( 52 | { 53 | gifWidth: width, 54 | gifHeight: height, 55 | video: filePath, 56 | numFrames: ((Math.round(videoDuration) * 1000) / 100) - (Math.round(videoDuration) * 2), 57 | progressCallback: captureProgress => { 58 | this.setState({ captureProgress }); 59 | if (captureProgress === 1) { 60 | this.setState({ captureProgress: null }); 61 | } 62 | if (!saving) { 63 | this.setState({ saving: true }); 64 | } 65 | } 66 | }, 67 | obj => { 68 | if (!obj.error) { 69 | let outputFileName = ''; 70 | if (fs.existsSync(`${filePath}.gif`)) { 71 | outputFileName = `${filePath.split('/') 72 | .splice(-1, 1)[0]}-${Math.floor(Math.random() * 899999 + 100000)}`; 73 | } else { 74 | outputFileName = `${filePath.split('/') 75 | .splice(-1, 1)[0]}`; 76 | } 77 | base64ImageToFile( 78 | obj.image, 79 | outputPath, 80 | outputFileName, 81 | () => { 82 | this.setState({ saving: false }); 83 | } 84 | ); 85 | } 86 | } 87 | ); 88 | return true; 89 | }) 90 | .catch(error => { 91 | console.log(error); 92 | }); 93 | return true; 94 | }) 95 | .catch(error => { 96 | console.log(error); 97 | }); 98 | }; 99 | 100 | ondragleave = e => { 101 | e.preventDefault(); 102 | const { activeDropZone } = this.state; 103 | if (activeDropZone) { 104 | this.setState({ activeDropZone: false }); 105 | } 106 | return false; 107 | }; 108 | 109 | render() { 110 | const { activeDropZone, captureProgress, saving } = this.state; 111 | return ( 112 |
119 | { 120 | captureProgress && 121 |
122 | 141 |
142 | } 143 | {saving && !captureProgress &&

Saving...

} 144 | {!saving &&

Drop a Video to Convert to GIF

} 145 |
146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /configs/webpack.config.renderer.prod.babel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Build config for electron renderer process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import MiniCssExtractPlugin from 'mini-css-extract-plugin'; 8 | import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import merge from 'webpack-merge'; 11 | import TerserPlugin from 'terser-webpack-plugin'; 12 | import baseConfig from './webpack.config.base'; 13 | import CheckNodeEnv from '../internals/scripts/CheckNodeEnv'; 14 | 15 | CheckNodeEnv('production'); 16 | export default merge.smart(baseConfig, { 17 | devtool: 'source-map', 18 | 19 | mode: 'production', 20 | 21 | target: 'electron-renderer', 22 | 23 | entry: path.join(__dirname, '..', 'app/index'), 24 | 25 | output: { 26 | path: path.join(__dirname, '..', 'app/dist'), 27 | publicPath: './dist/', 28 | filename: 'renderer.prod.js' 29 | }, 30 | 31 | module: { 32 | rules: [ 33 | // Extract all .global.css to style.css as is 34 | { 35 | test: /\.global\.css$/, 36 | use: [ 37 | { 38 | loader: MiniCssExtractPlugin.loader, 39 | options: { 40 | publicPath: './' 41 | } 42 | }, 43 | { 44 | loader: 'css-loader', 45 | options: { 46 | sourceMap: true 47 | } 48 | } 49 | ] 50 | }, 51 | // Pipe other styles through css modules and append to style.css 52 | { 53 | test: /^((?!\.global).)*\.css$/, 54 | use: [ 55 | { 56 | loader: MiniCssExtractPlugin.loader 57 | }, 58 | { 59 | loader: 'css-loader', 60 | options: { 61 | modules: true, 62 | localIdentName: '[name]__[local]__[hash:base64:5]', 63 | sourceMap: true 64 | } 65 | } 66 | ] 67 | }, 68 | // Add SASS support - compile all .global.scss files and pipe it to style.css 69 | { 70 | test: /\.global\.(scss|sass)$/, 71 | use: [ 72 | { 73 | loader: MiniCssExtractPlugin.loader 74 | }, 75 | { 76 | loader: 'css-loader', 77 | options: { 78 | sourceMap: true, 79 | importLoaders: 1 80 | } 81 | }, 82 | { 83 | loader: 'sass-loader', 84 | options: { 85 | sourceMap: true 86 | } 87 | } 88 | ] 89 | }, 90 | // Add SASS support - compile all other .scss files and pipe it to style.css 91 | { 92 | test: /^((?!\.global).)*\.(scss|sass)$/, 93 | use: [ 94 | { 95 | loader: MiniCssExtractPlugin.loader 96 | }, 97 | { 98 | loader: 'css-loader', 99 | options: { 100 | modules: true, 101 | importLoaders: 1, 102 | localIdentName: '[name]__[local]__[hash:base64:5]', 103 | sourceMap: true 104 | } 105 | }, 106 | { 107 | loader: 'sass-loader', 108 | options: { 109 | sourceMap: true 110 | } 111 | } 112 | ] 113 | }, 114 | // WOFF Font 115 | { 116 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 117 | use: { 118 | loader: 'url-loader', 119 | options: { 120 | limit: 10000, 121 | mimetype: 'application/font-woff' 122 | } 123 | } 124 | }, 125 | // WOFF2 Font 126 | { 127 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 128 | use: { 129 | loader: 'url-loader', 130 | options: { 131 | limit: 10000, 132 | mimetype: 'application/font-woff' 133 | } 134 | } 135 | }, 136 | // TTF Font 137 | { 138 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 139 | use: { 140 | loader: 'url-loader', 141 | options: { 142 | limit: 10000, 143 | mimetype: 'application/octet-stream' 144 | } 145 | } 146 | }, 147 | // EOT Font 148 | { 149 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 150 | use: 'file-loader' 151 | }, 152 | // SVG Font 153 | { 154 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 155 | use: { 156 | loader: 'url-loader', 157 | options: { 158 | limit: 10000, 159 | mimetype: 'image/svg+xml' 160 | } 161 | } 162 | }, 163 | // Common Image Formats 164 | { 165 | test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, 166 | use: 'url-loader' 167 | } 168 | ] 169 | }, 170 | 171 | optimization: { 172 | minimizer: process.env.E2E_BUILD 173 | ? [] 174 | : [ 175 | new TerserPlugin({ 176 | parallel: true, 177 | sourceMap: true, 178 | cache: true 179 | }), 180 | new OptimizeCSSAssetsPlugin({ 181 | cssProcessorOptions: { 182 | map: { 183 | inline: false, 184 | annotation: true 185 | } 186 | } 187 | }) 188 | ] 189 | }, 190 | 191 | plugins: [ 192 | /** 193 | * Create global constants which can be configured at compile time. 194 | * 195 | * Useful for allowing different behaviour between development builds and 196 | * release builds 197 | * 198 | * NODE_ENV should be production so that modules do not perform certain 199 | * development checks 200 | */ 201 | new webpack.EnvironmentPlugin({ 202 | NODE_ENV: 'production' 203 | }), 204 | 205 | new MiniCssExtractPlugin({ 206 | filename: 'style.css' 207 | }), 208 | 209 | new BundleAnalyzerPlugin({ 210 | analyzerMode: 211 | process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 212 | openAnalyzer: process.env.OPEN_ANALYZER === 'true' 213 | }) 214 | ] 215 | }); 216 | -------------------------------------------------------------------------------- /configs/webpack.config.renderer.dev.babel.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: off, import/no-dynamic-require: off */ 2 | 3 | /** 4 | * Build config for development electron renderer process that uses 5 | * Hot-Module-Replacement 6 | * 7 | * https://webpack.js.org/concepts/hot-module-replacement/ 8 | */ 9 | 10 | import path from 'path'; 11 | import fs from 'fs'; 12 | import webpack from 'webpack'; 13 | import chalk from 'chalk'; 14 | import merge from 'webpack-merge'; 15 | import { spawn, execSync } from 'child_process'; 16 | import baseConfig from './webpack.config.base'; 17 | import CheckNodeEnv from '../internals/scripts/CheckNodeEnv'; 18 | 19 | CheckNodeEnv('development'); 20 | 21 | const port = process.env.PORT || 1212; 22 | const publicPath = `http://localhost:${port}/dist`; 23 | const dll = path.join(__dirname, '..', 'dll'); 24 | const manifest = path.resolve(dll, 'renderer.json'); 25 | const requiredByDLLConfig = module.parent.filename.includes( 26 | 'webpack.config.renderer.dev.dll' 27 | ); 28 | 29 | /** 30 | * Warn if the DLL is not built 31 | */ 32 | if (!requiredByDLLConfig && !(fs.existsSync(dll) && fs.existsSync(manifest))) { 33 | console.log( 34 | chalk.black.bgYellow.bold( 35 | 'The DLL files are missing. Sit back while we build them for you with "yarn build-dll"' 36 | ) 37 | ); 38 | execSync('yarn build-dll'); 39 | } 40 | 41 | export default merge.smart(baseConfig, { 42 | devtool: 'inline-source-map', 43 | 44 | mode: 'development', 45 | 46 | target: 'electron-renderer', 47 | 48 | entry: [ 49 | 'react-hot-loader/patch', 50 | `webpack-dev-server/client?http://localhost:${port}/`, 51 | 'webpack/hot/only-dev-server', 52 | require.resolve('../app/index') 53 | ], 54 | 55 | output: { 56 | publicPath: `http://localhost:${port}/dist/`, 57 | filename: 'renderer.dev.js' 58 | }, 59 | 60 | module: { 61 | rules: [ 62 | { 63 | test: /\.jsx?$/, 64 | exclude: /node_modules/, 65 | use: { 66 | loader: 'babel-loader', 67 | options: { 68 | cacheDirectory: true 69 | } 70 | } 71 | }, 72 | { 73 | test: /\.global\.css$/, 74 | use: [ 75 | { 76 | loader: 'style-loader' 77 | }, 78 | { 79 | loader: 'css-loader', 80 | options: { 81 | sourceMap: true 82 | } 83 | } 84 | ] 85 | }, 86 | { 87 | test: /^((?!\.global).)*\.css$/, 88 | use: [ 89 | { 90 | loader: 'style-loader' 91 | }, 92 | { 93 | loader: 'css-loader', 94 | options: { 95 | modules: true, 96 | sourceMap: true, 97 | importLoaders: 1, 98 | localIdentName: '[name]__[local]__[hash:base64:5]' 99 | } 100 | } 101 | ] 102 | }, 103 | // SASS support - compile all .global.scss files and pipe it to style.css 104 | { 105 | test: /\.global\.(scss|sass)$/, 106 | use: [ 107 | { 108 | loader: 'style-loader' 109 | }, 110 | { 111 | loader: 'css-loader', 112 | options: { 113 | sourceMap: true 114 | } 115 | }, 116 | { 117 | loader: 'sass-loader' 118 | } 119 | ] 120 | }, 121 | // SASS support - compile all other .scss files and pipe it to style.css 122 | { 123 | test: /^((?!\.global).)*\.(scss|sass)$/, 124 | use: [ 125 | { 126 | loader: 'style-loader' 127 | }, 128 | { 129 | loader: 'css-loader', 130 | options: { 131 | modules: true, 132 | sourceMap: true, 133 | importLoaders: 1, 134 | localIdentName: '[name]__[local]__[hash:base64:5]' 135 | } 136 | }, 137 | { 138 | loader: 'sass-loader' 139 | } 140 | ] 141 | }, 142 | // WOFF Font 143 | { 144 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 145 | use: { 146 | loader: 'url-loader', 147 | options: { 148 | limit: 10000, 149 | mimetype: 'application/font-woff' 150 | } 151 | } 152 | }, 153 | // WOFF2 Font 154 | { 155 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 156 | use: { 157 | loader: 'url-loader', 158 | options: { 159 | limit: 10000, 160 | mimetype: 'application/font-woff' 161 | } 162 | } 163 | }, 164 | // TTF Font 165 | { 166 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 167 | use: { 168 | loader: 'url-loader', 169 | options: { 170 | limit: 10000, 171 | mimetype: 'application/octet-stream' 172 | } 173 | } 174 | }, 175 | // EOT Font 176 | { 177 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 178 | use: 'file-loader' 179 | }, 180 | // SVG Font 181 | { 182 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 183 | use: { 184 | loader: 'url-loader', 185 | options: { 186 | limit: 10000, 187 | mimetype: 'image/svg+xml' 188 | } 189 | } 190 | }, 191 | // Common Image Formats 192 | { 193 | test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, 194 | use: 'url-loader' 195 | } 196 | ] 197 | }, 198 | 199 | plugins: [ 200 | requiredByDLLConfig 201 | ? null 202 | : new webpack.DllReferencePlugin({ 203 | context: path.join(__dirname, '..', 'dll'), 204 | manifest: require(manifest), 205 | sourceType: 'var' 206 | }), 207 | 208 | new webpack.HotModuleReplacementPlugin({ 209 | multiStep: true 210 | }), 211 | 212 | new webpack.NoEmitOnErrorsPlugin(), 213 | 214 | /** 215 | * Create global constants which can be configured at compile time. 216 | * 217 | * Useful for allowing different behaviour between development builds and 218 | * release builds 219 | * 220 | * NODE_ENV should be production so that modules do not perform certain 221 | * development checks 222 | * 223 | * By default, use 'development' as NODE_ENV. This can be overriden with 224 | * 'staging', for example, by changing the ENV variables in the npm scripts 225 | */ 226 | new webpack.EnvironmentPlugin({ 227 | NODE_ENV: 'development' 228 | }), 229 | 230 | new webpack.LoaderOptionsPlugin({ 231 | debug: true 232 | }) 233 | ], 234 | 235 | node: { 236 | __dirname: false, 237 | __filename: false 238 | }, 239 | 240 | devServer: { 241 | port, 242 | publicPath, 243 | compress: true, 244 | noInfo: true, 245 | stats: 'errors-only', 246 | inline: true, 247 | lazy: false, 248 | hot: true, 249 | headers: { 'Access-Control-Allow-Origin': '*' }, 250 | contentBase: path.join(__dirname, 'dist'), 251 | watchOptions: { 252 | aggregateTimeout: 300, 253 | ignored: /node_modules/, 254 | poll: 100 255 | }, 256 | historyApiFallback: { 257 | verbose: true, 258 | disableDotRule: false 259 | }, 260 | before() { 261 | if (process.env.START_HOT) { 262 | console.log('Starting Main Process...'); 263 | spawn('npm', ['run', 'start-main-dev'], { 264 | shell: true, 265 | env: process.env, 266 | stdio: 'inherit' 267 | }) 268 | .on('close', code => process.exit(code)) 269 | .on('error', spawnError => console.error(spawnError)); 270 | } 271 | } 272 | } 273 | }); 274 | -------------------------------------------------------------------------------- /app/menu.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { app, Menu, shell, BrowserWindow } from 'electron'; 3 | 4 | export default class MenuBuilder { 5 | mainWindow: BrowserWindow; 6 | 7 | constructor(mainWindow: BrowserWindow) { 8 | this.mainWindow = mainWindow; 9 | } 10 | 11 | buildMenu() { 12 | if ( 13 | process.env.NODE_ENV === 'development' || 14 | process.env.DEBUG_PROD === 'true' 15 | ) { 16 | this.setupDevelopmentEnvironment(); 17 | } 18 | 19 | const template = 20 | process.platform === 'darwin' 21 | ? this.buildDarwinTemplate() 22 | : this.buildDefaultTemplate(); 23 | 24 | const menu = Menu.buildFromTemplate(template); 25 | Menu.setApplicationMenu(menu); 26 | 27 | return menu; 28 | } 29 | 30 | setupDevelopmentEnvironment() { 31 | this.mainWindow.openDevTools(); 32 | this.mainWindow.webContents.on('context-menu', (e, props) => { 33 | const { x, y } = props; 34 | 35 | Menu.buildFromTemplate([ 36 | { 37 | label: 'Inspect element', 38 | click: () => { 39 | this.mainWindow.inspectElement(x, y); 40 | } 41 | } 42 | ]).popup(this.mainWindow); 43 | }); 44 | } 45 | 46 | buildDarwinTemplate() { 47 | const subMenuAbout = { 48 | label: 'Electron', 49 | submenu: [ 50 | { 51 | label: 'About Vif', 52 | selector: 'orderFrontStandardAboutPanel:' 53 | }, 54 | { type: 'separator' }, 55 | { label: 'Services', submenu: [] }, 56 | { type: 'separator' }, 57 | { 58 | label: 'Hide ElectronReact', 59 | accelerator: 'Command+H', 60 | selector: 'hide:' 61 | }, 62 | { 63 | label: 'Hide Others', 64 | accelerator: 'Command+Shift+H', 65 | selector: 'hideOtherApplications:' 66 | }, 67 | { label: 'Show All', selector: 'unhideAllApplications:' }, 68 | { type: 'separator' }, 69 | { 70 | label: 'Quit', 71 | accelerator: 'Command+Q', 72 | click: () => { 73 | app.quit(); 74 | } 75 | } 76 | ] 77 | }; 78 | const subMenuEdit = { 79 | label: 'Edit', 80 | submenu: [ 81 | { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, 82 | { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, 83 | { type: 'separator' }, 84 | { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, 85 | { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, 86 | { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, 87 | { 88 | label: 'Select All', 89 | accelerator: 'Command+A', 90 | selector: 'selectAll:' 91 | } 92 | ] 93 | }; 94 | const subMenuViewDev = { 95 | label: 'View', 96 | submenu: [ 97 | { 98 | label: 'Reload', 99 | accelerator: 'Command+R', 100 | click: () => { 101 | this.mainWindow.webContents.reload(); 102 | } 103 | }, 104 | { 105 | label: 'Toggle Full Screen', 106 | accelerator: 'Ctrl+Command+F', 107 | click: () => { 108 | this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); 109 | } 110 | }, 111 | { 112 | label: 'Toggle Developer Tools', 113 | accelerator: 'Alt+Command+I', 114 | click: () => { 115 | this.mainWindow.toggleDevTools(); 116 | } 117 | } 118 | ] 119 | }; 120 | const subMenuViewProd = { 121 | label: 'View', 122 | submenu: [ 123 | { 124 | label: 'Toggle Full Screen', 125 | accelerator: 'Ctrl+Command+F', 126 | click: () => { 127 | this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); 128 | } 129 | } 130 | ] 131 | }; 132 | const subMenuWindow = { 133 | label: 'Window', 134 | submenu: [ 135 | { 136 | label: 'Minimize', 137 | accelerator: 'Command+M', 138 | selector: 'performMiniaturize:' 139 | }, 140 | { label: 'Close', accelerator: 'Command+W', selector: 'performClose:' }, 141 | { type: 'separator' }, 142 | { label: 'Bring All to Front', selector: 'arrangeInFront:' } 143 | ] 144 | }; 145 | const subMenuHelp = { 146 | label: 'Help', 147 | submenu: [ 148 | { 149 | label: 'Learn More', 150 | click() { 151 | shell.openExternal('http://electron.atom.io'); 152 | } 153 | }, 154 | { 155 | label: 'Documentation', 156 | click() { 157 | shell.openExternal( 158 | 'https://github.com/atom/electron/tree/master/docs#readme' 159 | ); 160 | } 161 | }, 162 | { 163 | label: 'Community Discussions', 164 | click() { 165 | shell.openExternal('https://discuss.atom.io/c/electron'); 166 | } 167 | }, 168 | { 169 | label: 'Search Issues', 170 | click() { 171 | shell.openExternal('https://github.com/atom/electron/issues'); 172 | } 173 | } 174 | ] 175 | }; 176 | 177 | const subMenuView = 178 | process.env.NODE_ENV === 'development' ? subMenuViewDev : subMenuViewProd; 179 | 180 | return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp]; 181 | } 182 | 183 | buildDefaultTemplate() { 184 | const templateDefault = [ 185 | { 186 | label: '&File', 187 | submenu: [ 188 | { 189 | label: '&Open', 190 | accelerator: 'Ctrl+O' 191 | }, 192 | { 193 | label: '&Close', 194 | accelerator: 'Ctrl+W', 195 | click: () => { 196 | this.mainWindow.close(); 197 | } 198 | } 199 | ] 200 | }, 201 | { 202 | label: '&View', 203 | submenu: 204 | process.env.NODE_ENV === 'development' 205 | ? [ 206 | { 207 | label: '&Reload', 208 | accelerator: 'Ctrl+R', 209 | click: () => { 210 | this.mainWindow.webContents.reload(); 211 | } 212 | }, 213 | { 214 | label: 'Toggle &Full Screen', 215 | accelerator: 'F11', 216 | click: () => { 217 | this.mainWindow.setFullScreen( 218 | !this.mainWindow.isFullScreen() 219 | ); 220 | } 221 | }, 222 | { 223 | label: 'Toggle &Developer Tools', 224 | accelerator: 'Alt+Ctrl+I', 225 | click: () => { 226 | this.mainWindow.toggleDevTools(); 227 | } 228 | } 229 | ] 230 | : [ 231 | { 232 | label: 'Toggle &Full Screen', 233 | accelerator: 'F11', 234 | click: () => { 235 | this.mainWindow.setFullScreen( 236 | !this.mainWindow.isFullScreen() 237 | ); 238 | } 239 | } 240 | ] 241 | }, 242 | { 243 | label: 'Help', 244 | submenu: [ 245 | { 246 | label: 'Learn More', 247 | click() { 248 | shell.openExternal('http://electron.atom.io'); 249 | } 250 | }, 251 | { 252 | label: 'Documentation', 253 | click() { 254 | shell.openExternal( 255 | 'https://github.com/atom/electron/tree/master/docs#readme' 256 | ); 257 | } 258 | }, 259 | { 260 | label: 'Community Discussions', 261 | click() { 262 | shell.openExternal('https://discuss.atom.io/c/electron'); 263 | } 264 | }, 265 | { 266 | label: 'Search Issues', 267 | click() { 268 | shell.openExternal('https://github.com/atom/electron/issues'); 269 | } 270 | } 271 | ] 272 | } 273 | ]; 274 | 275 | return templateDefault; 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vif", 3 | "productName": "Vif", 4 | "version": "1.0.0", 5 | "description": "cross-platform desktop application to convert videos to high-quality GIFs", 6 | "scripts": { 7 | "build": "concurrently \"yarn build-main\" \"yarn build-renderer\"", 8 | "build-dll": "cross-env NODE_ENV=development webpack --config ./configs/webpack.config.renderer.dev.dll.babel.js --colors", 9 | "build-e2e": "cross-env E2E_BUILD=true yarn build", 10 | "build-main": "cross-env NODE_ENV=production webpack --config ./configs/webpack.config.main.prod.babel.js --colors", 11 | "build-renderer": "cross-env NODE_ENV=production webpack --config ./configs/webpack.config.renderer.prod.babel.js --colors", 12 | "dev": "cross-env START_HOT=1 node -r @babel/register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 yarn start-renderer-dev", 13 | "flow": "flow", 14 | "flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true", 15 | "lint": "cross-env NODE_ENV=development eslint --cache --format=pretty .", 16 | "lint-fix": "yarn --silent lint --fix; exit 0", 17 | "lint-styles": "stylelint --ignore-path .eslintignore '**/*.*(css|scss)' --syntax scss", 18 | "lint-styles-fix": "yarn --silent lint-styles --fix; exit 0", 19 | "package": "yarn build && electron-builder build --publish never", 20 | "package-all": "yarn build && electron-builder build -mwl", 21 | "package-ci": "yarn postinstall && yarn build && electron-builder --publish always", 22 | "package-linux": "yarn build && electron-builder build --linux", 23 | "package-win": "yarn build && electron-builder build --win --x64", 24 | "postinstall": "yarn flow-typed && electron-builder install-app-deps package.json && yarn build-dll && opencollective-postinstall", 25 | "postlint-fix": "prettier --ignore-path .eslintignore --single-quote --write '**/*.{*{js,jsx,json},babelrc,eslintrc,prettierrc,stylelintrc}'", 26 | "postlint-styles-fix": "prettier --ignore-path .eslintignore --single-quote --write '**/*.{css,scss}'", 27 | "prestart": "yarn build", 28 | "start": "cross-env NODE_ENV=production electron ./app/main.prod.js", 29 | "start-main-dev": "cross-env HOT=1 NODE_ENV=development electron -r @babel/register ./app/main.dev.js", 30 | "start-renderer-dev": "cross-env NODE_ENV=development webpack-dev-server --config configs/webpack.config.renderer.dev.babel.js", 31 | "test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 jest", 32 | "test-all": "yarn lint && yarn flow && yarn build && yarn test && yarn build-e2e && yarn test-e2e", 33 | "test-e2e": "node -r @babel/register ./internals/scripts/CheckBuiltsExist.js && cross-env NODE_ENV=test testcafe electron:./ ./test/e2e/HomePage.e2e.js", 34 | "test-e2e-live": "node -r @babel/register ./internals/scripts/CheckBuiltsExist.js && cross-env NODE_ENV=test testcafe-live electron:./ ./test/e2e/HomePage.e2e.js", 35 | "test-watch": "yarn test --watch" 36 | }, 37 | "lint-staged": { 38 | "*.{js,jsx}": [ 39 | "cross-env NODE_ENV=development eslint --cache --format=pretty", 40 | "prettier --ignore-path .eslintignore --single-quote --write", 41 | "git add" 42 | ], 43 | "{*.json,.{babelrc,eslintrc,prettierrc,stylelintrc}}": [ 44 | "prettier --ignore-path .eslintignore --parser json --write", 45 | "git add" 46 | ], 47 | "*.{css,scss}": [ 48 | "stylelint --ignore-path .eslintignore --syntax scss --fix", 49 | "prettier --ignore-path .eslintignore --single-quote --write", 50 | "git add" 51 | ], 52 | "*.{yml,md}": [ 53 | "prettier --ignore-path .eslintignore --single-quote --write", 54 | "git add" 55 | ] 56 | }, 57 | "main": "./app/main.prod.js", 58 | "build": { 59 | "productName": "Vif", 60 | "appId": "org.develar.Vif", 61 | "files": [ 62 | "app/dist/", 63 | "app/app.html", 64 | "app/main.prod.js", 65 | "app/main.prod.js.map", 66 | "package.json" 67 | ], 68 | "dmg": { 69 | "contents": [ 70 | { 71 | "x": 130, 72 | "y": 220 73 | }, 74 | { 75 | "x": 410, 76 | "y": 220, 77 | "type": "link", 78 | "path": "/Applications" 79 | } 80 | ] 81 | }, 82 | "win": { 83 | "target": [ 84 | "nsis", 85 | "msi" 86 | ] 87 | }, 88 | "linux": { 89 | "target": [ 90 | "deb", 91 | "rpm", 92 | "snap", 93 | "AppImage" 94 | ], 95 | "category": "Development" 96 | }, 97 | "directories": { 98 | "buildResources": "resources", 99 | "output": "release" 100 | }, 101 | "publish": { 102 | "provider": "github", 103 | "owner": "soroushchehresa", 104 | "repo": "Vif", 105 | "private": false 106 | } 107 | }, 108 | "repository": { 109 | "type": "git", 110 | "url": "git+https://github.com/soroushchehresa/Vif.git" 111 | }, 112 | "author": { 113 | "name": "Soroush Chehresa", 114 | "email": "s1996ch@gmail.com", 115 | "url": "https://github.com/soroushchehresa/Vif" 116 | }, 117 | "contributors": [ 118 | { 119 | "name": "Vikram Rangaraj", 120 | "email": "vikr01@icloud.com", 121 | "url": "https://github.com/vikr01" 122 | }, 123 | { 124 | "name": "Amila Welihinda", 125 | "email": "amilajack@gmail.com", 126 | "url": "https://github.com/amilajack" 127 | } 128 | ], 129 | "license": "MIT", 130 | "bugs": { 131 | "url": "https://github.com/soroushchehresa/Vif/issues" 132 | }, 133 | "keywords": [ 134 | "vif", 135 | "video-to-gif", 136 | "gif", 137 | "gif-animation", 138 | "converter", 139 | "video-converter", 140 | "convert-videos", 141 | "react", 142 | "electron-app" 143 | ], 144 | "homepage": "https://github.com/soroushchehresa/Vif#readme", 145 | "jest": { 146 | "testURL": "http://localhost/", 147 | "moduleNameMapper": { 148 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/internals/mocks/fileMock.js", 149 | "\\.(css|less|sass|scss)$": "identity-obj-proxy" 150 | }, 151 | "moduleFileExtensions": [ 152 | "js", 153 | "jsx", 154 | "json" 155 | ], 156 | "transform": { 157 | "^.+\\.jsx?$": "babel-jest" 158 | }, 159 | "setupFiles": [ 160 | "./internals/scripts/CheckBuiltsExist.js" 161 | ] 162 | }, 163 | "devDependencies": { 164 | "@babel/core": "^7.1.6", 165 | "@babel/plugin-proposal-class-properties": "^7.1.0", 166 | "@babel/plugin-proposal-decorators": "^7.1.6", 167 | "@babel/plugin-proposal-do-expressions": "^7.0.0", 168 | "@babel/plugin-proposal-export-default-from": "^7.0.0", 169 | "@babel/plugin-proposal-export-namespace-from": "^7.0.0", 170 | "@babel/plugin-proposal-function-bind": "^7.0.0", 171 | "@babel/plugin-proposal-function-sent": "^7.1.0", 172 | "@babel/plugin-proposal-json-strings": "^7.0.0", 173 | "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0", 174 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", 175 | "@babel/plugin-proposal-numeric-separator": "^7.0.0", 176 | "@babel/plugin-proposal-optional-chaining": "^7.0.0", 177 | "@babel/plugin-proposal-pipeline-operator": "^7.0.0", 178 | "@babel/plugin-proposal-throw-expressions": "^7.0.0", 179 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 180 | "@babel/plugin-syntax-import-meta": "^7.0.0", 181 | "@babel/plugin-transform-react-constant-elements": "^7.0.0", 182 | "@babel/plugin-transform-react-inline-elements": "^7.0.0", 183 | "@babel/preset-env": "^7.1.6", 184 | "@babel/preset-flow": "^7.0.0", 185 | "@babel/preset-react": "^7.0.0", 186 | "@babel/register": "^7.0.0", 187 | "babel-core": "7.0.0-bridge.0", 188 | "babel-eslint": "^10.0.1", 189 | "babel-jest": "^23.6.0", 190 | "babel-loader": "^8.0.4", 191 | "babel-plugin-dev-expression": "^0.2.1", 192 | "babel-plugin-transform-react-remove-prop-types": "^0.4.20", 193 | "chalk": "^2.4.1", 194 | "concurrently": "^4.1.0", 195 | "connected-react-router": "^5.0.1", 196 | "cross-env": "^5.2.0", 197 | "cross-spawn": "^6.0.5", 198 | "css-loader": "^1.0.1", 199 | "detect-port": "^1.3.0", 200 | "electron": "^5.0.8", 201 | "electron-builder": "^20.34.0", 202 | "electron-devtools-installer": "^2.2.4", 203 | "enzyme": "^3.7.0", 204 | "enzyme-adapter-react-16": "^1.7.0", 205 | "enzyme-to-json": "^3.3.4", 206 | "eslint": "^5.9.0", 207 | "eslint-config-airbnb": "^17.1.0", 208 | "eslint-config-prettier": "^3.3.0", 209 | "eslint-formatter-pretty": "^2.0.0", 210 | "eslint-import-resolver-webpack": "^0.10.1", 211 | "eslint-plugin-compat": "^2.6.3", 212 | "eslint-plugin-flowtype": "^3.2.0", 213 | "eslint-plugin-import": "^2.14.0", 214 | "eslint-plugin-jest": "^22.0.0", 215 | "eslint-plugin-jsx-a11y": "6.1.2", 216 | "eslint-plugin-promise": "^4.0.1", 217 | "eslint-plugin-react": "^7.11.1", 218 | "eslint-plugin-testcafe": "^0.2.1", 219 | "fbjs-scripts": "^1.0.1", 220 | "file-loader": "^2.0.0", 221 | "flow-bin": "^0.77.0", 222 | "flow-runtime": "^0.17.0", 223 | "flow-typed": "^2.5.1", 224 | "husky": "^1.1.4", 225 | "identity-obj-proxy": "^3.0.0", 226 | "jest": "^23.6.0", 227 | "lint-staged": "^8.1.0", 228 | "mini-css-extract-plugin": "^0.4.4", 229 | "node-sass": "^4.10.0", 230 | "opencollective-postinstall": "^2.0.1", 231 | "optimize-css-assets-webpack-plugin": "^5.0.1", 232 | "prettier": "^1.15.2", 233 | "react-test-renderer": "^16.6.3", 234 | "redux-logger": "^3.0.6", 235 | "rimraf": "^2.6.2", 236 | "sass-loader": "^7.1.0", 237 | "sinon": "^7.1.1", 238 | "spectron": "^5.0.0", 239 | "style-loader": "^0.23.1", 240 | "stylelint": "^9.8.0", 241 | "stylelint-config-prettier": "^4.0.0", 242 | "stylelint-config-standard": "^18.2.0", 243 | "terser-webpack-plugin": "^1.1.0", 244 | "testcafe": "^0.23.2", 245 | "testcafe-browser-provider-electron": "^0.0.6", 246 | "testcafe-live": "^0.1.4", 247 | "testcafe-react-selectors": "^3.0.0", 248 | "url-loader": "^1.1.2", 249 | "webpack": "^4.26.0", 250 | "webpack-bundle-analyzer": "^3.0.3", 251 | "webpack-cli": "^3.1.2", 252 | "webpack-dev-server": "^3.1.10", 253 | "webpack-merge": "^4.1.4", 254 | "yarn": "^1.12.3" 255 | }, 256 | "dependencies": { 257 | "@fortawesome/fontawesome-free": "^5.5.0", 258 | "base64image-to-file": "^1.2.0", 259 | "devtron": "^1.4.0", 260 | "electron-debug": "^2.0.0", 261 | "electron-is": "^3.0.0", 262 | "electron-log": "^2.2.17", 263 | "electron-updater": "^3.2.3", 264 | "get-video-dimensions": "^1.0.0", 265 | "get-video-duration": "^1.0.3", 266 | "gifshot": "^0.4.5", 267 | "history": "^4.7.2", 268 | "is-video": "^1.0.1", 269 | "react": "^16.8.6", 270 | "react-circular-progressbar": "^1.2.1", 271 | "react-dom": "^16.8.6", 272 | "react-dropzone": "^10.1.4", 273 | "react-hot-loader": "^4.3.12", 274 | "react-redux": "^5.1.1", 275 | "react-router": "^4.3.1", 276 | "react-router-dom": "^4.3.1", 277 | "redux": "^4.0.1", 278 | "redux-thunk": "^2.3.0", 279 | "source-map-support": "^0.5.9" 280 | }, 281 | "devEngines": { 282 | "node": ">=7.x", 283 | "npm": ">=4.x", 284 | "yarn": ">=0.21.3" 285 | }, 286 | "collective": { 287 | "url": "" 288 | }, 289 | "browserslist": "electron 1.6" 290 | } 291 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.17.1 (2018.11.20) 2 | 3 | - Fix `yarn test-e2e` and testcafe for single package.json structure 4 | - Fixes incorrect path in `yarn start` script 5 | - Bumped deps 6 | - Bump g++ in travis 7 | - Change clone arguments to clone only master 8 | - Change babel config to target current electron version 9 | 10 | For full change list, see https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2021 11 | 12 | # 0.17.0 (2018.10.30) 13 | 14 | - upgraded to `babel@7` (thanks to @vikr01 🎉🎉🎉) 15 | - migrated from [two `package.json` structure](https://www.electron.build/tutorials/two-package-structure) (thanks to @HyperSprite!) 16 | - initial auto update support (experimental) 17 | - migrate from greenkeeper to [renovate](https://renovatebot.com) 18 | - added issue template 19 | - use `babel-preset-env` to target current electron version 20 | - add [opencollective](https://opencollective.com/electron-react-boilerplate-594) banner message display in postinstall script (help support ERB 🙏) 21 | - fix failing ci issues 22 | 23 | # 0.16.0 (2018.10.3) 24 | 25 | - removed unused dependencies 26 | - migrate from `react-redux-router` to `connect-react-router` 27 | - move webpack configs to `./webpack` dir 28 | - use `g++` on travis when testing linux 29 | - migrate from `spectron` to `testcafe` for e2e tests 30 | - add linting support for config styles 31 | - changed stylelint config 32 | - temporarily disabled flow in appveyor to make ci pass 33 | - added necessary infra to publish releases from ci 34 | 35 | # 0.15.0 (2018.8.25) 36 | 37 | - Performance: cache webpack uglify results 38 | - Feature: add start minimized feature 39 | - Feature: lint and fix styles with prettier and stylelint 40 | - Feature: add greenkeeper support 41 | 42 | # 0.14.0 (2018.5.24) 43 | 44 | - Improved CI timings 45 | - Migrated README commands to yarn from npm 46 | - Improved vscode config 47 | - Updated all dependencies to latest semver 48 | - Fix `electron-rebuild` script bug 49 | - Migrated to `mini-css-extract-plugin` from `extract-text-plugin` 50 | - Added `optimize-css-assets-webpack-plugin` 51 | - Run `prettier` on json, css, scss, and more filetypes 52 | 53 | # 0.13.3 (2018.5.24) 54 | 55 | - Add git precommit hook, when git commit will use `prettier` to format git add code 56 | - Add format code function in `lint-fix` npm script which can use `prettier` to format project js code 57 | 58 | # 0.13.2 (2018.1.31) 59 | 60 | - Hot Module Reload (HMR) fixes 61 | - Bumped all dependencies to latest semver 62 | - Prevent error propagation of `CheckNativeDeps` script 63 | 64 | # 0.13.1 (2018.1.13) 65 | 66 | - Hot Module Reload (HMR) fixes 67 | - Bumped all dependencies to latest semver 68 | - Fixed electron-rebuild script 69 | - Fixed tests scripts to run on all platforms 70 | - Skip redux logs in console in test ENV 71 | 72 | # 0.13.0 (2018.1.6) 73 | 74 | #### Additions 75 | 76 | - Add native dependencies check on postinstall 77 | - Updated all dependencies to latest semver 78 | 79 | # 0.12.0 (2017.7.8) 80 | 81 | #### Misc 82 | 83 | - Removed `babel-polyfill` 84 | - Renamed and alphabetized npm scripts 85 | 86 | #### Breaking 87 | 88 | - Changed node dev `__dirname` and `__filename` to node built in fn's (https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/1035) 89 | - Renamed `app/bundle.js` to `app/renderer.prod.js` for consistency 90 | - Renamed `dll/vendor.js` to `dll/renderer.dev.dll.js` for consistency 91 | 92 | #### Additions 93 | 94 | - Enable node_modules cache on CI 95 | 96 | # 0.11.2 (2017.5.1) 97 | 98 | Yay! Another patch release. This release mostly includes refactorings and router bug fixes. Huge thanks to @anthonyraymond! 99 | 100 | ⚠️ Windows electron builds are failing because of [this issue](https://github.com/electron/electron/issues/9321). This is not an issue with the boilerplate ⚠️ 101 | 102 | #### Breaking 103 | 104 | - **Renamed `./app/main.development.js` => `./app/main.{dev,prod}.js`:** [#963](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/963) 105 | 106 | #### Fixes 107 | 108 | - **Fixed reloading when not on `/` path:** [#958](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/958) [#949](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/949) 109 | 110 | #### Additions 111 | 112 | - **Added support for stylefmt:** [#960](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/960) 113 | 114 | # 0.11.1 (2017.4.23) 115 | 116 | You can now debug the production build with devtools like so: 117 | 118 | ``` 119 | DEBUG_PROD=true npm run package 120 | ``` 121 | 122 | 🎉🎉🎉 123 | 124 | #### Additions 125 | 126 | - **Added support for debugging production build:** [#fab245a](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/941/commits/fab245a077d02a09630f74270806c0c534a4ff95) 127 | 128 | #### Bug Fixes 129 | 130 | - **Fixed bug related to importing native dependencies:** [#933](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/933) 131 | 132 | #### Improvements 133 | 134 | - **Updated all deps to latest semver** 135 | 136 | # 0.11.0 (2017.4.19) 137 | 138 | Here's the most notable changes since `v0.10.0`. Its been about a year since a release has been pushed. Expect a new release to be published every 3-4 weeks. 139 | 140 | #### Breaking Changes 141 | 142 | - **Dropped support for node < 6** 143 | - **Refactored webpack config files** 144 | - **Migrate to two-package.json project structure** 145 | - **Updated all devDeps to latest semver** 146 | - **Migrated to Jest:** [#768](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/768) 147 | - **Migrated to `react-router@4`** 148 | - **Migrated to `electron-builder@4`** 149 | - **Migrated to `webpack@2`** 150 | - **Migrated to `react-hot-loader@3`** 151 | - **Changed default live reload server PORT to `1212` from `3000`** 152 | 153 | #### Additions 154 | 155 | - **Added support for Yarn:** [#451](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/451) 156 | - **Added support for Flow:** [#425](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/425) 157 | - **Added support for stylelint:** [#911](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/911) 158 | - **Added support for electron-builder:** [#876](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/876) 159 | - **Added optional support for SASS:** [#880](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/880) 160 | - **Added support for eslint-plugin-flowtype:** [#911](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/911) 161 | - **Added support for appveyor:** [#280](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/280) 162 | - **Added support for webpack dlls:** [#860](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/860) 163 | - **Route based code splitting:** [#884](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/884) 164 | - **Added support for Webpack Bundle Analyzer:** [#922](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/922) 165 | 166 | #### Improvements 167 | 168 | - **Parallelize renderer and main build processes when running `npm run build`** 169 | - **Dynamically generate electron app menu** 170 | - **Improved vscode integration:** [#856](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/856) 171 | 172 | #### Bug Fixes 173 | 174 | - **Fixed hot module replacement race condition bug:** [#917](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/917) [#920](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/920) 175 | 176 | # 0.10.0 (2016.4.18) 177 | 178 | #### Improvements 179 | 180 | - **Use Babel in main process with Webpack build:** [#201](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/201) 181 | - **Change targets to built-in support by webpack:** [#197](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/197) 182 | - **use es2015 syntax for webpack configs:** [#195](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/195) 183 | - **Open application when webcontent is loaded:** [#192](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/192) 184 | - **Upgraded dependencies** 185 | 186 | #### Bug fixed 187 | 188 | - **Fix `npm list electron-prebuilt` in package.js:** [#188](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/188) 189 | 190 | # 0.9.0 (2016.3.23) 191 | 192 | #### Improvements 193 | 194 | - **Added [redux-logger](https://github.com/fcomb/redux-logger)** 195 | - **Upgraded [react-router-redux](https://github.com/reactjs/react-router-redux) to v4** 196 | - **Upgraded dependencies** 197 | - **Added `npm run dev` command:** [#162](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/162) 198 | - **electron to v0.37.2** 199 | 200 | #### Breaking Changes 201 | 202 | - **css module as default:** [#154](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/154). 203 | - **set default NODE_ENV to production:** [#140](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/140) 204 | 205 | # 0.8.0 (2016.2.17) 206 | 207 | #### Bug fixed 208 | 209 | - **Fix lint errors** 210 | - **Fix Webpack publicPath for production builds**: [#119](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/119). 211 | - **package script now chooses correct OS icon extension** 212 | 213 | #### Improvements 214 | 215 | - **babel 6** 216 | - **Upgrade Dependencies** 217 | - **Enable CSS source maps** 218 | - **Add json-loader**: [#128](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/128). 219 | - **react-router 2.0 and react-router-redux 3.0** 220 | 221 | # 0.7.1 (2015.12.27) 222 | 223 | #### Bug fixed 224 | 225 | - **Fixed npm script on windows 10:** [#103](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/103). 226 | - **history and react-router version bump**: [#109](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/109), [#110](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/110). 227 | 228 | #### Improvements 229 | 230 | - **electron 0.36** 231 | 232 | # 0.7.0 (2015.12.16) 233 | 234 | #### Bug fixed 235 | 236 | - **Fixed process.env.NODE_ENV variable in webpack:** [#74](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/74). 237 | - **add missing object-assign**: [#76](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/76). 238 | - **packaging in npm@3:** [#77](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/77). 239 | - **compatibility in windows:** [#100](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/100). 240 | - **disable chrome debugger in production env:** [#102](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/102). 241 | 242 | #### Improvements 243 | 244 | - **redux** 245 | - **css-modules** 246 | - **upgrade to react-router 1.x** 247 | - **unit tests** 248 | - **e2e tests** 249 | - **travis-ci** 250 | - **upgrade to electron 0.35.x** 251 | - **use es2015** 252 | - **check dev engine for node and npm** 253 | 254 | # 0.6.5 (2015.11.7) 255 | 256 | #### Improvements 257 | 258 | - **Bump style-loader to 0.13** 259 | - **Bump css-loader to 0.22** 260 | 261 | # 0.6.4 (2015.10.27) 262 | 263 | #### Improvements 264 | 265 | - **Bump electron-debug to 0.3** 266 | 267 | # 0.6.3 (2015.10.26) 268 | 269 | #### Improvements 270 | 271 | - **Initialize ExtractTextPlugin once:** [#64](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/64). 272 | 273 | # 0.6.2 (2015.10.18) 274 | 275 | #### Bug fixed 276 | 277 | - **Babel plugins production env not be set properly:** [#57](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/57). 278 | 279 | # 0.6.1 (2015.10.17) 280 | 281 | #### Improvements 282 | 283 | - **Bump electron to v0.34.0** 284 | 285 | # 0.6.0 (2015.10.16) 286 | 287 | #### Breaking Changes 288 | 289 | - **From react-hot-loader to react-transform** 290 | 291 | # 0.5.2 (2015.10.15) 292 | 293 | #### Improvements 294 | 295 | - **Run tests with babel-register:** [#29](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/29). 296 | 297 | # 0.5.1 (2015.10.12) 298 | 299 | #### Bug fixed 300 | 301 | - **Fix #51:** use `path.join(__dirname` instead of `./`. 302 | 303 | # 0.5.0 (2015.10.11) 304 | 305 | #### Improvements 306 | 307 | - **Simplify webpack config** see [#50](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/50). 308 | 309 | #### Breaking Changes 310 | 311 | - **webpack configs** 312 | - **port changed:** changed default port from 2992 to 3000. 313 | - **npm scripts:** remove `start-dev` and `dev-server`. rename `hot-dev-server` to `hot-server`. 314 | 315 | # 0.4.3 (2015.9.22) 316 | 317 | #### Bug fixed 318 | 319 | - **Fix #45 zeromq crash:** bump version of `electron-prebuilt`. 320 | 321 | # 0.4.2 (2015.9.15) 322 | 323 | #### Bug fixed 324 | 325 | - **run start-hot breaks chrome refresh(CTRL+R) (#42)**: bump `electron-debug` to `0.2.1` 326 | 327 | # 0.4.1 (2015.9.11) 328 | 329 | #### Improvements 330 | 331 | - **use electron-prebuilt version for packaging (#33)** 332 | 333 | # 0.4.0 (2015.9.5) 334 | 335 | #### Improvements 336 | 337 | - **update dependencies** 338 | 339 | # 0.3.0 (2015.8.31) 340 | 341 | #### Improvements 342 | 343 | - **eslint-config-airbnb** 344 | 345 | # 0.2.10 (2015.8.27) 346 | 347 | #### Features 348 | 349 | - **custom placeholder icon** 350 | 351 | #### Improvements 352 | 353 | - **electron-renderer as target:** via [webpack-target-electron-renderer](https://github.com/chentsulin/webpack-target-electron-renderer) 354 | 355 | # 0.2.9 (2015.8.18) 356 | 357 | #### Bug fixed 358 | 359 | - **Fix hot-reload** 360 | 361 | # 0.2.8 (2015.8.13) 362 | 363 | #### Improvements 364 | 365 | - **bump electron-debug** 366 | - **babelrc** 367 | - **organize webpack scripts** 368 | 369 | # 0.2.7 (2015.7.9) 370 | 371 | #### Bug fixed 372 | 373 | - **defaultProps:** fix typos. 374 | 375 | # 0.2.6 (2015.7.3) 376 | 377 | #### Features 378 | 379 | - **menu** 380 | 381 | #### Bug fixed 382 | 383 | - **package.js:** include webpack build. 384 | 385 | # 0.2.5 (2015.7.1) 386 | 387 | #### Features 388 | 389 | - **NPM Script:** support multi-platform 390 | - **package:** `--all` option 391 | 392 | # 0.2.4 (2015.6.9) 393 | 394 | #### Bug fixed 395 | 396 | - **Eslint:** typo, [#17](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/17) and improve `.eslintrc` 397 | 398 | # 0.2.3 (2015.6.3) 399 | 400 | #### Features 401 | 402 | - **Package Version:** use latest release electron version as default 403 | - **Ignore Large peerDependencies** 404 | 405 | #### Bug fixed 406 | 407 | - **Npm Script:** typo, [#6](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/6) 408 | - **Missing css:** [#7](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/7) 409 | 410 | # 0.2.2 (2015.6.2) 411 | 412 | #### Features 413 | 414 | - **electron-debug** 415 | 416 | #### Bug fixed 417 | 418 | - **Webpack:** add `.json` and `.node` to extensions for imitating node require. 419 | - **Webpack:** set `node_modules` to externals for native module support. 420 | 421 | # 0.2.1 (2015.5.30) 422 | 423 | #### Bug fixed 424 | 425 | - **Webpack:** #1, change build target to `atom`. 426 | 427 | # 0.2.0 (2015.5.30) 428 | 429 | #### Features 430 | 431 | - **Ignore:** `test`, `tools`, `release` folder and devDependencies in `package.json`. 432 | - **Support asar** 433 | - **Support icon** 434 | 435 | # 0.1.0 (2015.5.27) 436 | 437 | #### Features 438 | 439 | - **Webpack:** babel, react-hot, ... 440 | - **Flux:** actions, api, components, containers, stores.. 441 | - **Package:** darwin (osx), linux and win32 (windows) platform. 442 | --------------------------------------------------------------------------------