@ -0,0 +1,12 @@ |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
end_of_line = lf |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
@ -0,0 +1,7 @@ |
|||
{ |
|||
"rules": { |
|||
"no-console": "off", |
|||
"global-require": "off", |
|||
"import/no-dynamic-require": "off" |
|||
} |
|||
} |
@ -0,0 +1,46 @@ |
|||
/** |
|||
* Base webpack config used across other specific configs |
|||
*/ |
|||
|
|||
import path from 'path'; |
|||
import webpack from 'webpack'; |
|||
import { dependencies as externals } from '../../src/package.json'; |
|||
|
|||
export default { |
|||
externals: [...Object.keys(externals || {})], |
|||
|
|||
module: { |
|||
rules: [ |
|||
{ |
|||
test: /\.tsx?$/, |
|||
exclude: /node_modules/, |
|||
use: { |
|||
loader: 'babel-loader', |
|||
options: { |
|||
cacheDirectory: true, |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
|
|||
output: { |
|||
path: path.join(__dirname, '../../src'), |
|||
// https://github.com/webpack/webpack/issues/1114
|
|||
libraryTarget: 'commonjs2', |
|||
}, |
|||
|
|||
/** |
|||
* Determine the array of extensions that should be used to resolve modules. |
|||
*/ |
|||
resolve: { |
|||
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], |
|||
modules: [path.join(__dirname, '../../src'), 'node_modules'], |
|||
}, |
|||
|
|||
plugins: [ |
|||
new webpack.EnvironmentPlugin({ |
|||
NODE_ENV: 'production', |
|||
}), |
|||
], |
|||
}; |
@ -0,0 +1,4 @@ |
|||
/* eslint import/no-unresolved: off, import/no-self-import: off */ |
|||
require('@babel/register'); |
|||
|
|||
module.exports = require('./webpack.config.renderer.dev.babel').default; |
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Webpack config for production electron main process |
|||
*/ |
|||
|
|||
import path from 'path'; |
|||
import webpack from 'webpack'; |
|||
import { merge } from 'webpack-merge'; |
|||
import TerserPlugin from 'terser-webpack-plugin'; |
|||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; |
|||
import baseConfig from './webpack.config.base'; |
|||
import CheckNodeEnv from '../scripts/CheckNodeEnv'; |
|||
import DeleteSourceMaps from '../scripts/DeleteSourceMaps'; |
|||
|
|||
CheckNodeEnv('production'); |
|||
DeleteSourceMaps(); |
|||
|
|||
const devtoolsConfig = process.env.DEBUG_PROD === 'true' ? { |
|||
devtool: 'source-map' |
|||
} : {}; |
|||
|
|||
export default merge(baseConfig, { |
|||
...devtoolsConfig, |
|||
|
|||
mode: 'production', |
|||
|
|||
target: 'electron-main', |
|||
|
|||
entry: './src/main.dev.ts', |
|||
|
|||
output: { |
|||
path: path.join(__dirname, '../../'), |
|||
filename: './src/main.prod.js', |
|||
}, |
|||
|
|||
optimization: { |
|||
minimizer: [ |
|||
new TerserPlugin({ |
|||
parallel: true, |
|||
}), |
|||
] |
|||
}, |
|||
|
|||
plugins: [ |
|||
new BundleAnalyzerPlugin({ |
|||
analyzerMode: |
|||
process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', |
|||
openAnalyzer: process.env.OPEN_ANALYZER === 'true', |
|||
}), |
|||
|
|||
/** |
|||
* Create global constants which can be configured at compile time. |
|||
* |
|||
* Useful for allowing different behaviour between development builds and |
|||
* release builds |
|||
* |
|||
* NODE_ENV should be production so that modules do not perform certain |
|||
* development checks |
|||
*/ |
|||
new webpack.EnvironmentPlugin({ |
|||
NODE_ENV: 'production', |
|||
DEBUG_PROD: false, |
|||
START_MINIMIZED: false, |
|||
}), |
|||
], |
|||
|
|||
/** |
|||
* Disables webpack processing of __dirname and __filename. |
|||
* If you run the bundle in node.js it falls back to these values of node.js. |
|||
* https://github.com/webpack/webpack/issues/2010
|
|||
*/ |
|||
node: { |
|||
__dirname: false, |
|||
__filename: false, |
|||
}, |
|||
}); |
@ -0,0 +1,335 @@ |
|||
import path from 'path'; |
|||
import fs from 'fs'; |
|||
import webpack from 'webpack'; |
|||
import chalk from 'chalk'; |
|||
import { merge } from 'webpack-merge'; |
|||
import { spawn, execSync } from 'child_process'; |
|||
import baseConfig from './webpack.config.base'; |
|||
import CheckNodeEnv from '../scripts/CheckNodeEnv'; |
|||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; |
|||
|
|||
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
|
|||
// at the dev webpack config is not accidentally run in a production environment
|
|||
if (process.env.NODE_ENV === 'production') { |
|||
CheckNodeEnv('development'); |
|||
} |
|||
|
|||
const port = process.env.PORT || 4343; |
|||
const publicPath = `http://localhost:${port}/dist`; |
|||
const dllDir = path.join(__dirname, '../dll'); |
|||
const manifest = path.resolve(dllDir, 'renderer.json'); |
|||
const requiredByDLLConfig = module.parent.filename.includes( |
|||
'webpack.config.renderer.dev.dll' |
|||
); |
|||
|
|||
/** |
|||
* Warn if the DLL is not built |
|||
*/ |
|||
if ( |
|||
!requiredByDLLConfig && |
|||
!(fs.existsSync(dllDir) && fs.existsSync(manifest)) |
|||
) { |
|||
console.log( |
|||
chalk.black.bgYellow.bold( |
|||
'The DLL files are missing. Sit back while we build them for you with "yarn build-dll"' |
|||
) |
|||
); |
|||
execSync('yarn postinstall'); |
|||
} |
|||
|
|||
export default merge(baseConfig, { |
|||
devtool: 'inline-source-map', |
|||
|
|||
mode: 'development', |
|||
|
|||
target: 'electron-renderer', |
|||
|
|||
entry: [ |
|||
'core-js', |
|||
'regenerator-runtime/runtime', |
|||
require.resolve('../../src/index.tsx'), |
|||
], |
|||
|
|||
output: { |
|||
publicPath: `http://localhost:${port}/dist/`, |
|||
filename: 'renderer.dev.js', |
|||
}, |
|||
|
|||
module: { |
|||
rules: [ |
|||
{ |
|||
test: /\.[jt]sx?$/, |
|||
exclude: /node_modules/, |
|||
use: [ |
|||
{ |
|||
loader: require.resolve('babel-loader'), |
|||
options: { |
|||
plugins: [require.resolve('react-refresh/babel')].filter(Boolean), |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
test: /\.global\.css$/, |
|||
use: [ |
|||
{ |
|||
loader: 'style-loader', |
|||
}, |
|||
{ |
|||
loader: 'css-loader', |
|||
options: { |
|||
sourceMap: true, |
|||
}, |
|||
}, |
|||
{ |
|||
loader: 'less-loader', |
|||
options: { |
|||
lessOptions: { |
|||
javascriptEnabled: true, |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
test: /^((?!\.global).)*\.css$/, |
|||
use: [ |
|||
{ |
|||
loader: 'style-loader', |
|||
}, |
|||
{ |
|||
loader: 'css-loader', |
|||
options: { |
|||
modules: { |
|||
localIdentName: '[name]__[local]__[hash:base64:5]', |
|||
}, |
|||
sourceMap: true, |
|||
importLoaders: 1, |
|||
}, |
|||
}, |
|||
{ |
|||
loader: 'less-loader', |
|||
options: { |
|||
lessOptions: { |
|||
javascriptEnabled: true, |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
// SASS support - compile all .global.scss files and pipe it to style.css
|
|||
{ |
|||
test: /\.global\.(scss|sass)$/, |
|||
use: [ |
|||
{ |
|||
loader: 'style-loader', |
|||
}, |
|||
{ |
|||
loader: 'css-loader', |
|||
options: { |
|||
sourceMap: true, |
|||
}, |
|||
}, |
|||
{ |
|||
loader: 'sass-loader', |
|||
}, |
|||
{ |
|||
loader: 'less-loader', |
|||
options: { |
|||
lessOptions: { |
|||
javascriptEnabled: true, |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
// SASS support - compile all other .scss files and pipe it to style.css
|
|||
{ |
|||
test: /^((?!\.global).)*\.(scss|sass)$/, |
|||
use: [ |
|||
{ |
|||
loader: 'style-loader', |
|||
}, |
|||
{ |
|||
loader: '@teamsupercell/typings-for-css-modules-loader', |
|||
}, |
|||
{ |
|||
loader: 'css-loader', |
|||
options: { |
|||
modules: { |
|||
localIdentName: '[name]__[local]__[hash:base64:5]', |
|||
}, |
|||
sourceMap: true, |
|||
importLoaders: 1, |
|||
}, |
|||
}, |
|||
{ |
|||
loader: 'sass-loader', |
|||
}, |
|||
{ |
|||
loader: 'less-loader', |
|||
options: { |
|||
lessOptions: { |
|||
javascriptEnabled: true, |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
test: /\.less$/i, |
|||
use: [ |
|||
{ |
|||
loader: 'style-loader', |
|||
}, |
|||
{ |
|||
loader: 'css-loader', |
|||
}, |
|||
{ |
|||
loader: 'less-loader', |
|||
options: { |
|||
lessOptions: { |
|||
javascriptEnabled: true, |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
// WOFF Font
|
|||
{ |
|||
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'application/font-woff', |
|||
}, |
|||
}, |
|||
}, |
|||
// WOFF2 Font
|
|||
{ |
|||
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'application/font-woff', |
|||
}, |
|||
}, |
|||
}, |
|||
// OTF Font
|
|||
{ |
|||
test: /\.otf(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'font/otf', |
|||
}, |
|||
}, |
|||
}, |
|||
// TTF Font
|
|||
{ |
|||
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'application/octet-stream', |
|||
}, |
|||
}, |
|||
}, |
|||
// EOT Font
|
|||
{ |
|||
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: 'file-loader', |
|||
}, |
|||
// SVG Font
|
|||
{ |
|||
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'image/svg+xml', |
|||
}, |
|||
}, |
|||
}, |
|||
// Common Image Formats
|
|||
{ |
|||
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, |
|||
use: 'url-loader', |
|||
}, |
|||
], |
|||
}, |
|||
plugins: [ |
|||
requiredByDLLConfig |
|||
? null |
|||
: new webpack.DllReferencePlugin({ |
|||
context: path.join(__dirname, '../dll'), |
|||
manifest: require(manifest), |
|||
sourceType: 'var', |
|||
}), |
|||
|
|||
new webpack.NoEmitOnErrorsPlugin(), |
|||
|
|||
/** |
|||
* Create global constants which can be configured at compile time. |
|||
* |
|||
* Useful for allowing different behaviour between development builds and |
|||
* release builds |
|||
* |
|||
* NODE_ENV should be production so that modules do not perform certain |
|||
* development checks |
|||
* |
|||
* By default, use 'development' as NODE_ENV. This can be overriden with |
|||
* 'staging', for example, by changing the ENV variables in the npm scripts |
|||
*/ |
|||
new webpack.EnvironmentPlugin({ |
|||
NODE_ENV: 'development', |
|||
}), |
|||
|
|||
new webpack.LoaderOptionsPlugin({ |
|||
debug: true, |
|||
}), |
|||
|
|||
new ReactRefreshWebpackPlugin(), |
|||
], |
|||
|
|||
node: { |
|||
__dirname: false, |
|||
__filename: false, |
|||
}, |
|||
|
|||
devServer: { |
|||
port, |
|||
publicPath, |
|||
compress: true, |
|||
noInfo: false, |
|||
stats: 'errors-only', |
|||
inline: true, |
|||
lazy: false, |
|||
hot: true, |
|||
headers: { 'Access-Control-Allow-Origin': '*' }, |
|||
contentBase: path.join(__dirname, 'dist'), |
|||
watchOptions: { |
|||
aggregateTimeout: 300, |
|||
ignored: /node_modules/, |
|||
poll: 100, |
|||
}, |
|||
historyApiFallback: { |
|||
verbose: true, |
|||
disableDotRule: false, |
|||
}, |
|||
before() { |
|||
console.log('Starting Main Process...'); |
|||
spawn('npm', ['run', 'start:main'], { |
|||
shell: true, |
|||
env: process.env, |
|||
stdio: 'inherit', |
|||
}) |
|||
.on('close', (code) => process.exit(code)) |
|||
.on('error', (spawnError) => console.error(spawnError)); |
|||
}, |
|||
}, |
|||
}); |
@ -0,0 +1,72 @@ |
|||
/** |
|||
* Builds the DLL for development electron renderer process |
|||
*/ |
|||
|
|||
import webpack from 'webpack'; |
|||
import path from 'path'; |
|||
import { merge } from 'webpack-merge'; |
|||
import baseConfig from './webpack.config.base'; |
|||
import { dependencies } from '../../package.json'; |
|||
import CheckNodeEnv from '../scripts/CheckNodeEnv'; |
|||
|
|||
CheckNodeEnv('development'); |
|||
|
|||
const dist = path.join(__dirname, '../dll'); |
|||
|
|||
export default merge(baseConfig, { |
|||
context: path.join(__dirname, '../..'), |
|||
|
|||
devtool: 'eval', |
|||
|
|||
mode: 'development', |
|||
|
|||
target: 'electron-renderer', |
|||
|
|||
externals: ['fsevents', 'crypto-browserify'], |
|||
|
|||
/** |
|||
* Use `module` from `webpack.config.renderer.dev.js` |
|||
*/ |
|||
module: require('./webpack.config.renderer.dev.babel').default.module, |
|||
|
|||
entry: { |
|||
renderer: Object.keys(dependencies || {}), |
|||
}, |
|||
|
|||
output: { |
|||
library: 'renderer', |
|||
path: dist, |
|||
filename: '[name].dev.dll.js', |
|||
libraryTarget: 'var', |
|||
}, |
|||
|
|||
plugins: [ |
|||
new webpack.DllPlugin({ |
|||
path: path.join(dist, '[name].json'), |
|||
name: '[name]', |
|||
}), |
|||
|
|||
/** |
|||
* Create global constants which can be configured at compile time. |
|||
* |
|||
* Useful for allowing different behaviour between development builds and |
|||
* release builds |
|||
* |
|||
* NODE_ENV should be production so that modules do not perform certain |
|||
* development checks |
|||
*/ |
|||
new webpack.EnvironmentPlugin({ |
|||
NODE_ENV: 'development', |
|||
}), |
|||
|
|||
new webpack.LoaderOptionsPlugin({ |
|||
debug: true, |
|||
options: { |
|||
context: path.join(__dirname, '../../src'), |
|||
output: { |
|||
path: path.join(__dirname, '../dll'), |
|||
}, |
|||
}, |
|||
}), |
|||
], |
|||
}); |
@ -0,0 +1,165 @@ |
|||
/** |
|||
* Build config for electron renderer process |
|||
*/ |
|||
|
|||
import path from 'path'; |
|||
import webpack from 'webpack'; |
|||
import MiniCssExtractPlugin from 'mini-css-extract-plugin'; |
|||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; |
|||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; |
|||
import { merge } from 'webpack-merge'; |
|||
import TerserPlugin from 'terser-webpack-plugin'; |
|||
import baseConfig from './webpack.config.base'; |
|||
import CheckNodeEnv from '../scripts/CheckNodeEnv'; |
|||
import DeleteSourceMaps from '../scripts/DeleteSourceMaps'; |
|||
|
|||
CheckNodeEnv('production'); |
|||
DeleteSourceMaps(); |
|||
|
|||
const devtoolsConfig = |
|||
process.env.DEBUG_PROD === 'true' |
|||
? { |
|||
devtool: 'source-map', |
|||
} |
|||
: {}; |
|||
|
|||
export default merge(baseConfig, { |
|||
...devtoolsConfig, |
|||
|
|||
mode: 'production', |
|||
|
|||
target: 'electron-renderer', |
|||
|
|||
entry: [ |
|||
'core-js', |
|||
'regenerator-runtime/runtime', |
|||
path.join(__dirname, '../../src/index.tsx'), |
|||
], |
|||
|
|||
output: { |
|||
path: path.join(__dirname, '../../src/dist'), |
|||
publicPath: './dist/', |
|||
filename: 'renderer.prod.js', |
|||
}, |
|||
|
|||
module: { |
|||
rules: [ |
|||
{ |
|||
test: /.s?l?c?e?ss$/, |
|||
use: [ |
|||
{ |
|||
loader: MiniCssExtractPlugin.loader, |
|||
options: { |
|||
// `./dist` can't be inerhited for publicPath for styles. Otherwise generated paths will be ./dist/dist
|
|||
publicPath: './', |
|||
}, |
|||
}, |
|||
'css-loader', |
|||
'less-loader', |
|||
'sass-loader', |
|||
], |
|||
}, |
|||
// WOFF Font
|
|||
{ |
|||
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'application/font-woff', |
|||
}, |
|||
}, |
|||
}, |
|||
// WOFF2 Font
|
|||
{ |
|||
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'application/font-woff', |
|||
}, |
|||
}, |
|||
}, |
|||
// OTF Font
|
|||
{ |
|||
test: /\.otf(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'font/otf', |
|||
}, |
|||
}, |
|||
}, |
|||
// TTF Font
|
|||
{ |
|||
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'application/octet-stream', |
|||
}, |
|||
}, |
|||
}, |
|||
// EOT Font
|
|||
{ |
|||
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: 'file-loader', |
|||
}, |
|||
// SVG Font
|
|||
{ |
|||
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, |
|||
use: { |
|||
loader: 'url-loader', |
|||
options: { |
|||
limit: 10000, |
|||
mimetype: 'image/svg+xml', |
|||
}, |
|||
}, |
|||
}, |
|||
// Common Image Formats
|
|||
{ |
|||
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/, |
|||
use: 'url-loader', |
|||
}, |
|||
], |
|||
}, |
|||
|
|||
optimization: { |
|||
minimize: true, |
|||
minimizer: [ |
|||
new TerserPlugin({ |
|||
parallel: true, |
|||
}), |
|||
new CssMinimizerPlugin(), |
|||
], |
|||
}, |
|||
|
|||
plugins: [ |
|||
/** |
|||
* Create global constants which can be configured at compile time. |
|||
* |
|||
* Useful for allowing different behaviour between development builds and |
|||
* release builds |
|||
* |
|||
* NODE_ENV should be production so that modules do not perform certain |
|||
* development checks |
|||
*/ |
|||
new webpack.EnvironmentPlugin({ |
|||
NODE_ENV: 'production', |
|||
DEBUG_PROD: false, |
|||
}), |
|||
|
|||
new MiniCssExtractPlugin({ |
|||
filename: 'style.css', |
|||
}), |
|||
|
|||
new BundleAnalyzerPlugin({ |
|||
analyzerMode: |
|||
process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', |
|||
openAnalyzer: process.env.OPEN_ANALYZER === 'true', |
|||
}), |
|||
], |
|||
}); |
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1 @@ |
|||
export default 'test-file-stub'; |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"rules": { |
|||
"no-console": "off", |
|||
"global-require": "off", |
|||
"import/no-dynamic-require": "off", |
|||
"import/no-extraneous-dependencies": "off" |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
const path = require('path'); |
|||
|
|||
require('@babel/register')({ |
|||
extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'], |
|||
cwd: path.join(__dirname, '../..'), |
|||
}); |
@ -0,0 +1,25 @@ |
|||
// Check if the renderer and main bundles are built
|
|||
import path from 'path'; |
|||
import chalk from 'chalk'; |
|||
import fs from 'fs'; |
|||
|
|||
const mainPath = path.join(__dirname, '../../src/main.prod.js'); |
|||
const rendererPath = path.join( |
|||
__dirname, '../../src/dist/renderer.prod.js' |
|||
); |
|||
|
|||
if (!fs.existsSync(mainPath)) { |
|||
throw new Error( |
|||
chalk.whiteBright.bgRed.bold( |
|||
'The main process is not built yet. Build it by running "yarn build:main"' |
|||
) |
|||
); |
|||
} |
|||
|
|||
if (!fs.existsSync(rendererPath)) { |
|||
throw new Error( |
|||
chalk.whiteBright.bgRed.bold( |
|||
'The renderer process is not built yet. Build it by running "yarn build:renderer"' |
|||
) |
|||
); |
|||
} |
@ -0,0 +1,52 @@ |
|||
import fs from 'fs'; |
|||
import chalk from 'chalk'; |
|||
import { execSync } from 'child_process'; |
|||
import { dependencies } from '../../package.json'; |
|||
|
|||
if (dependencies) { |
|||
const dependenciesKeys = Object.keys(dependencies); |
|||
const nativeDeps = fs |
|||
.readdirSync('node_modules') |
|||
.filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); |
|||
if (nativeDeps.length === 0) { |
|||
process.exit(0); |
|||
} |
|||
try { |
|||
// Find the reason for why the dependency is installed. If it is installed
|
|||
// because of a devDependency then that is okay. Warn when it is installed
|
|||
// because of a dependency
|
|||
const { dependencies: dependenciesObject } = JSON.parse( |
|||
execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString() |
|||
); |
|||
const rootDependencies = Object.keys(dependenciesObject); |
|||
const filteredRootDependencies = rootDependencies.filter((rootDependency) => |
|||
dependenciesKeys.includes(rootDependency) |
|||
); |
|||
if (filteredRootDependencies.length > 0) { |
|||
const plural = filteredRootDependencies.length > 1; |
|||
console.log(` |
|||
${chalk.whiteBright.bgYellow.bold( |
|||
'Webpack does not work with native dependencies.' |
|||
)} |
|||
${chalk.bold(filteredRootDependencies.join(', '))} ${ |
|||
plural ? 'are native dependencies' : 'is a native dependency' |
|||
} and should be installed inside of the "./src" folder. |
|||
First, uninstall the packages from "./package.json": |
|||
${chalk.whiteBright.bgGreen.bold('yarn remove your-package')} |
|||
${chalk.bold( |
|||
'Then, instead of installing the package to the root "./package.json":' |
|||
)} |
|||
${chalk.whiteBright.bgRed.bold('yarn add your-package')} |
|||
${chalk.bold('Install the package to "./src/package.json"')} |
|||
${chalk.whiteBright.bgGreen.bold('cd ./src && yarn add your-package')} |
|||
Read more about native dependencies at: |
|||
${chalk.bold( |
|||
'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure' |
|||
)} |
|||
`);
|
|||
process.exit(1); |
|||
} |
|||
} catch (e) { |
|||
console.log('Native dependencies could not be checked'); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
import chalk from 'chalk'; |
|||
|
|||
export default function CheckNodeEnv(expectedEnv) { |
|||
if (!expectedEnv) { |
|||
throw new Error('"expectedEnv" not set'); |
|||
} |
|||
|
|||
if (process.env.NODE_ENV !== expectedEnv) { |
|||
console.log( |
|||
chalk.whiteBright.bgRed.bold( |
|||
`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` |
|||
) |
|||
); |
|||
process.exit(2); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
import chalk from 'chalk'; |
|||
import detectPort from 'detect-port'; |
|||
|
|||
const port = process.env.PORT || '4343'; |
|||
|
|||
detectPort(port, (err, availablePort) => { |
|||
if (port !== String(availablePort)) { |
|||
throw new Error( |
|||
chalk.whiteBright.bgRed.bold( |
|||
`Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 yarn start` |
|||
) |
|||
); |
|||
} else { |
|||
process.exit(0); |
|||
} |
|||
}); |
@ -0,0 +1,7 @@ |
|||
import path from 'path'; |
|||
import rimraf from 'rimraf'; |
|||
|
|||
export default function deleteSourceMaps() { |
|||
rimraf.sync(path.join(__dirname, '../../src/dist/*.js.map')); |
|||
rimraf.sync(path.join(__dirname, '../../src/*.js.map')); |
|||
} |
@ -0,0 +1,22 @@ |
|||
import path from 'path'; |
|||
import { execSync } from 'child_process'; |
|||
import fs from 'fs'; |
|||
import { dependencies } from '../../src/package.json'; |
|||
|
|||
const nodeModulesPath = path.join(__dirname, '../../src/node_modules'); |
|||
|
|||
if ( |
|||
Object.keys(dependencies || {}).length > 0 && |
|||
fs.existsSync(nodeModulesPath) |
|||
) { |
|||
const electronRebuildCmd = |
|||
'../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; |
|||
const cmd = |
|||
process.platform === 'win32' |
|||
? electronRebuildCmd.replace(/\//g, '\\') |
|||
: electronRebuildCmd; |
|||
execSync(cmd, { |
|||
cwd: path.join(__dirname, '../../src'), |
|||
stdio: 'inherit', |
|||
}); |
|||
} |
@ -0,0 +1,28 @@ |
|||
const { notarize } = require('electron-notarize'); |
|||
const { build } = require('../../package.json'); |
|||
|
|||
exports.default = async function notarizeMacos(context) { |
|||
const { electronPlatformName, appOutDir } = context; |
|||
if (electronPlatformName !== 'darwin') { |
|||
return; |
|||
} |
|||
|
|||
if (!process.env.CI) { |
|||
console.warn('Skipping notarizing step. Packaging is not running in CI'); |
|||
return; |
|||
} |
|||
|
|||
if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) { |
|||
console.warn('Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set'); |
|||
return; |
|||
} |
|||
|
|||
const appName = context.packager.appInfo.productFilename; |
|||
|
|||
await notarize({ |
|||
appBundleId: build.appId, |
|||
appPath: `${appOutDir}/${appName}.app`, |
|||
appleId: process.env.APPLE_ID, |
|||
appleIdPassword: process.env.APPLE_ID_PASS, |
|||
}); |
|||
}; |
@ -0,0 +1,56 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
.eslintcache |
|||
|
|||
# Dependency directory |
|||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git |
|||
node_modules |
|||
|
|||
# OSX |
|||
.DS_Store |
|||
|
|||
# App packaged |
|||
release |
|||
src/*.main.prod.js |
|||
src/main.prod.js |
|||
src/main.prod.js.map |
|||
src/renderer.prod.js |
|||
src/renderer.prod.js.map |
|||
src/style.css |
|||
src/style.css.map |
|||
dist |
|||
dll |
|||
main.js |
|||
main.js.map |
|||
|
|||
.idea |
|||
npm-debug.log.* |
|||
__snapshots__ |
|||
|
|||
# Package.json |
|||
package.json |
|||
.travis.yml |
|||
*.css.d.ts |
|||
*.sass.d.ts |
|||
*.scss.d.ts |
@ -0,0 +1,32 @@ |
|||
module.exports = { |
|||
extends: 'erb', |
|||
rules: { |
|||
// A temporary hack related to IDE not resolving correct package.json
|
|||
'import/no-extraneous-dependencies': 'off', |
|||
'import/prefer-default-export': 'off', |
|||
'@typescript-eslint/no-explicit-any': 'off', |
|||
'no-underscore-dangle': 'off', |
|||
'no-console': 'off', |
|||
'jsx-quotes': ['error', 'prefer-double'], |
|||
'react/jsx-props-no-spreading': 'off', |
|||
}, |
|||
parserOptions: { |
|||
ecmaVersion: 2020, |
|||
sourceType: 'module', |
|||
project: './tsconfig.json', |
|||
tsconfigRootDir: __dirname, |
|||
createDefaultProgram: true, |
|||
}, |
|||
settings: { |
|||
'import/resolver': { |
|||
// See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
|
|||
node: {}, |
|||
webpack: { |
|||
config: require.resolve('./.erb/configs/webpack.config.eslint.js'), |
|||
}, |
|||
}, |
|||
'import/parsers': { |
|||
'@typescript-eslint/parser': ['.ts', '.tsx'], |
|||
}, |
|||
}, |
|||
}; |
@ -0,0 +1,12 @@ |
|||
* text eol=lf |
|||
*.exe binary |
|||
*.png binary |
|||
*.jpg binary |
|||
*.jpeg binary |
|||
*.ico binary |
|||
*.icns binary |
|||
*.eot binary |
|||
*.otf binary |
|||
*.ttf binary |
|||
*.woff binary |
|||
*.woff2 binary |
@ -0,0 +1,5 @@ |
|||
# These are supported funding model platforms |
|||
|
|||
github: [electron-react-boilerplate, amilajack] |
|||
patreon: amilajack |
|||
open_collective: electron-react-boilerplate-594 |
@ -0,0 +1,67 @@ |
|||
--- |
|||
name: Bug report |
|||
about: You're having technical issues. 🐞 |
|||
labels: 'bug' |
|||
--- |
|||
|
|||
<!-- Please use the following issue template or your issue will be closed --> |
|||
|
|||
## Prerequisites |
|||
|
|||
<!-- If the following boxes are not ALL checked, your issue is likely to be closed --> |
|||
|
|||
- [ ] Using yarn |
|||
- [ ] Using an up-to-date [`master` branch](https://github.com/electron-react-boilerplate/electron-react-boilerplate/tree/master) |
|||
- [ ] Using latest version of devtools. [Check the docs for how to update](https://electron-react-boilerplate.js.org/docs/dev-tools/) |
|||
- [ ] Tried solutions mentioned in [#400](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/400) |
|||
- [ ] For issue in production release, add devtools output of `DEBUG_PROD=true yarn build && yarn start` |
|||
|
|||
## Expected Behavior |
|||
|
|||
<!--- What should have happened? --> |
|||
|
|||
## Current Behavior |
|||
|
|||
<!--- What went wrong? --> |
|||
|
|||
## Steps to Reproduce |
|||
|
|||
<!-- Add relevant code and/or a live example --> |
|||
<!-- Add stack traces --> |
|||
|
|||
1. |
|||
|
|||
2. |
|||
|
|||
3. |
|||
|
|||
4. |
|||
|
|||
## Possible Solution (Not obligatory) |
|||
|
|||
<!--- Suggest a reason for the bug or how to fix it. --> |
|||
|
|||
## Context |
|||
|
|||
<!--- How has this issue affected you? What are you trying to accomplish? --> |
|||
<!--- Did you make any changes to the boilerplate after cloning it? --> |
|||
<!--- Providing context helps us come up with a solution that is most useful in the real world --> |
|||
|
|||
## Your Environment |
|||
|
|||
<!--- Include as many relevant details about the environment you experienced the bug in --> |
|||
|
|||
- Node version : |
|||
- electron-react-boilerplate version or branch : |
|||
- Operating System and version : |
|||
- Link to your project : |
|||
|
|||
<!--- |
|||
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️ |
|||
|
|||
Donations will ensure the following: |
|||
|
|||
🔨 Long term maintenance of the project |
|||
🛣 Progress on the roadmap |
|||
🐛 Quick responses to bug reports and help requests |
|||
--> |
@ -0,0 +1,19 @@ |
|||
--- |
|||
name: Question |
|||
about: Ask a question.❓ |
|||
labels: 'question' |
|||
--- |
|||
|
|||
## Summary |
|||
|
|||
<!-- What do you need help with? --> |
|||
|
|||
<!--- |
|||
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️ |
|||
|
|||
Donations will ensure the following: |
|||
|
|||
🔨 Long term maintenance of the project |
|||
🛣 Progress on the roadmap |
|||
🐛 Quick responses to bug reports and help requests |
|||
--> |
@ -0,0 +1,15 @@ |
|||
--- |
|||
name: Feature request |
|||
about: You want something added to the boilerplate. 🎉 |
|||
labels: 'enhancement' |
|||
--- |
|||
|
|||
<!--- |
|||
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️ |
|||
|
|||
Donations will ensure the following: |
|||
|
|||
🔨 Long term maintenance of the project |
|||
🛣 Progress on the roadmap |
|||
🐛 Quick responses to bug reports and help requests |
|||
--> |
@ -0,0 +1,6 @@ |
|||
requiredHeaders: |
|||
- Prerequisites |
|||
- Expected Behavior |
|||
- Current Behavior |
|||
- Possible Solution |
|||
- Your Environment |
@ -0,0 +1,17 @@ |
|||
# Number of days of inactivity before an issue becomes stale |
|||
daysUntilStale: 40 |
|||
# Number of days of inactivity before a stale issue is closed |
|||
daysUntilClose: 7 |
|||
# Issues with these labels will never be considered stale |
|||
exemptLabels: |
|||
- pr |
|||
- discussion |
|||
- e2e |
|||
- enhancement |
|||
# Comment to post when marking an issue as stale. Set to `false` to disable |
|||
markComment: > |
|||
This issue has been automatically marked as stale because it has not had |
|||
recent activity. It will be closed if no further activity occurs. Thank you |
|||
for your contributions. |
|||
# Comment to post when closing a stale issue. Set to `false` to disable |
|||
closeComment: false |
@ -0,0 +1,55 @@ |
|||
name: Publish |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
- master |
|||
|
|||
jobs: |
|||
publish: |
|||
runs-on: ${{ matrix.os }} |
|||
|
|||
strategy: |
|||
matrix: |
|||
os: [ macos-latest ] |
|||
|
|||
steps: |
|||
- name: Checkout git repo |
|||
uses: actions/checkout@v1 |
|||
|
|||
- name: Install Node, NPM and Yarn |
|||
uses: actions/setup-node@v1 |
|||
with: |
|||
node-version: 15 |
|||
|
|||
- name: Get yarn cache directory path |
|||
id: yarn-cache-dir-path |
|||
run: echo "::set-output name=dir::$(yarn cache dir)" |
|||
|
|||
- uses: actions/cache@v1 |
|||
id: yarn-cache |
|||
with: |
|||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |
|||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |
|||
restore-keys: | |
|||
${{ runner.os }}-yarn- |
|||
|
|||
- name: Install dependencies |
|||
run: | |
|||
yarn install --prefer-offline |
|||
|
|||
- name: Install dependencies |
|||
run: | |
|||
yarn install |
|||
|
|||
- name: Publish releases |
|||
env: |
|||
# These values are used for auto updates signing |
|||
APPLE_ID: ${{ secrets.APPLE_ID }} |
|||
APPLE_ID_PASS: ${{ secrets.APPLE_ID_PASS }} |
|||
CSC_LINK: ${{ secrets.CSC_LINK }} |
|||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} |
|||
# This is used for uploading release assets to github |
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
|||
run: | |
|||
yarn postinstall && yarn build && yarn electron-builder --publish always --win --mac --linux |
@ -0,0 +1,33 @@ |
|||
name: Test |
|||
|
|||
on: [push, pull_request] |
|||
|
|||
jobs: |
|||
release: |
|||
runs-on: ${{ matrix.os }} |
|||
|
|||
strategy: |
|||
matrix: |
|||
os: [macos-latest, windows-latest, ubuntu-latest] |
|||
|
|||
steps: |
|||
- name: Check out Git repository |
|||
uses: actions/checkout@v1 |
|||
|
|||
- name: Install Node.js, NPM and Yarn |
|||
uses: actions/setup-node@v1 |
|||
with: |
|||
node-version: 15 |
|||
|
|||
- name: yarn install |
|||
run: | |
|||
yarn install --frozen-lockfile --network-timeout 300000 |
|||
|
|||
- name: yarn test |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
|||
run: | |
|||
yarn package |
|||
yarn lint |
|||
yarn tsc |
|||
yarn test |
@ -0,0 +1,50 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
.eslintcache |
|||
|
|||
# Dependency directory |
|||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git |
|||
node_modules |
|||
|
|||
# OSX |
|||
.DS_Store |
|||
|
|||
# App packaged |
|||
release |
|||
src/main.prod.js |
|||
src/main.prod.js.map |
|||
src/renderer.prod.js |
|||
src/renderer.prod.js.map |
|||
src/style.css |
|||
src/style.css.map |
|||
dist |
|||
dll |
|||
main.js |
|||
main.js.map |
|||
|
|||
.idea |
|||
npm-debug.log.* |
|||
*.css.d.ts |
|||
*.sass.d.ts |
|||
*.scss.d.ts |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"trailingComma": "es5", |
|||
"tabWidth": 2, |
|||
"semi": true, |
|||
"singleQuote": false |
|||
} |
@ -0,0 +1,7 @@ |
|||
{ |
|||
"recommendations": [ |
|||
"dbaeumer.vscode-eslint", |
|||
"EditorConfig.EditorConfig", |
|||
"msjsdiag.debugger-for-chrome" |
|||
] |
|||
} |
@ -0,0 +1,28 @@ |
|||
{ |
|||
"version": "0.2.0", |
|||
"configurations": [ |
|||
{ |
|||
"name": "Electron: Main", |
|||
"type": "node", |
|||
"request": "launch", |
|||
"protocol": "inspector", |
|||
"runtimeExecutable": "yarn", |
|||
"runtimeArgs": ["start:main --inspect=5858 --remote-debugging-port=9223"], |
|||
"preLaunchTask": "Start Webpack Dev" |
|||
}, |
|||
{ |
|||
"name": "Electron: Renderer", |
|||
"type": "chrome", |
|||
"request": "attach", |
|||
"port": 9223, |
|||
"webRoot": "${workspaceFolder}", |
|||
"timeout": 15000 |
|||
} |
|||
], |
|||
"compounds": [ |
|||
{ |
|||
"name": "Electron: All", |
|||
"configurations": ["Electron: Main", "Electron: Renderer"] |
|||
} |
|||
] |
|||
} |
@ -0,0 +1,28 @@ |
|||
{ |
|||
"files.associations": { |
|||
".babelrc": "jsonc", |
|||
".eslintrc": "jsonc", |
|||
".prettierrc": "jsonc", |
|||
".eslintignore": "ignore" |
|||
}, |
|||
|
|||
"javascript.validate.enable": false, |
|||
"javascript.format.enable": false, |
|||
"typescript.format.enable": false, |
|||
|
|||
"search.exclude": { |
|||
".git": true, |
|||
".eslintcache": true, |
|||
"src/dist": true, |
|||
"src/main.prod.js": true, |
|||
"src/main.prod.js.map": true, |
|||
"bower_components": true, |
|||
"dll": true, |
|||
"release": true, |
|||
"node_modules": true, |
|||
"npm-debug.log.*": true, |
|||
"test/**/__snapshots__": true, |
|||
"yarn.lock": true, |
|||
"*.{css,sass,scss}.d.ts": true |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
{ |
|||
"version": "2.0.0", |
|||
"tasks": [ |
|||
{ |
|||
"type": "npm", |
|||
"label": "Start Webpack Dev", |
|||
"script": "start:renderer", |
|||
"options": { |
|||
"cwd": "${workspaceFolder}" |
|||
}, |
|||
"isBackground": true, |
|||
"problemMatcher": { |
|||
"owner": "custom", |
|||
"pattern": { |
|||
"regexp": "____________" |
|||
}, |
|||
"background": { |
|||
"activeOnStart": true, |
|||
"beginsPattern": "Compiling\\.\\.\\.$", |
|||
"endsPattern": "(Compiled successfully|Failed to compile)\\.$" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
@ -0,0 +1,533 @@ |
|||
# 2.1.0 |
|||
|
|||
- Migrate to `css-minifier-webpack-plugin` |
|||
|
|||
# 2.0.1 |
|||
|
|||
## Fixes |
|||
|
|||
- Fix broken css linking in production build |
|||
|
|||
# 2.0.0 |
|||
|
|||
## Breaking Changes |
|||
|
|||
- drop redux |
|||
- remove counter example app |
|||
- simplify directory structure |
|||
- move `dll` dir to `.erb` dir |
|||
- fix icon/font import paths |
|||
- migrate to `react-refresh` from `react-hot-loader` |
|||
- migrate to webpack@5 |
|||
- migrate to electron@11 |
|||
- remove e2e tests and testcafe integration |
|||
- rename `app` dir to more conventional `src` dir |
|||
- rename `resources` dir to `assets` |
|||
- simplify npm scripts |
|||
- drop stylelint |
|||
- simplify styling of boilerplate app |
|||
- remove `START_HOT` env variable |
|||
- notarize support |
|||
- landing page boilerplate |
|||
- docs updates |
|||
- restore removed debugging support |
|||
|
|||
# 1.4.0 |
|||
|
|||
- Migrate to `eslint-config-erb@2` |
|||
- Rename `dev` npm script to `start` |
|||
- GitHub Actions: only publish GitHub releases when on master branch |
|||
|
|||
# 1.3.1 |
|||
|
|||
- Fix sass building bug ([#2540](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2540)) |
|||
- Fix CI bug related to E2E tests and network timeouts |
|||
- Move automated dependency PRs to `next` ([#2554](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2554)) |
|||
- Bump dependencies to patch semver |
|||
|
|||
# 1.3.0 |
|||
|
|||
- Fixes E2E tests ([#2516](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2516)) |
|||
- Fixes preload entrypoint ([#2503](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2503)) |
|||
- Downgrade to `electron@8` |
|||
- Bump dependencies to latest semver |
|||
|
|||
# 1.2.0 |
|||
|
|||
- Migrate to redux toolkit |
|||
- Lazy load routes with react suspense |
|||
- Drop support for azure-pipelines and use only github actions |
|||
- Bump all deps to latest semver |
|||
- Remove `test-e2e` script from tests (blocked on release of https://github.com/DevExpress/testcafe-browser-provider-electron/pull/65) |
|||
- Swap `typed-css-modules-webpack-plugin` for `typings-for-css-modules-loader` |
|||
- Use latest version of `eslint-config-erb` |
|||
- Remove unnecessary file extensions from ts exclude |
|||
- Add experimental support for vscode debugging |
|||
- Revert https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2365 as default for users, provide as opt in option |
|||
|
|||
# 1.1.0 |
|||
|
|||
- Fix #2402 |
|||
- Simplify configs (https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2406) |
|||
|
|||
# 1.0.0 |
|||
|
|||
- Migrate to TypeScript from Flow ([#2363](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2363)) |
|||
- Use browserslist for `@babel/preset-env` targets ([#2368](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2368)) |
|||
- Use preload script, disable `nodeIntegration` in renderer process for [improved security](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) ([#2365](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2365)) |
|||
- Add support for azure pipelines ([#2369](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2369)) |
|||
- Disable sourcemaps in production |
|||
|
|||
# 0.18.1 (2019.12.12) |
|||
|
|||
- Fix HMR env bug ([#2343](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2343)) |
|||
- Bump all deps to latest semver |
|||
- Bump to `electron@7` |
|||
|
|||
# 0.18.0 (2019.11.19) |
|||
|
|||
- Bump electron to `electron@6` (`electron@7` introduces breaking changes to testcafe end to end tests) |
|||
- Revert back to [two `package.json` structure](https://www.electron.build/tutorials/two-package-structure) |
|||
- Bump all deps to latest semver |
|||
|
|||
# 0.17.1 (2018.11.20) |
|||
|
|||
- Fix `yarn test-e2e` and testcafe for single package.json structure |
|||
- Fixes incorrect path in `yarn start` script |
|||
- Bumped deps |
|||
- Bump g++ in travis |
|||
- Change clone arguments to clone only master |
|||
- Change babel config to target current electron version |
|||
|
|||
For full change list, see https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2021 |
|||
|
|||
# 0.17.0 (2018.10.30) |
|||
|
|||
- upgraded to `babel@7` (thanks to @vikr01 🎉🎉🎉) |
|||
- migrated from [two `package.json` structure](https://www.electron.build/tutorials/two-package-structure) (thanks to @HyperSprite!) |
|||
- initial auto update support (experimental) |
|||
- migrate from greenkeeper to [renovate](https://renovatebot.com) |
|||
- added issue template |
|||
- use `babel-preset-env` to target current electron version |
|||
- add [opencollective](https://opencollective.com/electron-react-boilerplate-594) banner message display in postinstall script (help support ERB 🙏) |
|||
- fix failing ci issues |
|||
|
|||
# 0.16.0 (2018.10.3) |
|||
|
|||
- removed unused dependencies |
|||
- migrate from `react-redux-router` to `connect-react-router` |
|||
- move webpack configs to `./webpack` dir |
|||
- use `g++` on travis when testing linux |
|||
- migrate from `spectron` to `testcafe` for e2e tests |
|||
- add linting support for config styles |
|||
- changed stylelint config |
|||
- temporarily disabled flow in appveyor to make ci pass |
|||
- added necessary infra to publish releases from ci |
|||
|
|||
# 0.15.0 (2018.8.25) |
|||
|
|||
- Performance: cache webpack uglify results |
|||
- Feature: add start minimized feature |
|||
- Feature: lint and fix styles with prettier and stylelint |
|||
- Feature: add greenkeeper support |
|||
|
|||
# 0.14.0 (2018.5.24) |
|||
|
|||
- Improved CI timings |
|||
- Migrated README commands to yarn from npm |
|||
- Improved vscode config |
|||
- Updated all dependencies to latest semver |
|||
- Fix `electron-rebuild` script bug |
|||
- Migrated to `mini-css-extract-plugin` from `extract-text-plugin` |
|||
- Added `optimize-css-assets-webpack-plugin` |
|||
- Run `prettier` on json, css, scss, and more filetypes |
|||
|
|||
# 0.13.3 (2018.5.24) |
|||
|
|||
- Add git precommit hook, when git commit will use `prettier` to format git add code |
|||
- Add format code function in `lint-fix` npm script which can use `prettier` to format project js code |
|||
|
|||
# 0.13.2 (2018.1.31) |
|||
|
|||
- Hot Module Reload (HMR) fixes |
|||
- Bumped all dependencies to latest semver |
|||
- Prevent error propagation of `CheckNativeDeps` script |
|||
|
|||
# 0.13.1 (2018.1.13) |
|||
|
|||
- Hot Module Reload (HMR) fixes |
|||
- Bumped all dependencies to latest semver |
|||
- Fixed electron-rebuild script |
|||
- Fixed tests scripts to run on all platforms |
|||
- Skip redux logs in console in test ENV |
|||
|
|||
# 0.13.0 (2018.1.6) |
|||
|
|||
#### Additions |
|||
|
|||
- Add native dependencies check on postinstall |
|||
- Updated all dependencies to latest semver |
|||
|
|||
# 0.12.0 (2017.7.8) |
|||
|
|||
#### Misc |
|||
|
|||
- Removed `babel-polyfill` |
|||
- Renamed and alphabetized npm scripts |
|||
|
|||
#### Breaking |
|||
|
|||
- Changed node dev `__dirname` and `__filename` to node built in fn's (https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/1035) |
|||
- Renamed `src/bundle.js` to `src/renderer.prod.js` for consistency |
|||
- Renamed `dll/vendor.js` to `dll/renderer.dev.dll.js` for consistency |
|||
|
|||
#### Additions |
|||
|
|||
- Enable node_modules cache on CI |
|||
|
|||
# 0.11.2 (2017.5.1) |
|||
|
|||
Yay! Another patch release. This release mostly includes refactorings and router bug fixes. Huge thanks to @anthonyraymond! |
|||
|
|||
⚠️ Windows electron builds are failing because of [this issue](https://github.com/electron/electron/issues/9321). This is not an issue with the boilerplate ⚠️ |
|||
|
|||
#### Breaking |
|||
|
|||
- **Renamed `./src/main.development.js` => `./src/main.{dev,prod}.js`:** [#963](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/963) |
|||
|
|||
#### Fixes |
|||
|
|||
- **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) |
|||
|
|||
#### Additions |
|||
|
|||
- **Added support for stylefmt:** [#960](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/960) |
|||
|
|||
# 0.11.1 (2017.4.23) |
|||
|
|||
You can now debug the production build with devtools like so: |
|||
|
|||
``` |
|||
DEBUG_PROD=true npm run package |
|||
``` |
|||
|
|||
🎉🎉🎉 |
|||
|
|||
#### Additions |
|||
|
|||
- **Added support for debugging production build:** [#fab245a](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/941/commits/fab245a077d02a09630f74270806c0c534a4ff95) |
|||
|
|||
#### Bug Fixes |
|||
|
|||
- **Fixed bug related to importing native dependencies:** [#933](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/933) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Updated all deps to latest semver** |
|||
|
|||
# 0.11.0 (2017.4.19) |
|||
|
|||
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. |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **Dropped support for node < 6** |
|||
- **Refactored webpack config files** |
|||
- **Migrate to two-package.json project structure** |
|||
- **Updated all devDeps to latest semver** |
|||
- **Migrated to Jest:** [#768](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/768) |
|||
- **Migrated to `react-router@4`** |
|||
- **Migrated to `electron-builder@4`** |
|||
- **Migrated to `webpack@2`** |
|||
- **Migrated to `react-hot-loader@3`** |
|||
- **Changed default live reload server PORT to `1212` from `3000`** |
|||
|
|||
#### Additions |
|||
|
|||
- **Added support for Yarn:** [#451](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/451) |
|||
- **Added support for Flow:** [#425](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/425) |
|||
- **Added support for stylelint:** [#911](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/911) |
|||
- **Added support for electron-builder:** [#876](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/876) |
|||
- **Added optional support for SASS:** [#880](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/880) |
|||
- **Added support for eslint-plugin-flowtype:** [#911](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/911) |
|||
- **Added support for appveyor:** [#280](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/280) |
|||
- **Added support for webpack dlls:** [#860](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/860) |
|||
- **Route based code splitting:** [#884](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/884) |
|||
- **Added support for Webpack Bundle Analyzer:** [#922](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/922) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Parallelize renderer and main build processes when running `npm run build`** |
|||
- **Dynamically generate electron app menu** |
|||
- **Improved vscode integration:** [#856](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/856) |
|||
|
|||
#### Bug Fixes |
|||
|
|||
- **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) |
|||
|
|||
# 0.10.0 (2016.4.18) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Use Babel in main process with Webpack build:** [#201](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/201) |
|||
- **Change targets to built-in support by webpack:** [#197](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/197) |
|||
- **use es2015 syntax for webpack configs:** [#195](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/195) |
|||
- **Open application when webcontent is loaded:** [#192](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/192) |
|||
- **Upgraded dependencies** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix `npm list electron-prebuilt` in package.js:** [#188](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/188) |
|||
|
|||
# 0.9.0 (2016.3.23) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Added [redux-logger](https://github.com/fcomb/redux-logger)** |
|||
- **Upgraded [react-router-redux](https://github.com/reactjs/react-router-redux) to v4** |
|||
- **Upgraded dependencies** |
|||
- **Added `npm run dev` command:** [#162](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/162) |
|||
- **electron to v0.37.2** |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **css module as default:** [#154](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/154). |
|||
- **set default NODE_ENV to production:** [#140](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/140) |
|||
|
|||
# 0.8.0 (2016.2.17) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix lint errors** |
|||
- **Fix Webpack publicPath for production builds**: [#119](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/119). |
|||
- **package script now chooses correct OS icon extension** |
|||
|
|||
#### Improvements |
|||
|
|||
- **babel 6** |
|||
- **Upgrade Dependencies** |
|||
- **Enable CSS source maps** |
|||
- **Add json-loader**: [#128](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/128). |
|||
- **react-router 2.0 and react-router-redux 3.0** |
|||
|
|||
# 0.7.1 (2015.12.27) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fixed npm script on windows 10:** [#103](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/103). |
|||
- **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). |
|||
|
|||
#### Improvements |
|||
|
|||
- **electron 0.36** |
|||
|
|||
# 0.7.0 (2015.12.16) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fixed process.env.NODE_ENV variable in webpack:** [#74](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/74). |
|||
- **add missing object-assign**: [#76](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/76). |
|||
- **packaging in npm@3:** [#77](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/77). |
|||
- **compatibility in windows:** [#100](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/100). |
|||
- **disable chrome debugger in production env:** [#102](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/102). |
|||
|
|||
#### Improvements |
|||
|
|||
- **redux** |
|||
- **css-modules** |
|||
- **upgrade to react-router 1.x** |
|||
- **unit tests** |
|||
- **e2e tests** |
|||
- **travis-ci** |
|||
- **upgrade to electron 0.35.x** |
|||
- **use es2015** |
|||
- **check dev engine for node and npm** |
|||
|
|||
# 0.6.5 (2015.11.7) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Bump style-loader to 0.13** |
|||
- **Bump css-loader to 0.22** |
|||
|
|||
# 0.6.4 (2015.10.27) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Bump electron-debug to 0.3** |
|||
|
|||
# 0.6.3 (2015.10.26) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Initialize ExtractTextPlugin once:** [#64](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/64). |
|||
|
|||
# 0.6.2 (2015.10.18) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Babel plugins production env not be set properly:** [#57](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/57). |
|||
|
|||
# 0.6.1 (2015.10.17) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Bump electron to v0.34.0** |
|||
|
|||
# 0.6.0 (2015.10.16) |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **From react-hot-loader to react-transform** |
|||
|
|||
# 0.5.2 (2015.10.15) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Run tests with babel-register:** [#29](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/29). |
|||
|
|||
# 0.5.1 (2015.10.12) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix #51:** use `path.join(__dirname` instead of `./`. |
|||
|
|||
# 0.5.0 (2015.10.11) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Simplify webpack config** see [#50](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/50). |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **webpack configs** |
|||
- **port changed:** changed default port from 2992 to 3000. |
|||
- **npm scripts:** remove `start-dev` and `dev-server`. rename `hot-dev-server` to `hot-server`. |
|||
|
|||
# 0.4.3 (2015.9.22) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix #45 zeromq crash:** bump version of `electron-prebuilt`. |
|||
|
|||
# 0.4.2 (2015.9.15) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **run start-hot breaks chrome refresh(CTRL+R) (#42)**: bump `electron-debug` to `0.2.1` |
|||
|
|||
# 0.4.1 (2015.9.11) |
|||
|
|||
#### Improvements |
|||
|
|||
- **use electron-prebuilt version for packaging (#33)** |
|||
|
|||
# 0.4.0 (2015.9.5) |
|||
|
|||
#### Improvements |
|||
|
|||
- **update dependencies** |
|||
|
|||
# 0.3.0 (2015.8.31) |
|||
|
|||
#### Improvements |
|||
|
|||
- **eslint-config-airbnb** |
|||
|
|||
# 0.2.10 (2015.8.27) |
|||
|
|||
#### Features |
|||
|
|||
- **custom placeholder icon** |
|||
|
|||
#### Improvements |
|||
|
|||
- **electron-renderer as target:** via [webpack-target-electron-renderer](https://github.com/chentsulin/webpack-target-electron-renderer) |
|||
|
|||
# 0.2.9 (2015.8.18) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix hot-reload** |
|||
|
|||
# 0.2.8 (2015.8.13) |
|||
|
|||
#### Improvements |
|||
|
|||
- **bump electron-debug** |
|||
- **babelrc** |
|||
- **organize webpack scripts** |
|||
|
|||
# 0.2.7 (2015.7.9) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **defaultProps:** fix typos. |
|||
|
|||
# 0.2.6 (2015.7.3) |
|||
|
|||
#### Features |
|||
|
|||
- **menu** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **package.js:** include webpack build. |
|||
|
|||
# 0.2.5 (2015.7.1) |
|||
|
|||
#### Features |
|||
|
|||
- **NPM Script:** support multi-platform |
|||
- **package:** `--all` option |
|||
|
|||
# 0.2.4 (2015.6.9) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Eslint:** typo, [#17](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/17) and improve `.eslintrc` |
|||
|
|||
# 0.2.3 (2015.6.3) |
|||
|
|||
#### Features |
|||
|
|||
- **Package Version:** use latest release electron version as default |
|||
- **Ignore Large peerDependencies** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Npm Script:** typo, [#6](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/6) |
|||
- **Missing css:** [#7](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/7) |
|||
|
|||
# 0.2.2 (2015.6.2) |
|||
|
|||
#### Features |
|||
|
|||
- **electron-debug** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Webpack:** add `.json` and `.node` to extensions for imitating node require. |
|||
- **Webpack:** set `node_modules` to externals for native module support. |
|||
|
|||
# 0.2.1 (2015.5.30) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Webpack:** #1, change build target to `atom`. |
|||
|
|||
# 0.2.0 (2015.5.30) |
|||
|
|||
#### Features |
|||
|
|||
- **Ignore:** `test`, `tools`, `release` folder and devDependencies in `package.json`. |
|||
- **Support asar** |
|||
- **Support icon** |
|||
|
|||
# 0.1.0 (2015.5.27) |
|||
|
|||
#### Features |
|||
|
|||
- **Webpack:** babel, react-hot, ... |
|||
- **Flux:** actions, api, components, containers, stores.. |
|||
- **Package:** darwin (osx), linux and win32 (windows) platform. |
@ -0,0 +1,76 @@ |
|||
# Contributor Covenant Code of Conduct |
|||
|
|||
## Our Pledge |
|||
|
|||
In the interest of fostering an open and welcoming environment, we as |
|||
contributors and maintainers pledge to making participation in our project and |
|||
our community a harassment-free experience for everyone, regardless of age, body |
|||
size, disability, ethnicity, sex characteristics, gender identity and expression, |
|||
level of experience, education, socio-economic status, nationality, personal |
|||
appearance, race, religion, or sexual identity and orientation. |
|||
|
|||
## Our Standards |
|||
|
|||
Examples of behavior that contributes to creating a positive environment |
|||
include: |
|||
|
|||
* Using welcoming and inclusive language |
|||
* Being respectful of differing viewpoints and experiences |
|||
* Gracefully accepting constructive criticism |
|||
* Focusing on what is best for the community |
|||
* Showing empathy towards other community members |
|||
|
|||
Examples of unacceptable behavior by participants include: |
|||
|
|||
* The use of sexualized language or imagery and unwelcome sexual attention or |
|||
advances |
|||
* Trolling, insulting/derogatory comments, and personal or political attacks |
|||
* Public or private harassment |
|||
* Publishing others' private information, such as a physical or electronic |
|||
address, without explicit permission |
|||
* Other conduct which could reasonably be considered inappropriate in a |
|||
professional setting |
|||
|
|||
## Our Responsibilities |
|||
|
|||
Project maintainers are responsible for clarifying the standards of acceptable |
|||
behavior and are expected to take appropriate and fair corrective action in |
|||
response to any instances of unacceptable behavior. |
|||
|
|||
Project maintainers have the right and responsibility to remove, edit, or |
|||
reject comments, commits, code, wiki edits, issues, and other contributions |
|||
that are not aligned to this Code of Conduct, or to ban temporarily or |
|||
permanently any contributor for other behaviors that they deem inappropriate, |
|||
threatening, offensive, or harmful. |
|||
|
|||
## Scope |
|||
|
|||
This Code of Conduct applies both within project spaces and in public spaces |
|||
when an individual is representing the project or its community. Examples of |
|||
representing a project or community include using an official project e-mail |
|||
address, posting via an official social media account, or acting as an appointed |
|||
representative at an online or offline event. Representation of a project may be |
|||
further defined and clarified by project maintainers. |
|||
|
|||
## Enforcement |
|||
|
|||
Instances of abusive, harassing, or otherwise unacceptable behavior may be |
|||
reported by contacting the project team at electronreactboilerplate@gmail.com. All |
|||
complaints will be reviewed and investigated and will result in a response that |
|||
is deemed necessary and appropriate to the circumstances. The project team is |
|||
obligated to maintain confidentiality with regard to the reporter of an incident. |
|||
Further details of specific enforcement policies may be posted separately. |
|||
|
|||
Project maintainers who do not follow or enforce the Code of Conduct in good |
|||
faith may face temporary or permanent repercussions as determined by other |
|||
members of the project's leadership. |
|||
|
|||
## Attribution |
|||
|
|||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, |
|||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html |
|||
|
|||
[homepage]: https://www.contributor-covenant.org |
|||
|
|||
For answers to common questions about this code of conduct, see |
|||
https://www.contributor-covenant.org/faq |
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2015-present Electron React Boilerplate |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
@ -0,0 +1,157 @@ |
|||
<img src=".erb/img/erb-banner.png" width="100%" /> |
|||
|
|||
<br> |
|||
|
|||
<p> |
|||
Electron React Boilerplate uses <a href="https://electron.atom.io/">Electron</a>, <a href="https://facebook.github.io/react/">React</a>, <a href="https://github.com/reactjs/react-router">React Router</a>, <a href="https://webpack.js.org/">Webpack</a> and <a href="https://www.npmjs.com/package/react-refresh">React Fast Refresh</a>. |
|||
</p> |
|||
|
|||
<br> |
|||
|
|||
<div align="center"> |
|||
|
|||
[![Build Status][github-actions-status]][github-actions-url] |
|||
[![Dependency Status][david-image]][david-url] |
|||
[![DevDependency Status][david-dev-image]][david-dev-url] |
|||
[![Github Tag][github-tag-image]][github-tag-url] |
|||
|
|||
[![OpenCollective](https://opencollective.com/electron-react-boilerplate/backers/badge.svg)](#backers) |
|||
[![OpenCollective](https://opencollective.com/electron-react-boilerplate/sponsors/badge.svg)](#sponsors) |
|||
[![Good first issues open][good-first-issue-image]][good-first-issue-url] |
|||
[![StackOverflow][stackoverflow-img]][stackoverflow-url] |
|||
|
|||
</div> |
|||
|
|||
## Install |
|||
|
|||
- **If you have installation or compilation issues with this project, please see [our debugging guide](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/400)** |
|||
|
|||
First, clone the repo via git and install dependencies: |
|||
|
|||
```bash |
|||
git clone --depth 1 --single-branch https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name |
|||
cd your-project-name |
|||
yarn |
|||
``` |
|||
|
|||
## Starting Development |
|||
|
|||
Start the app in the `dev` environment: |
|||
|
|||
```bash |
|||
yarn start |
|||
``` |
|||
|
|||
## Packaging for Production |
|||
|
|||
To package apps for the local platform: |
|||
|
|||
```bash |
|||
yarn package |
|||
``` |
|||
|
|||
## Docs |
|||
|
|||
See our [docs and guides here](https://electron-react-boilerplate.js.org/docs/installation) |
|||
|
|||
## Donations |
|||
|
|||
**Donations will ensure the following:** |
|||
|
|||
- 🔨 Long term maintenance of the project |
|||
- 🛣 Progress on the [roadmap](https://electron-react-boilerplate.js.org/docs/roadmap) |
|||
- 🐛 Quick responses to bug reports and help requests |
|||
|
|||
## Backers |
|||
|
|||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/electron-react-boilerplate#backer)] |
|||
|
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/0/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/1/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/2/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/3/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/4/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/5/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/6/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/7/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/8/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/9/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/10/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/11/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/12/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/13/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/14/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/15/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/16/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/17/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/18/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/19/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/20/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/21/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/22/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/23/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/24/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/25/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/26/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/27/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/28/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/29/avatar.svg"></a> |
|||
|
|||
## Sponsors |
|||
|
|||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/electron-react-boilerplate-594#sponsor)] |
|||
|
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/0/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/1/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/2/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/3/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/4/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/5/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/6/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/7/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/8/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/9/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/10/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/11/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/12/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/13/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/14/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/15/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/16/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/17/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/18/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/19/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/20/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/21/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/22/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/23/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/24/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/25/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/26/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/27/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/28/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/29/avatar.svg"></a> |
|||
|
|||
## Maintainers |
|||
|
|||
- [Amila Welihinda](https://github.com/amilajack) |
|||
- [John Tran](https://github.com/jooohhn) |
|||
- [C. T. Lin](https://github.com/chentsulin) |
|||
- [Jhen-Jie Hong](https://github.com/jhen0409) |
|||
|
|||
## License |
|||
|
|||
MIT © [Electron React Boilerplate](https://github.com/electron-react-boilerplate) |
|||
|
|||
[github-actions-status]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/workflows/Test/badge.svg |
|||
[github-actions-url]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/actions |
|||
[github-tag-image]: https://img.shields.io/github/tag/electron-react-boilerplate/electron-react-boilerplate.svg?label=version |
|||
[github-tag-url]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/releases/latest |
|||
[stackoverflow-img]: https://img.shields.io/badge/stackoverflow-electron_react_boilerplate-blue.svg |
|||
[stackoverflow-url]: https://stackoverflow.com/questions/tagged/electron-react-boilerplate |
|||
[david-image]: https://img.shields.io/david/electron-react-boilerplate/electron-react-boilerplate.svg |
|||
[david-url]: https://david-dm.org/electron-react-boilerplate/electron-react-boilerplate |
|||
[david-dev-image]: https://img.shields.io/david/dev/electron-react-boilerplate/electron-react-boilerplate.svg?label=devDependencies |
|||
[david-dev-url]: https://david-dm.org/electron-react-boilerplate/electron-react-boilerplate?type=dev |
|||
[good-first-issue-image]: https://img.shields.io/github/issues/electron-react-boilerplate/electron-react-boilerplate/good%20first%20issue.svg?label=good%20first%20issues |
|||
[good-first-issue-url]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues?q=is%3Aopen+is%3Aissue+label%3A"good+first+issue" |
@ -0,0 +1,14 @@ |
|||
declare module '*.svg' { |
|||
const content: any; |
|||
export default content; |
|||
} |
|||
|
|||
declare module '*.png' { |
|||
const content: any; |
|||
export default content; |
|||
} |
|||
|
|||
declare module '*.jpg' { |
|||
const content: any; |
|||
export default content; |
|||
} |
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key> |
|||
<true/> |
|||
<key>com.apple.security.cs.allow-jit</key> |
|||
<true/> |
|||
</dict> |
|||
</plist> |
After Width: | Height: | Size: 361 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 954 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,62 @@ |
|||
/* eslint global-require: off, import/no-extraneous-dependencies: off */ |
|||
|
|||
const developmentEnvironments = ['development', 'test']; |
|||
|
|||
const developmentPlugins = [require('@babel/plugin-transform-runtime')]; |
|||
|
|||
const productionPlugins = [ |
|||
require('babel-plugin-dev-expression'), |
|||
|
|||
// babel-preset-react-optimize
|
|||
require('@babel/plugin-transform-react-constant-elements'), |
|||
require('@babel/plugin-transform-react-inline-elements'), |
|||
require('babel-plugin-transform-react-remove-prop-types'), |
|||
]; |
|||
|
|||
module.exports = (api) => { |
|||
// See docs about api at https://babeljs.io/docs/en/config-files#apicache
|
|||
|
|||
const development = api.env(developmentEnvironments); |
|||
|
|||
return { |
|||
presets: [ |
|||
// @babel/preset-env will automatically target our browserslist targets
|
|||
require('@babel/preset-env'), |
|||
require('@babel/preset-typescript'), |
|||
[require('@babel/preset-react'), { development }], |
|||
], |
|||
plugins: [ |
|||
// Stage 0
|
|||
require('@babel/plugin-proposal-function-bind'), |
|||
|
|||
// Stage 1
|
|||
require('@babel/plugin-proposal-export-default-from'), |
|||
require('@babel/plugin-proposal-logical-assignment-operators'), |
|||
[require('@babel/plugin-proposal-optional-chaining'), { loose: false }], |
|||
[ |
|||
require('@babel/plugin-proposal-pipeline-operator'), |
|||
{ proposal: 'minimal' }, |
|||
], |
|||
[ |
|||
require('@babel/plugin-proposal-nullish-coalescing-operator'), |
|||
{ loose: false }, |
|||
], |
|||
require('@babel/plugin-proposal-do-expressions'), |
|||
|
|||
// Stage 2
|
|||
[require('@babel/plugin-proposal-decorators'), { legacy: true }], |
|||
require('@babel/plugin-proposal-function-sent'), |
|||
require('@babel/plugin-proposal-export-namespace-from'), |
|||
require('@babel/plugin-proposal-numeric-separator'), |
|||
require('@babel/plugin-proposal-throw-expressions'), |
|||
|
|||
// Stage 3
|
|||
require('@babel/plugin-syntax-dynamic-import'), |
|||
require('@babel/plugin-syntax-import-meta'), |
|||
[require('@babel/plugin-proposal-class-properties'), { loose: true }], |
|||
require('@babel/plugin-proposal-json-strings'), |
|||
|
|||
...(development ? developmentPlugins : productionPlugins), |
|||
], |
|||
}; |
|||
}; |
@ -0,0 +1,320 @@ |
|||
{ |
|||
"name": "sonicd-electron", |
|||
"productName": "sonicd", |
|||
"description": "An electron-based subsonic-api compatible desktop media player", |
|||
"scripts": { |
|||
"build": "concurrently \"yarn build:main\" \"yarn build:renderer\"", |
|||
"build:main": "cross-env NODE_ENV=production webpack --config ./.erb/configs/webpack.config.main.prod.babel.js", |
|||
"build:renderer": "cross-env NODE_ENV=production webpack --config ./.erb/configs/webpack.config.renderer.prod.babel.js", |
|||
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir src", |
|||
"lint": "cross-env NODE_ENV=development eslint . --cache --ext .js,.jsx,.ts,.tsx", |
|||
"package": "rm -rf src/dist && yarn build && electron-builder build --publish never", |
|||
"postinstall": "node -r @babel/register .erb/scripts/CheckNativeDep.js && electron-builder install-app-deps && yarn cross-env NODE_ENV=development webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.babel.js && opencollective-postinstall && yarn-deduplicate yarn.lock", |
|||
"start": "node -r @babel/register ./.erb/scripts/CheckPortInUse.js && yarn start:renderer", |
|||
"start:main": "cross-env NODE_ENV=development electron -r ./.erb/scripts/BabelRegister ./src/main.dev.ts", |
|||
"start:renderer": "cross-env NODE_ENV=development webpack serve --config ./.erb/configs/webpack.config.renderer.dev.babel.js", |
|||
"test": "jest" |
|||
}, |
|||
"lint-staged": { |
|||
"*.{js,jsx,ts,tsx}": [ |
|||
"cross-env NODE_ENV=development eslint --cache" |
|||
], |
|||
"{*.json,.{babelrc,eslintrc,prettierrc}}": [ |
|||
"prettier --ignore-path .eslintignore --parser json --write" |
|||
], |
|||
"*.{css,scss}": [ |
|||
"prettier --ignore-path .eslintignore --single-quote --write" |
|||
], |
|||
"*.{html,md,yml}": [ |
|||
"prettier --ignore-path .eslintignore --single-quote --write" |
|||
] |
|||
}, |
|||
"build": { |
|||
"productName": "sonicd", |
|||
"appId": "org.erb.sonicd", |
|||
"files": [ |
|||
"dist/", |
|||
"node_modules/", |
|||
"index.html", |
|||
"main.prod.js", |
|||
"main.prod.js.map", |
|||
"package.json" |
|||
], |
|||
"afterSign": ".erb/scripts/Notarize.js", |
|||
"mac": { |
|||
"target": [ |
|||
"dmg" |
|||
], |
|||
"type": "distribution", |
|||
"hardenedRuntime": true, |
|||
"entitlements": "assets/entitlements.mac.plist", |
|||
"entitlementsInherit": "assets/entitlements.mac.plist", |
|||
"gatekeeperAssess": false |
|||
}, |
|||
"dmg": { |
|||
"contents": [ |
|||
{ |
|||
"x": 130, |
|||
"y": 220 |
|||
}, |
|||
{ |
|||
"x": 410, |
|||
"y": 220, |
|||
"type": "link", |
|||
"path": "/Applications" |
|||
} |
|||
] |
|||
}, |
|||
"win": { |
|||
"target": [ |
|||
"nsis" |
|||
] |
|||
}, |
|||
"linux": { |
|||
"target": [ |
|||
"AppImage" |
|||
], |
|||
"category": "Development" |
|||
}, |
|||
"directories": { |
|||
"app": "src", |
|||
"buildResources": "assets", |
|||
"output": "release" |
|||
}, |
|||
"extraResources": [ |
|||
"./assets/**" |
|||
], |
|||
"publish": { |
|||
"provider": "github", |
|||
"owner": "electron-react-boilerplate", |
|||
"repo": "electron-react-boilerplate" |
|||
} |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/electron-react-boilerplate/electron-react-boilerplate.git" |
|||
}, |
|||
"author": { |
|||
"name": "Electron React Boilerplate Maintainers", |
|||
"email": "electronreactboilerplate@gmail.com", |
|||
"url": "https://electron-react-boilerplate.js.org" |
|||
}, |
|||
"contributors": [ |
|||
{ |
|||
"name": "Amila Welihinda", |
|||
"email": "amilajack@gmail.com", |
|||
"url": "https://github.com/amilajack" |
|||
}, |
|||
{ |
|||
"name": "John Tran", |
|||
"email": "jptran318@gmail.com", |
|||
"url": "https://github.com/jooohhn" |
|||
} |
|||
], |
|||
"license": "MIT", |
|||
"bugs": { |
|||
"url": "https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues" |
|||
}, |
|||
"keywords": [ |
|||
"electron", |
|||
"boilerplate", |
|||
"react", |
|||
"typescript", |
|||
"ts", |
|||
"sass", |
|||
"webpack", |
|||
"hot", |
|||
"reload" |
|||
], |
|||
"homepage": "https://github.com/electron-react-boilerplate/electron-react-boilerplate#readme", |
|||
"jest": { |
|||
"testURL": "http://localhost/", |
|||
"moduleNameMapper": { |
|||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js", |
|||
"\\.(css|less|sass|scss)$": "identity-obj-proxy" |
|||
}, |
|||
"moduleFileExtensions": [ |
|||
"js", |
|||
"jsx", |
|||
"ts", |
|||
"tsx", |
|||
"json" |
|||
], |
|||
"moduleDirectories": [ |
|||
"node_modules", |
|||
"src/node_modules" |
|||
], |
|||
"setupFiles": [ |
|||
"./.erb/scripts/CheckBuildsExist.js" |
|||
] |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/core": "^7.12.9", |
|||
"@babel/plugin-proposal-class-properties": "^7.12.1", |
|||
"@babel/plugin-proposal-decorators": "^7.12.1", |
|||
"@babel/plugin-proposal-do-expressions": "^7.12.1", |
|||
"@babel/plugin-proposal-export-default-from": "^7.12.1", |
|||
"@babel/plugin-proposal-export-namespace-from": "^7.12.1", |
|||
"@babel/plugin-proposal-function-bind": "^7.12.1", |
|||
"@babel/plugin-proposal-function-sent": "^7.12.1", |
|||
"@babel/plugin-proposal-json-strings": "^7.12.1", |
|||
"@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", |
|||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", |
|||
"@babel/plugin-proposal-optional-chaining": "^7.12.7", |
|||
"@babel/plugin-proposal-pipeline-operator": "^7.12.1", |
|||
"@babel/plugin-proposal-throw-expressions": "^7.12.1", |
|||
"@babel/plugin-syntax-dynamic-import": "^7.8.3", |
|||
"@babel/plugin-syntax-import-meta": "^7.10.4", |
|||
"@babel/plugin-transform-react-constant-elements": "^7.12.1", |
|||
"@babel/plugin-transform-react-inline-elements": "^7.12.1", |
|||
"@babel/plugin-transform-runtime": "^7.12.1", |
|||
"@babel/preset-env": "^7.12.7", |
|||
"@babel/preset-react": "^7.12.7", |
|||
"@babel/preset-typescript": "^7.12.7", |
|||
"@babel/register": "^7.12.1", |
|||
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", |
|||
"@teamsupercell/typings-for-css-modules-loader": "^2.4.0", |
|||
"@testing-library/jest-dom": "^5.11.6", |
|||
"@testing-library/react": "^11.2.2", |
|||
"@types/enzyme": "^3.10.5", |
|||
"@types/enzyme-adapter-react-16": "^1.0.6", |
|||
"@types/history": "4.7.6", |
|||
"@types/jest": "^26.0.15", |
|||
"@types/md5": "^2.3.1", |
|||
"@types/node": "14.14.10", |
|||
"@types/randomstring": "^1.1.7", |
|||
"@types/react": "^16.9.44", |
|||
"@types/react-dom": "^16.9.9", |
|||
"@types/react-redux": "^7.1.18", |
|||
"@types/react-router-dom": "^5.1.6", |
|||
"@types/react-test-renderer": "^16.9.3", |
|||
"@types/webpack-env": "^1.15.2", |
|||
"@typescript-eslint/eslint-plugin": "^4.8.1", |
|||
"@typescript-eslint/parser": "^4.8.1", |
|||
"babel-eslint": "^10.1.0", |
|||
"babel-jest": "^26.1.0", |
|||
"babel-loader": "^8.2.2", |
|||
"babel-plugin-dev-expression": "^0.2.2", |
|||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24", |
|||
"browserslist-config-erb": "^0.0.1", |
|||
"chalk": "^4.1.0", |
|||
"concurrently": "^5.3.0", |
|||
"core-js": "^3.6.5", |
|||
"cross-env": "^7.0.2", |
|||
"css-loader": "^5.0.1", |
|||
"css-minimizer-webpack-plugin": "^1.1.5", |
|||
"detect-port": "^1.3.0", |
|||
"electron": "^11.0.1", |
|||
"electron-builder": "^22.10.5", |
|||
"electron-devtools-installer": "^3.1.1", |
|||
"electron-notarize": "^1.0.0", |
|||
"electron-rebuild": "^2.3.2", |
|||
"enzyme": "^3.11.0", |
|||
"enzyme-adapter-react-16": "^1.15.3", |
|||
"enzyme-to-json": "^3.5.0", |
|||
"eslint": "^7.5.0", |
|||
"eslint-config-airbnb": "^18.2.0", |
|||
"eslint-config-airbnb-typescript": "^12.0.0", |
|||
"eslint-config-erb": "^2.0.0", |
|||
"eslint-config-prettier": "^6.11.0", |
|||
"eslint-import-resolver-webpack": "^0.13.0", |
|||
"eslint-plugin-compat": "^3.8.0", |
|||
"eslint-plugin-import": "^2.22.0", |
|||
"eslint-plugin-jest": "^24.1.3", |
|||
"eslint-plugin-jsx-a11y": "6.4.1", |
|||
"eslint-plugin-prettier": "^3.1.4", |
|||
"eslint-plugin-promise": "^4.2.1", |
|||
"eslint-plugin-react": "^7.20.6", |
|||
"eslint-plugin-react-hooks": "^4.0.8", |
|||
"extract-text-webpack-plugin": "^3.0.2", |
|||
"file-loader": "^6.0.0", |
|||
"husky": "^4.2.5", |
|||
"identity-obj-proxy": "^3.0.0", |
|||
"jest": "^26.1.0", |
|||
"less": "3.7.0", |
|||
"less-loader": "7.3.0", |
|||
"lint-staged": "^10.2.11", |
|||
"mini-css-extract-plugin": "^2.1.0", |
|||
"node-sass": "^5.0.0", |
|||
"opencollective-postinstall": "^2.0.3", |
|||
"prettier": "^2.0.5", |
|||
"react-refresh": "^0.10.0", |
|||
"react-test-renderer": "^17.0.1", |
|||
"rimraf": "^3.0.0", |
|||
"sass-loader": "^10.1.0", |
|||
"style-loader": "^2.0.0", |
|||
"terser-webpack-plugin": "^5.0.3", |
|||
"typescript": "^4.0.5", |
|||
"url-loader": "^4.1.0", |
|||
"webpack": "^5.5.1", |
|||
"webpack-bundle-analyzer": "^4.1.0", |
|||
"webpack-cli": "^4.2.0", |
|||
"webpack-dev-server": "^3.11.0", |
|||
"webpack-merge": "^5.4.0", |
|||
"yarn-deduplicate": "^3.1.0" |
|||
}, |
|||
"dependencies": { |
|||
"@reduxjs/toolkit": "^1.6.1", |
|||
"axios": "^0.21.1", |
|||
"classnames": "^2.3.1", |
|||
"electron-debug": "^3.1.0", |
|||
"electron-log": "^4.2.4", |
|||
"electron-updater": "^4.3.4", |
|||
"history": "^5.0.0", |
|||
"md5": "^2.3.0", |
|||
"md5-es": "^1.8.2", |
|||
"moment": "^2.29.1", |
|||
"nanoid": "^3.1.23", |
|||
"randomstring": "^1.2.1", |
|||
"react": "^17.0.1", |
|||
"react-dom": "^17.0.1", |
|||
"react-helmet-async": "^1.0.9", |
|||
"react-query": "^3.19.1", |
|||
"react-redux": "^7.2.4", |
|||
"react-router-dom": "^5.2.0", |
|||
"react-sound": "^1.2.0", |
|||
"regenerator-runtime": "^0.13.5", |
|||
"rsuite": "^4.10.2", |
|||
"source-map-support": "^0.5.19", |
|||
"styled-components": "^5.3.0", |
|||
"swr": "^0.5.6", |
|||
"xml-js": "^1.6.11" |
|||
}, |
|||
"devEngines": { |
|||
"node": ">=10.x", |
|||
"npm": ">=6.x", |
|||
"yarn": ">=1.21.3" |
|||
}, |
|||
"collective": { |
|||
"url": "https://opencollective.com/electron-react-boilerplate-594" |
|||
}, |
|||
"browserslist": [], |
|||
"prettier": { |
|||
"overrides": [ |
|||
{ |
|||
"files": [ |
|||
".prettierrc", |
|||
".babelrc", |
|||
".eslintrc" |
|||
], |
|||
"options": { |
|||
"parser": "json" |
|||
} |
|||
} |
|||
], |
|||
"singleQuote": true |
|||
}, |
|||
"renovate": { |
|||
"extends": [ |
|||
"bliss" |
|||
], |
|||
"baseBranches": [ |
|||
"next" |
|||
] |
|||
}, |
|||
"husky": { |
|||
"hooks": { |
|||
"pre-commit": "lint-staged" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
import React from 'react'; |
|||
import { HashRouter as Router, Switch, Route, Link } from 'react-router-dom'; |
|||
import { QueryClient, QueryClientProvider } from 'react-query'; |
|||
|
|||
import './styles/App.global.css'; |
|||
import Layout from './components/layout/Layout'; |
|||
import PlaylistList from './components/playlist/PlaylistList'; |
|||
import PlaylistView from './components/playlist/PlaylistView'; |
|||
import Settings from './components/settings/Settings'; |
|||
|
|||
const queryClient = new QueryClient(); |
|||
|
|||
const App = () => { |
|||
return ( |
|||
<QueryClientProvider client={queryClient}> |
|||
<Router> |
|||
<Layout> |
|||
<Switch> |
|||
<Route path="/settings"> |
|||
<Settings /> |
|||
</Route> |
|||
<Route path="/playlist/:id"> |
|||
<PlaylistView /> |
|||
</Route> |
|||
<Route path="/playlists"> |
|||
<PlaylistList /> |
|||
</Route> |
|||
<Route path="/">Main route</Route> |
|||
</Switch> |
|||
</Layout> |
|||
</Router> |
|||
</QueryClientProvider> |
|||
); |
|||
}; |
|||
|
|||
export default App; |
@ -0,0 +1,10 @@ |
|||
import React from 'react'; |
|||
import '@testing-library/jest-dom'; |
|||
import { render } from '@testing-library/react'; |
|||
import App from '../App'; |
|||
|
|||
describe('App', () => { |
|||
it('should render', () => { |
|||
expect(render(<App />)).toBeTruthy(); |
|||
}); |
|||
}); |
@ -0,0 +1,98 @@ |
|||
import axios from 'axios'; |
|||
import { formatSongDuration } from '../shared/utils'; |
|||
import getAuth from './auth'; |
|||
|
|||
const auth = getAuth(); |
|||
const API_BASE_URL = `${auth.server}/rest`; |
|||
|
|||
export const api = axios.create({ |
|||
baseURL: API_BASE_URL, |
|||
}); |
|||
|
|||
api.interceptors.request.use((config) => { |
|||
config.params = config.params || {}; |
|||
config.params.u = auth.username; |
|||
config.params.s = auth.salt; |
|||
config.params.t = auth.hash; |
|||
config.params.v = '1.15.0'; |
|||
config.params.c = 'sonicd'; |
|||
config.params.f = 'json'; |
|||
return config; |
|||
}); |
|||
|
|||
api.interceptors.response.use( |
|||
(res) => { |
|||
// Return the subsonic response directly
|
|||
res.data = res.data['subsonic-response']; |
|||
return res; |
|||
}, |
|||
(err) => { |
|||
return Promise.reject(err); |
|||
} |
|||
); |
|||
|
|||
const getCoverArtUrl = (item: any) => { |
|||
if (!item.coverArt) { |
|||
return undefined; |
|||
} |
|||
|
|||
return ( |
|||
`${API_BASE_URL}/getCoverArt` + |
|||
`?id=${item.coverArt}` + |
|||
`&u=${auth.username}` + |
|||
`&s=${auth.salt}` + |
|||
`&t=${auth.hash}` + |
|||
`&v=1.15.0` + |
|||
`&c=sonicd` + |
|||
`&size=200` |
|||
); |
|||
}; |
|||
|
|||
const getStreamUrl = (id: string) => { |
|||
return ( |
|||
`${API_BASE_URL}/stream` + |
|||
`?id=${id}` + |
|||
`&u=${auth.username}` + |
|||
`&s=${auth.salt}` + |
|||
`&t=${auth.hash}` + |
|||
`&v=1.15.0` + |
|||
`&c=sonicd` |
|||
); |
|||
}; |
|||
|
|||
export const getPlaylists = async () => { |
|||
const { data } = await api.get('/getPlaylists'); |
|||
|
|||
return (data.playlists?.playlist || []).map((playlist: any) => ({ |
|||
...playlist, |
|||
name: playlist.name, |
|||
image: playlist.songCount > 0 ? getCoverArtUrl(playlist) : undefined, |
|||
})); |
|||
}; |
|||
|
|||
export const getPlaylist = async (id: string) => { |
|||
const { data } = await api.get(`/getPlaylist?id=${id}`); |
|||
return { |
|||
...data.playlist, |
|||
entry: data.playlist.entry.map((entry: any, index: any) => ({ |
|||
...entry, |
|||
streamUrl: getStreamUrl(entry.id), |
|||
duration: formatSongDuration(entry.duration), |
|||
index: index + 1, |
|||
})), |
|||
image: |
|||
data.playlist.songCount > 0 ? getCoverArtUrl(data.playlist) : undefined, |
|||
}; |
|||
}; |
|||
|
|||
export const getPing = async () => { |
|||
const { data } = await api.get(`/ping`); |
|||
|
|||
return data; |
|||
}; |
|||
|
|||
export const getStream = async (id: string) => { |
|||
const { data } = await api.get(`/stream?id=${id}`); |
|||
console.log(data); |
|||
return data; |
|||
}; |
@ -0,0 +1,12 @@ |
|||
const getAuth = () => { |
|||
const serverConfig = { |
|||
username: localStorage.getItem('username') || '', |
|||
salt: localStorage.getItem('salt') || '', |
|||
hash: localStorage.getItem('hash') || '', |
|||
server: localStorage.getItem('server') || '', |
|||
}; |
|||
|
|||
return serverConfig; |
|||
}; |
|||
|
|||
export default getAuth; |
@ -0,0 +1,96 @@ |
|||
import axios from 'axios'; |
|||
import { randomString, md5 } from '../shared/utils'; |
|||
import { config } from '../shared/config'; |
|||
|
|||
class AuthService { |
|||
public server = ''; |
|||
|
|||
public username = ''; |
|||
|
|||
public salt = ''; |
|||
|
|||
public hash = ''; |
|||
|
|||
private authenticated = false; |
|||
|
|||
constructor() { |
|||
this.server = config.serverUrl || localStorage.getItem('server') || ''; |
|||
this.username = localStorage.getItem('username') || ''; |
|||
this.salt = localStorage.getItem('salt') || ''; |
|||
this.hash = localStorage.getItem('hash') || ''; |
|||
} |
|||
|
|||
private saveSession() { |
|||
if (!config.serverUrl) { |
|||
localStorage.setItem('server', this.server); |
|||
} |
|||
localStorage.setItem('username', this.username); |
|||
localStorage.setItem('salt', this.salt); |
|||
localStorage.setItem('hash', this.hash); |
|||
} |
|||
|
|||
async autoLogin(): Promise<boolean> { |
|||
if (!this.server || !this.username) { |
|||
return false; |
|||
} |
|||
return this.loginWithHash( |
|||
this.server, |
|||
this.username, |
|||
this.salt, |
|||
this.hash, |
|||
false |
|||
) |
|||
.then(() => true) |
|||
.catch(() => false); |
|||
} |
|||
|
|||
async loginWithPassword( |
|||
server: string, |
|||
username: string, |
|||
password: string, |
|||
remember: boolean |
|||
) { |
|||
const salt = randomString(); |
|||
const hash = md5(password + salt); |
|||
return this.loginWithHash(server, username, salt, hash, remember); |
|||
} |
|||
|
|||
private async loginWithHash( |
|||
server: string, |
|||
username: string, |
|||
salt: string, |
|||
hash: string, |
|||
remember: boolean |
|||
) { |
|||
const url = `${server}/rest/ping?u=${username}&s=${salt}&t=${hash}&v=1.15.0&c=app&f=json`; |
|||
return axios.get(url).then((response: any) => { |
|||
const subsonicResponse = response.data['subsonic-response']; |
|||
if (!subsonicResponse || subsonicResponse.status !== 'ok') { |
|||
const err = new Error(subsonicResponse.status); |
|||
// eslint-disable-next-line promise/no-return-wrap
|
|||
return Promise.reject(err); |
|||
} |
|||
this.authenticated = true; |
|||
this.server = server; |
|||
this.username = username; |
|||
this.salt = salt; |
|||
this.hash = hash; |
|||
// eslint-disable-next-line promise/always-return
|
|||
if (remember) { |
|||
this.saveSession(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// eslint-disable-next-line class-methods-use-this
|
|||
logout() { |
|||
localStorage.clear(); |
|||
sessionStorage.clear(); |
|||
} |
|||
|
|||
isAuthenticated() { |
|||
return this.authenticated; |
|||
} |
|||
} |
|||
|
|||
export default AuthService; |
@ -0,0 +1,66 @@ |
|||
import React, { useState } from 'react'; |
|||
import { useQuery } from 'react-query'; |
|||
import { Table, Checkbox } from 'rsuite'; |
|||
import { getPlaylists } from '../api/api'; |
|||
|
|||
const CheckCell = ({ rowData, onChange, checkedKeys, dataKey, ...props }) => ( |
|||
<Table.Cell {...props} style={{ padding: 0 }}> |
|||
<div style={{ lineHeight: '46px' }}> |
|||
<Checkbox |
|||
value={rowData[dataKey]} |
|||
inline |
|||
onChange={onChange} |
|||
checked={checkedKeys.some((item) => item === rowData[dataKey])} |
|||
/> |
|||
</div> |
|||
</Table.Cell> |
|||
); |
|||
|
|||
const Playlists = () => { |
|||
const { isLoading, isError, data: playlists, error }: any = useQuery( |
|||
'playlists', |
|||
getPlaylists |
|||
); |
|||
const [checkedKeys, setCheckedKeys] = useState([]); |
|||
|
|||
const handleRowClick = (e) => { |
|||
console.log(e); |
|||
}; |
|||
|
|||
// console.log(data);
|
|||
console.log(playlists); |
|||
|
|||
if (isLoading) { |
|||
return <span>Loading...</span>; |
|||
} |
|||
|
|||
if (isError) { |
|||
return <span>Error: {error.message}</span>; |
|||
} |
|||
|
|||
return ( |
|||
<Table |
|||
data={playlists} |
|||
height={420} |
|||
autoHeight |
|||
bordered |
|||
cellBordered |
|||
onRowClick={handleRowClick} |
|||
> |
|||
<Table.Column width={70} align="center" resizable> |
|||
<Table.HeaderCell>Id</Table.HeaderCell> |
|||
<Table.Cell dataKey="id" /> |
|||
</Table.Column> |
|||
<Table.Column width={150} align="left" resizable> |
|||
<Table.HeaderCell>Name</Table.HeaderCell> |
|||
<Table.Cell dataKey="name" /> |
|||
</Table.Column> |
|||
<Table.Column width={150} align="left" resizable> |
|||
<Table.HeaderCell>Description</Table.HeaderCell> |
|||
<Table.Cell dataKey="comment" /> |
|||
</Table.Column> |
|||
</Table> |
|||
); |
|||
}; |
|||
|
|||
export default Playlists; |
@ -0,0 +1,15 @@ |
|||
import React from 'react'; |
|||
import { Container, Header, Content, Divider } from 'rsuite'; |
|||
import '../../styles/GenericPage.global.css'; |
|||
|
|||
const GenericPage = ({ header, children }: any) => { |
|||
return ( |
|||
<Container id="page" className="container__page"> |
|||
<Header className="page__header">{header}</Header> |
|||
<Divider /> |
|||
<Content className="page__content">{children}</Content> |
|||
</Container> |
|||
); |
|||
}; |
|||
|
|||
export default GenericPage; |
@ -0,0 +1,77 @@ |
|||
import React, { useState } from 'react'; |
|||
import { useHistory } from 'react-router-dom'; |
|||
import { Container, Sidebar as Sb, Content, Footer } from 'rsuite'; |
|||
import classNames from 'classnames'; |
|||
|
|||
import Sidebar from './Sidebar'; |
|||
import '../../styles/Layout.global.css'; |
|||
|
|||
const Layout = ({ footer, children }: any) => { |
|||
const [expandSidebar, setExpandSidebar] = useState(true); |
|||
const [activeSidebarNav, setActiveSidebarNav] = useState('discover'); |
|||
const history = useHistory(); |
|||
|
|||
const handleToggle = () => { |
|||
setExpandSidebar(!expandSidebar); |
|||
}; |
|||
|
|||
const handleSidebarSelect = (e: string) => { |
|||
let route; |
|||
const navItem = String(e); |
|||
setActiveSidebarNav(navItem); |
|||
switch (navItem) { |
|||
case 'discover': |
|||
route = '/'; |
|||
break; |
|||
case 'nowplaying': |
|||
route = '/nowplaying'; |
|||
break; |
|||
case 'playlists': |
|||
route = '/playlists'; |
|||
break; |
|||
case 'settings': |
|||
route = '/settings'; |
|||
break; |
|||
default: |
|||
route = '/'; |
|||
break; |
|||
} |
|||
|
|||
history.push(route); |
|||
}; |
|||
|
|||
const containerSidebarClasses = classNames({ |
|||
container__sidebar: true, |
|||
container__sidebar_expanded: expandSidebar === true, |
|||
container__sidebar_shrunk: expandSidebar === false, |
|||
}); |
|||
|
|||
const containerRootClasses = classNames({ |
|||
container__root: true, |
|||
container__root_expanded: expandSidebar === true, |
|||
container__root_shrunk: expandSidebar === false, |
|||
}); |
|||
|
|||
return ( |
|||
<> |
|||
<Sidebar |
|||
expand={expandSidebar} |
|||
handleToggle={handleToggle} |
|||
active={activeSidebarNav} |
|||
handleSidebarSelect={handleSidebarSelect} |
|||
/> |
|||
<Container id="container__root" className={containerRootClasses}> |
|||
<Container id="main" className="container__main"> |
|||
<Content id="content" className="container__content"> |
|||
{children} |
|||
</Content> |
|||
</Container> |
|||
<Footer id="footer" className="container__footer"> |
|||
Footer{footer} |
|||
</Footer> |
|||
</Container> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default Layout; |
@ -0,0 +1,42 @@ |
|||
import React, { useState } from 'react'; |
|||
import { Nav, Dropdown, Navbar, Icon } from 'rsuite'; |
|||
|
|||
const NavToggle = ({ expand, onChange }: any) => { |
|||
const iconStyles = { |
|||
width: 56, |
|||
height: 56, |
|||
lineHeight: '56px', |
|||
textAlign: 'center', |
|||
}; |
|||
|
|||
return ( |
|||
<Navbar appearance="subtle" className="nav-toggle"> |
|||
<Navbar.Body> |
|||
<Nav> |
|||
<Dropdown |
|||
placement="topStart" |
|||
trigger="click" |
|||
renderTitle={(children) => { |
|||
return <Icon style={iconStyles} icon="cog" />; |
|||
}} |
|||
> |
|||
<Dropdown.Item>Help</Dropdown.Item> |
|||
<Dropdown.Item>Settings</Dropdown.Item> |
|||
<Dropdown.Item>Sign out</Dropdown.Item> |
|||
</Dropdown> |
|||
</Nav> |
|||
|
|||
<Nav pullRight> |
|||
<Nav.Item |
|||
onClick={onChange} |
|||
style={{ width: 56, textAlign: 'center' }} |
|||
> |
|||
<Icon icon={expand ? 'angle-left' : 'angle-right'} /> |
|||
</Nav.Item> |
|||
</Nav> |
|||
</Navbar.Body> |
|||
</Navbar> |
|||
); |
|||
}; |
|||
|
|||
export default NavToggle; |
@ -0,0 +1,159 @@ |
|||
import React from 'React'; |
|||
import classNames from 'classnames'; |
|||
import { |
|||
Sidebar as Sb, |
|||
Sidenav, |
|||
Dropdown, |
|||
Nav, |
|||
Navbar, |
|||
Icon, |
|||
IconButton, |
|||
} from 'rsuite'; |
|||
import '../../styles/Sidebar.global.css'; |
|||
|
|||
const Sidebar = ({ |
|||
expand, |
|||
handleToggle, |
|||
active, |
|||
handleSidebarSelect, |
|||
}: any) => { |
|||
const sidebarClasses = classNames({ |
|||
sidebar__main: true, |
|||
sidebar__main_expanded: expand === true, |
|||
}); |
|||
|
|||
const containerSidebarClasses = classNames({ |
|||
container__sidebar: true, |
|||
container__sidebar_expanded: expand === true, |
|||
container__sidebar_shrunk: expand === false, |
|||
}); |
|||
|
|||
const sidebarExpanderClasses = classNames({ |
|||
sidebar__expander_true: expand === true, |
|||
sidebar__expander_false: expand === false, |
|||
}); |
|||
|
|||
const iconStyles = { |
|||
width: 56, |
|||
height: 56, |
|||
lineHeight: '56px', |
|||
textAlign: 'center', |
|||
}; |
|||
|
|||
return ( |
|||
<Sb |
|||
id="sidebar" |
|||
className="container__sidebar" |
|||
width={expand ? 193 : 56} |
|||
collapsible |
|||
> |
|||
<Sidenav |
|||
className={sidebarClasses} |
|||
expanded={expand} |
|||
appearance="default" |
|||
> |
|||
<Sidenav.Header></Sidenav.Header> |
|||
<Sidenav.Body> |
|||
<Nav> |
|||
<Nav.Item |
|||
active={active === 'discover'} |
|||
eventKey="discover" |
|||
icon={<Icon icon="dashboard" />} |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Discover |
|||
</Nav.Item> |
|||
<Nav.Item |
|||
active={active === 'nowplaying'} |
|||
eventKey="nowplaying" |
|||
icon={<Icon icon="music" />} |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Now Playing |
|||
</Nav.Item> |
|||
<Nav.Item |
|||
active={active === 'playlists'} |
|||
eventKey="playlists" |
|||
icon={<Icon icon="bookmark" />} |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Playlists |
|||
</Nav.Item> |
|||
<Dropdown |
|||
placement="rightStart" |
|||
eventKey="library" |
|||
title="Library" |
|||
noCaret |
|||
icon={<Icon icon="book2" />} |
|||
> |
|||
<Dropdown.Item |
|||
active={active === 'library-1'} |
|||
eventKey="library-1" |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Album |
|||
</Dropdown.Item> |
|||
<Dropdown.Item |
|||
active={active === 'library-2'} |
|||
eventKey="library-2" |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Artists |
|||
</Dropdown.Item> |
|||
<Dropdown.Item |
|||
active={active === 'library-3'} |
|||
eventKey="library-3" |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Genres |
|||
</Dropdown.Item> |
|||
<Dropdown.Item |
|||
active={active === 'library-4'} |
|||
eventKey="library-4" |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Starred |
|||
</Dropdown.Item> |
|||
<Dropdown.Item |
|||
active={active === 'library-5'} |
|||
eventKey="library-5" |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Podcasts |
|||
</Dropdown.Item> |
|||
<Dropdown.Item |
|||
active={active === 'library-6'} |
|||
eventKey="library-6" |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Radio |
|||
</Dropdown.Item> |
|||
</Dropdown> |
|||
<Nav.Item |
|||
active={active === 'settings'} |
|||
eventKey="settings" |
|||
icon={<Icon icon="gear-circle" />} |
|||
onSelect={handleSidebarSelect} |
|||
> |
|||
Config |
|||
</Nav.Item> |
|||
<Nav.Item |
|||
icon={ |
|||
<Icon |
|||
icon={ |
|||
expand ? 'exclamation-triangle' : 'exclamation-triangle' |
|||
} |
|||
/> |
|||
} |
|||
onSelect={handleToggle} |
|||
> |
|||
Toggle Expand |
|||
</Nav.Item> |
|||
</Nav> |
|||
</Sidenav.Body> |
|||
</Sidenav> |
|||
</Sb> |
|||
); |
|||
}; |
|||
|
|||
export default Sidebar; |
@ -0,0 +1,13 @@ |
|||
import React from 'react'; |
|||
import { Loader as RsLoader } from 'rsuite'; |
|||
import '../../styles/Loader.global.css'; |
|||
|
|||
const Loader = () => { |
|||
return ( |
|||
<div className="loader__main"> |
|||
<RsLoader size="md" /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default Loader; |
@ -0,0 +1,7 @@ |
|||
import React from 'react'; |
|||
|
|||
const NowPlayingList = () => { |
|||
return <div />; |
|||
}; |
|||
|
|||
export default NowPlayingList; |
@ -0,0 +1,36 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
import Sound from 'react-sound'; |
|||
|
|||
/* const useAudio = (url) => { |
|||
const [audio] = useState(new Audio(url)); |
|||
const [playing, setPlaying] = useState(false); |
|||
|
|||
const toggle = () => setPlaying(!playing); |
|||
|
|||
useEffect(() => { |
|||
playing ? audio.play() : audio.pause(); |
|||
}, [playing]); |
|||
|
|||
useEffect(() => { |
|||
audio.addEventListener('ended', () => setPlaying(false)); |
|||
return () => { |
|||
audio.removeEventListener('ended', () => setPlaying(false)); |
|||
}; |
|||
}, []); |
|||
|
|||
return [playing, toggle]; |
|||
}; */ |
|||
|
|||
const Player = ({ url }) => { |
|||
return ( |
|||
<div> |
|||
<Sound |
|||
url={url} |
|||
playStatus={Sound.status.PLAYING} |
|||
playFromPosition={0} |
|||
></Sound> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default Player; |
@ -0,0 +1,7 @@ |
|||
import React from 'react'; |
|||
|
|||
const Playlist = () => { |
|||
return <div></div>; |
|||
}; |
|||
|
|||
export default Playlist; |
@ -0,0 +1,86 @@ |
|||
import React from 'react'; |
|||
import { useQuery } from 'react-query'; |
|||
import { useHistory } from 'react-router-dom'; |
|||
import { Table, Icon } from 'rsuite'; |
|||
import { Helmet } from 'react-helmet-async'; |
|||
import { getPlaylists } from '../../api/api'; |
|||
import ListView from '../views/ListView'; |
|||
import Loader from '../loader/Loader'; |
|||
import GenericPage from '../layout/GenericPage'; |
|||
import PlaylistViewHeader from './PlaylistViewHeader'; |
|||
|
|||
const tableColumns = [ |
|||
{ |
|||
header: 'Name', |
|||
dataKey: 'name', |
|||
alignment: 'left', |
|||
flexGrow: 2, |
|||
resizable: false, |
|||
}, |
|||
{ |
|||
header: 'Tracks', |
|||
dataKey: 'songCount', |
|||
alignment: 'center', |
|||
flexGrow: 1, |
|||
resizable: false, |
|||
}, |
|||
{ |
|||
header: 'Description', |
|||
dataKey: 'comment', |
|||
alignment: 'left', |
|||
flexGrow: 2, |
|||
resizable: false, |
|||
}, |
|||
{ |
|||
header: 'Created', |
|||
dataKey: 'created', |
|||
alignment: 'left', |
|||
flexGrow: 1, |
|||
resizable: false, |
|||
}, |
|||
]; |
|||
|
|||
const PlaylistList = () => { |
|||
const { isLoading, isError, data: playlists, error }: any = useQuery( |
|||
'playlists', |
|||
getPlaylists |
|||
); |
|||
const history = useHistory(); |
|||
|
|||
// console.log(data);
|
|||
console.log(playlists); |
|||
|
|||
const handleRowClick = (e: any) => { |
|||
history.push(`playlist/${e.id}`); |
|||
}; |
|||
|
|||
if (isLoading) { |
|||
return <Loader />; |
|||
} |
|||
|
|||
if (isError) { |
|||
return <span>Error: {error.message}</span>; |
|||
} |
|||
|
|||
return ( |
|||
<GenericPage> |
|||
<Helmet> |
|||
<title>sonicd - Playlists</title> |
|||
</Helmet> |
|||
<ListView |
|||
data={playlists} |
|||
handleRowClick={handleRowClick} |
|||
tableColumns={tableColumns} |
|||
> |
|||
<Table.Column width={150} align="center" flexGrow={1}> |
|||
<Table.HeaderCell>Actions</Table.HeaderCell> |
|||
<Table.Cell> |
|||
<Icon icon="ellipsis-v" /> |
|||
</Table.Cell> |
|||
</Table.Column> |
|||
</ListView> |
|||
</GenericPage> |
|||
); |
|||
}; |
|||
|
|||
export default PlaylistList; |
@ -0,0 +1,103 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { useQuery } from 'react-query'; |
|||
import { useParams } from 'react-router-dom'; |
|||
import { Helmet } from 'react-helmet-async'; |
|||
import { getPlaylist, getStream } from '../../api/api'; |
|||
import GenericPage from '../layout/GenericPage'; |
|||
import ListView from '../views/ListView'; |
|||
import Loader from '../loader/Loader'; |
|||
import PlaylistViewHeader from './PlaylistViewHeader'; |
|||
import Player from '../player/Player'; |
|||
|
|||
type PlaylistParams = { |
|||
id: string; |
|||
}; |
|||
|
|||
const tableColumns = [ |
|||
{ |
|||
header: '#', |
|||
dataKey: 'index', |
|||
alignment: 'center', |
|||
resizable: true, |
|||
width: 70, |
|||
}, |
|||
{ |
|||
header: 'Title', |
|||
dataKey: 'title', |
|||
alignment: 'left', |
|||
resizable: true, |
|||
width: 350, |
|||
}, |
|||
|
|||
{ |
|||
header: 'Artist', |
|||
dataKey: 'artist', |
|||
alignment: 'center', |
|||
resizable: true, |
|||
width: 300, |
|||
}, |
|||
{ |
|||
header: 'Album', |
|||
dataKey: 'album', |
|||
alignment: 'center', |
|||
resizable: true, |
|||
width: 300, |
|||
}, |
|||
{ |
|||
header: 'Duration', |
|||
dataKey: 'duration', |
|||
alignment: 'center', |
|||
resizable: true, |
|||
width: 70, |
|||
}, |
|||
]; |
|||
|
|||
const PlaylistView = () => { |
|||
const { id } = useParams<PlaylistParams>(); |
|||
const { isLoading, isError, data: playlist, error }: any = useQuery( |
|||
['playlist', id], |
|||
() => getPlaylist(id) |
|||
); |
|||
const [playing, setPlaying] = useState(null); |
|||
|
|||
const handleRowClick = (e: any) => { |
|||
console.log(e); |
|||
setPlaying(e.streamUrl); |
|||
}; |
|||
|
|||
if (isLoading) { |
|||
return <Loader />; |
|||
} |
|||
|
|||
if (isError) { |
|||
return <span>Error: {error.message}</span>; |
|||
} |
|||
|
|||
console.log(playlist); |
|||
|
|||
return ( |
|||
<GenericPage |
|||
title="Playlists" |
|||
header={ |
|||
<PlaylistViewHeader |
|||
name={playlist.name} |
|||
comment={playlist.comment} |
|||
songCount={playlist.songCount} |
|||
image={playlist.image} |
|||
/> |
|||
} |
|||
> |
|||
<Helmet> |
|||
<title>sonicd - {playlist.name}</title> |
|||
</Helmet> |
|||
<ListView |
|||
data={playlist.entry} |
|||
tableColumns={tableColumns} |
|||
handleRowClick={handleRowClick} |
|||
/> |
|||
{/* {setPlaying && <Player url={playing} />} */} |
|||
</GenericPage> |
|||
); |
|||
}; |
|||
|
|||
export default PlaylistView; |
@ -0,0 +1,19 @@ |
|||
import React from 'react'; |
|||
import '../../styles/PlaylistViewHeader.global.css'; |
|||
|
|||
const PlaylistViewHeader = ({ name, comment, image, songCount }: any) => { |
|||
return ( |
|||
<div id="playlist__header" className="container__playlist"> |
|||
<div className="playlist__column_left"> |
|||
<img src={image} alt="playlist-img" /> |
|||
</div> |
|||
<div className="playlist__column_middle"> |
|||
<h1 className="playlist__title">{name}</h1> |
|||
<h2 className="playlist__comment">{comment}</h2> |
|||
<h3 className="playlist__songcount">{songCount}</h3> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default PlaylistViewHeader; |
@ -0,0 +1,140 @@ |
|||
import React, { useState } from 'react'; |
|||
import md5 from 'md5'; |
|||
import randomstring from 'randomstring'; |
|||
|
|||
import { |
|||
Button, |
|||
Icon, |
|||
Container, |
|||
Panel, |
|||
Form, |
|||
FormGroup, |
|||
ControlLabel, |
|||
FormControl, |
|||
ButtonToolbar, |
|||
Alert, |
|||
} from 'rsuite'; |
|||
import axios from 'axios'; |
|||
import GenericPage from '../layout/GenericPage'; |
|||
import '../../styles/Settings.global.css'; |
|||
|
|||
const Settings = () => { |
|||
const [serverName, setServerName] = useState( |
|||
localStorage.getItem('server') || '' |
|||
); |
|||
const [userName, setUserName] = useState(''); |
|||
const [password, setPassword] = useState(''); |
|||
|
|||
const handleConnect = async () => { |
|||
console.log({ |
|||
serverName, |
|||
userName, |
|||
password, |
|||
}); |
|||
|
|||
const salt = randomstring.generate({ length: 16, charset: 'alphanumeric' }); |
|||
const hash = md5(password + salt); |
|||
|
|||
try { |
|||
const testConnection = await axios.get( |
|||
`${serverName}/rest/getUsers?v=1.15.0&c=sonicd&f=json&u=${userName}&s=${salt}&t=${hash}` |
|||
); |
|||
|
|||
// Since a valid request will return a 200 response, we need to check that there
|
|||
// are no additional failures reported by the server
|
|||
if (testConnection.data['subsonic-response'].status === 'failed') { |
|||
Alert.error(testConnection.data['subsonic-response'].error.message); |
|||
return; |
|||
} |
|||
} catch (err) { |
|||
Alert.error(`Error validating server hostname: ${err.message}`); |
|||
return; |
|||
} |
|||
|
|||
localStorage.setItem('server', serverName); |
|||
localStorage.setItem('username', userName); |
|||
localStorage.setItem('salt', salt); |
|||
localStorage.setItem('hash', hash); |
|||
window.location.reload(); |
|||
}; |
|||
|
|||
const handleDisconnect = () => { |
|||
localStorage.removeItem('server'); |
|||
localStorage.removeItem('username'); |
|||
localStorage.removeItem('salt'); |
|||
localStorage.removeItem('hash'); |
|||
window.location.reload(); |
|||
}; |
|||
|
|||
return ( |
|||
<GenericPage id="settings" header={<h3>Sonicd Config</h3>}> |
|||
<Container> |
|||
<Panel |
|||
className="settings__server" |
|||
header="Server configuration" |
|||
bordered |
|||
> |
|||
{localStorage.getItem('server') && ( |
|||
<p className="settings__connection color__green"> |
|||
Currently connected to [{localStorage.getItem('server')}]{' '} |
|||
<Icon icon="check" /> |
|||
</p> |
|||
)} |
|||
<p className="settings__description"> |
|||
Enter your Subsonic-API compatible server configuration: |
|||
<ul> |
|||
<li>Subsonic</li> |
|||
<li>Airsonic</li> |
|||
<li>Navidrome</li> |
|||
<li>Gonic</li> |
|||
</ul> |
|||
</p> |
|||
<Form> |
|||
<FormGroup> |
|||
<ControlLabel>Server Hostname</ControlLabel> |
|||
<FormControl |
|||
name="servername" |
|||
value={serverName} |
|||
onChange={(e) => setServerName(e)} |
|||
/> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<ControlLabel>Username</ControlLabel> |
|||
<FormControl |
|||
name="name" |
|||
value={userName} |
|||
onChange={(e) => setUserName(e)} |
|||
/> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<ControlLabel>Password</ControlLabel> |
|||
<FormControl |
|||
name="password" |
|||
type="password" |
|||
value={password} |
|||
onChange={(e) => setPassword(e)} |
|||
/> |
|||
</FormGroup> |
|||
|
|||
<FormGroup> |
|||
<ButtonToolbar> |
|||
<Button |
|||
appearance="primary" |
|||
type="submit" |
|||
onClick={handleConnect} |
|||
> |
|||
Connect |
|||
</Button> |
|||
<Button appearance="default" onClick={handleDisconnect}> |
|||
<Icon icon="trash" /> Delete Current Configuration |
|||
</Button> |
|||
</ButtonToolbar> |
|||
</FormGroup> |
|||
</Form> |
|||
</Panel> |
|||
</Container> |
|||
</GenericPage> |
|||
); |
|||
}; |
|||
|
|||
export default Settings; |
@ -0,0 +1,7 @@ |
|||
import React from 'react'; |
|||
|
|||
const GridView = () => { |
|||
return <div />; |
|||
}; |
|||
|
|||
export default GridView; |
@ -0,0 +1,34 @@ |
|||
import React from 'react'; |
|||
import { Table } from 'rsuite'; |
|||
import { nanoid } from '@reduxjs/toolkit'; |
|||
import '../../styles/ListView.global.css'; |
|||
|
|||
const ListView = ({ data, handleRowClick, tableColumns, children }: any) => { |
|||
return ( |
|||
<Table |
|||
data={data} |
|||
height={420} |
|||
autoHeight |
|||
onRowClick={handleRowClick} |
|||
affixHeader |
|||
affixHorizontalScrollbar |
|||
> |
|||
{tableColumns.map((column: any) => ( |
|||
<Table.Column |
|||
key={nanoid()} |
|||
align={column.alignment} |
|||
flexGrow={column.flexGrow} |
|||
resizable={column.resizable} |
|||
width={column.width} |
|||
fixed={column.fixed} |
|||
> |
|||
<Table.HeaderCell>{column.header}</Table.HeaderCell> |
|||
<Table.Cell dataKey={column.dataKey} /> |
|||
</Table.Column> |
|||
))} |
|||
{children} |
|||
</Table> |
|||
); |
|||
}; |
|||
|
|||
export default ListView; |