├── app
├── src
│ ├── redux
│ │ ├── reducers
│ │ │ ├── configReducer.js
│ │ │ ├── dataReducer.js
│ │ │ ├── homeReducer.js
│ │ │ └── chartReducer.js
│ │ ├── constants
│ │ │ ├── chartProperties.js
│ │ │ └── homeConstants.js
│ │ ├── store.js
│ │ └── actions
│ │ │ ├── chartActions.js
│ │ │ ├── actionConstants.js
│ │ │ ├── homeActions.js
│ │ │ └── dataActions.js
│ ├── components
│ │ ├── App.jsx
│ │ ├── loaders
│ │ │ ├── CubeLoader.jsx
│ │ │ ├── CodeLoader.jsx
│ │ │ ├── statusMessage.jsx
│ │ │ ├── ImportLoader.jsx
│ │ │ └── awesomeBee.jsx
│ │ ├── data_viz
│ │ │ ├── helper_components
│ │ │ │ └── DisplayButton.jsx
│ │ │ ├── BarChart.jsx
│ │ │ └── D3StarBurstChart.jsx
│ │ ├── Card.jsx
│ │ ├── OpenFolderButtons.jsx
│ │ ├── OpenFolderOfOneButton.jsx
│ │ ├── DropZone.jsx
│ │ ├── Chart.jsx
│ │ ├── ModalPrompt.jsx
│ │ └── Main.jsx
│ ├── index.js
│ ├── styles
│ │ ├── styles.scss
│ │ ├── base
│ │ │ ├── _settings.scss
│ │ │ ├── _footer.scss
│ │ │ └── _base.scss
│ │ └── components
│ │ │ ├── _code-loader.scss
│ │ │ ├── _loading.scss
│ │ │ ├── _bar-chart.scss
│ │ │ ├── _cube-loader.scss
│ │ │ ├── _buttons.scss
│ │ │ ├── _chart.scss
│ │ │ └── _d3.scss
│ └── utils
│ │ └── dataParser.js
├── assets
│ ├── 64x64.png
│ ├── logo.png
│ ├── compare_all.png
│ ├── file_icon.png
│ ├── parcel_icon.png
│ ├── rollup_icon.png
│ ├── cloud_upload.png
│ └── webpack_icon.png
└── index.html
├── dist
└── .gitignore
├── .babelrc
├── assets
└── icons
│ ├── mac
│ └── icon.icns
│ ├── png
│ ├── 16x16.png
│ ├── 24x24.png
│ ├── 32x32.png
│ ├── 48x48.png
│ ├── 64x64.png
│ ├── 128x128.png
│ ├── 256x256.png
│ ├── 512x512.png
│ └── 1024x1024.png
│ └── win
│ └── icon.ico
├── backend
├── create-config
│ └── utils
│ │ ├── createWebPackConfigHelpers
│ │ ├── SetEntryFromDragPathIfDragPathExists.js
│ │ ├── getSavedProjectDataFromFile.js
│ │ ├── template.html
│ │ ├── parseWebpackConfig.js
│ │ ├── parseEntryFromWebpack.js
│ │ ├── createConfigStringFromParams.js
│ │ ├── getInfoForWebpackConfigFromFileList.js
│ │ └── create-rules.js
│ │ ├── parcelBundleHelpers
│ │ ├── addParcelDependenciesToUserDataObject.js
│ │ └── parcelBundler.js
│ │ ├── rollupConfigHelpers
│ │ ├── deleteCreatedFilesFromUsersRoot.js
│ │ ├── copyFilesToElectron.js
│ │ ├── rollupBundler.js
│ │ ├── runRollupFromUsersRoot.js
│ │ ├── createRollupConfigFromParams.js
│ │ ├── custom_plugin
│ │ │ └── size_timing.js
│ │ └── createRollupRulesFromFileTypes.js
│ │ ├── indexFilesFromRootHelpers
│ │ ├── returnCurrentDirectoryFromPath.js
│ │ └── getAllFilesInCurrentDirectory.js
│ │ ├── file-and-system-actions
│ │ ├── run-webpack-from-separate-dir-child-process.js
│ │ └── writeToFile.js
│ │ ├── runWebpack.js
│ │ ├── indexFilesFromRoot.js
│ │ ├── createWebpackConfig.js
│ │ ├── runRollup.js
│ │ └── runParcel.js
└── menuBar.js
├── testing
└── create-webpack-config.test.js
├── .gitignore
├── webpack.config.js
├── package.json
├── README.md
└── main_process.js
/app/src/redux/reducers/configReducer.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | */
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/app/assets/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/64x64.png
--------------------------------------------------------------------------------
/app/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/logo.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["transform-object-rest-spread", { "useBuiltIns": true }]
4 | ]
5 | }
--------------------------------------------------------------------------------
/app/assets/compare_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/compare_all.png
--------------------------------------------------------------------------------
/app/assets/file_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/file_icon.png
--------------------------------------------------------------------------------
/app/assets/parcel_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/parcel_icon.png
--------------------------------------------------------------------------------
/app/assets/rollup_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/rollup_icon.png
--------------------------------------------------------------------------------
/assets/icons/mac/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/mac/icon.icns
--------------------------------------------------------------------------------
/assets/icons/png/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/16x16.png
--------------------------------------------------------------------------------
/assets/icons/png/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/24x24.png
--------------------------------------------------------------------------------
/assets/icons/png/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/32x32.png
--------------------------------------------------------------------------------
/assets/icons/png/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/48x48.png
--------------------------------------------------------------------------------
/assets/icons/png/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/64x64.png
--------------------------------------------------------------------------------
/assets/icons/win/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/win/icon.ico
--------------------------------------------------------------------------------
/app/assets/cloud_upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/cloud_upload.png
--------------------------------------------------------------------------------
/app/assets/webpack_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/app/assets/webpack_icon.png
--------------------------------------------------------------------------------
/assets/icons/png/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/128x128.png
--------------------------------------------------------------------------------
/assets/icons/png/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/256x256.png
--------------------------------------------------------------------------------
/assets/icons/png/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/512x512.png
--------------------------------------------------------------------------------
/assets/icons/png/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundlebee/bundle-bee/HEAD/assets/icons/png/1024x1024.png
--------------------------------------------------------------------------------
/app/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Main from './Main.jsx';
3 |
4 | const App = () => {
5 | return
6 | };
7 |
8 | export default App;
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/SetEntryFromDragPathIfDragPathExists.js:
--------------------------------------------------------------------------------
1 | module.exports = (res, path) => {
2 | res.entry = path !== 'undefined' ? path : res.entry;
3 | return res;
4 | };
5 |
--------------------------------------------------------------------------------
/app/src/components/loaders/CubeLoader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | return (
5 |
9 | )
10 | };
11 |
--------------------------------------------------------------------------------
/app/src/components/loaders/CodeLoader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | return (
5 |
6 | {`{`}{`}`}
7 |
8 | )
9 | };
10 |
--------------------------------------------------------------------------------
/backend/create-config/utils/parcelBundleHelpers/addParcelDependenciesToUserDataObject.js:
--------------------------------------------------------------------------------
1 | module.exports = res => {
2 | res.parcelDependencies = {
3 | parcel: '^1.9.7',
4 | 'parcel-bundler': '^1.9.7',
5 | };
6 | return res;
7 | };
8 |
--------------------------------------------------------------------------------
/app/src/redux/constants/chartProperties.js:
--------------------------------------------------------------------------------
1 | export const SIZE = 'size';
2 | export const FACTORY_TIME = 'factory';
3 | export const BUILDING_TIME = 'building';
4 | export const WEBPACK = 'webpack';
5 | export const PARCEL = 'parcel';
6 | export const ROLLUP = 'rollup';
7 | export const TOTALS = 'totals';
8 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/getSavedProjectDataFromFile.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | module.exports = path =>
4 | new Promise((resolve, reject) => {
5 | fs.readFile(path, 'utf-8', (err, res) => {
6 | if (err) reject(err);
7 | else resolve(JSON.parse(res));
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/src/components/loaders/statusMessage.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | export default (props) => {
5 | return (
6 |
7 |
{props.loadingMessage}
8 |
9 |
10 |
11 |
12 | )
13 | };
14 |
--------------------------------------------------------------------------------
/app/src/redux/constants/homeConstants.js:
--------------------------------------------------------------------------------
1 | export const DIRECTORY_PENDING = 'DIRECTORY_PENDING';
2 | export const LOADING_MODAL = 'LOADING_MODAL';
3 | export const SHOW_MODAL = 'SHOW_MODAL';
4 | export const WAIT_FOR_ENTRY = 'WAIT_FOR_ENTRY';
5 | export const LOADING_BUNDLE = 'LOADING_BUNDLE';
6 | export const BUNDLE_COMPLETE = 'BUNDLE_COMPLETE';
7 | export const SHOW_STARBURST = 'SHOW_STARBURST';
8 |
--------------------------------------------------------------------------------
/app/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import App from './components/App.jsx';
4 | import { Provider } from 'react-redux';
5 | import configureStore from './redux/store.js';
6 |
7 | import './styles/styles.scss';
8 |
9 | const store = configureStore();
10 |
11 | render(
12 |
13 |
14 | ,
15 | document.getElementById('app')
16 | );
17 |
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/deleteCreatedFilesFromUsersRoot.js:
--------------------------------------------------------------------------------
1 | const rimraf = require('rimraf');
2 | const path = require('path');
3 |
4 | module.exports = res =>
5 | new Promise((resolve, reject) => {
6 | const usersDirectoryToRemove = path.join(res.rootDir, 'bundle-bee-rollup-dist');
7 | rimraf(usersDirectoryToRemove, err => {
8 | if (err) reject(err);
9 | resolve(res);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/backend/create-config/utils/indexFilesFromRootHelpers/returnCurrentDirectoryFromPath.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = pathFromDragAndDrop => {
4 | return new Promise((resolve, reject) => {
5 | if (!path.isAbsolute(pathFromDragAndDrop)) reject();
6 | const rootDir = path.extname(pathFromDragAndDrop)
7 | ? path.dirname(pathFromDragAndDrop)
8 | : pathFromDragAndDrop;
9 | resolve(rootDir);
10 | });
11 | };
12 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Bundle Bee HTML Template
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/testing/create-webpack-config.test.js:
--------------------------------------------------------------------------------
1 | const { indexFilesFromRoot } = require('../backend/create-config/process-and-bundle-project.js');
2 |
3 | jest.setTimeout(10000);
4 | const mockbusterPath = '/Users/bren/Codesmith/mockbuster';
5 |
6 | let mockbusterFileIndexResult;
7 | test('finds webpack config if it exists, and doesnt find config if it doesnt exist', done => {
8 | indexFilesFromRoot(mockbusterPath).then(res => {
9 | mockbusterFileIndexResult = res;
10 | expect(res.webpackConfig.exists).toBe(true);
11 | done();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/app/src/styles/styles.scss:
--------------------------------------------------------------------------------
1 | // import all of your partials here. they will be combined at compile time to a single file to import in your app's index page
2 | // scss/sass partials import names do not include the underscore. just the name starting after the _
3 | @import './base/settings';
4 | @import './base/base';
5 | @import './components/d3';
6 | @import './components/buttons';
7 | @import './components/code-loader';
8 | @import './components/cube-loader';
9 | @import './components/loading';
10 | @import './components/chart';
11 | @import './components/bar-chart';
12 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Bundle Bee
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/components/data_viz/helper_components/DisplayButton.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const DisplayButton = props => {
4 | let className;
5 | if (!props.isActive) {
6 | className = "d3_btn_disabled d3";
7 | } else if (props.isHighligthed) {
8 | className = "d3_btn_highligthed d3";
9 | } else {
10 | className = "d3_btn_default d3";
11 | }
12 |
13 | return (
14 |
21 | );
22 | };
23 |
24 | export default DisplayButton;
25 |
--------------------------------------------------------------------------------
/app/src/styles/base/_settings.scss:
--------------------------------------------------------------------------------
1 | // place scss variables here. This is just an example. these can all be deleted
2 | //Colors
3 | $blue: #1c88bf;
4 | $dark-blue: #364051;
5 | $light-blue: #464b5e;
6 | $grey: #666;
7 | $dark-grey: #333;
8 | $light-grey: #f7f7f7;
9 | //Spacing
10 | $xs-size: .6rem;
11 | $s-size: 1.2rem;
12 | $m-size: 1.6rem;
13 | $l-size: 3.2rem;
14 | $xl-size: 4.8rem;
15 | $mobile-breakpoint: 45rem;
16 | //Font size
17 | $font-size-small: 1.4rem;
18 | $font-size-large: 1.8rem;
19 | //Font Weight
20 | $s-weight: 300;
21 | $m-weight: 500;
22 | $l-weight: 700;
23 | //Border Radius
24 | $s-border-radius: .5em;
25 | $m-border-radius: 1em;
26 |
27 | $btn_lightBlue: #b7e2ec;
--------------------------------------------------------------------------------
/backend/create-config/utils/file-and-system-actions/run-webpack-from-separate-dir-child-process.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const spawn = require('child_process').spawn;
3 |
4 | const statsWritePath = process.argv[process.argv.length - 1];
5 | const resultsStream = fs.createWriteStream(statsWritePath);
6 |
7 | const child = spawn('webpack', ['--profile', '--json']);
8 |
9 | child.stdout.pipe(resultsStream);
10 |
11 | child.on('close', function(code) {
12 | console.log('child process exited with code ' + code);
13 | process.send('done');
14 | process.exit();
15 | });
16 |
17 | child.on('error', error => {
18 | process.send({ error });
19 | process.exit();
20 | });
21 |
--------------------------------------------------------------------------------
/backend/create-config/utils/file-and-system-actions/writeToFile.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | module.exports = (data, fileName, res) => {
5 | return new Promise((resolve, reject) => {
6 | let stringData = typeof data === 'string' ? data : JSON.stringify(data);
7 | const savePath = path.join(__dirname, '..', '..', '..', '..', 'electronUserData', fileName);
8 | const saveDir = path.dirname(savePath);
9 | if (!fs.existsSync(saveDir)) {
10 | fs.mkdirSync(saveDir);
11 | }
12 | fs.writeFile(savePath, stringData, err => {
13 | if (err) reject(`error writing file ${fileName}`);
14 | resolve(res);
15 | });
16 | });
17 | };
18 |
--------------------------------------------------------------------------------
/app/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { composeWithDevTools } from 'redux-devtools-extension';
4 |
5 | import homeReducer from './reducers/homeReducer.js';
6 | import chartReducer from './reducers/chartReducer.js';
7 | import dataReducer from './reducers/dataReducer.js';
8 |
9 |
10 | export default () => {
11 | const store = createStore(
12 | combineReducers({
13 | home: homeReducer,
14 | chart: chartReducer,
15 | data: dataReducer,
16 | }),
17 | composeWithDevTools(applyMiddleware(thunk))
18 | );
19 |
20 | return store;
21 | };
22 |
23 |
--------------------------------------------------------------------------------
/backend/create-config/utils/runWebpack.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { exec } = require('child_process');
3 |
4 | const statsWritePath = process.argv[process.argv.length - 1];
5 |
6 | const webpackCommandAbsoluteLocation = path.join(
7 | require.resolve('webpack'),
8 | '..',
9 | '..',
10 | 'bin',
11 | 'webpack.js'
12 | );
13 | console.log('cwd: ', process.cwd());
14 | console.log('webpack command: ');
15 | console.log(`${webpackCommandAbsoluteLocation} --profile --json > ${statsWritePath}`);
16 | exec(`${webpackCommandAbsoluteLocation} --profile --json > ${statsWritePath}`, null, error => {
17 | if (error) process.send({ error });
18 | else process.send({ status: 'done' });
19 | process.exit();
20 | });
21 |
--------------------------------------------------------------------------------
/app/src/redux/actions/chartActions.js:
--------------------------------------------------------------------------------
1 | import * as types from './actionConstants.js';
2 |
3 | export const displaySizes = () => ({
4 | type: types.DISPLAY_SIZES
5 | });
6 |
7 | export const displayFactoryTimes = () => ({
8 | type: types.DISPLAY_FACTORY_TIMES
9 | });
10 |
11 | export const displayBuildingTimes = () => ({
12 | type: types.DISPLAY_BUILDING_TIMES
13 | });
14 |
15 | export const displayWebpack = () => ({
16 | type: types.DISPLAY_WEBPACK
17 | });
18 |
19 | export const displayParcel = () => ({
20 | type: types.DISPLAY_PARCEL
21 | });
22 |
23 | export const displayRollup = () => ({
24 | type: types.DISPLAY_ROLLUP
25 | });
26 | export const displayTotals = () => ({
27 | type: types.DISPLAY_TOTALS
28 | });
29 |
30 |
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/copyFilesToElectron.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const copydir = require('copy-dir');
3 | const path = require('path');
4 |
5 | module.exports = res =>
6 | new Promise((resolve, reject) => {
7 | const localRollupDistPath = path.join(
8 | __dirname,
9 | '..',
10 | '..',
11 | '..',
12 | '..',
13 | 'electronUserData',
14 | 'rollup-dist'
15 | );
16 | const usersRollupDistPath = path.join(res.rootDir, 'bundle-bee-rollup-dist');
17 | if (!fs.existsSync(localRollupDistPath)) fs.mkdirSync(localRollupDistPath);
18 | copydir(usersRollupDistPath, localRollupDistPath, err => {
19 | if (err) reject(err);
20 | resolve(res);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/parseWebpackConfig.js:
--------------------------------------------------------------------------------
1 | const babylon = require('babylon');
2 | const traverse = require('babel-traverse').default;
3 |
4 | module.exports = (res, { content }) => {
5 | if (res.output) return res;
6 | try {
7 | let output;
8 | const ast = babylon.parse(content, { sourceType: 'module' });
9 | traverse(ast, {
10 | Identifier: {
11 | enter(path) {
12 | if (path.node.name === 'entry') {
13 | let entryValue = content.slice(path.parent.value.start, path.parent.value.end);
14 | output = entryValue;
15 | }
16 | },
17 | },
18 | });
19 | res.output = output;
20 | return res;
21 | } catch (error) {
22 | return res;
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/app/src/redux/reducers/dataReducer.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | webpackStarBurstData: null,
3 | parcelStarBurstData: null,
4 | rollupStarBurstData: null
5 | };
6 |
7 | import * as types from '../actions/actionConstants.js';
8 |
9 | const dataReducer = (state = initialState, action) => {
10 | switch (action.type) {
11 | case types.BUNDLE_WEBPACK_COMPLETE: {
12 | return { ... state, webpackStarBurstData: action.payload };
13 | }
14 | case types.BUNDLE_PARCEL_COMPLETE: {
15 | return {... state, parcelStarBurstData: action.payload };
16 | }
17 | case types.BUNDLE_ROLLUP_COMPLETE: {
18 | return {... state, rollupStarBurstData: action.payload };
19 | }
20 | default: {
21 | return state;
22 | }
23 | }
24 | };
25 |
26 | export default dataReducer;
27 |
--------------------------------------------------------------------------------
/app/src/styles/components/_code-loader.scss:
--------------------------------------------------------------------------------
1 | .code-loader {
2 | color: #fff;
3 | font-family: Consolas, Menlo, Monaco, monospace;
4 | font-weight: bold;
5 | font-size: 30vh;
6 | opacity: 0.8;
7 | }
8 |
9 | .code-loader span {
10 | margin: 30px;
11 | display: inline-block;
12 | color: #f4b01a;
13 | -webkit-animation: pulse 0.4s alternate infinite ease-in-out;
14 | animation: pulse 0.4s alternate infinite ease-in-out;
15 | }
16 |
17 | .code-loader span:nth-child(odd) {
18 | -webkit-animation-delay: 0.4s;
19 | animation-delay: 0.4s;
20 | }
21 |
22 | @-webkit-keyframes pulse {
23 | to {
24 | -webkit-transform: scale(0.8);
25 | transform: scale(0.8);
26 | opacity: 0.5;
27 | }
28 | }
29 |
30 | @keyframes pulse {
31 | to {
32 | -webkit-transform: scale(0.8);
33 | transform: scale(0.8);
34 | opacity: 0.5;
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/styles/base/_footer.scss:
--------------------------------------------------------------------------------
1 | /* -----------------------------------------------------
2 | Footer
3 | -------------------------------------------------------- */
4 | footer {
5 | display: block;
6 | overflow: hidden;
7 | width: 100%;
8 | min-height: 80px;
9 | background-color: #FFF;
10 | border-top: 1px solid #EEE;
11 |
12 | .row {
13 | width: 100%;
14 | max-width: 860px;
15 | margin: 0 auto;
16 | }
17 |
18 | .footer-content {
19 | margin: 0 8px;
20 | padding: 25px 0 18px;
21 | text-align: center;
22 |
23 | span {
24 | color: rgba(0, 0, 0, 0.525);
25 | font-size: 0.857em;
26 | margin-right: 5px;
27 | }
28 | }
29 |
30 | @media only screen and (max-width: 768px) {
31 | span {
32 | display: block;
33 | width: 100%;
34 | margin-bottom: 8px;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/backend/create-config/utils/indexFilesFromRootHelpers/getAllFilesInCurrentDirectory.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const readdirp = require('readdirp');
3 | let results = [];
4 |
5 | const folderIndexer = (root, callback) => {
6 | const settings = {
7 | root,
8 | entryType: 'all',
9 | directoryFilter: ['!.git'],
10 | };
11 | readdirp(settings)
12 | .on('data', function(entry) {
13 | if (path.extname(entry.name)) {
14 | results.push(entry);
15 | }
16 | })
17 | .on('error', function(err) {
18 | callback(err, results);
19 | })
20 | .on('end', function() {
21 | callback(null, results);
22 | results = [];
23 | });
24 | };
25 |
26 | module.exports = rootDir => {
27 | return new Promise((resolve, reject) => {
28 | folderIndexer(rootDir, (err, res) => {
29 | if (err) reject(err);
30 | if (res) resolve(res);
31 | });
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # electron project build
2 | app/build
3 |
4 | .DS_Store
5 | *-lock.json
6 |
7 | # Logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules
37 | jspm_packages
38 |
39 | # Optional npm cache directory
40 | .npm
41 |
42 | # Optional REPL history
43 | .node_repl_history
44 |
45 | .vs/
46 | .vscode/
47 |
48 | dist/
49 | .cache/
50 | electronUserData/
51 | release-build/
52 | release-builds/
53 |
--------------------------------------------------------------------------------
/app/src/styles/components/_loading.scss:
--------------------------------------------------------------------------------
1 |
2 | #loader {
3 | display: flex;
4 | justify-content: center;
5 | flex-direction: column;
6 | width: 50vw;
7 | }
8 |
9 | .load {
10 | font-size: 1.8rem;
11 | font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
12 | font-weight: bold;
13 | text-align: center;
14 | }
15 |
16 | .progress {
17 | height: 2rem;
18 | position: relative;
19 | background: rgba(159, 159, 159, 0.5);
20 | border-radius: 10px;
21 | padding: 2px;
22 | }
23 |
24 | .progress span {
25 | transition: all 100ms ease;
26 | height: 20px;
27 | width: calc(100% - 10px);
28 | border-radius: 8px;
29 | background: #474747;
30 | position: absolute;
31 | margin: 3px;
32 | animation: load 5s ease infinite;
33 | }
34 |
35 |
36 | @keyframes load {
37 | 50%, 100% {
38 | transform: scale3d(1,1,1);
39 | }
40 | 50% {
41 | transform: scale3d(0,1,1);
42 | }
43 | }
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/rollupBundler.js:
--------------------------------------------------------------------------------
1 | const rollup = require('rollup');
2 | const fs = require('fs');
3 | const path = require('path');
4 |
5 | const rollupConfigPath = process.argv[2];
6 | const config = require(rollupConfigPath);
7 | const inputOptions = { input: config.input, plugins: config.plugins };
8 | const outputOptions = config.output;
9 |
10 | const beginTime = Date.now();
11 | let Bundle;
12 | rollup
13 | .rollup(inputOptions)
14 | .then(bundle => {
15 | Bundle = bundle;
16 | return bundle.generate(outputOptions);
17 | })
18 | .then(result => {
19 | const totalElapsedTime = Date.now() - beginTime;
20 | const totalBundleSize = Buffer.from(result.code).length;
21 | fs.writeFileSync(
22 | path.join(__dirname, '..', '..', '..', '..', 'electronUserData', 'rollup-totals-stats.json'),
23 | JSON.stringify({ totalBundleSize, totalElapsedTime }, null, 2)
24 | );
25 | Bundle.write(outputOptions);
26 | });
27 |
--------------------------------------------------------------------------------
/app/src/redux/reducers/homeReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from '../actions/actionConstants.js';
2 | import * as home from '../constants/homeConstants.js';
3 |
4 | const initialState = {
5 | screen: home.DIRECTORY_PENDING,
6 | };
7 |
8 | const homeReducer = (state = initialState, action) => {
9 | switch (action.type) {
10 | case types.RESET_HOME:
11 | return { screen: home.DIRECTORY_PENDING };
12 | case types.LOAD_MODAL:
13 | return { screen: home.LOADING_MODAL };
14 | case types.SHOW_MODAL:
15 | return { screen: home.SHOW_MODAL };
16 | case types.WAIT_FOR_ENTRY:
17 | return { screen: home.WAIT_FOR_ENTRY };
18 | case types.LOAD_BUNDLE:
19 | return { screen: home.LOADING_BUNDLE };
20 | case types.BUNDLE_WEBPACK_COMPLETE:
21 | case types.BUNDLE_PARCEL_COMPLETE:
22 | case types.BUNDLE_ROLLUP_COMPLETE:
23 | return { screen: home.SHOW_STARBURST };
24 | default:
25 | return state;
26 | }
27 | };
28 |
29 | export default homeReducer;
30 |
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/runRollupFromUsersRoot.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { exec } = require('child_process');
3 | const fs = require('fs');
4 |
5 | module.exports = res =>
6 | new Promise((resolve, reject) => {
7 | const originalProcessDir = process.cwd();
8 | const rollupCommandAbsoluteLocation = path.join(require.resolve('rollup'), 'bin', 'rollup');
9 | const rollupConfigPath = path.join(res.rootDir, 'bundle-bee-rollup.config.js');
10 | console.log('rollupCommandAbsoluteLocation', rollupCommandAbsoluteLocation);
11 | console.log('cwd: ', process.cwd());
12 | process.chdir(res.rootDir);
13 |
14 | const rollupBundlerPath = path.join(__dirname, 'rollupBundler.js');
15 | exec(`node ${rollupBundlerPath} ${rollupConfigPath}`, (err, stdout, stderr) => {
16 | process.chdir(originalProcessDir);
17 | if (err) reject(err);
18 | fs.unlink(rollupConfigPath, err => {
19 | if (err) reject(err);
20 | console.log('finished running rollup');
21 | resolve(res);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/app/src/redux/actions/actionConstants.js:
--------------------------------------------------------------------------------
1 | // home state constants
2 | export const RESET_HOME = 'RESET_HOME';
3 | export const LOAD_MODAL = 'LOAD_MODAL';
4 | export const SHOW_MODAL = 'SHOW_MODAL';
5 | export const WAIT_FOR_ENTRY = 'WAIT_FOR_ENTRY';
6 | export const LOAD_BUNDLE = 'LOAD_BUNDLE';
7 | export const BUNDLE_COMPLETE = 'BUNDLE_COMPLETE'; // 8/7/2018 - do we still wanna keep this?
8 |
9 | // bundle state constansts
10 | export const BUNDLE_WEBPACK_COMPLETE = 'BUNDLE_WEBPACK_COMPLETE';
11 | export const BUNDLE_PARCEL_COMPLETE = 'BUNDLE_PARCEL_COMPLETE';
12 | export const BUNDLE_ROLLUP_COMPLETE = 'BUNDLE_ROLLUP_COMPLETE';
13 |
14 | // chart state constants
15 | export const DISPLAY_SIZES = 'DISPLAY_SIZES';
16 | export const DISPLAY_FACTORY_TIMES = 'DISPLAY_FACTORY_TIMES';
17 | export const DISPLAY_BUILDING_TIMES = 'DISPLAY_BUILDING_TIMES';
18 |
19 | // chart bundleType state constants
20 | export const DISPLAY_WEBPACK = 'DISPLAY_WEBPACK';
21 | export const DISPLAY_PARCEL = 'DISPLAY_PARCEL';
22 | export const DISPLAY_ROLLUP = 'DISPLAY_ROLLUP';
23 | export const DISPLAY_TOTALS = 'DISPLAY_TOTALS';
24 |
--------------------------------------------------------------------------------
/app/src/redux/actions/homeActions.js:
--------------------------------------------------------------------------------
1 | import * as types from './actionConstants.js';
2 |
3 | export const resetHome = () => ({
4 | type: types.RESET_HOME,
5 | });
6 |
7 | export const loadModal = () => ({
8 | type: types.LOAD_MODAL,
9 | });
10 |
11 | // will eventually need to take payload
12 | export const showModal = () => ({
13 | type: types.SHOW_MODAL,
14 | });
15 | export const waitForEntry = () => ({
16 | type: types.WAIT_FOR_ENTRY,
17 | });
18 |
19 | export const loadBundle = () => ({
20 | type: types.LOAD_BUNDLE,
21 | });
22 |
23 | // will eventually need to take payload
24 | export const bundleComplete = () => ({
25 | type: types.BUNDLE_COMPLETE,
26 | });
27 |
28 | // will eventually need to take payload
29 | export const bundleWebpackComplete = () => ({
30 | type: types.BUNDLE_WEBPACK_COMPLETE,
31 | });
32 |
33 | // will eventually need to take payload
34 | export const bundleParcelComplete = () => ({
35 | type: types.BUNDLE_PARCEL_COMPLETE,
36 | });
37 |
38 | // will eventually need to take payload
39 | export const bundleRollupComplete = () => ({
40 | type: types.BUNDLE_ROLLUP_COMPLETE,
41 | });
42 |
--------------------------------------------------------------------------------
/app/src/components/Card.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 |
4 | // import { addResult } from '../redux/actions/homeActions.js';
5 |
6 | class Card extends Component {
7 | constructor(props) {
8 | super(props);
9 | this.handleAddResult = this.handleAddResult.bind(this);
10 | }
11 | handleAddResult() {
12 | console.log('dispatch fired. current props for reference: ', this.props);
13 | // this.props.addResult(/*normally you would use 'e' or something here*/);
14 | }
15 | render() {
16 | return (
17 |
18 |
Webpack
19 |
20 |
21 | );
22 | }
23 | }
24 |
25 | const mapStateToProps = ({ results }) => ({
26 | results,
27 | });
28 | const mapDispatchToProps = dispatch => ({
29 | // normally you would use e in both the anonymous function and the invocation of the addresult function
30 | // addResult: () => dispatch(addResult('add this to the results')),
31 | });
32 |
33 | export default connect(
34 | mapStateToProps,
35 | mapDispatchToProps
36 | )(Card);
37 |
--------------------------------------------------------------------------------
/app/src/components/loaders/ImportLoader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | return (
5 |
6 |
16 |
17 | )
18 | };
19 |
--------------------------------------------------------------------------------
/app/src/styles/components/_bar-chart.scss:
--------------------------------------------------------------------------------
1 | .axis path,
2 | .axis line {
3 | fill: none;
4 | stroke: #000;
5 | shape-rendering: crispEdges;
6 | }
7 |
8 | .y0.axis path,
9 | .y0.axis line {
10 | stroke: #98abc5;
11 | }
12 |
13 | .y1.axis path,
14 | .y1.axis line {
15 | stroke: #d0743c;
16 | }
17 |
18 | .x.axis path,
19 | .x.axis line {
20 | stroke: orange;
21 | }
22 |
23 | .x.axis text {
24 | fill: orange;
25 | }
26 |
27 | .barchart{
28 | min-width: 1200px;
29 | min-height: 900px;
30 | margin: auto;
31 | display: flex;
32 | align-items: center;
33 | justify-content: center;
34 | flex-direction: column;
35 | margin-top: 88px;
36 | animation: fadein .8s;
37 | -moz-animation: fadein .8s; /* Firefox */
38 | -webkit-animation: fadein .8s; /* Safari and Chrome */
39 | -o-animation: fadein .8s; /* Opera */
40 | }
41 |
42 | .barchart_inner{
43 |
44 | margin: auto;
45 | margin-top: 20px;
46 | display: flex;
47 | align-items: flex-start;
48 | justify-content: center;
49 | // min-height: 780px;
50 | // flex-direction: column;
51 | }
52 |
53 | .barchart_btn{
54 |
55 | display: flex;
56 | align-items: center;
57 | justify-content: center;
58 | }
--------------------------------------------------------------------------------
/app/src/styles/components/_cube-loader.scss:
--------------------------------------------------------------------------------
1 | .cube-loader {
2 | width: 50px;
3 | height: 50px;
4 | margin-top: 3rem;
5 | margin-bottom: 10rem;
6 | }
7 | .cube-loader:before {
8 | content: '';
9 | width: 150px;
10 | height: 20px;
11 | background: #000;
12 | opacity: 0.1;
13 | position: absolute;
14 | top: 210px;
15 | margin-bottom: 1rem;
16 | border-radius: 50%;
17 | animation: shadow .5s linear infinite;
18 | }
19 | .cube-loader:after {
20 | content: '';
21 | width: 150px;
22 | height: 150px;
23 | background: #f4b01a;
24 | animation: animate .5s linear infinite;
25 | position: absolute;
26 | border-radius: 3px;
27 | }
28 | @keyframes animate {
29 | 17% {
30 | border-bottom-right-radius: 3px;
31 | }
32 | 25% {
33 | transform: translateY(9px) rotate(22.5deg);
34 | }
35 | 50% {
36 | transform: translateY(18px) scale(1, 0.9) rotate(45deg);
37 | border-bottom-right-radius: 40px;
38 | }
39 | 75% {
40 | transform: translateY(9px) rotate(67.5deg);
41 | }
42 | 100% {
43 | transform: translateY(0) rotate(90deg);
44 | }
45 | }
46 | @keyframes shadow {
47 | 0%,
48 | 100% {
49 | transform: scale(1, 1);
50 | }
51 | 50% {
52 | transform: scale(1.2, 1);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/parseEntryFromWebpack.js:
--------------------------------------------------------------------------------
1 | const babylon = require('babylon');
2 | const traverse = require('babel-traverse').default;
3 | const fs = require('fs');
4 | const path = require('path');
5 |
6 | module.exports = (res, { content }) => {
7 | try {
8 | let entry;
9 | const ast = babylon.parse(content, { sourceType: 'module' });
10 | traverse(ast, {
11 | Identifier: {
12 | enter(traversePath) {
13 | if (traversePath.node.name === 'entry') {
14 | let entryValueInWebpackConfig = content
15 | .slice(traversePath.parent.value.start + 1, traversePath.parent.value.end - 1)
16 | .trim();
17 | let entryValuePlusRootDir = path.join(res.rootDir, entryValueInWebpackConfig);
18 | if (fs.existsSync(entryValueInWebpackConfig)) {
19 | entry = entryValueInWebpackConfig;
20 | } else if (fs.existsSync(entryValuePlusRootDir)) {
21 | entry = entryValuePlusRootDir;
22 | }
23 | }
24 | },
25 | },
26 | });
27 | if (entry) {
28 | res.entry = entry;
29 | }
30 | return res;
31 | } catch (error) {
32 | return res;
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/backend/create-config/utils/indexFilesFromRoot.js:
--------------------------------------------------------------------------------
1 | const returnCurrentDirectoryFromPath = require('./indexFilesFromRootHelpers/returnCurrentDirectoryFromPath.js');
2 | const getAllFilesInCurrentDirectory = require('./indexFilesFromRootHelpers/getAllFilesInCurrentDirectory.js');
3 | const getInfoForWebpackConfigFromFileList = require('./createWebPackConfigHelpers/getInfoForWebpackConfigFromFileList.js');
4 | const tryAndSetEntryFromWebpackConfigEntry = require('./createWebPackConfigHelpers/parseEntryFromWebpack.js');
5 | const writeToFile = require('./file-and-system-actions/writeToFile.js');
6 |
7 | const pathFromDrag = process.argv[process.argv.length - 1];
8 |
9 | returnCurrentDirectoryFromPath(pathFromDrag)
10 | .then(rootDir => getAllFilesInCurrentDirectory(rootDir))
11 | .then(res => getInfoForWebpackConfigFromFileList(res))
12 | .then(res => tryAndSetEntryFromWebpackConfigEntry(res, res.webpackConfig))
13 | .then(res => writeToFile(res, 'configurationData.js', res))
14 | .then(res => {
15 | process.send({
16 | foundWebpackConfig: res.webpackConfig.exists,
17 | foundEntryFile: res.entry ? true : false,
18 | });
19 | process.exit();
20 | })
21 | .catch(e => {
22 | process.send({ e });
23 | process.exit();
24 | });
25 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
2 | const CSSExtract = new ExtractTextPlugin('bundle.css');
3 |
4 | module.exports = {
5 | watch: true,
6 | target: 'electron-renderer',
7 | entry: './app/src/index.js',
8 | output: {
9 | path: __dirname + '/app/build',
10 | publicPath: 'build/',
11 | filename: 'bundle.js',
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.jsx?$/,
17 | loader: 'babel-loader',
18 | options: {
19 | presets: ['react'],
20 | },
21 | },
22 | {
23 | test: /\.s?(c|a)ss$/,
24 | use: CSSExtract.extract({
25 | use: [
26 | {
27 | loader: 'css-loader',
28 | options: {
29 | sourceMap: true,
30 | },
31 | },
32 | {
33 | loader: 'sass-loader',
34 | options: {
35 | sourceMap: true,
36 | },
37 | },
38 | ],
39 | }),
40 | },
41 | {
42 | test: /\.(png|jpg|gif|svg)$/,
43 | loader: 'file-loader',
44 | query: {
45 | name: '[name].[ext]?[hash]',
46 | },
47 | },
48 | ],
49 | },
50 |
51 | plugins: [CSSExtract],
52 |
53 | resolve: {
54 | extensions: ['.js', '.json', '.jsx'],
55 | },
56 | };
57 |
--------------------------------------------------------------------------------
/app/src/components/OpenFolderButtons.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shell } from 'electron';
3 | const path = require('path');
4 | const upath = require('upath');
5 |
6 | export default ({ dirname }) => {
7 | const dists = ['webpack-dist', 'parcel-dist', 'rollup-dist'];
8 | let webpackDist, parcelDist, rollupDist;
9 | if (dirname) {
10 | [webpackDist, parcelDist, rollupDist] = dists.map(dist =>
11 | upath.normalize(path.join(dirname, 'electronUserData', dist, 'package.json'))
12 | );
13 | }
14 |
15 | return (
16 |
17 |
Download Files
18 |
26 |
35 |
44 |
45 | );
46 | };
47 |
48 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebpackConfig.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const getSavedProjectDataFromLocalFile = require('./createWebPackConfigHelpers/getSavedProjectDataFromFile.js');
4 | const setEntryFromDragPathIfDragPathExists = require('./createWebPackConfigHelpers/SetEntryFromDragPathIfDragPathExists.js');
5 | const createWebpackConfig = require('./createWebPackConfigHelpers/createConfigStringFromParams.js');
6 | const writeToFile = require('./file-and-system-actions/writeToFile.js');
7 |
8 | const pathToUserDataFolder = path.join(__dirname, '..', '..', '..', 'electronUserData');
9 | const pathToLocalFile = path.join(pathToUserDataFolder, 'configurationData.js');
10 |
11 | getSavedProjectDataFromLocalFile(pathToLocalFile)
12 | .then(res => setEntryFromDragPathIfDragPathExists(res, process.argv[process.argv.length - 1]))
13 | .then(res => createWebpackConfig(res))
14 | .then(res => writeToFile(res.webpackConfigString, 'webpack.config.js', res))
15 | .then(res =>
16 | writeToFile(res.webpackConfigString, path.join('webpack-dist', 'webpack.config.js'), res)
17 | )
18 | .then(res => writeToFile(res.webpackDependencies, path.join('webpack-dist', 'package.json'), res))
19 | .then(res => writeToFile(res, 'configurationData.js'))
20 | .then(() => {
21 | process.send({ webpackDirectory: pathToUserDataFolder });
22 | process.exit();
23 | })
24 | .catch(err => {
25 | process.send({ err });
26 | process.exit();
27 | });
28 |
--------------------------------------------------------------------------------
/app/src/styles/components/_buttons.scss:
--------------------------------------------------------------------------------
1 | // you can either define styling for specific components in these, or for specific types of elements. This example would be a bunch of classes for the different buttons you want on your page
2 |
3 |
4 |
5 | button {
6 | text-decoration:none;
7 | color: #777;
8 | text-transform:uppercase;
9 | padding:0;
10 | border: none;
11 | background:none;
12 | padding:10px 30px;
13 | display:inline-block;
14 | position:relative;
15 | z-index:1;
16 | transition-duration:0.3s;
17 | cursor:pointer;
18 | border: 2px solid transparent;
19 | border-radius: 50px;
20 | margin: 0 5px;
21 |
22 | }
23 |
24 | button:focus {
25 | outline: none;
26 | border: 2px solid #ff9900;
27 | border-radius: 50px;
28 |
29 | }
30 |
31 | button:hover {
32 | outline: none;
33 | border: 2px solid #ff9900;
34 | border-radius: 50px;
35 |
36 | }
37 | .btn_icon {
38 | height: 20px;
39 | margin-right: 12px;
40 | }
41 | .btn_icon_download_config {
42 | height: 16px;
43 | width: 16px;
44 | margin-left: 8px;
45 | margin-right: 8px;
46 | }
47 | .button_default{
48 | -webkit-transition-duration: 0.4s; /* Safari */
49 | border: 2px solid #008CBA;
50 | transition-duration: 0.4s;
51 | border-radius: 50px;
52 | }
53 |
54 | .button_default:hover {
55 | // background-color:#008CBA;
56 | color: black;
57 | border-radius: 50px;
58 | border: 2px solid #ff9900;
59 |
60 | }
61 |
62 | .modal_text {
63 | color: black !important;
64 | margin-bottom: 30px;
65 | }
--------------------------------------------------------------------------------
/app/src/components/OpenFolderOfOneButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shell } from 'electron';
3 | const path = require('path');
4 | const upath = require('upath');
5 |
6 | export default ({ dirname, bundleType }) => {
7 | console.log('inside helper func', dirname, bundleType);
8 | const dists = ['webpack-dist', 'parcel-dist', 'rollup-dist'];
9 | let webpackDist, parcelDist, rollupDist;
10 | if (dirname) {
11 | [webpackDist, parcelDist, rollupDist] = dists.map(dist =>
12 | upath.normalize(path.join(dirname, 'electronUserData', dist, 'package.json'))
13 | );
14 | }
15 |
16 | let dist_folder;
17 | let img;
18 |
19 | if (bundleType === 'webpack') {
20 | dist_folder = webpackDist;
21 | img = './assets/webpack_icon.png';
22 | } else if (bundleType === 'parcel') {
23 | dist_folder = parcelDist;
24 | img = './assets/parcel_icon.png';
25 | } else if (bundleType === 'rollup') {
26 | dist_folder = rollupDist;
27 | img = './assets/rollup_icon.png';
28 | }
29 |
30 |
31 | return (
32 |
33 | {(webpackDist || parcelDist || rollupDist) && (
34 |
35 |
45 |
46 | )}
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/app/src/redux/reducers/chartReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from '../actions/actionConstants.js';
2 | import * as chart from '../constants/chartProperties.js';
3 |
4 | const initialState = {
5 | screen: chart.SIZE,
6 | bundleType: chart.WEBPACK
7 | };
8 |
9 | const chartReducer = (state = initialState, action) => {
10 | let loadBuildingTime = state.screen;
11 |
12 | switch (action.type) {
13 | case types.DISPLAY_SIZES:
14 | return {
15 | ...state,
16 | screen: chart.SIZE};
17 | case types.DISPLAY_FACTORY_TIMES:
18 | return {
19 | ...state,
20 | screen: chart.FACTORY_TIME};
21 | case types.DISPLAY_BUILDING_TIMES:
22 | return {
23 | ...state,
24 | screen: chart.BUILDING_TIME};
25 |
26 | case types.DISPLAY_WEBPACK:
27 | return {
28 | ...state,
29 | bundleType: chart.WEBPACK};
30 | case types.DISPLAY_PARCEL:
31 | if (state.screen === chart.FACTORY_TIME) {
32 | loadBuildingTime = chart.BUILDING_TIME;
33 | }
34 | return {
35 | ...state,
36 | bundleType: chart.PARCEL,
37 | screen: loadBuildingTime};
38 | case types.DISPLAY_ROLLUP:
39 |
40 | if (state.screen === chart.FACTORY_TIME) {
41 | loadBuildingTime = chart.BUILDING_TIME;
42 | }
43 | return {
44 | ...state,
45 | bundleType: chart.ROLLUP,
46 | screen: loadBuildingTime};
47 | case types.DISPLAY_TOTALS:
48 | return {
49 | ...state,
50 | bundleType: chart.TOTALS};
51 |
52 |
53 | default:
54 | return state;
55 | }
56 |
57 | };
58 |
59 | export default chartReducer;
60 |
61 |
--------------------------------------------------------------------------------
/backend/create-config/utils/parcelBundleHelpers/parcelBundler.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const Bundler = require('parcel-bundler');
3 | const fs = require('fs');
4 |
5 | const parcelEntryFile = process.argv[2];
6 | const rootDir = process.argv[3];
7 | const pathToWriteStatsFile = process.argv[4];
8 | const outputDir = process.argv[5];
9 | // hack for production mode
10 | process.env.NODE_ENV = 'production';
11 |
12 | // hack to change node.js directory
13 | process.chdir(rootDir);
14 | // define the bundler
15 | const bundler = new Bundler(parcelEntryFile, { detailedReport: true, outDir: outputDir });
16 | bundler.bundle();
17 |
18 | // on bundler completion, gather size and timing information
19 | bundler.on('bundled', mainBundle => {
20 | /* one main bundle, and (possibly) multiple child bundles from the main bundle, each of which contain assets that need conversion. */
21 | /* we're only going one level deep, without recursively going through the child bundles. */
22 | const bundles = [mainBundle].concat(Array.from(mainBundle.childBundles));
23 |
24 | const bundleOutputs = bundles.map(bundle =>
25 | Array.from(bundle.assets).map(asset => ({
26 | name: path.relative(rootDir, asset.name),
27 | buildTime: asset.buildTime,
28 | bundledSize: asset.bundledSize,
29 | }))
30 | );
31 |
32 | const output = bundleOutputs.reduce((acc, arr) => acc.concat(arr));
33 |
34 | output.forEach(asset => {
35 | asset.relativeName = asset.relativeName;
36 | });
37 |
38 | fs.writeFileSync(pathToWriteStatsFile, JSON.stringify({totalBundleSize: mainBundle.totalSize, totalElapsedTime: mainBundle.bundleTime, files: output}, null, 2));
39 | });
40 |
--------------------------------------------------------------------------------
/backend/create-config/utils/runRollup.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const getSavedProjectDataFromFile = require('./createWebPackConfigHelpers/getSavedProjectDataFromFile.js');
4 | const createRollupRulesFromFileTypes = require('./rollupConfigHelpers/createRollupRulesFromFileTypes.js');
5 | const createRollupConfigFromParams = require('./rollupConfigHelpers/createRollupConfigFromParams.js');
6 | const runRollupFromUsersRoot = require('./rollupConfigHelpers/runRollupFromUsersRoot.js');
7 | const copyFilesToElectron = require('./rollupConfigHelpers/copyFilesToElectron.js');
8 | const deleteCreatedFilesFromUsersRoot = require('./rollupConfigHelpers/deleteCreatedFilesFromUsersRoot.js');
9 | const writeToFile = require('./file-and-system-actions/writeToFile.js');
10 |
11 | // const pathToWriteStats = process.argv[process.argv.length - 1];
12 | const pathToUserDataFolder = path.join(__dirname, '..', '..', '..', 'electronUserData');
13 | const pathToLocalFile = path.join(pathToUserDataFolder, 'configurationData.js');
14 |
15 | getSavedProjectDataFromFile(pathToLocalFile)
16 | .then(res => createRollupRulesFromFileTypes(res))
17 | .then(res => createRollupConfigFromParams(res))
18 | .then(res =>
19 | writeToFile(res.rollupConfigString, path.join('rollup-dist', 'rollup.config.js'), res)
20 | )
21 | .then(res => writeToFile(res.rollupDependencies, path.join('rollup-dist', 'package.json'), res))
22 | .then(res => runRollupFromUsersRoot(res))
23 | .then(res => copyFilesToElectron(res))
24 | .then(res => deleteCreatedFilesFromUsersRoot(res))
25 | .then(res => {
26 | writeToFile(res, 'configurationData.js');
27 | })
28 | .then(() => {
29 | process.send('');
30 | process.exit();
31 | })
32 | .catch(err => {
33 | process.send({ err });
34 | process.exit();
35 | });
36 |
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/createRollupConfigFromParams.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const upath = require('upath');
4 |
5 | const sizeTimingPluginPath = upath.normalize(
6 | path.join(__dirname, 'custom_plugin', 'size_timing.js')
7 | );
8 |
9 | module.exports = res =>
10 | new Promise((resolve, reject) => {
11 | let { plugins, modules } = res.rollupRules;
12 |
13 | modules = modules.reduce(
14 | (acc, rule) =>
15 | acc +
16 | `const ${rule.variableName} = require('${upath.normalize(
17 | require.resolve(rule.packageName)
18 | )}');` +
19 | '\n',
20 | ''
21 | );
22 | plugins = plugins.reduce((acc, plugin) => acc + `${plugin}` + '\n', '');
23 | const localRollupConfigSavePath = upath.normalize(
24 | path.join(__dirname, '..', '..', '..', '..', 'electronUserData', 'rollup.config.js')
25 | );
26 | const usersRollupConfigSavePath = upath.normalize(
27 | path.join(res.rootDir, 'bundle-bee-rollup.config.js')
28 | );
29 | const configString = `
30 | ${modules}
31 | const sizeTimingPlugin = require('${sizeTimingPluginPath}');
32 |
33 | module.exports = {
34 | input: '${upath.normalize(res.entry)}',
35 | output: {
36 | file: './bundle-bee-rollup-dist/bundle.js',
37 | format: 'iife',
38 | },
39 | plugins: [${plugins} uglify.uglify(), sizeTimingPlugin()]
40 | };
41 | `;
42 | res.rollupDependencies = JSON.stringify({
43 | devDependencies: res.rollupDependencies,
44 | });
45 | res.rollupConfigString = configString;
46 | fs.writeFile(localRollupConfigSavePath, configString, err => {
47 | if (err) reject(err);
48 | fs.writeFile(usersRollupConfigSavePath, configString, err => {
49 | if (err) reject(err);
50 | resolve(res);
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/app/src/styles/components/_chart.scss:
--------------------------------------------------------------------------------
1 |
2 |
3 | .wrapper{
4 | text-align:center;
5 | margin:50px auto;
6 | }
7 |
8 |
9 | .tabs1{
10 | // margin-top:10px;
11 | font-size:15px;
12 | padding: 3px 0px;
13 | list-style:none;
14 | background:#fff;
15 | box-shadow:0px 5px 20px rgba(0,0,0,0.1);
16 | // display:inline-block;
17 | border-radius:50px;
18 | position:relative;
19 | margin-top: 4px;
20 | display: flex;
21 | align-content: center;
22 | justify-content: flex-start;
23 | /* This section calls the slideInFromLeft animation we defined above */
24 | animation: .5s ease-out 0s 1 slideInFromLeft;
25 |
26 |
27 | }
28 |
29 | .tabs2{
30 | // margin-top:10px;
31 | font-size:15px;
32 | padding: 3px 0px;
33 | list-style:none;
34 | background:#fff;
35 | box-shadow:0px 5px 20px rgba(0,0,0,0.1);
36 | // display:inline-block;
37 | border-radius:50px;
38 | position:relative;
39 | margin-top: 4px;
40 | display: flex;
41 | align-content: center;
42 | justify-content: flex-start;
43 | /* This section calls the slideInFromLeft animation we defined above */
44 | animation: .5s ease-out 0s 1 slideInFromRight;
45 |
46 |
47 | }
48 |
49 | .rounded_div{
50 | margin-left: 40px;
51 | min-width: 100px;
52 | min-height: 100px;
53 | padding:5px;
54 | list-style:none;
55 | background:#fff;
56 | box-shadow:0px 5px 20px rgba(0,0,0,0.1);
57 | border-radius:30px;
58 | position:relative;
59 | margin-top: 20px;
60 | display: flex;
61 | // align-self: center;
62 | align-items:center;
63 | flex-direction: column;
64 | padding: 20px;
65 | justify-content: space-between
66 | }
67 |
68 | .rounded_div button{
69 | justify-self: flex-start;
70 | }
71 |
72 |
73 | .rounded_div button img{
74 | justify-self: flex-start;
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/app/src/redux/actions/dataActions.js:
--------------------------------------------------------------------------------
1 | import * as types from './actionConstants.js';
2 | import * as d3 from 'd3';
3 | import {
4 | parseWebpackOutput,
5 | parseParcelOutput,
6 | parseRollupOutput,
7 | } from '../../utils/dataParser.js';
8 |
9 | export const retrieveWebpackStats = bundleDir => {
10 | return function(dispatch) {
11 | d3.json('../electronUserData/stats.json')
12 | .then(function(data) {
13 | console.log(data);
14 |
15 | const parsedData = parseWebpackOutput(data, bundleDir);
16 | dispatch({ type: types.BUNDLE_WEBPACK_COMPLETE, payload: parsedData });
17 | })
18 | .catch(function(error) {
19 | alert('error:');
20 | console.log(error);
21 | });
22 | };
23 | };
24 |
25 | export const retrieveParcelStats = bundleDir => {
26 | return function(dispatch) {
27 | d3.json('../electronUserData/parcel-stats.json')
28 | .then(function(data) {
29 | console.log(data);
30 |
31 | const parsedData = parseParcelOutput(data, bundleDir);
32 | dispatch({ type: types.BUNDLE_PARCEL_COMPLETE, payload: parsedData });
33 | })
34 | .catch(function(error) {
35 | alert('error:');
36 | console.log(error);
37 | });
38 | };
39 | };
40 |
41 | export const retrieveRollupStats = bundleDir => {
42 | return function(dispatch) {
43 | Promise.all(
44 | ['../electronUserData/rollup-stats.json', '../electronUserData/rollup-totals-stats.json'].map(
45 | x => d3.json(x)
46 | )
47 | )
48 | .then(function(dataArray) {
49 | const data = {
50 | files: dataArray[0],
51 | totalElapsedTime: dataArray[1].totalElapsedTime,
52 | totalBundleSize: dataArray[1].totalBundleSize,
53 | };
54 |
55 | console.log('retrieverollupstats data: ', data);
56 |
57 | const parsedData = parseRollupOutput(data);
58 | console.log(parsedData, 'ROLLUP PARSED');
59 |
60 | dispatch({ type: types.BUNDLE_ROLLUP_COMPLETE, payload: parsedData });
61 | })
62 | .catch(function(error) {
63 | alert('error:');
64 | console.log(error);
65 | });
66 | };
67 | };
68 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/createConfigStringFromParams.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const upath = require('upath');
3 | const util = require('util');
4 | const createRules = require('./create-rules.js');
5 |
6 | const pathToOurTemplate = path.join(__dirname, 'template.html');
7 |
8 | const reformatPath = file => {
9 | if (process.platform === 'win32') {
10 | return file.replace(/\//g, '\\\\');
11 | }
12 | return file;
13 | }
14 |
15 | module.exports = res => {
16 | let { entry, extensions, indexHtmlPath, rootDir } = res;
17 | const importantExtensions = ['.js', '.jsx', '.css', '.sass', '.scss', '.less'];
18 | const extensionsToResolve = extensions.reduce((acc, x) => {
19 | if (!acc.includes(x) && importantExtensions.includes(x)) acc.push(`'${x}'`);
20 | return acc;
21 | }, []);
22 | indexHtmlPath = indexHtmlPath
23 | ? `{title: 'template', template: '${indexHtmlPath}'}`
24 | : `{title: 'template', template: '${pathToOurTemplate}'}`;
25 | // util.inspect preserves regex (unlike) JSON.stringify. showHidden : false allows for deeply nested objects
26 | let { rules, dependencies } = createRules(extensions);
27 | res.webpackDependencies = JSON.stringify({
28 | devDependencies: dependencies,
29 | });
30 | // rules = util.inspect(rules, { showHidden: false, depth: null });
31 | const output = path.join(__dirname, '..', '..', '..', '..', 'electronUserData', 'webpack-dist');
32 | res.webpackConfigString = `
33 | const path = require('${require.resolve('path')}');
34 |
35 | const HtmlWebpackPlugin = require('${upath.normalize(require.resolve('html-webpack-plugin'))}');
36 | const MiniCssExtractPlugin = require('${upath.normalize(require.resolve('mini-css-extract-plugin'))}');
37 |
38 | module.exports = {
39 | entry: '${upath.normalize(entry)}',
40 | context: '${reformatPath(upath.normalize(rootDir))}',
41 | output: {
42 | path: '${reformatPath(upath.normalize(output))}',
43 | filename: 'bundle.js',
44 | },
45 | module: {
46 | rules:[${rules}],
47 | },
48 | devServer: {
49 | contentBase: path.join(__dirname, 'webpack-dist'),
50 | },
51 | plugins: [
52 | new HtmlWebpackPlugin(${upath.normalize(indexHtmlPath)}),
53 | new MiniCssExtractPlugin('bundle.css')
54 | ],
55 | resolve: {
56 | extensions: ${extensionsToResolve.length ? '[' + extensionsToResolve + ']' : ''},
57 | },
58 | };`;
59 | return res;
60 | };
61 |
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/getInfoForWebpackConfigFromFileList.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const upath = require('upath');
4 | function packageJSONExistsInDir(fileEntry, rootDir) {
5 | return fileEntry.name === 'package.json' && fileEntry.fullParentDir === rootDir;
6 | }
7 |
8 | module.exports = files => {
9 | const rootDir = files[0] ? files[0].fullParentDir : null;
10 | const webpackConfig = { exists: false, path: null, content: null };
11 | let entry;
12 | let entryIsInRoot;
13 | let indexHtmlPath;
14 | let filePaths = files.reduce((files, fileInfo) => {
15 | // check if entry file is in root of project
16 | if (!entryIsInRoot && packageJSONExistsInDir(fileInfo, rootDir)) entryIsInRoot = true;
17 | let { name, fullPath, fullParentDir } = fileInfo;
18 | fullPath = upath.normalize(fullPath);
19 | fullParentDir = upath.normalize(fullParentDir);
20 | // TODO prompt if multiple webpack configs found
21 | // check for webpack config outside of node modules
22 | if (
23 | name === 'webpack.config.js' &&
24 | !fullPath.includes('/node_modules/') &&
25 | !webpackConfig.exists
26 | ) {
27 | webpackConfig.exists = true;
28 | webpackConfig.path = fullPath;
29 | webpackConfig.content = fs.readFileSync(fullPath, 'utf-8');
30 | webpackConfig.name = name;
31 | webpackConfig.dir = fullParentDir;
32 | }
33 | {
34 | if (
35 | name === 'index.html' &&
36 | !fullPath.includes('/node_modules/') &&
37 | !indexHtmlPath &&
38 | !fullPath.includes('/dist/') &&
39 | !fullPath.includes('/build/')
40 | )
41 | indexHtmlPath = fullPath;
42 | }
43 | // make sure /src/ is in the root of the project (name should be src/index.js when you remove src/index.js)
44 | if (
45 | (fullPath.includes('/src/index.js') && fullPath.replace('/src/index.js', '') === rootDir) ||
46 | (fullPath.includes('\\src\\index.js') && fullPath.replace('\\src\\index.js', '') === rootDir)
47 | ) {
48 | entry = fullPath;
49 | }
50 | return files.concat(name);
51 | }, []);
52 | const extensions = files
53 | .map(file => path.extname(file.name))
54 | .reduce((acc, ext) => (acc.includes(ext) ? acc : acc.concat(ext)), []);
55 |
56 | return {
57 | webpackConfig,
58 | entryIsInRoot,
59 | indexHtmlPath,
60 | extensions,
61 | filePaths,
62 | rootDir,
63 | entry,
64 | };
65 | };
66 |
--------------------------------------------------------------------------------
/backend/menuBar.js:
--------------------------------------------------------------------------------
1 | const shell = require('electron').shell;
2 | const path = require('path');
3 | const upath = require('path');
4 | const fs = require('fs');
5 |
6 | const dists = ['webpack-dist', 'parcel-dist', 'rollup-dist'];
7 | let webpackDist, parcelDist, rollupDist;
8 | [webpackDist, parcelDist, rollupDist] = dists.map(dist =>
9 | upath.normalize(path.join(__dirname, '..', 'electronUserData', dist, 'package.json'))
10 | );
11 |
12 | function getStatus(dist) {
13 | const isEnabled = fs.existsSync(dist);
14 | console.log('getStatus ran: ', isEnabled);
15 | return isEnabled;
16 | }
17 |
18 | module.exports = function createMenuBar(mainWindow, ResetDir, OpenDir) {
19 |
20 | const menuBar = [
21 | {
22 | label: 'File',
23 | submenu: [
24 | {
25 | label: 'Open Root Directory',
26 | accelerator: 'CmdOrCtrl+O',
27 | click() {
28 | OpenDir();
29 | }
30 | },
31 | {
32 | label: 'Reset Directory',
33 | accelerator: 'CmdOrCtrl+D',
34 | click() {
35 | ResetDir();
36 | },
37 | }
38 | ],
39 | },
40 | {
41 | label: 'View Config',
42 | submenu: [
43 | {
44 | label: 'Show Webpack Config',
45 | accelerator: 'CmdOrCtrl+W',
46 | click() {
47 | console.log('clicked: webpack');
48 | shell.showItemInFolder(webpackDist);
49 | },
50 | },
51 | {
52 | label: 'Show Parcel Config',
53 | accelerator: 'CmdOrCtrl+P',
54 | click() {
55 | shell.showItemInFolder(parcelDist);
56 | },
57 | },
58 | {
59 | label: 'Show Rollup Config',
60 | accelerator: 'CmdOrCtrl+R',
61 | click() {
62 | shell.showItemInFolder(rollupDist);
63 | },
64 | },
65 | ],
66 | },
67 |
68 | {
69 | role: 'window',
70 | submenu: [{ role: 'minimize' }, { role: 'close' }],
71 | },
72 | {
73 | label: 'Developer',
74 | submenu: [
75 | {
76 | label: 'Toggle Developer Tools',
77 | accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
78 | click() {
79 | mainWindow.webContents.toggleDevTools();
80 | },
81 | },
82 | ],
83 | },
84 | {
85 | role: 'help',
86 | submenu: [
87 | {
88 | label: 'Learn More',
89 | click() {
90 | require('electron').shell.openExternal('https://github.com/bundlebee/bundle-bee');
91 | },
92 | },
93 | ],
94 | },
95 | ];
96 |
97 | return menuBar;
98 | };
99 |
--------------------------------------------------------------------------------
/backend/create-config/utils/runParcel.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { exec } = require('child_process');
3 | const getSavedProjectDataFromFile = require('./createWebPackConfigHelpers/getSavedProjectDataFromFile.js');
4 | const addParcelDependenciesToRes = require('./parcelBundleHelpers/addParcelDependenciesToUserDataObject.js');
5 | const writeToFile = require('./file-and-system-actions/writeToFile.js');
6 | const validator = require('html-validator');
7 | const fs = require('fs');
8 |
9 | const rootDir = process.argv[2];
10 | const pathToWriteStatsFile = process.argv[3];
11 | const outputDir = path.join(path.dirname(pathToWriteStatsFile), 'parcel-dist');
12 | const pathToSavedData = path.join(
13 | __dirname,
14 | '..',
15 | '..',
16 | '..',
17 | 'electronUserData',
18 | 'configurationData.js'
19 | );
20 |
21 | getSavedProjectDataFromFile(pathToSavedData)
22 | .then(res => addParcelDependenciesToRes(res))
23 | .then(res => writeToFile(res.parcelDependencies, path.join('parcel-dist', 'package.json'), res))
24 | .then(res => {
25 | const options = {
26 | format: 'text',
27 | };
28 | const parcelBundlerProcess = path.join(__dirname, 'parcelBundleHelpers', 'parcelBundler.js');
29 | let entry;
30 | if (res.indexHtmlPath) {
31 | fs.readFile(res.indexHtmlPath, 'utf8', (err, html) => {
32 | if (err) console.log(err);
33 | options.data = html;
34 | validator(options).then(data => {
35 | if (!data.includes('Error:')) {
36 | entry = res.indexHtmlPath;
37 | console.log('cwd: ', process.cwd());
38 | console.log('parcel command: ');
39 | console.log(
40 | `node ${parcelBundlerProcess} ${entry} ${rootDir} ${pathToWriteStatsFile} ${outputDir}`
41 | );
42 | exec(
43 | `node ${parcelBundlerProcess} ${entry} ${rootDir} ${pathToWriteStatsFile} ${outputDir}`,
44 | null,
45 | error => {
46 | if (error) process.send({ error });
47 | else {
48 | process.send('');
49 | process.exit();
50 | }
51 | }
52 | );
53 | }
54 | });
55 | });
56 | }
57 | entry = res.entry;
58 | console.log('cwd: ', process.cwd());
59 | console.log('parcel command: ');
60 | console.log(
61 | `node ${parcelBundlerProcess} ${entry} ${rootDir} ${pathToWriteStatsFile} ${outputDir}`
62 | );
63 | exec(
64 | `node ${parcelBundlerProcess} ${entry} ${rootDir} ${pathToWriteStatsFile} ${outputDir}`,
65 | null,
66 | error => {
67 | if (error) process.send({ error });
68 | else {
69 | process.send('');
70 | process.exit();
71 | }
72 | }
73 | );
74 | })
75 | .catch(e => console.log(e));
76 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bundle-bee",
3 | "version": "1.0.2",
4 | "description": "\"A minimal Electron App to compare bundles\"",
5 | "main": "main_process.js",
6 | "jest": {
7 | "verbose": false,
8 | "testURL": "http://localhost/"
9 | },
10 | "scripts": {
11 | "bundle": "webpack --mode development",
12 | "serve": "electron .",
13 | "test": "jest",
14 | "start": "npm-run-all --parallel bundle serve",
15 | "build": "webpack -p --env production",
16 | "electron": "electron .",
17 | "dev": "webpack-dev-server --mode development",
18 | "electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http: //localhost:3000 \""
19 | },
20 | "author": "Goldberg, Mariano, Modras, Morrell",
21 | "repository": "bundle-bee/bundle-bee",
22 | "license": "WTFPL",
23 | "devDependencies": {
24 | "ajv-keywords": "^3.2.0",
25 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
26 | "concurrently": "^3.6.1",
27 | "electron": "^9.1.0",
28 | "electron-packager": "^12.1.0",
29 | "extract-text-webpack-plugin": "^4.0.0-beta.0",
30 | "jest": "^23.4.2",
31 | "npm-run-all": "^4.1.2",
32 | "webpack-dev-server": "^3.1.5"
33 | },
34 | "dependencies": {
35 | "babel": "^6.23.0",
36 | "babel-core": "^6.26.3",
37 | "babel-loader": "^7.1.5",
38 | "babel-preset-env": "^1.7.0",
39 | "babel-preset-es2015-rollup": "^3.0.0",
40 | "babel-preset-react": "^6.24.1",
41 | "babel-preset-stage-0": "^6.24.1",
42 | "babel-traverse": "^6.26.0",
43 | "babylon": "^6.18.0",
44 | "copy-dir": "^0.4.0",
45 | "css-loader": "^0.28.10",
46 | "d3": "^5.5.0",
47 | "file-loader": "^1.1.11",
48 | "gsap": "^2.0.1",
49 | "html-validator": "^3.0.5",
50 | "html-webpack-plugin": "^3.2.0",
51 | "image-webpack-loader": "^4.3.1",
52 | "less-loader": "^4.1.0",
53 | "mini-css-extract-plugin": "^0.4.1",
54 | "node-sass": "^4.9.2",
55 | "nodemon": "^1.18.3",
56 | "parcel": "^1.9.7",
57 | "parcel-bundler": "^1.9.7",
58 | "path": "^0.12.7",
59 | "react": "^16.4.1",
60 | "react-dom": "^16.4.1",
61 | "react-modal": "^3.5.1",
62 | "react-redux": "^5.0.7",
63 | "react-tooltip": "^3.6.1",
64 | "redux": "^4.0.0",
65 | "redux-devtools-extension": "^2.13.5",
66 | "redux-thunk": "^2.3.0",
67 | "rimraf": "^2.6.2",
68 | "rollup": "^0.64.1",
69 | "rollup-plugin-babel": "^3.0.7",
70 | "rollup-plugin-commonjs": "^9.1.4",
71 | "rollup-plugin-fill-html": "^1.1.0",
72 | "rollup-plugin-image": "^1.0.2",
73 | "rollup-plugin-json": "^3.0.0",
74 | "rollup-plugin-node-resolve": "^3.3.0",
75 | "rollup-plugin-replace": "^2.0.0",
76 | "rollup-plugin-sass": "^0.9.2",
77 | "rollup-plugin-scss": "^0.4.0",
78 | "rollup-plugin-uglify": "^4.0.0",
79 | "sass-loader": "^7.0.3",
80 | "style-loader": "^0.21.0",
81 | "upath": "^1.1.0",
82 | "wait-on": "^2.1.0",
83 | "webpack": "^4.16.2",
84 | "webpack-cli": "^3.1.0"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/components/DropZone.jsx:
--------------------------------------------------------------------------------
1 | import { ipcRenderer } from 'electron';
2 | import React, { Component } from 'react';
3 |
4 | import { connect } from 'react-redux';
5 | import { loadModal, loadBundle } from '../redux/actions/homeActions';
6 |
7 | class DropZone extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | className: 'drop-zone-hide',
12 | };
13 | this._onDragEnter = this._onDragEnter.bind(this);
14 | this._onDragLeave = this._onDragLeave.bind(this);
15 | this._onDragOver = this._onDragOver.bind(this);
16 | this._onDrop = this._onDrop.bind(this);
17 | }
18 |
19 | componentDidMount() {
20 | window.addEventListener('mouseup', this._onDragLeave);
21 | window.addEventListener('dragenter', this._onDragEnter);
22 | window.addEventListener('dragover', this._onDragOver);
23 | document.getElementById('dragbox').addEventListener('dragleave', this._onDragLeave);
24 | window.addEventListener('drop', this._onDrop);
25 | }
26 |
27 | componentWillUnmount() {
28 | window.removeEventListener('mouseup', this._onDragLeave);
29 | window.removeEventListener('dragenter', this._onDragEnter);
30 | window.addEventListener('dragover', this._onDragOver);
31 | document.getElementById('dragbox').removeEventListener('dragleave', this._onDragLeave);
32 | window.removeEventListener('drop', this._onDrop);
33 | }
34 |
35 | _onDragEnter(e) {
36 | console.log('entered');
37 | this.setState({ className: 'drop-zone-show' });
38 | e.stopPropagation();
39 | e.preventDefault();
40 | return false;
41 | }
42 |
43 | _onDragOver(e) {
44 | e.preventDefault();
45 | e.stopPropagation();
46 | return false;
47 | }
48 |
49 | _onDragLeave(e) {
50 | this.setState({ className: 'drop-zone-hide' });
51 | e.stopPropagation();
52 | e.preventDefault();
53 | return false;
54 | }
55 |
56 | _onDrop(e) {
57 | e.preventDefault();
58 | // Upload files
59 |
60 | let files = e.dataTransfer.files;
61 | const { path } = files[0];
62 | if (this.props.isEntry) {
63 | ipcRenderer.send('run-webpack', { pathFromDrag: path, createNewConfig: true });
64 | this.props.loadBundle();
65 | } else {
66 | ipcRenderer.send('index-project-files-from-dropped-item-path', path);
67 | this.props.loadModal();
68 | }
69 |
70 | return false;
71 | }
72 | render() {
73 | return (
74 |
75 | {this.props.children}
76 |
77 |
Drop Here to Upload
78 |
79 |
80 | );
81 | }
82 | }
83 |
84 | const mapDispatchToProps = dispatch => ({
85 | loadModal: () => dispatch(loadModal()),
86 | loadBundle: () => dispatch(loadBundle()),
87 | });
88 |
89 | const mapStateToProps = state => ({ home: state.home });
90 |
91 | export default connect(
92 | mapStateToProps,
93 | mapDispatchToProps
94 | )(DropZone);
95 |
--------------------------------------------------------------------------------
/app/src/components/Chart.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import D3StarBurstChart from './data_viz/D3StarBurstChart.jsx';
3 | import BarChart from './data_viz/BarChart.jsx';
4 | import { connect } from 'react-redux';
5 |
6 | import {
7 | displayWebpack,
8 | displayParcel,
9 | displayRollup,
10 | displayTotals,
11 | } from '../redux/actions/chartActions.js';
12 | import DisplayButton from './data_viz/helper_components/DisplayButton.jsx';
13 |
14 | import * as chart from '../redux/constants/chartProperties.js';
15 |
16 | class Chart extends Component {
17 | constructor(props) {
18 | super(props);
19 | }
20 |
21 | render() {
22 | console.log('props: ', this.props);
23 | return (
24 |
25 |
62 |
63 | {this.props.chart.bundleType === chart.TOTALS ? (
64 |
65 |
67 |
68 | ) : (
69 |
70 |
71 |
72 | {/* && */}
73 |
74 | )}
75 |
76 | );
77 | }
78 | }
79 |
80 | const mapDispatchToProps = dispatch => ({
81 | displayWebpack: () => dispatch(displayWebpack()),
82 | displayParcel: () => dispatch(displayParcel()),
83 | displayRollup: () => dispatch(displayRollup()),
84 | displayTotals: () => dispatch(displayTotals()),
85 | });
86 |
87 | const mapStateToProps = state => ({ chart: state.chart, data: state.data });
88 |
89 | export default connect(
90 | mapStateToProps,
91 | mapDispatchToProps
92 | )(Chart);
93 |
--------------------------------------------------------------------------------
/app/src/components/ModalPrompt.jsx:
--------------------------------------------------------------------------------
1 | import { ipcRenderer } from 'electron';
2 | import React, { Component } from 'react';
3 | import Modal from 'react-modal';
4 | import { loadBundle } from '../redux/actions/homeActions';
5 | import { connect } from 'react-redux';
6 |
7 | import ReactTooltip from 'react-tooltip'
8 |
9 |
10 | const customStyles = {
11 | content: {
12 | top: '50%',
13 | left: '50%',
14 | minWidth: '600px',
15 | minHeight: '320px',
16 | right: 'auto',
17 | bottom: 'auto',
18 | marginRight: '-50%',
19 | padding: '.5rem',
20 | transform: 'translate(-50%, -50%)',
21 | boxShadow: '0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22)',
22 | justifyContent: 'center',
23 | backgroundColor: 'rgba(255,255,255,0.0)',
24 | alignItems: 'center',
25 | display: 'flex',
26 | flexDirection: 'column',
27 | color: 'black',
28 | },
29 | };
30 |
31 | // Make sure to bind modal to your appElement (http://reactcommunity.org/react-modal/accessibility/)
32 | Modal.setAppElement('#app');
33 |
34 | class ModalPrompt extends Component {
35 | constructor() {
36 | super();
37 | this.state = {
38 | modalIsOpen: true,
39 | };
40 | this.openModal = this.openModal.bind(this);
41 | this.afterOpenModal = this.afterOpenModal.bind(this);
42 | this.closeModal = this.closeModal.bind(this);
43 | }
44 |
45 | componentWillReceiveProps(nextProps) {
46 | this.setState({ modalIsOpen: true });
47 | }
48 |
49 | openModal() {
50 | this.setState({ modalIsOpen: true });
51 | }
52 |
53 | afterOpenModal() {
54 | this.subtitle.style.color = '#1B6453';
55 | }
56 |
57 | closeModal() {
58 | this.setState({ modalIsOpen: false });
59 | }
60 |
61 | render() {
62 | return (
63 |
64 |
71 | (this.subtitle = subtitle)} className="modal_text">Choose an option to continue:
72 |
81 |
82 |
91 |
92 |
93 |
94 | );
95 | }
96 | }
97 |
98 | const mapDispatchToProps = dispatch => ({
99 | loadBundle: (loaded) => dispatch(loadBundle(loaded))
100 | });
101 | export default connect(
102 | null,
103 | mapDispatchToProps
104 | )(ModalPrompt);
105 |
--------------------------------------------------------------------------------
/app/src/styles/components/_d3.scss:
--------------------------------------------------------------------------------
1 | .sb_d3_container {
2 | justify-content: center;
3 | display: flex;
4 | }
5 |
6 | .sb_d3_container button {
7 | margin: 0 1rem;
8 | width: 12rem;
9 | background-color: rgb(11, 67, 124);
10 | }
11 |
12 | #sb_d3_explanation {
13 | position: relative;
14 | margin-top: 2rem;
15 | margin-bottom: -9rem;
16 | text-align: center;
17 | }
18 |
19 | #sb_d3_percentage {
20 | }
21 |
22 | #sb_d3_fileNames {
23 | color: blue;
24 | }
25 |
26 | .sb_d3_box {
27 | margin-top: 58px;
28 |
29 | }
30 |
31 | #svgStarBurst {
32 | margin: auto;
33 | display: block;
34 | animation: fadein 2s;
35 | -moz-animation: fadein .8s; /* Firefox */
36 | -webkit-animation: fadein .8s; /* Safari and Chrome */
37 | -o-animation: fadein .8s; /* Opera */
38 | }
39 |
40 | .ar-chart {
41 | margin: auto;
42 | display: block;
43 | justify-content: center;
44 | display: flex;
45 | background-color: blue;
46 | margin-bottom: 100px;
47 | }
48 |
49 | .d3_btn_highligthed {
50 | -webkit-transition-duration: 0.1s; /* Safari */
51 | border: 2px solid #ff9900;
52 | transition-duration: 0.1s;
53 | border-radius: 50px;
54 | }
55 |
56 | .d3_btn_default {
57 | background-color: Transparent;
58 | background-repeat:no-repeat;
59 | border: none;
60 | border: 2px solid transparent;
61 | }
62 |
63 | .d3_btn_disabled {
64 | color: gray;
65 | text-decoration: line-through underline;
66 | }
67 |
68 | #sequence {
69 | width: 1200px;
70 | height: 0px;
71 | }
72 |
73 | #sequence text, #legend text {
74 | fill: #fff;
75 | }
76 |
77 | .chart {
78 | min-width: 1200px;
79 | // border: 1px solid black;
80 | // border-left: 1px solid black;
81 | // border-right: 1px solid black;
82 | margin: auto;
83 | display: flex;
84 | align-items: center;
85 | justify-content: center;
86 | }
87 |
88 | h1 .d3_title {
89 | font-size: $l-size;
90 | color: darkslategray;
91 | }
92 |
93 | .d3_tooltip {
94 | background: rgba(255, 255, 255, 0.8);
95 | color: #333;
96 | font-size: 12px;
97 | left: 130px;
98 | padding: 18px;
99 | position: absolute;
100 | text-align: left;
101 | top: 95px;
102 | width: 200px;
103 | z-index: 10;
104 | border-radius: 10px;
105 | // box-shadow: 0 0 0 0, 1px 1px 6px 4px rgba(10, 10, 0, 0.055);
106 | // opacity: 0;
107 | }
108 |
109 |
110 | .d3_tooltip_bar_chart {
111 | background: rgba(255, 255, 255, 0.8);
112 | color: #333;
113 | font-size: 12px;
114 | left: 130px;
115 | padding: 18px;
116 | position: absolute;
117 | text-align: left;
118 | top: 95px;
119 | width: 150px;
120 | z-index: 10;
121 | border-radius: 10px;
122 | // box-shadow: 0 0 0 0, 1px 1px 6px 4px rgba(10, 10, 0, 0.055);
123 | // opacity: 0;
124 | }
125 | .legend {
126 | font-size: 12px;
127 | }
128 | rect {
129 | cursor: pointer; /* NEW */
130 | stroke-width: 2;
131 | }
132 | rect.disabled { /* NEW */
133 | fill: transparent !important; /* NEW */
134 | }
135 |
136 | #totals_display {
137 | background: rgba(255, 255, 255, 0.8);
138 | color: #333;
139 | font-size: 12px;
140 | left: 130px;
141 | padding: 18px;
142 | position: absolute;
143 | text-align: left;
144 | bottom: 20px;
145 | // width: 200px;
146 | // z-index: 10;
147 | border-radius: 10px;
148 | // box-shadow: 0 0 0 0, 1px 1px 6px 4px rgba(10, 10, 0, 0.025);
149 | text-align: center;
150 | animation: fadein .8s;
151 | -moz-animation: fadein .8s; /* Firefox */
152 | -webkit-animation: fadein .8s; /* Safari and Chrome */
153 | -o-animation: fadein .8s; /* Opera */
154 | }
155 |
156 | #d3_data_type {
157 | margin-top: 8px;
158 | margin-bottom: 10px;
159 | }
160 |
161 | #d3_bundle_type button{
162 | font-size: 1em;
163 | }
164 | .flexbuttons {
165 | display: flex;
166 | flex-direction: row;
167 | justify-content: space-between;
168 | }
--------------------------------------------------------------------------------
/backend/create-config/utils/createWebPackConfigHelpers/create-rules.js:
--------------------------------------------------------------------------------
1 | const upath = require('upath');
2 |
3 | const JS_X = {
4 | rules: `
5 | {
6 | test: /\.jsx?$/,
7 | exclude: /node_modules/,
8 | use: {
9 | loader: '${upath.normalize(require.resolve('babel-loader'))}',
10 | options: {
11 | presets: ['${upath.normalize(require.resolve('babel-preset-env'))}', '${upath.normalize(require.resolve('babel-preset-react'))}', '${upath.normalize(require.resolve('babel-preset-stage-0'))}'],
12 | },
13 | },
14 | }`,
15 | dependencies: {
16 | babel: '^6.23.0',
17 | 'babel-core': '^6.26.3',
18 | 'babel-loader': '^7.1.5',
19 | 'babel-preset-env': '^1.7.0',
20 | 'babel-preset-react': '^6.24.1',
21 | 'babel-preset-stage-0': '^6.24.1',
22 | },
23 | };
24 | const GIF_PNG_SVG_JPG_JPEG = {
25 | rules: `{
26 | test: /\.(gif|png|jpe?g|svg)$/,
27 | use: [
28 | '${upath.normalize(require.resolve('file-loader'))}',
29 | {
30 | loader: '${upath.normalize(require.resolve('image-webpack-loader'))}',
31 | options: {
32 | bypassOnDebug: true, // webpack@1.x
33 | disable: true, // webpack@2.x and newer
34 | },
35 | },
36 | ],
37 | }`,
38 | dependencies: { 'file-loader': '^1.1.11' },
39 | };
40 | const CSS_SASS_SCSS = {
41 | rules: `
42 | {
43 | test: /\.(sa|sc|c)ss$/,
44 | use: [MiniCssExtractPlugin.loader, '${upath.normalize(require.resolve('css-loader'))}', '${upath.normalize(require.resolve('sass-loader'))}'],
45 | }`,
46 | dependencies: {
47 | 'node-sass': '^4.9.2',
48 | 'sass-loader': '^7.0.3',
49 | 'style-loader': '^0.21.0',
50 | 'css-loader': '^0.28.10',
51 | },
52 | };
53 | const LESS = {
54 | rules: `
55 | {
56 | test: /\.less$/,
57 | use: [
58 | '${upath.normalize(require.resolve('style-loader'))}' /* // creates style nodes from JS strings */,
59 | '${upath.normalize(require.resolve('css-loader'))}' /* // translates CSS into CommonJS */,
60 | '${upath.normalize(require.resolve('less-loader'))}' /* // compiles Less to CSS */,
61 | ],
62 | }`,
63 | dependencies: {
64 | 'node-sass': '^4.9.2',
65 | 'sass-loader': '^7.0.3',
66 | 'style-loader': '^0.21.0',
67 | 'css-loader': '^0.28.10',
68 | },
69 | };
70 |
71 | module.exports = extensions => {
72 | let dependencies = {
73 | webpack: '^4.16.2',
74 | 'html-webpack-plugin': '^3.2.0',
75 | 'mini-css-extract-plugin': '^0.4.1',
76 | path: '^0.12.7',
77 | };
78 | const alreadyAdded = new Set();
79 | const rules = extensions.reduce((acc, ext) => {
80 | if ((ext === '.js' || ext === '.jsx') && !alreadyAdded.has(ext)) {
81 | alreadyAdded.add('.js');
82 | alreadyAdded.add('.jsx');
83 | acc.push(JS_X.rules);
84 | dependencies = Object.assign(JS_X.dependencies, dependencies);
85 | } else if (
86 | (ext === '.gif' || ext === '.png' || ext === '.svg' || ext === '.jpg' || ext === '.jpeg') &&
87 | !alreadyAdded.has(ext)
88 | ) {
89 | alreadyAdded.add('.gif');
90 | alreadyAdded.add('.png');
91 | alreadyAdded.add('.jpg');
92 | alreadyAdded.add('.jpeg');
93 | alreadyAdded.add('.svg');
94 | acc.push(GIF_PNG_SVG_JPG_JPEG.rules);
95 | dependencies = Object.assign(GIF_PNG_SVG_JPG_JPEG.dependencies, dependencies);
96 | } else if ((ext === '.css' || ext === '.scss' || ext === '.sass') && !alreadyAdded.has(ext)) {
97 | alreadyAdded.add('.scss');
98 | alreadyAdded.add('.sass');
99 | alreadyAdded.add('.css');
100 | acc.push(CSS_SASS_SCSS.rules);
101 | dependencies = Object.assign(CSS_SASS_SCSS.dependencies, dependencies);
102 | } else if (ext === '.less' && !alreadyAdded.has(ext)) {
103 | alreadyAdded.add('.less');
104 | acc.push(LESS.rules);
105 | dependencies = Object.assign(LESS.dependencies, dependencies);
106 | }
107 | return acc;
108 | }, []);
109 | return { rules, dependencies };
110 | };
111 |
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/custom_plugin/size_timing.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | const rootDir = process.cwd();
5 |
6 | console.log('ENTERING SIZE TIMING PLUGIN');
7 |
8 | // attempt 1
9 | // const data = {};
10 |
11 |
12 | // attempt 2
13 | const packets = [];
14 | let currPacket;
15 | let loadQ = false;
16 | let completeQ = false;
17 |
18 | // results for attempt 2
19 | const fileMap = new Map();
20 |
21 |
22 | // write sizeTimingPlugin functionality
23 | const sizeTimingPlugin = () => {
24 | // return properties
25 | return {
26 | name: 'Custom size-timing plugin',
27 | buildStart: () => {
28 | console.log('build start');
29 | },
30 | resolveId: (importee, importer) => {
31 | // console.log('resolveId: ' + importee);
32 | },
33 | load: (id) => {
34 | // attempt 1
35 | // data[id] = {begin: Date.now()};
36 |
37 | // attempt 2
38 | // switch to loading mode
39 | // console.log('loading ' + id);
40 | if (!loadQ) {
41 | loadQ = true;
42 | completeQ = false;
43 | currPacket = {begin: Date.now(), ids: []};
44 | packets.push(currPacket);
45 | }
46 |
47 | // add id (file path) to ids set to keep track of files that are being loaded
48 | currPacket.ids.push(id);
49 | },
50 | transform: (source, id) => {
51 | // attempt 1
52 | // if (/\u0000commonjs-proxy:/.test(id)) { // check for prefix '\u0000commonjs-proxy:'
53 | // trimmedId = id.replace(/\u0000commonjs-proxy:/, '');
54 | // data[id] = {postElapse: Date.now() - (data[trimmedId] ? data[trimmedId].begin + data[trimmedId].elapsed : 0)};
55 | // }
56 | // else {
57 | // if (data[id]) {
58 | // data[id].elapsed = Date.now() - data[id].begin;
59 | // }
60 | // else {
61 | // data[id] = {end: Date.now()};
62 | // }
63 | // }
64 |
65 | // attempt 2
66 | // switch to transform mode
67 | // console.log('transforming ' + id);
68 | if (loadQ) {
69 | loadQ = false;
70 | currPacket.end = Date.now();
71 | currPacket.elapsed = currPacket.end - currPacket.begin;
72 | }
73 |
74 | // check for prefix '\u0000commonjs-proxy:' followed by url that exists in the current packet ids set
75 | if (!completeQ && /\u0000commonjs-proxy:/.test(id)) {
76 | const trimmedId = id.replace(/\u0000commonjs-proxy:/, '');
77 | if (currPacket.ids.includes(trimmedId)) {
78 | currPacket.completeEnd = Date.now();
79 | currPacket.completeElapsed = currPacket.completeEnd - currPacket.begin;
80 | completeQ = true;
81 | }
82 | }
83 | },
84 | buildEnd: () => {
85 | // attempt 1
86 | // fs.writeFileSync(path.join(__dirname, 'data1.json'), JSON.stringify(data, null, 2));
87 |
88 | // attempt 2
89 | // post-processing
90 | const files = [];
91 |
92 | packets.forEach(packet => {
93 | const time = (packet.completeElapsed || packet.elapsed) / packet.ids.length;
94 | packet.ids.forEach(id => {
95 | files.push({name: path.relative(rootDir, id), time});
96 |
97 | fileMap.set(path.relative(rootDir, id), time);
98 | });
99 |
100 | });
101 |
102 | console.log('total accumulated time:');
103 | console.log(files.reduce((acc, next) => acc + next.time, 0));
104 |
105 | // write to file
106 | // fs.writeFileSync(path.join(__dirname, 'data2.json'), JSON.stringify(packets, null, 2));
107 | // fs.writeFileSync(path.join(__dirname, 'data3.json'), JSON.stringify(files, null, 2));
108 | },
109 | generateBundle: (outputOptions, bundle, isWrite) => {
110 | const modules = bundle['bundle.js'].modules;
111 | const bundleFiles = Object.keys(modules);
112 |
113 | const fileSizes = bundleFiles.map(key => {
114 | return {name: path.relative(rootDir, key), size: modules[key].renderedLength};
115 | });
116 |
117 | const fileTimesSizes = bundleFiles.map(key => {
118 | const name = path.relative(rootDir, key);
119 | return {name, size: modules[key].renderedLength, time: fileMap.get(name)};
120 | }).filter(item => {
121 | return !(/\u0000commonjs/.test(item.name));
122 | });
123 |
124 | // zip contents together
125 |
126 |
127 | // fs.writeFileSync(path.join(__dirname, 'data4.json'), JSON.stringify(fileSizes, null, 2));
128 | // fs.writeFileSync(path.join(__dirname, '..', '..', '..', '..', '..', 'electronUserData', 'cwd.json'), JSON.stringify(rootDir, null, 2));
129 | fs.writeFileSync(path.join(__dirname, '..', '..', '..', '..', '..', 'electronUserData', 'rollup-stats.json'), JSON.stringify(fileTimesSizes, null, 2));
130 | }
131 | };
132 | };
133 |
134 | module.exports = sizeTimingPlugin;
135 |
136 |
--------------------------------------------------------------------------------
/backend/create-config/utils/rollupConfigHelpers/createRollupRulesFromFileTypes.js:
--------------------------------------------------------------------------------
1 | const upath = require('upath');
2 |
3 | const JS_X = {
4 | modules: [
5 | {
6 | variableName: 'babel',
7 | packageName: 'rollup-plugin-babel',
8 | },
9 | ],
10 | plugins: [
11 | `babel({
12 | babelrc: false,
13 | exclude: 'node_modules/**',
14 | presets: [['${upath.normalize(
15 | require.resolve('babel-preset-es2015-rollup')
16 | )}'], ['${upath.normalize(require.resolve('babel-preset-react'))}'], ['${upath.normalize(
17 | require.resolve('babel-preset-stage-0')
18 | )}']],
19 | }),`,
20 | ],
21 | dependencies: {
22 | 'babel-preset-es2015-rollup': '^3.0.0',
23 | 'rollup-plugin-babel': '^3.0.7',
24 | },
25 | };
26 | const CSS_SASS_SCSS = {
27 | modules: [{ variableName: 'scss', packageName: 'rollup-plugin-scss' }],
28 | plugins: [
29 | `scss({
30 | output: './bundle-bee-rollup-dist/bundle.css',
31 | }),`,
32 | ],
33 | dependencies: {
34 | 'rollup-plugin-sass': '^0.9.2',
35 | 'rollup-plugin-scss': '^0.4.0',
36 | },
37 | };
38 | const GIF_PNG_SVG_JPG_JPEG = {
39 | modules: [{ variableName: 'image', packageName: 'rollup-plugin-image' }],
40 | plugins: ['image(),'],
41 | dependencies: {
42 | 'rollup-plugin-image': '^1.0.2',
43 | },
44 | };
45 |
46 | const HTML = templatePath => ({
47 | modules: [{ variableName: 'html', packageName: 'rollup-plugin-fill-html' }],
48 | plugins: [
49 | `html({
50 | template: '${upath.normalize(templatePath)}',
51 | filename: 'index.html',
52 | }),`,
53 | ],
54 | dependencies: {
55 | 'rollup-plugin-fill-html': '^1.1.0',
56 | },
57 | });
58 |
59 | const base_rules = {
60 | modules: [
61 | {
62 | variableName: 'commonjs',
63 | packageName: 'rollup-plugin-commonjs',
64 | },
65 | {
66 | variableName: 'resolve',
67 | packageName: 'rollup-plugin-node-resolve',
68 | },
69 | {
70 | variableName: 'replace',
71 | packageName: 'rollup-plugin-replace',
72 | },
73 | {
74 | variableName: 'json',
75 | packageName: 'rollup-plugin-json',
76 | },
77 | {
78 | variableName: 'uglify',
79 | packageName: 'rollup-plugin-uglify'
80 | }
81 | ],
82 | plugins: [
83 | `commonjs({
84 | include: 'node_modules/**',
85 | namedExports: {
86 | 'node_modules/react-dom/index.js': ['render', 'findDOMNode'],
87 | 'node_modules/react/index.js': ['Component', 'Children', 'createElement', 'cloneElement'],
88 | },
89 | }),`,
90 | `resolve({
91 | module: true,
92 | jsnext: true,
93 | main: true,
94 | browser: true,
95 | extensions: ['.mjs', '.js', '.jsx', '.json'],
96 | preferBuiltins: false,
97 | }),`,
98 | `replace({
99 | 'process.env.NODE_ENV': JSON.stringify('production'),
100 | }),`,
101 | `json(),`,
102 | ],
103 | dependencies: {
104 | webpack: '^4.16.2',
105 | 'webpack-cli': '^3.1.0',
106 | path: '^0.12.7',
107 | 'rollup-plugin-commonjs': '^9.1.4',
108 | 'rollup-plugin-json': '^3.0.0',
109 | 'rollup-plugin-node-resolve': '^3.3.0',
110 | 'rollup-plugin-replace': '^2.0.0',
111 | rollup: '^0.64.1',
112 | "rollup-plugin-uglify": "^4.0.0"
113 | },
114 | };
115 |
116 | module.exports = res => {
117 | res.rollupDependencies = base_rules.dependencies;
118 | const relevantPlugins = [
119 | ...res.extensions.reduce((acc, ext) => {
120 | if (ext === '.js' || ext === '.jsx') acc.add('babel');
121 | if (ext === '.gif' || ext === '.png' || ext === '.svg' || ext === '.jpg' || ext === '.jpeg')
122 | acc.add('image');
123 | if (ext === '.scss' || ext === '.sass' || ext === '.css') acc.add('scss');
124 | if (ext === '.html' && res.indexHtmlPath) acc.add('html');
125 | return acc;
126 | }, new Set()),
127 | ];
128 | const pluginsInOrder = relevantPlugins
129 | .reduce(
130 | (acc, x) => {
131 | if (x === 'scss') acc[0] = x;
132 | if (x === 'babel') acc[1] = x;
133 | if (x === 'image') acc[2] = x;
134 | if (x === 'html') acc[3] = x;
135 | return acc;
136 | },
137 | [null, null, null, null]
138 | )
139 | .filter(x => x);
140 | res.rollupRules = pluginsInOrder.reduce(
141 | (acc, name) => {
142 | if (name === 'babel') {
143 | acc.modules = acc.modules.concat(JS_X.modules);
144 | acc.plugins = acc.plugins.concat(JS_X.plugins);
145 | res.rollupDependencies = Object.assign(JS_X.dependencies, res.rollupDependencies);
146 | } else if (name === 'image') {
147 | acc.modules = acc.modules.concat(GIF_PNG_SVG_JPG_JPEG.modules);
148 | acc.plugins = acc.plugins.concat(GIF_PNG_SVG_JPG_JPEG.plugins);
149 | res.rollupDependencies = Object.assign(
150 | GIF_PNG_SVG_JPG_JPEG.dependencies,
151 | res.rollupDependencies
152 | );
153 | } else if (name === 'scss') {
154 | acc.modules = acc.modules.concat(CSS_SASS_SCSS.modules);
155 | acc.plugins = acc.plugins.concat(CSS_SASS_SCSS.plugins);
156 | res.rollupDependencies = Object.assign(CSS_SASS_SCSS.dependencies, res.rollupDependencies);
157 | } else if (name === 'html') {
158 | const htmlResult = HTML(res.indexHtmlPath);
159 | acc.modules = acc.modules.concat(htmlResult.modules);
160 | acc.plugins = acc.plugins.concat(htmlResult.plugins);
161 | res.rollupDependencies = Object.assign(htmlResult.dependencies, res.rollupDependencies);
162 | }
163 | return acc;
164 | },
165 | { modules: base_rules.modules, plugins: base_rules.plugins }
166 | );
167 | return res;
168 | };
169 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BUNDLE BEE
2 |
3 | Bundle Bee is an intuitive program that takes in your javascript project and automatically configures and runs it through popular bundlers, and then provides an interactive analysis tool after to help you compare your bundle composition, speed times and file sizes in each respective bundler, allowing you to choose the best bundler for your needs and migrate with zero-configuration and zero-hassle. Currently compatible with Webpack, Parcel and Rollup - our program will give you the needed configuration and build files to get started and launch with whichever bundler best suits your needs.
4 |
5 | ### Getting Started: _Auto-config your webpack, parcel & rollup build tools._
6 |
7 | These instructions will get you a copy of the project up and running on your local machine.
8 |
9 | ### Requirements
10 | Bundle Bee is currently only compatible with javascript projects using vanilla JS, React, SASS/SCSS, LESS, CSS, and standard image types. Application requiring javascript module loading of other file types are likely to encounter errors during bundling. The application is available in two formats: As a downloadable .app file for MacOS, and as an electron application launched from the terminal using npm or yarn.
11 |
12 | ### Installing
13 | Download the mac app from [bundlebee.io](https://www.bundlebee.io/),
14 |
15 | OR
16 |
17 | copy our repo to your local machine:
18 |
19 | ```
20 | git clone https://github.com/bundlebee/bundle-bee.git bundle-bee
21 | ```
22 |
23 | cd into the directory and install the dependencies
24 |
25 | ```
26 | cd bundle-bee
27 | ```
28 | ```
29 | npm i || yarn
30 | ```
31 | Then boot up the application:
32 | ```
33 | npm run electron || yarn electron
34 | ```
35 | After that, just drop your project folder into the electron app window, and let the bundler do the work. Bundle Bee will attempt to locate your entry file automatically, and you will be prompted to drop it in if we are unable to determine this. Additionally, if an existing configuration file is found, Bundle Bee will ask whether you would like to use that, or if you would like us to automatically generate a new configuration file for you. Speed and size data for each bundler displays on the screen as each bundler finishes its process. Navigate between the three bundlers using the top three buttons, and switch between build sizes and speeds using the lower three buttons broken down by asset. A broad overview bar chart of the total build times and sizes is available on the 'totals' tab, which displays a snapshot comparison of the three bundlers' results.
36 |
37 | After a preferred bundler has been chosen, click one of the 'view configuration' buttons on the right side of the screen to open up the project files folder specific to that bundler, which provides:
38 |
39 | 1) The configuration file used to bundler your project
40 | 2) The build files produced by each bundler
41 | 3) a package.json that includes all of the additional devDependencies required to run the bundler
42 |
43 |
44 | ### Troubleshooting
45 | If you encounter an error running Bundle Bee on your project, first check to see that:
46 |
47 | 1) Your project does not import modules of unsupported file types or format (javascript es6+ up to stage-0 proposals, React, SASS/SCSS/CSS/LESS)
48 | 2) Your imports are all es6 imports, commonJS imports (node), UMD or AMD format, and no dynamic imports are used
49 | 3) Your project does not have any errors prior to bundling
50 |
51 | Additionally, there are some limitations in the individual bundlers that may be affecting the bundling process. It is beyond the scope of this document to cover all of these (consult the relevant docs for each bundler)
52 |
53 | https://github.com/webpack/docs/wiki/troubleshooting
54 |
55 | https://github.com/rollup/rollup/wiki/Troubleshooting
56 |
57 | https://github.com/parcel-bundler/parcel/issues
58 |
59 | but some common issues are below:
60 |
61 | - In some cases, Parcel encounters errors in resolving url paths in SASS/SCSS if the path is absolute, or if it uses single or double quotes
62 | - When destructuring some named exports from node modules (e.g, 'import { render } from 'react-dom';), rollup may encounter an error. We have included some rules to account for this in our configuration, such as the most common examples in React, but obviously there is no way to anticipate what will be in your project in advance. To add more named exports to your rollup config, please add them to the commonjs plugin in the rollup config as follows:
63 | ```
64 | commonjs({
65 | include: 'node_modules/**',
66 | namedExports: {
67 | 'node_modules/react-dom/index.js': ['render', 'findDOMNode'],
68 | },
69 | ```
70 | - There is no hard and fast rule yet for how to order rollup plugins to ensure they will run properly. We attempted to find the order that resolves the most cases, but you may need to reorder the rollup plugins to see what works with your project.
71 |
72 | And finally, Bundle Bee is still in development, so bear with us as we iron out all the kinks!
73 |
74 | ## Built With
75 |
76 | * [Electron](https://github.com/electron/electron) - Application Framework
77 | * [React](https://github.com/facebook/react) - Front-End Rendering
78 | * [Redux](https://maven.apache.org/) - State Management
79 | * [Node](https://github.com/nodejs/node) - Backend and File System Operations
80 | * [Webpack](https://github.com/webpack) - Module Bundling
81 | * [Rollup](https://github.com/rollup/rollup) - Module Bundling
82 | * [Parcel](https://github.com/parcel-bundler) - Module Bundling
83 |
84 |
85 | ## Authors
86 |
87 | * **Brendan Morrell** (https://github.com/brendanmorrell)
88 |
89 | * **Clariz Mariano** (https://github.com/havengoer)
90 |
91 | * **Sam Goldberg** (https://github.com/sgoldber61)
92 |
93 | * **Adam Modras** (https://github.com/js-mode)
94 |
95 | ## License
96 |
97 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
98 |
99 | ## Acknowledgments
100 |
101 | * Hats off to michal hudecek for providing the bundle bee image!
102 |
--------------------------------------------------------------------------------
/app/src/utils/dataParser.js:
--------------------------------------------------------------------------------
1 | export const parseWebpackOutput = (data, bundleDir) => {
2 | console.log('@ parseWebpackOutput');
3 | const total = { size: 0, factory: 0, building: 0 };
4 | total.totalElapsedTime = data.time;
5 | total.totalBundleSize = data.assets.reduce((acc, asset) => acc + asset.size, 0);
6 | console.log('@ webpack size', total.totalBundleSize);
7 |
8 | const rootData = { name: 'rootData', children: [] };
9 | console.log('bundleDir: ' + bundleDir);
10 | data.chunks[0].modules.filter(x => !x.name.includes('C:/') && !x.name.includes('..') && x.name.slice(0, 3) !== 'css' && !x.name.includes(bundleDir)).forEach(element => {
11 | let directoryAndName = element.name.replace(/\.\//, '');
12 | let parts = directoryAndName.split('/');
13 |
14 | var currentNode = rootData;
15 | for (var j = 0; j < parts.length; j++) {
16 | var children = currentNode['children'];
17 | var nodeName = parts[j];
18 | var childNode;
19 | if (j + 1 < parts.length) {
20 | // Not yet at the end of the sequence; move down the tree.
21 | var foundChild = false;
22 | for (var k = 0; k < children.length; k++) {
23 | if (children[k]['name'] == nodeName) {
24 | childNode = children[k];
25 | foundChild = true;
26 | break;
27 | }
28 | }
29 | // If we don't already have a child node for this branch, create it.
30 | if (!foundChild) {
31 | childNode = { name: nodeName, children: [] };
32 | children.push(childNode);
33 | }
34 | currentNode = childNode;
35 | } else {
36 | // Reached the end of the sequence; create a leaf node.
37 | const size = element.size || 0;
38 | const factory = element.profile ? element.profile.factory : 0;
39 | const building = element.profile ? element.profile.building : 0;
40 |
41 | total.size += Number(size);
42 | total.factory += Number(factory);
43 | total.building += Number(building);
44 | childNode = { name: nodeName, size, factory, building };
45 | children.push(childNode);
46 | }
47 | }
48 | });
49 |
50 | return { hierarchicalData: rootData, total: total };
51 | };
52 |
53 | // parse our own custom parcel output. parcel doesn't keep track of factory times.
54 | export const parseParcelOutput = (data, bundleDir) => {
55 | console.log('@ parseParcelOutput');
56 | const total = { size: 0, building: 0 };
57 | total.totalElapsedTime = data.totalElapsedTime;
58 | total.totalBundleSize = data.totalBundleSize;
59 | console.log('@ parcel size', total.totalBundleSize);
60 |
61 | const rootData = { name: 'rootData', children: [] };
62 | data.files
63 | .slice()
64 | .filter(x => !x.name.includes('..')) // revised filter for not including the entirety of bundle bee
65 | .forEach(element => {
66 | let directoryAndName = element.name.replace(/\\/g, '/');
67 | let parts = directoryAndName.split('/');
68 |
69 | var currentNode = rootData;
70 | for (var j = 0; j < parts.length; j++) {
71 | var children = currentNode['children'];
72 | var nodeName = parts[j];
73 | var childNode;
74 | if (j + 1 < parts.length) {
75 | // Not yet at the end of the sequence; move down the tree.
76 | var foundChild = false;
77 | for (var k = 0; k < children.length; k++) {
78 | if (children[k]['name'] == nodeName) {
79 | childNode = children[k];
80 | foundChild = true;
81 | break;
82 | }
83 | }
84 | // If we don't already have a child node for this branch, create it.
85 | if (!foundChild) {
86 | childNode = { name: nodeName, children: [] };
87 | children.push(childNode);
88 | }
89 | currentNode = childNode;
90 | } else {
91 | // Reached the end of the sequence; create a leaf node.
92 | const size = element.bundledSize || 0;
93 | const building = element.buildTime;
94 |
95 | total.size += Number(size);
96 | total.building += Number(building);
97 | childNode = { name: nodeName, size, building };
98 | children.push(childNode);
99 | }
100 | }
101 | });
102 |
103 | return { hierarchicalData: rootData, total: total };
104 | };
105 |
106 | export const parseRollupOutput = data => {
107 | console.log('@ parseRollupOutput');
108 | const total = { size: 0, building: 0 };
109 | total.totalElapsedTime = data.totalElapsedTime;
110 | total.totalBundleSize = data.totalBundleSize;
111 | console.log('@ rollup size', total.totalBundleSize);
112 |
113 | const rootData = { name: 'rootData', children: [] };
114 | data.files.slice().forEach(element => {
115 | let directoryAndName = element.name.replace(/\\/g, '/');
116 | let parts = directoryAndName.split('/');
117 |
118 | var currentNode = rootData;
119 | for (var j = 0; j < parts.length; j++) {
120 | var children = currentNode['children'];
121 | var nodeName = parts[j];
122 | var childNode;
123 | if (j + 1 < parts.length) {
124 | // Not yet at the end of the sequence; move down the tree.
125 | var foundChild = false;
126 | for (var k = 0; k < children.length; k++) {
127 | if (children[k]['name'] == nodeName) {
128 | childNode = children[k];
129 | foundChild = true;
130 | break;
131 | }
132 | }
133 | // If we don't already have a child node for this branch, create it.
134 | if (!foundChild) {
135 | childNode = { name: nodeName, children: [] };
136 | children.push(childNode);
137 | }
138 | currentNode = childNode;
139 | } else {
140 | // Reached the end of the sequence; create a leaf node.
141 | const size = element.size || 0;
142 | const building = element.time;
143 |
144 | total.size += Number(size);
145 | total.building += Number(building);
146 | childNode = { name: nodeName, size, building };
147 | children.push(childNode);
148 | }
149 | }
150 | });
151 |
152 | return { hierarchicalData: rootData, total: total };
153 | };
154 |
--------------------------------------------------------------------------------
/app/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import { ipcRenderer } from 'electron';
2 | import React, { Component } from 'react';
3 | import DropZone from './DropZone.jsx';
4 | import ModalPrompt from './ModalPrompt.jsx';
5 | import Chart from './Chart.jsx';
6 |
7 | import {
8 | retrieveWebpackStats,
9 | retrieveRollupStats,
10 | retrieveParcelStats,
11 | } from '../redux/actions/dataActions';
12 |
13 | import { connect } from 'react-redux';
14 | import { isLoading, showModal, waitForEntry } from '../redux/actions/homeActions';
15 | import * as home from '../redux/constants/homeConstants';
16 |
17 | import Bee from './loaders/awesomeBee.jsx';
18 | import ImportLoader from './loaders/ImportLoader.jsx';
19 | import CodeLoader from './loaders/CodeLoader.jsx';
20 |
21 | import ReactTooltip from 'react-tooltip';
22 | export class Main extends Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | mainPageMessage: '',
27 | dirname: '',
28 | };
29 | this.handleRestart = this.handleRestart.bind(this);
30 | }
31 | componentDidMount() {
32 | ipcRenderer.on('handle-file-indexing-results', (event, res) => {
33 | if (res.foundWebpackConfig) {
34 | this.props.showModal();
35 | } else if (res.foundEntryFile) {
36 | ipcRenderer.send('run-webpack', { createNewConfig: true });
37 | } else {
38 | console.log('no index.js nor webpack.config found');
39 | this.setState({
40 | mainPageInstructions: `Please also drop your entry file (e.g., 'index.js')`,
41 | });
42 | this.props.waitForEntry();
43 | }
44 | });
45 |
46 | ipcRenderer.on('webpack-stats-results-json', (event, res) => {
47 | ipcRenderer.send('run-parcel');
48 | console.log('@webpack');
49 | this.props.retrieveWebpackStats(res);
50 | });
51 |
52 | ipcRenderer.on('parcel-stats-results-json', (event, res) => {
53 | ipcRenderer.send('run-rollup');
54 | console.log('@parcel');
55 |
56 | this.props.retrieveParcelStats(res);
57 | });
58 | ipcRenderer.on('rollup-stats-results-json', (event, res) => {
59 | console.log('build finished');
60 | this.setState({ dirname: res });
61 | this.props.retrieveRollupStats();
62 | });
63 | ipcRenderer.on('error', () => {
64 | this.setState({ mainPageMessage: 'An issue occurred while bundling your project.' });
65 | });
66 |
67 |
68 | }
69 | renderLoadingModal() {
70 | return ;
71 | }
72 |
73 | renderLoadingBundle() {
74 | return ;
75 | }
76 |
77 | dropZoneActive(isEntry) {
78 | return (
79 |
80 |
81 |

82 |
{this.state.mainPageInstructions}
83 |
84 |
85 | );
86 | }
87 |
88 | renderModal() {
89 | return ;
90 | }
91 | renderChart() {
92 | // change the width and height of the awesome bee to make more room for the d3 chart
93 | //svg
94 | document.getElementById('bee-happy').setAttribute('height', '50px');
95 | document.getElementById('bee-happy').setAttribute('width', '50px');
96 |
97 | // div container of the svg
98 | document.getElementById('bee_wrapper').style.top = '0px';
99 | document.getElementById('bee_wrapper').style.right = '150px';
100 | document.getElementById('bee_wrapper').style.position = 'absolute';
101 | console.log(this.state.dirname, 'MAIN JSX RENDER CHART');
102 | return ;
103 | }
104 | handleRestart() {
105 | ipcRenderer.send('restart');
106 | }
107 | render() {
108 | // THIS IS FOR DEBUGGING PURPOSES; must have data to work though
109 | // console.log(this.props.home.screen, home.SHOW_STARBURST, "MAIN JSX")
110 | // if ( this.props.home.screen !== home.SHOW_STARBURST) {
111 | // console.log("at if statement")
112 | // this.props.retrieveWebpackStats();
113 | // this.props.retrieveParcelStats();
114 | // this.props.retrieveRollupStats();
115 |
116 | // }
117 |
118 | let mainPage = null;
119 | if (this.props.home.screen === home.DIRECTORY_PENDING) mainPage = this.dropZoneActive();
120 | else if (this.props.home.screen === home.LOADING_MODAL) mainPage = this.renderLoadingModal();
121 | else if (this.props.home.screen === home.SHOW_MODAL) mainPage = this.renderModal();
122 | else if (this.props.home.screen === home.WAIT_FOR_ENTRY)
123 | mainPage = this.dropZoneActive({ isEntry: true });
124 | else if (this.props.home.screen === home.LOADING_BUNDLE) mainPage = this.renderLoadingBundle();
125 | else if (this.props.home.screen === home.SHOW_STARBURST) mainPage = this.renderChart();
126 |
127 | return (
128 |
129 |
130 |
131 | {this.state.mainPageMessage && (
132 |
133 |
{this.state.mainPageMessage}
134 |
135 | Check out the{' '}
136 |
141 | documentation
142 | {' '}
143 | for help.
144 |
145 |
148 |
149 | )}
150 |
{mainPage}
151 |
152 | );
153 | }
154 | }
155 |
156 | const mapDispatchToProps = dispatch => ({
157 | showModal: () => dispatch(showModal()),
158 | waitForEntry: () => dispatch(waitForEntry()),
159 | retrieveWebpackStats: bundleDir => dispatch(retrieveWebpackStats(bundleDir)),
160 | retrieveParcelStats: bundleDir => dispatch(retrieveParcelStats(bundleDir)),
161 | retrieveRollupStats: bundleDir => dispatch(retrieveRollupStats(bundleDir)),
162 | });
163 |
164 | const mapStateToProps = state => ({ home: state.home });
165 |
166 | export default connect(
167 | mapStateToProps,
168 | mapDispatchToProps
169 | )(Main);
170 |
--------------------------------------------------------------------------------
/main_process.js:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | const { app, BrowserWindow, ipcMain, Menu, Dialog } = require('electron');
3 | const createMenuBar = require('./backend/menuBar.js');
4 | const { fork, exec } = require('child_process');
5 | const path = require('path');
6 | const fs = require('fs');
7 |
8 | let mainWindow;
9 |
10 | app.on('ready', () => {
11 | mainWindow = new BrowserWindow({ width: 1300, height: 900 });
12 | mainWindow.loadURL(`file://${__dirname}/app/index.html`);
13 | //Adding Menu Bar
14 | const menu = Menu.buildFromTemplate(createMenuBar(mainWindow, ResetDir, OpenDir));
15 | Menu.setApplicationMenu(menu);
16 | if (process.env.NODE_ENV === 'packaging') {
17 | const initialStartFlagFilePath = path.join(__dirname, 'electronUserData', 'initialStartup.txt');
18 | if (!fs.existsSync(initialStartFlagFilePath)) {
19 | let pathToExecutable;
20 | if (process.platform === 'darwin') {
21 | pathToExecutable = path.join(__dirname, '..', '..', 'MacOS', 'bundle-bee');
22 | } else if (process.platform === 'win32') {
23 | pathToExecutable = path.join(__dirname, '..', '..', 'MacOS', 'bundle-bee');
24 | }
25 | fs.writeFile(
26 | initialStartFlagFilePath,
27 | `command to run executable:
28 | open ${pathToExecutable}
29 | dirname: ${__dirname}
30 | filename: ${__filename}
31 | initalstartflagfilepath: ${initialStartFlagFilePath}
32 | `
33 | );
34 | exec(`open ${pathToExecutable}`, err => {
35 | app.exit(0);
36 | });
37 | } else {
38 | fs.unlink(initialStartFlagFilePath, err => {
39 | if (err) console.log(err);
40 | });
41 | }
42 | mainWindow.on('close', () => {
43 | app.exit(0);
44 | });
45 | }
46 | });
47 |
48 | ipcMain.on('ondragstart', (event, filePath) => {
49 | event.sender.startDrag({
50 | file: filePath,
51 | icon: '/path/to/icon.png',
52 | });
53 | });
54 | ipcMain.on('restart', () => {
55 | ResetDir();
56 | });
57 |
58 | ipcMain.on('index-project-files-from-dropped-item-path', (event, rootDirPath) => {
59 | const pathToIndexFileModule = path.join(
60 | __dirname,
61 | 'backend',
62 | 'create-config',
63 | 'utils',
64 | 'indexFilesFromRoot.js'
65 | );
66 | const indexFilesChild = fork(pathToIndexFileModule, [rootDirPath]);
67 | indexFilesChild.on('message', ({ foundWebpackConfig, foundEntryFile, e }) => {
68 | if (e) {
69 | console.log(e);
70 | return event.sender.send('error');
71 | }
72 | event.sender.send('handle-file-indexing-results', {
73 | foundWebpackConfig,
74 | foundEntryFile,
75 | });
76 | });
77 | });
78 |
79 | ipcMain.on('run-webpack', (event, { createNewConfig, pathFromDrag }) => {
80 | const pathToCreateWebpackFileModule = path.join(
81 | __dirname,
82 | 'backend',
83 | 'create-config',
84 | 'utils',
85 | 'createWebpackConfig.js'
86 | );
87 | const pathToRunWebpackFileModule = path.join(
88 | __dirname,
89 | 'backend',
90 | 'create-config',
91 | 'utils',
92 | 'runWebpack.js'
93 | );
94 | const pathToWriteStatsFile = path.join(__dirname, 'electronUserData', 'stats.json');
95 |
96 | if (createNewConfig) {
97 | const createWebpackChild = fork(pathToCreateWebpackFileModule, [pathFromDrag]);
98 | createWebpackChild.on('message', ({ webpackDirectory, err }) => {
99 | if (err) return event.sender.send('error');
100 | const runWebpackChild = fork(pathToRunWebpackFileModule, [pathToWriteStatsFile], {
101 | cwd: webpackDirectory,
102 | });
103 | runWebpackChild.on('message', message => {
104 | if (message.error) {
105 | console.log(message.error);
106 | return event.sender.send('error');
107 | }
108 | console.log('webpack successfully run and stats.json successfully written...');
109 | event.sender.send('webpack-stats-results-json', __dirname);
110 | });
111 | });
112 | } else {
113 | const { rootDir } = JSON.parse(
114 | fs.readFileSync(path.join(__dirname, 'electronUserData', 'configurationData.js'), 'utf-8')
115 | );
116 | const runWebpackChild = fork(pathToRunWebpackFileModule, [pathToWriteStatsFile], {
117 | cwd: rootDir,
118 | });
119 | runWebpackChild.on('message', message => {
120 | if (message.error) {
121 | console.log(message.error);
122 | return event.sender.send('error');
123 | }
124 | console.log('webpack successfully run and stats.json successfully written...');
125 | event.sender.send('webpack-stats-results-json', __dirname);
126 | });
127 | }
128 | });
129 |
130 | ipcMain.on('run-parcel', event => {
131 | const pathToRunParcelFileModule = path.join(
132 | __dirname,
133 | 'backend',
134 | 'create-config',
135 | 'utils',
136 | 'runParcel.js'
137 | );
138 |
139 | const { rootDir } = JSON.parse(
140 | fs.readFileSync(path.join(__dirname, 'electronUserData', 'configurationData.js'), 'utf-8')
141 | );
142 |
143 | const pathToWriteStatsFile = path.join(__dirname, 'electronUserData', 'parcel-stats.json');
144 | const createParcelChild = fork(pathToRunParcelFileModule, [rootDir, pathToWriteStatsFile]);
145 | createParcelChild.on('message', message => {
146 | if (message.error) {
147 | console.log(message.error);
148 | return event.sender.send('error');
149 | }
150 | console.log('parcel successfully run and stats.json successfully written...');
151 | event.sender.send('parcel-stats-results-json', __dirname);
152 | });
153 | });
154 |
155 | ipcMain.on('run-rollup', event => {
156 | const pathToRunRollupModule = path.join(
157 | __dirname,
158 | 'backend',
159 | 'create-config',
160 | 'utils',
161 | 'runRollup.js'
162 | );
163 | const pathToWriteStatsFile = path.join(__dirname, 'electronUserData', 'rollup-stats.json');
164 | const createRollupChild = fork(pathToRunRollupModule, [pathToWriteStatsFile]);
165 | createRollupChild.on('message', message => {
166 | if (message.error) {
167 | console.log(message.error);
168 | return event.sender.send('error');
169 | }
170 | console.log('rollup successfully run and stats.json successfully written...');
171 | event.sender.send('rollup-stats-results-json', __dirname);
172 | });
173 | });
174 |
175 | function ResetDir() {
176 | app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) });
177 | console.log('running reset dir2');
178 | app.exit(0);
179 | }
180 |
181 | function OpenDir(build) { }
182 |
--------------------------------------------------------------------------------
/app/src/components/data_viz/BarChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as d3 from 'd3';
3 | import { connect } from 'react-redux';
4 | import OpenFolderButtons from '../OpenFolderButtons.jsx';
5 |
6 |
7 | class BarChart extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {};
11 | }
12 | componentDidMount() {
13 | this.instantiateBarGraph();
14 | }
15 | instantiateBarGraph() {
16 |
17 |
18 | var margin = { top: 20, right: 40, bottom: 30, left: 40 },
19 | width = 600 - margin.left - margin.right,
20 | height = 400 - margin.top - margin.bottom;
21 |
22 | var x0 = d3
23 | .scaleBand()
24 | .rangeRound([0, width])
25 | .padding(0.1);
26 | var x1 = d3.scaleOrdinal();
27 |
28 | var y0 = d3.scaleLinear().range([height, 0]);
29 | var y1 = d3.scaleLinear().range([height, 0]);
30 |
31 | var color = d3.scaleOrdinal().range(['#98abc5', '#d0743c']);
32 |
33 | var xAxis = d3.axisBottom(x0).ticks(5);
34 |
35 | var yAxisLeft = d3.axisLeft(y0).tickFormat(function(d) {
36 | return parseInt(d);
37 | });
38 |
39 | var yAxisRight = d3.axisRight(y1).tickFormat(function(d) {
40 | return parseInt(d);
41 | });
42 | var svg = d3
43 | .select('.bar-chart')
44 | .attr('width', width + margin.left + margin.right)
45 | .attr('height', height + margin.top + margin.bottom)
46 | .append('g')
47 | .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
48 |
49 | var data = {
50 | Webpack: { times: this.props.webpackData.time, sizes: this.props.webpackData.size },
51 | Parcel: { times: this.props.parcelData.time, sizes: this.props.parcelData.size },
52 | Rollup: { times: this.props.rollupData.time, sizes: this.props.rollupData.size },
53 | };
54 |
55 | const mouseleave = d => {
56 | d3.selectAll(".d3_tooltip_bar_chart").remove();
57 |
58 | }
59 | var x =0;
60 | const mouseover = d => {
61 | d3.selectAll(".d3_tooltip_bar_chart").remove();
62 |
63 | var tooltip = d3
64 | .select(".barchart_inner")
65 | .append("div")
66 | .attr("class", "d3_tooltip_bar_chart");
67 | tooltip.append("span");
68 | tooltip.append("span").attr("id", "sb_d3_details");
69 |
70 | tooltip
71 | .style("top", d3.event.layerY + "px")
72 | .style("left", d3.event.layerX + "px");
73 |
74 | tooltip.select("#sb_d3_details").html(
75 | `
76 | ${d.name}: ${d.value}
77 | ${d.name === 'Size' ? 'kb' : 'ms'}
78 | `
79 | );
80 | }
81 | var dataset = [];
82 |
83 | var keyNames = ['times', 'sizes'];
84 |
85 | for (let i = 0; i < Object.keys(data).length; i++) {
86 | var bundler = Object.keys(data)[i];
87 | dataset[i] = {
88 | bundler,
89 | values: [
90 | { name: 'Time', value: data[bundler][keyNames[0]] },
91 | { name: 'Size', value: data[bundler][keyNames[1]]/1000 },
92 | ],
93 | };
94 | }
95 |
96 | x0.domain(
97 | dataset.map(function(d) {
98 | return d.bundler;
99 | })
100 | );
101 | x1.domain(['Time', 'Size']).range([0, 20]);
102 |
103 | y0.domain([
104 | 0,
105 | d3.max(dataset, function(d) {
106 | return d.values[0].value;
107 | }),
108 | ]);
109 | y1.domain([
110 | 0,
111 | d3.max(dataset, function(d) {
112 | return d.values[1].value;
113 | }),
114 | ]);
115 |
116 | // Ticks on x-axis and y-axis
117 | svg
118 | .append('g')
119 | .attr('class', 'x axis')
120 | .attr('transform', 'translate(0,' + height + ')')
121 | .call(xAxis);
122 |
123 | svg
124 | .append('g')
125 | .attr('class', 'y0 axis')
126 | .call(yAxisLeft)
127 | .append('text')
128 | .attr('transform', 'rotate(-90)')
129 | .attr('y', 6)
130 | .attr('dy', '.71em')
131 | .style('text-anchor', 'end')
132 | .style('fill', '#98abc5')
133 | .text('Time');
134 |
135 | svg
136 | .select('.y0.axis')
137 | .selectAll('.tick')
138 | .style('fill', '#98abc5');
139 |
140 | svg
141 | .append('g')
142 | .attr('class', 'y1 axis')
143 | .attr('transform', 'translate(' + width + ',0)')
144 | .call(yAxisRight)
145 | .append('text')
146 | .attr('transform', 'rotate(-90)')
147 | .attr('y', -16)
148 | .attr('dy', '.71em')
149 | .style('text-anchor', 'end')
150 | .style('fill', '#d0743c')
151 | .text('Size');
152 |
153 | svg
154 | .select('.y1.axis')
155 | .selectAll('.tick')
156 | .style('fill', '#d0743c');
157 | // End ticks
158 |
159 | var graph = svg
160 | .selectAll('.date')
161 | .data(dataset)
162 | .enter()
163 | .append('g')
164 | .attr('class', 'g')
165 | .attr('transform', function(d) {
166 | return 'translate(' + (x0(d.bundler) + 78) + ',0)';
167 | });
168 |
169 | graph
170 | .selectAll('rect')
171 | .data(function(d) {
172 | return d.values;
173 | })
174 | .enter()
175 | .append('rect')
176 | .attr('width', 22 /* x1.bandwidth() */)
177 | .attr('x', function(d) {
178 | return x1(d.name)-20;
179 | })
180 | .attr('y', function(d) {
181 | return y0(d.value);
182 | })
183 | .attr("height", 0)
184 | .transition()
185 | .delay(function (d, i) { return i*100; })
186 | .attr('height', function(d) {
187 | return height - y0(d.value);
188 | })
189 | .style('fill', function(d) {
190 | return color(d.name);
191 | })
192 | // .on('mouseover', mouseover)
193 | //
194 |
195 | graph.selectAll('rect').on('mouseover', mouseover)
196 | .on('mouseover', mouseover)
197 | .on('mouseleave', mouseleave)
198 | console.log(graph, "graph")
199 |
200 | // Legend
201 | var legend = svg
202 | .selectAll('.legend')
203 | .data(['Time (ms)', 'Size (kb)'].slice())
204 | .enter()
205 | .append('g')
206 | .attr('class', 'legend')
207 | .attr('transform', function(d, i) {
208 | return 'translate(-390,' + -(i * 20) + ')';
209 | });
210 |
211 | legend
212 | .append('rect')
213 | .attr('x', width - 48)
214 | .attr('width', 18)
215 | .attr('height', 18)
216 | .style('fill', color);
217 |
218 | legend
219 | .append('text')
220 | .attr('x', width - 54)
221 | .attr('y', 9)
222 | .attr('dy', '.35em')
223 | .style('text-anchor', 'end')
224 | .text(function(d) {
225 | return d;
226 | });
227 |
228 |
229 | }
230 |
231 | render() {
232 | return (
233 |
234 |
Build Comparison
235 |
236 |
237 |
238 |
239 |
240 |
241 | );
242 | }
243 | }
244 |
245 | const mapStateToProps = ({ data }) => ({
246 | webpackData: {
247 | size: data.webpackStarBurstData.total.totalBundleSize,
248 | time: data.webpackStarBurstData.total.totalElapsedTime,
249 | },
250 | parcelData: {
251 | size: data.parcelStarBurstData.total.totalBundleSize,
252 | time: data.parcelStarBurstData.total.totalElapsedTime,
253 | },
254 | rollupData: {
255 | size: data.rollupStarBurstData.total.totalBundleSize,
256 | time: data.rollupStarBurstData.total.totalElapsedTime,
257 | },
258 | });
259 | export default connect(mapStateToProps)(BarChart);
260 |
--------------------------------------------------------------------------------
/app/src/styles/base/_base.scss:
--------------------------------------------------------------------------------
1 | // place the base settings in here. Anything that is the base/starting point for your html elements
2 | //Border box sets the sizing to a more intuitive model (padding,e tc. don't change the overall size of an element so it stays the size you define it as regardless of other changes in size of box-model elements (margin, padding, etc.)
3 | @import url('https://fonts.googleapis.com/css?family=Roboto');
4 |
5 | ::selection {
6 | background:transparent;
7 | }
8 |
9 | * {
10 | box-sizing: border-box;
11 | font-family: 'Roboto', sans-serif;
12 |
13 | }
14 | @mixin keyframes($animation-name) {
15 | @-webkit-keyframes #{$animation-name} {
16 | @content;
17 | }
18 | @-moz-keyframes #{$animation-name} {
19 | @content;
20 | }
21 | @-ms-keyframes #{$animation-name} {
22 | @content;
23 | }
24 | @-o-keyframes #{$animation-name} {
25 | @content;
26 | }
27 | @keyframes #{$animation-name} {
28 | @content;
29 | }
30 | }
31 | // Column generator
32 | @mixin colmk($numRows, $margin) {
33 | width: ((100% - (($numRows - 1) * $margin)) / $numRows);
34 |
35 | &:nth-child(n) {
36 | margin-right: $margin;
37 | margin-bottom: $margin;
38 | }
39 |
40 | &:nth-child(#{$numRows}n) {
41 | margin-right: 0;
42 | margin-bottom: 0;
43 | }
44 | }
45 |
46 | /* -----------------------------------------------------
47 | Body
48 | -------------------------------------------------------- */
49 | body {
50 | height: 100vh;
51 | margin: 0;
52 | padding: 0;
53 | color: #000000;
54 | font-size: 16px;
55 | font-family: 'Roboto', sans-serif;
56 | background-color: rgb(236, 235, 235);
57 | }
58 |
59 | *, *::after, *::before {
60 | -webkit-box-sizing: border-box;
61 | -moz-box-sizing: border-box;
62 | -ms-box-sizing: border-box;
63 | -o-box-sizing: border-box;
64 | box-sizing: border-box;
65 | }
66 |
67 | .dropzone {
68 | font-size: 55px;
69 | color: white;
70 | }
71 | h2 {
72 | font-size: $m-size;
73 | color: gray;
74 | }
75 | a {
76 | color: teal;
77 | outline: none;
78 | text-decoration: none;
79 | &:hover { text-decoration: underline; }
80 | }
81 |
82 | /* -----------------------------------------------------
83 | Main
84 | -------------------------------------------------------- */
85 | main {
86 | width: 100%;
87 | max-width: 940px;
88 | margin: 1.4rem auto;
89 |
90 | section {
91 | display: block;
92 | margin-bottom: 1.4rem;
93 | padding: 1.2rem;
94 | background-color: #fff;
95 | box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.157);
96 |
97 | &:last-child { margin-bottom: 0; }
98 |
99 | .heading {
100 | margin: 0;
101 | padding: 0;
102 | font-size: 1.2rem;
103 | }
104 |
105 | .wrap {
106 | overflow: hidden;
107 | display: block;
108 | margin: 1rem 0 0.8rem;
109 | text-align: center;
110 |
111 | .holder {
112 | float: left;
113 | text-align: center;
114 | background-color: #f2f2f2;
115 |
116 | &:last-child { margin-right: 0; }
117 |
118 | &.col-3 {
119 | @include colmk(3, 1%);
120 | }
121 | &.col-4 {
122 | @include colmk(4, 1%);
123 | }
124 |
125 | .demo {
126 | min-height: 120px;
127 | padding: 2em 0;
128 | display: flex;
129 | justify-content: center;
130 | align-content: center;
131 | }
132 |
133 | .captions {
134 | padding: 8px;
135 | color: rgba(0,0,0,0.5);
136 | font-size: 13px;
137 | font-weight: bold;
138 | border-top: 1px solid rgba(0,0,0,0.1);
139 | }
140 |
141 | @media only screen and (max-width: 860px) {
142 | &.col-4 {
143 | @include colmk(3, 1%);
144 | }
145 | }
146 |
147 | @media only screen and (max-width: 640px) {
148 | float: none;
149 |
150 | &.col-3,
151 | &.col-4 {
152 | display: block;
153 | width: 100%;
154 |
155 | &:nth-child(n) { margin: 0; margin-bottom: 14px; }
156 | }
157 | }
158 | }
159 | }
160 | }
161 |
162 | @media only screen and (max-width: 960px) {
163 | padding: 0 1.5%;
164 | }
165 | }
166 |
167 | /* -----------------------------------------------------
168 | Header
169 | -------------------------------------------------------- */
170 | header {
171 | display: block;
172 | width: 100%;
173 | padding: 12px 0;
174 | text-align: center;
175 | background-color: #FFF;
176 | box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.1);
177 |
178 | h2 {
179 | margin: 0;
180 | padding: 25px 0;
181 | font-size: 2em;
182 | font-weight: 300;
183 | letter-spacing: 1px;
184 | color: black;
185 | }
186 | }
187 |
188 |
189 |
190 |
191 |
192 | /* :root {
193 | --primary-color: #42b983;
194 | } */
195 |
196 | /*
197 | * Global CSS goes here, it requires to use :global before each style
198 | */
199 |
200 |
201 | #app {
202 | display: flex;
203 | align-items: center;
204 | justify-content: center;
205 | color: #2c3e50;
206 | }
207 |
208 | .card {
209 | margin: 2rem;
210 | min-width: 800px;
211 | background-color: purple;
212 | color: white;
213 | display: block
214 | }
215 |
216 | .card-hide {
217 | display: none;
218 | }
219 |
220 | .loading {
221 | font-size: 3rem;
222 | }
223 |
224 | ::-webkit-scrollbar {
225 | width: 20px;
226 | }
227 |
228 | /* Track */
229 |
230 | ::-webkit-scrollbar-track {
231 | box-shadow: inset 0 0 5px grey;
232 | border-radius: 10px;
233 | }
234 |
235 | /* Handle */
236 |
237 | ::-webkit-scrollbar-thumb {
238 | background: grey;
239 | border-radius: 10px;
240 | }
241 |
242 | /* Handle on hover */
243 |
244 | ::-webkit-scrollbar-thumb:hover {
245 | background: darkgrey;
246 | }
247 |
248 | div.header {
249 | display: inline-block;
250 | }
251 |
252 | #bee_wrapper {
253 | display: inline-block;
254 | z-index: 100;
255 | }
256 |
257 | .drop_div {
258 | min-width: 600px;
259 | min-height: 400px;
260 | padding: 40px;
261 | margin: 10px;
262 | line-height: 1.3em;
263 | border: 2px dashed lightgrey;
264 | border-radius: 20px;
265 | box-shadow: 0 0 0 0, 1px 1px 6px 4px rgba(10, 10, 0, 0.2);
266 | }
267 |
268 | .modal {
269 | min-width: 400px;
270 | min-height: 600px;
271 | }
272 |
273 |
274 | .drop-zone-hide{
275 | display: none;
276 | }
277 |
278 | .drop-zone-show{
279 | position: fixed;
280 | display: flex;
281 | background: rgba(17, 109, 210, 0.8);
282 | width: 100%;
283 | height: 100%;
284 | z-index: 200;
285 | top: 0;
286 | left: 0;
287 | right: 0;
288 | bottom: 0;
289 | justify-content: center;
290 | align-items: center;
291 | text-align: center;
292 | }
293 |
294 | .drop_here_txt{
295 | color: #ffffff;
296 | }
297 |
298 | .dropzone_div{
299 | margin-top: 40px;
300 |
301 | }
302 | .drag_div{
303 | min-width: 800px;
304 | min-height: 400px;
305 | max-width: 800px;
306 | max-height: 400px;
307 | padding: 20px;
308 | margin: 10px;
309 | border: 6px dashed lightgrey;
310 | border-radius: 10px;
311 | background-color: #f4f4f4;
312 | justify-content: center;
313 | align-items:center;
314 | display: flex;
315 | flex-direction: column;
316 | }
317 |
318 | .cloud_upload{
319 | height: 30%;
320 | width: 30%;
321 | align-self: center;
322 | }
323 |
324 | .header{
325 | display: block;
326 | align-self: center;
327 | }
328 |
329 | .parent{
330 | justify-content: center;
331 | align-items: center;
332 | display: flex;
333 | flex-direction: column;
334 | }
335 |
336 | .main {
337 | display: flex;
338 | justify-content: center;
339 | align-items:center;
340 | flex-direction: column;
341 | }
342 |
343 |
344 | @keyframes slideInFromLeft {
345 | 0% {
346 | transform: translateX(-100%);
347 | }
348 | 100% {
349 | transform: translateX(0);
350 | }
351 | }
352 |
353 | @keyframes slideInFromRight {
354 | 0% {
355 | transform: translateX(100%);
356 | }
357 | 100% {
358 | transform: translateX(0);
359 | }
360 | }
361 |
362 | @keyframes fadein {
363 | from {
364 | opacity:0;
365 | }
366 | to {
367 | opacity:1;
368 | }
369 | }
370 | @-moz-keyframes fadein { /* Firefox */
371 | from {
372 | opacity:0;
373 | }
374 | to {
375 | opacity:1;
376 | }
377 | }
378 | @-webkit-keyframes fadein { /* Safari and Chrome */
379 | from {
380 | opacity:0;
381 | }
382 | to {
383 | opacity:1;
384 | }
385 | }
386 | @-o-keyframes fadein { /* Opera */
387 | from {
388 | opacity:0;
389 | }
390 | to {
391 | opacity: 1;
392 | }
393 | }
394 |
395 |
396 |
397 | /* make keyframes that tell the start state and the end state of our object */
398 | @-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
399 | @-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
400 | @keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
401 |
402 | .fade-in {
403 | opacity:.8; /* make things invisible upon start */
404 | -webkit-animation:fadeIn ease-in 1; /* call our keyframe named fadeIn, use animattion ease-in and repeat it only 1 time */
405 | -moz-animation:fadeIn ease-in 1;
406 | animation:fadeIn ease-in 1;
407 |
408 | -webkit-animation-fill-mode:forwards; /* this makes sure that after animation is done we remain at the last keyframe value (opacity: 1)*/
409 | -moz-animation-fill-mode:forwards;
410 | animation-fill-mode:forwards;
411 |
412 | -webkit-animation-duration:.2s;
413 | -moz-animation-duration:.2s;
414 | animation-duration:.2s;
415 |
416 |
417 | }
418 |
--------------------------------------------------------------------------------
/app/src/components/data_viz/D3StarBurstChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import * as d3 from "d3";
3 | import { connect } from "react-redux";
4 |
5 | import DisplayButton from "./helper_components/DisplayButton.jsx";
6 | import * as chart from "../../redux/constants/chartProperties.js";
7 | import {
8 | displaySizes,
9 | displayFactoryTimes,
10 | displayBuildingTimes
11 | } from "../../redux/actions/chartActions.js";
12 | import OpenFolderOfOneButton from "../OpenFolderOfOneButton";
13 |
14 | class D3StarBurstChart extends Component {
15 | constructor(props) {
16 | super(props);
17 | }
18 |
19 | componentDidMount() {
20 | this.instantiateStarburstChart();
21 | }
22 |
23 | componentDidUpdate() {
24 | this.instantiateStarburstChart();
25 | }
26 |
27 | // D3 Starburst init
28 | instantiateStarburstChart() {
29 |
30 | // hidden until mouseover-ed
31 | d3.select(".d3_tooltip").style("visibility", "hidden");
32 | d3.select("#sb_d3_explanation").style("visibility", "hidden");
33 |
34 | // show total size and speed
35 | d3.selectAll("#totals_display").remove();
36 |
37 | var totals_display = d3
38 | .select(".sb_d3_box")
39 | .append("div")
40 | .attr("class", "totals_display");
41 | totals_display.append("span").attr("id", "totals_display");
42 |
43 | // specify totals according to data type
44 | let totals_display_html;
45 | if (this.props.chart.screen === chart.SIZE) {
46 | totals_display_html = `Total Size: ${this.props.activeData.total.totalBundleSize/1000} kb
47 | `
48 | } else if (this.props.chart.screen === chart.BUILDING_TIME) {
49 | totals_display_html = `Total Building Time: ${this.props.activeData.total.totalElapsedTime} milliseconds
50 | `
51 | } else if (this.props.chart.screen === chart.FACTORY_TIME) {
52 | totals_display_html = `Total Factory Time: ${this.props.activeData.total.factory} milliseconds
53 | `
54 | }
55 |
56 | totals_display.select("#totals_display").html(totals_display_html);
57 |
58 | // MOUSEOVER EVENTS
59 | const mouseover = d => {
60 | var percentage = (
61 | (100.0 * d.value) /
62 | this.props.activeData.total[this.props.chart.screen]
63 | ).toPrecision(3);
64 |
65 | var percentageString = percentage + "%";
66 | if (percentage < 0.1) {
67 | percentageString = "< 0.1%";
68 | }
69 | // set tooltip position and content
70 | var tooltip = d3
71 | .select(".sb_d3_box")
72 | .append("div")
73 | .attr("class", "d3_tooltip");
74 | tooltip.append("span");
75 | tooltip.append("span").attr("id", "sb_d3_details");
76 |
77 | tooltip
78 | .style("top", d3.event.layerY +280+ "px")
79 | .style("left", d3.event.layerX + 150 + "px");
80 |
81 | console.log(d3.event.layerY , d3.event.layerX )
82 | tooltip.select("#sb_d3_details").html(
83 | `
84 | Filename: ${d.data.name}
85 | Size: ${d.value / 1000} kb
86 | Percentage: ${percentageString}
87 | `
88 | );
89 |
90 | var percent = 10;
91 |
92 | tooltip.style("display", "block");
93 |
94 | tooltip.style("position", "absolute");
95 |
96 |
97 | // BREADCRUMBS info
98 | var sequenceArray = d.ancestors().reverse();
99 | console.log(sequenceArray, "BREADCRUMBS");
100 | sequenceArray.shift(); // remove root node from the array
101 | let trickArray = sequenceArray.slice(0);
102 |
103 | // convert path array to a '/' seperated path string. add '/' at the end if it's a directory.
104 | const path =
105 | "./" +
106 | trickArray.map(node => node.data.name).join("/") +
107 | (trickArray[trickArray.length - 1].children ? "/" : "");
108 |
109 | console.log(path, "PATH");
110 | for (var i = 1; i < trickArray.length + 1; i++) {
111 | updateBreadcrumbs(trickArray.slice(0, i), percentageString);
112 | }
113 |
114 | // Fade all the segments.
115 | d3.selectAll("#chart")
116 | .selectAll("path")
117 | .style("opacity", 0.3);
118 |
119 | // Then highlight only those that are an ancestor of the current segment.
120 | vis
121 | .selectAll("path")
122 | .filter(function(node) {
123 | return sequenceArray.indexOf(node) >= 0;
124 | })
125 | .style("opacity", 1);
126 | };
127 |
128 | // MOUSELEAVE EVENTS
129 |
130 | const mouseleave = d => {
131 | d3.select(".d3_tooltip").style("visibility", "hidden");
132 | d3.select("#trail").style("visibility", "hidden");
133 | d3.select("#sb_d3_explanation").style("visibility", "hidden");
134 | d3.select("#sb_d3_explanation").style("visibility", "hidden");
135 | d3.selectAll("#chart")
136 | .selectAll("path")
137 | .transition()
138 | .duration(1000)
139 | .style("opacity", 1)
140 | .on("end", function() {
141 | d3.select(this).on("mouseover", mouseover);
142 | });
143 | d3.selectAll(".d3_tooltip").remove();
144 |
145 | console.log("mouseleave");
146 | };
147 |
148 | function initializeBreadcrumbTrail() {
149 | // Add the svg area.
150 | var trail = d3
151 | .select("#sequence")
152 | .append("svg:svg")
153 | .attr("width", width + 500)
154 | .attr("height", 50)
155 | .attr("id", "trail");
156 | }
157 |
158 | // Generate a string that describes the points of a breadcrumb polygon.
159 | function breadcrumbPoints(d, i) {
160 | var points = [];
161 | points.push("0,0");
162 | points.push(b.w + d.data.name.length * 7.5 + ",0"); //CONTROLS THE SHAPE OF THE POLYGON
163 | points.push(b.w + d.data.name.length * 7.5 + b.t + "," + b.h / 2);
164 | points.push(b.w + d.data.name.length * 7.5 + "," + b.h);
165 | points.push("0," + b.h);
166 | if (i > 0) {
167 | // Leftmost breadcrumb; don't include 6th vertex.
168 | points.push(b.t + "," + b.h / 2);
169 | }
170 | return points.join(" ");
171 | }
172 | // Update the breadcrumb trail to show the current sequence and percentage.
173 | function updateBreadcrumbs(nodeArray, percentageString) {
174 | // Data join; key function combines name and depth (= position in sequence).
175 | var trail = d3
176 | .select("#trail")
177 | .selectAll("g")
178 | .data(nodeArray, function(d) {
179 | return d.data.name + d.depth;
180 | });
181 |
182 | // Remove exiting nodes.
183 | trail.exit().remove();
184 |
185 | // Add breadcrumb and label for entering nodes.
186 | var entering = trail.enter().append("svg:g");
187 |
188 | entering
189 | .append("svg:polygon")
190 | .attr("points", breadcrumbPoints)
191 | .style("fill", function(d) {
192 | return "#53c79f";
193 | });
194 |
195 | entering
196 | .append("svg:text")
197 | .attr("x", (b.w + b.t) / 2)
198 | .attr("y", b.h / 2)
199 | .attr("dy", "0.35em")
200 | .attr("text-anchor", "start")
201 | .text(function(d) {
202 | return d.data.name;
203 | });
204 |
205 | // Now move and update the percentage at the end.
206 | var nodeAryFlat = "";
207 |
208 | for (var i = 0; i < nodeArray.length; i++) {
209 | nodeAryFlat = nodeAryFlat + " " + nodeArray[i].data.name;
210 | }
211 |
212 | var nodeAryFlatLength = 0;
213 | var nodeAryFlatLengthPercentage = 0;
214 | for (var i = 1; i < nodeArray.length; i++) {
215 | nodeAryFlatLength =
216 | nodeAryFlatLength +
217 | b.w +
218 | nodeArray[i - 1].data.name.length * 7.5 +
219 | b.t;
220 | nodeAryFlatLengthPercentage =
221 | nodeAryFlatLength +
222 | b.w +
223 | nodeArray[i].data.name.length * 7.5 +
224 | b.t +
225 | 15;
226 | }
227 |
228 | entering.attr("transform", function(d, i) {
229 | if (i === 0) {
230 | return "translate(0, 0)";
231 | } else {
232 | return "translate(" + nodeAryFlatLength + ", 0)"; //POSITIONING OF WORDS
233 | }
234 | });
235 |
236 | d3.select("#trail")
237 | .select("#endlabel")
238 | .attr("x", nodeAryFlatLengthPercentage) //CONTROLS WHERE THE PERCENTAGE IS LOCATED
239 | .attr("y", b.h / 2)
240 | .attr("dy", "0.35em")
241 | .attr("text-anchor", "start")
242 | .text(percentageString);
243 |
244 | // Make the breadcrumb trail visible, if it's hidden.
245 | d3.select("#trail").style("visibility", "");
246 | }
247 |
248 | // remove element from