Full Code of threepointone/ratpack for AI

master f6ba35e97b39 cached
30 files
111.7 KB
48.8k tokens
28 symbols
1 requests
Download .txt
Repository: threepointone/ratpack
Branch: master
Commit: f6ba35e97b39
Files: 30
Total size: 111.7 KB

Directory structure:
gitextract___5ovaq3/

├── .babelrc
├── .gitignore
├── bin/
│   └── ratpack
├── docs/
│   └── pragmas.md
├── examples/
│   ├── polyf.js
│   ├── pragmas.js
│   └── scratch.js
├── lib/
│   ├── app.js
│   ├── index.html
│   ├── main.js
│   ├── offline-plugin-runtime.js
│   ├── polyfills.js
│   ├── pragmas.js
│   └── stats.js
├── package.json
├── public/
│   └── index.html
├── readme.md
├── resources/
│   ├── .eslintrc
│   ├── Info.plist
│   └── icon.icns
├── scripts/
│   └── package-app.js
├── spiel.md
├── src/
│   ├── app.js
│   ├── index.html
│   ├── main.js
│   ├── offline-plugin-runtime.js
│   ├── polyfills.js
│   ├── pragmas.js
│   └── stats.js
└── todo.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [ [ "babel-preset-env", {
    "targets": {
      "browsers": ["last 2 versions", "safari >= 7"]
    } } ], "stage-0", "react" ],
  "plugins": [
    "glamor/babel-hoist",
    ["transform-react-jsx", {
      "pragma": "require('glamor/react').createElement"
    }]
  ],
  "ignore": [
    "node_modules/*"
  ]
}

================================================
FILE: .gitignore
================================================
# osx noise
.DS_Store
profile

# xcode noise
build/*
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
*.xcworkspace
xcuserdata

# svn & cvs
.svn
CVS
node_modules
out


================================================
FILE: bin/ratpack
================================================
#!/usr/bin/env node
// todo - windows
let opn = require('opn')
let spawn = require('child_process').spawn
let fs = require('fs')
let path = require('path')
let appdir = fs.readdirSync(path.join(__dirname, '../out')).filter(x => /^ratpack\-/.test(x))[0] 
let argv = require('minimist')(process.argv.slice(1))
if(argv.app) {
  // ratpack --app  
  // opens directory holding the app
  opn(path.join(__dirname, `../out/${appdir}`), { wait: false }).then(
    () => process.exit(0), 
    err => console.error(err)) // eslint-disable-line no-console
}
else {
  // ratpack
  // ratpack path/to/script.js 
  // this should open a new app instance, not hold a process in terminal. 
  // todo - windows, linux
  let binpath = path.join(__dirname, `../out/${appdir}/ratpack.app/Contents/MacOS/ratpack`)

  let script = argv._[1], 
    opts = argv.dev ? { stdio: 'inherit' } : { detached: true }, 
    args = script ? 
      [ binpath, [ path.join(process.cwd(), script) ], opts ]:
      [ binpath, [], opts ],
    child = spawn(...args)
  
  if(!argv.dev) {
    child.unref()
    process.exit(0)
  } 
}

// that's it. no other apis planned for now.


================================================
FILE: docs/pragmas.md
================================================
magic pragmas
---

ratpack supports some magic incantations you can put at the top of your entry file 
to customize the webpack/babel backends. A simple example would look like this - 

```jsx
/* @ratpack {
  devtool: 'eval',
  alias: {
    react: 'preact-compat',
    react-dom: 'preact-compat'
  }
}
*/
require('react-dom').render(<div>look, preact!</div>, window.root)
```
(assuming preact-compat is available locally or in `.ratpack/node_modules`)


Here's a full list of working pragmas

- `devtool: 'source-map'` [(ref.)](https://webpack.js.org/configuration/devtool/#devtool)
- `reload: false` toggle live reloads, defaults to `true`
- `port: 3999` 
- `stats: true` shows a [stats.js](https://github.com/mrdoob/stats.js/) fps window
- `production: true` particularly good for running benchmarks, etc
- `public: './my/public/folder'` [(ref.)](https://webpack.js.org/configuration/dev-server/#devserver-contentbase)
- `jsx: 'Inferno.createElement'` This is different from babel's `@jsx` pragma, in that it will apply to *all* js files, not just the one 
- `proxy: { '/api': 'http://localhost:3000' }` [(ref.)](https://webpack.js.org/configuration/dev-server/#devserver-proxy)
- `provide: { 'Glamor': 'glamor/react' }` [(ref.)](https://webpack.js.org/guides/shimming/#provide-plugin)
- `alias`
  
  ```
  alias: {
    react: 'preact-compat',
    react-dom: 'preact-compat'
  }
  ```
  [(ref.)](https://webpack.js.org/configuration/resolve/#resolve-alias)
- `define: { 'process.env.NODE_ENV': 'test' }` [(ref.)](https://webpack.js.org/plugins/define-plugin/)
- `rules: [ { files: '*.vue', loader: 'vue-loader', options: { some: 'options' } } , ...]` 
  
  nb: `files` is a glob, which gets converted to a regex for webpack's `test` prop 
- babel presets and plugins
  
  ```
  babel: {
    presets: ['vue', [ 'a11y', {...} ],
    plugins: ['./some/path', ... ]
  }
  ```



These don't 'work' yet, but could/will in the future

- `target: 'node'` [(ref.)](https://webpack.js.org/configuration/target/#target)
- `hot: true` [(ref.)](https://webpack.js.org/concepts/hot-module-replacement/)
- `offline: true || <options>` [(ref.)](https://github.com/NekR/offline-plugin)
- `autoinstall: true` [(ref.)](https://github.com/ericclemmons/npm-install-webpack-plugin)
- `plugins: [ {<module> <options>}, ... ]`



thoughts/questions/ideas?

================================================
FILE: examples/polyf.js
================================================
let x = function* () {

}


================================================
FILE: examples/pragmas.js
================================================
/* @ratpack {
  target: 'node',
  devtool: 'eval',
  public: '../public',
  proxy: {
    '/yahoo': 'http://www.yahoo.com'
  },
  define: {
    'process.env.NODE_ENV': 'test'
  },
  offline: true,
  autoinstall: true,
  rules: [{ loader: 'cowsay-loader' }]
}
*/

/* eslint semi: 0 */

import { render } from 'react-dom';
render(<div>456</div>, window.root);

fetch('/yahoo')
 


================================================
FILE: examples/scratch.js
================================================
// @ratpack { stats: true }
console.log('here') //eslint-disable-line no-console

require('react-dom').render(<div>what up</div>, window.root)
console.log(123)

================================================
FILE: lib/app.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _querystring = require('querystring');

var _querystring2 = _interopRequireDefault(_querystring);

var _autoprefixer = require('autoprefixer');

var _autoprefixer2 = _interopRequireDefault(_autoprefixer);

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _reactDom = require('react-dom');

require('glamor/reset');

var _hash = require('glamor/lib/hash');

var _hash2 = _interopRequireDefault(_hash);

var _pragmas = require('./pragmas');

var _pragmas2 = _interopRequireDefault(_pragmas);

var _globToRegexp = require('glob-to-regexp');

var _globToRegexp2 = _interopRequireDefault(_globToRegexp);

var _openBrowser = require('react-dev-utils/openBrowser');

var _openBrowser2 = _interopRequireDefault(_openBrowser);

var _offlinePlugin = require('offline-plugin');

var _offlinePlugin2 = _interopRequireDefault(_offlinePlugin);

var _webpackDevServer = require('webpack-dev-server');

var _webpackDevServer2 = _interopRequireDefault(_webpackDevServer);

var _webpack = require('webpack');

var _webpack2 = _interopRequireDefault(_webpack);

var _glamor = require('glamor');

var _nedb = require('nedb');

var _nedb2 = _interopRequireDefault(_nedb);

var _mkdirp = require('mkdirp');

var _mkdirp2 = _interopRequireDefault(_mkdirp);

var _touch = require('touch');

var _touch2 = _interopRequireDefault(_touch);

var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var electron = require('electron');
var app = electron.app || electron.remote.app;

var yellow = '#f7df1e';

var db = new _nedb2.default({
  filename: _path2.default.join(app.getPath('userData'), 'store.db'),
  autoload: true
});
db.find({ _id: 'recently' }, function (err, docs) {
  if (docs.length === 0) {
    db.insert({ _id: 'recently', files: [] }, function (err) {
      if (err) return console.error(err); //eslint-disable-line no-console
      console.log('db initialized'); //eslint-disable-line no-console
    });
  } else console.log('db restarted'); //eslint-disable-line no-console
});

// todo - move this to main.js 


// todo - windows
(0, _mkdirp2.default)(_path2.default.join(app.getPath('home'), '.ratpack'), function (err) {
  if (err) {
    throw err;
  }
  var pkjson = _path2.default.join(app.getPath('home'), '.ratpack/package.json');
  if (!_fs2.default.existsSync(pkjson)) {
    _touch2.default.sync(pkjson);
    _fs2.default.writeFileSync(pkjson, JSON.stringify({
      name: 'ratpack-local',
      description: 'these modules are available to all scripts launched by ratpack'
    }));
  }
  // among other things, this makes loaders defined in pragmas to work 
  require('module').globalPaths.push(_path2.default.join(app.getPath('home'), '.ratpack/node_modules'));
});

function times(n, fn) {
  var arr = [];
  for (var i = 0; i < n; i++) {
    arr.push(fn(i));
  }
  return arr;
}

_glamor.css.global('html, body, #root', { position: 'relative', width: '100%', height: '100%', display: 'block', backgroundColor: yellow });

var Logo = function Logo() {
  return require('glamor/react').createElement(
    'div',
    { css: _defineProperty({ width: 100, height: 100, position: 'absolute', bottom: 0, right: 0 }, _glamor.presets.Phablet, { width: 200, height: 200 }) },
    require('glamor/react').createElement(
      'svg',
      { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 630 630' },
      require('glamor/react').createElement('path', { d: 'm423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z' })
    )
  );
};

var _ref = { width: '100%', height: '100%', fontFamily: 'helvetica' };
var _ref2 = { padding: 20 };

var App = function (_React$Component) {
  _inherits(App, _React$Component);

  function App() {
    var _ref4;

    var _temp, _this, _ret;

    _classCallCheck(this, App);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref4 = App.__proto__ || Object.getPrototypeOf(App)).call.apply(_ref4, [this].concat(args))), _this), _this.state = {
      tick: 0,
      filepath: undefined,
      webpackCompiler: null,
      webpackServer: null,
      recently: [],
      errors: [],
      port: 0,
      running: false
    }, _this.onOpenFile = function (e, filepath) {
      _this.loadFile(filepath);
    }, _this.onDrop = function (e) {
      e.preventDefault();
      e.stopPropagation();
      var filepath = e.nativeEvent.dataTransfer.files[0].path;
      _this.loadFile(filepath);
    }, _temp), _possibleConstructorReturn(_this, _ret);
  }

  _createClass(App, [{
    key: 'refreshRecentList',
    value: function refreshRecentList(cb) {
      var _this2 = this;

      db.find({ _id: 'recently' }, function (err, docs) {
        if (err) {
          _this2.setState({
            errors: [].concat(_toConsumableArray(_this2.state.errors), [err])
          });
          return;
        }
        _this2.setState({
          recently: docs[0].files
        });
        if (cb) cb();
      });
    }
  }, {
    key: 'componentDidMount',
    value: function componentDidMount() {
      var _this3 = this;

      app.on('open-file', this.onOpenFile);
      this.refreshRecentList(function () {
        if (window.location.search) {
          var filepath = _querystring2.default.parse(window.location.search.slice(1)).startsWith;
          filepath && _this3.loadFile(filepath);
        }
      });
      this.interval = setInterval(function () {
        _this3.setState({
          tick: (_this3.state.tick + 1) % 4
        });
      }, 400);
    }
  }, {
    key: 'componentWillUnmount',
    value: function componentWillUnmount() {
      clearInterval(this.interval);
      this.interval = null;

      if (this.state.webpackServer) {
        this.state.webpackServer.close();
      }
      if (this.watcher) {
        this.watcher.close();
        this.watcher = null;
      }
      app.removeEventListener('open-file', this.onOpenFile);
    }
  }, {
    key: '_loadFile',
    value: function _loadFile(filepath) {
      var _this4 = this;

      _fs2.default.readFile(filepath, 'utf8', function (err, src) {
        if (err) throw err;
        var options = (0, _pragmas2.default)(src);
        _this4.setState(_extends({}, webpackify(filepath, options), { filepath: filepath, running: true, pragmas: options }));

        // simultaneously start watching the entry file 
        _this4.watcher = _fs2.default.watch(filepath, function (e) {
          if (e === 'rename') {
            // ???
            return;
          }
          // if any of the pragmas change, redo this shindig 
          _fs2.default.readFile(filepath, 'utf8', function (err, src) {
            var options = (0, _pragmas2.default)(src);
            if (JSON.stringify(options) !== JSON.stringify(_this4.state.pragmas)) {
              _this4.loadFile(filepath);
            }
            // todo - prevent double read 
          });
        });
      });
    }
  }, {
    key: 'loadFile',
    value: function loadFile(filepath) {
      var _this5 = this;

      db.update({ _id: 'recently' }, { _id: 'recently', files: [{ path: filepath }].concat(_toConsumableArray(this.state.recently.filter(function (x) {
          return x.path !== filepath;
        }))).slice(0, 10) }, {}, function (err) {
        if (err) {
          _this5.setState({
            errors: [].concat(_toConsumableArray(_this5.state.errors), [err])
          });
          return;
        }
        _this5.refreshRecentList();
      });
      if (this.watcher) {
        this.watcher.close();
        this.watcher = null;
      }

      if (this.state.webpackServer) {
        this.state.webpackServer.close();
        setTimeout(function () {
          _this5._loadFile(filepath);
        }, 500);
      } else {
        this._loadFile(filepath);
      }
    }
  }, {
    key: 'clearRecentList',
    value: function clearRecentList() {
      var _this6 = this;

      db.update({ _id: 'recently' }, { _id: 'recently', files: [] }, function () {
        _this6.refreshRecentList();
      });
    }
  }, {
    key: 'render',
    value: function render() {
      var _this7 = this;

      return require('glamor/react').createElement(
        'div',
        {
          css: _ref,
          onDragOver: function onDragOver(e) {
            return e.preventDefault();
          } // chrome bug 
          , onDrop: this.onDrop },
        require('glamor/react').createElement(
          'div',
          { css: _defineProperty({ fontWeight: 'bolder', fontSize: 32, padding: 20 }, _glamor.presets.Phablet, { fontSize: 64 }) },
          this.state.running ? _path2.default.basename(this.state.filepath) + ' running at \n        localhost:' + this.state.port + times(this.state.tick, function () {
            return '.';
          }).join('') : 'Drop a .js file here to get started '
        ),
        this.state.recently.length > 0 && require('glamor/react').createElement(
          'div',
          { css: _ref2 },
          require('glamor/react').createElement(
            'h1',
            null,
            'previously...'
          ),
          this.state.recently.map(function (x) {
            return require('glamor/react').createElement(
              'div',
              { key: x.path, onClick: function onClick() {
                  return _this7.loadFile(x.path);
                } },
              x.path
            );
          }),
          require('glamor/react').createElement(
            'h4',
            { onClick: function onClick() {
                return _this7.clearRecentList();
              } },
            'clear list'
          )
        ),
        require('glamor/react').createElement(Logo, null)
      );
    }
  }]);

  return App;
}(_react2.default.Component);

function webpackify(filepath) {
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


  var webpackCompiler = (0, _webpack2.default)({
    devtool: options.production ? false : options.devtool || 'cheap-module-source-map',
    entry: [options.reload !== false || options.production !== true ? require.resolve('react-dev-utils/webpackHotDevClient.js') : undefined, options.stats ? require.resolve('./stats.js') : undefined, require.resolve('./polyfills'), options.offline ? require.resolve('./offline-plugin-runtime.js') : undefined, filepath].filter(function (x) {
      return !!x;
    }),
    output: {
      path: _path2.default.join(__dirname, '../public'),
      pathinfo: true,
      filename: 'bundle.js'
    },
    performance: {
      hints: false
    },
    module: {
      rules: [].concat(_toConsumableArray((options.rules || []).map(function (_ref6) {
        var loader = _ref6.loader,
            files = _ref6.files,
            options = _ref6.options;
        return { loader: require.resolve(loader), options: options, test: (0, _globToRegexp2.default)(files || '*') };
      })), [{
        enforce: 'pre',
        test: /\.(js|jsx)$/,
        loader: require.resolve('eslint-loader'),
        exclude: /node_modules/,
        options: {
          configFile: _path2.default.join(__dirname, '../resources/.eslintrc')
        }
      }, {
        exclude: [/\.html$/, /\.(js|jsx)$/, /\.css$/, /\.json$/, /\.svg$/],
        loader: require.resolve('url-loader'),
        query: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]'
        }
      }, {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: require.resolve('babel-loader'),
        options: {
          'presets': [[require('babel-preset-env'), {
            'targets': {
              'browsers': ['last 2 versions', 'safari >= 7']
            },
            modules: false
          }], require('babel-preset-stage-0'), require('babel-preset-react')].concat(_toConsumableArray((options.babel || {}).presets || [])),
          'plugins': [[require.resolve('babel-plugin-transform-runtime'), {
            helpers: false,
            polyfill: false,
            regenerator: true
            // Resolve the Babel runtime relative to the config.
            // moduleName: path.dirname(require.resolve('babel-runtime/package'))
          }], options.jsx ? [require('babel-plugin-transform-react-jsx'), { 'pragma': options.jsx }] : undefined, require('babel-plugin-transform-decorators-legacy').default, require('babel-plugin-transform-react-require').default].concat(_toConsumableArray((options.babel || {}).plugins || [])).filter(function (x) {
            return !!x;
          }),
          cacheDirectory: false
        }
      }, {
        test: /\.css$/,
        use: [require.resolve('style-loader'), {
          loader: require.resolve('css-loader'),
          options: { importLoaders: 1 }
        }, require.resolve('postcss-loader') // options in the plugins section below             
        ]
      },
      // {
      //   test: /\.json$/,
      //   loader: require.resolve('json-loader')
      // },
      {
        test: /\.svg$/,
        loader: require.resolve('file-loader'),
        query: {
          name: 'static/media/[name].[hash:8].[ext]'
        }
      }])
    },
    resolve: {
      alias: options.alias || {},
      extensions: ['.js', '.json', '.jsx'],
      modules: ['node_modules', _path2.default.join(app.getPath('home'), '.ratpack/node_modules'), _path2.default.join(__dirname, '../node_modules')]
    },
    plugins: [new _webpack2.default.DefinePlugin(_extends({
      'process.env.NODE_ENV': JSON.stringify(options.production && 'production' || process.env.NODE_ENV || 'development')
    }, Object.keys(options.define || {}).reduce(function (o, key) {
      return _extends({}, o, _defineProperty({}, key, JSON.stringify(options.define[key])));
    }, {}))), options.offline ? new _offlinePlugin2.default(options.offline === true ? {} : options.offline) : undefined, new _webpack2.default.ProvidePlugin(options.provide || {}), new _webpack2.default.LoaderOptionsPlugin({
      test: /\.css$/,
      debug: true,
      options: {
        postcss: [(0, _autoprefixer2.default)({
          browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9' // React doesn't support IE8 anyway
          ]
        })]
      }
    })].filter(function (x) {
      return !!x;
    }),
    stats: 'errors-only',
    node: {
      fs: 'empty',
      net: 'empty',
      tls: 'empty'
    }
  });

  var webpackServer = new _webpackDevServer2.default(webpackCompiler, {
    contentBase: [options.public ? _path2.default.join(_path2.default.dirname(filepath), options.public) : '', _path2.default.join(_path2.default.dirname(filepath), 'public'), _path2.default.join(__dirname, '../public')].filter(function (x) {
      return !!x;
    }),
    historyApiFallback: true,
    compress: true,
    proxy: options.proxy || {},
    // setup()
    // staticOptions 

    quiet: true,
    stats: { colors: false }
  });
  // this is to workaround some weird bug where webpack keeps the first loaded file 
  // also makes it look cool ha
  var h = (0, _hash2.default)(filepath, filepath.length) + '';
  var port = options.port || 3000 + parseInt(h.substr(h.length - 4), 10);
  webpackServer.listen(port);
  (0, _openBrowser2.default)('http://localhost:' + port);
  return { webpackServer: webpackServer, webpackCompiler: webpackCompiler, port: port };
}

(0, _reactDom.render)(require('glamor/react').createElement(App, null), document.getElementById('root'));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9hcHAuanMiXSwibmFtZXMiOlsiZWxlY3Ryb24iLCJyZXF1aXJlIiwiYXBwIiwicmVtb3RlIiwieWVsbG93IiwiZGIiLCJmaWxlbmFtZSIsImpvaW4iLCJnZXRQYXRoIiwiYXV0b2xvYWQiLCJmaW5kIiwiX2lkIiwiZXJyIiwiZG9jcyIsImxlbmd0aCIsImluc2VydCIsImZpbGVzIiwiY29uc29sZSIsImVycm9yIiwibG9nIiwicGtqc29uIiwiZXhpc3RzU3luYyIsInN5bmMiLCJ3cml0ZUZpbGVTeW5jIiwiSlNPTiIsInN0cmluZ2lmeSIsIm5hbWUiLCJkZXNjcmlwdGlvbiIsImdsb2JhbFBhdGhzIiwicHVzaCIsInRpbWVzIiwibiIsImZuIiwiYXJyIiwiaSIsImdsb2JhbCIsInBvc2l0aW9uIiwid2lkdGgiLCJoZWlnaHQiLCJkaXNwbGF5IiwiYmFja2dyb3VuZENvbG9yIiwiTG9nbyIsImJvdHRvbSIsInJpZ2h0IiwiUGhhYmxldCIsImZvbnRGYW1pbHkiLCJwYWRkaW5nIiwiQXBwIiwic3RhdGUiLCJ0aWNrIiwiZmlsZXBhdGgiLCJ1bmRlZmluZWQiLCJ3ZWJwYWNrQ29tcGlsZXIiLCJ3ZWJwYWNrU2VydmVyIiwicmVjZW50bHkiLCJlcnJvcnMiLCJwb3J0IiwicnVubmluZyIsIm9uT3BlbkZpbGUiLCJlIiwibG9hZEZpbGUiLCJvbkRyb3AiLCJwcmV2ZW50RGVmYXVsdCIsInN0b3BQcm9wYWdhdGlvbiIsIm5hdGl2ZUV2ZW50IiwiZGF0YVRyYW5zZmVyIiwicGF0aCIsImNiIiwic2V0U3RhdGUiLCJvbiIsInJlZnJlc2hSZWNlbnRMaXN0Iiwid2luZG93IiwibG9jYXRpb24iLCJzZWFyY2giLCJwYXJzZSIsInNsaWNlIiwic3RhcnRzV2l0aCIsImludGVydmFsIiwic2V0SW50ZXJ2YWwiLCJjbGVhckludGVydmFsIiwiY2xvc2UiLCJ3YXRjaGVyIiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsInJlYWRGaWxlIiwic3JjIiwib3B0aW9ucyIsIndlYnBhY2tpZnkiLCJwcmFnbWFzIiwid2F0Y2giLCJ1cGRhdGUiLCJmaWx0ZXIiLCJ4Iiwic2V0VGltZW91dCIsIl9sb2FkRmlsZSIsImZvbnRXZWlnaHQiLCJmb250U2l6ZSIsImJhc2VuYW1lIiwibWFwIiwiY2xlYXJSZWNlbnRMaXN0IiwiQ29tcG9uZW50IiwiZGV2dG9vbCIsInByb2R1Y3Rpb24iLCJlbnRyeSIsInJlbG9hZCIsInJlc29sdmUiLCJzdGF0cyIsIm9mZmxpbmUiLCJvdXRwdXQiLCJfX2Rpcm5hbWUiLCJwYXRoaW5mbyIsInBlcmZvcm1hbmNlIiwiaGludHMiLCJtb2R1bGUiLCJydWxlcyIsImxvYWRlciIsInRlc3QiLCJlbmZvcmNlIiwiZXhjbHVkZSIsImNvbmZpZ0ZpbGUiLCJxdWVyeSIsImxpbWl0IiwibW9kdWxlcyIsImJhYmVsIiwicHJlc2V0cyIsImhlbHBlcnMiLCJwb2x5ZmlsbCIsInJlZ2VuZXJhdG9yIiwianN4IiwiZGVmYXVsdCIsInBsdWdpbnMiLCJjYWNoZURpcmVjdG9yeSIsInVzZSIsImltcG9ydExvYWRlcnMiLCJhbGlhcyIsImV4dGVuc2lvbnMiLCJEZWZpbmVQbHVnaW4iLCJwcm9jZXNzIiwiZW52IiwiTk9ERV9FTlYiLCJPYmplY3QiLCJrZXlzIiwiZGVmaW5lIiwicmVkdWNlIiwibyIsImtleSIsIlByb3ZpZGVQbHVnaW4iLCJwcm92aWRlIiwiTG9hZGVyT3B0aW9uc1BsdWdpbiIsImRlYnVnIiwicG9zdGNzcyIsImJyb3dzZXJzIiwibm9kZSIsImZzIiwibmV0IiwidGxzIiwiY29udGVudEJhc2UiLCJwdWJsaWMiLCJkaXJuYW1lIiwiaGlzdG9yeUFwaUZhbGxiYWNrIiwiY29tcHJlc3MiLCJwcm94eSIsInF1aWV0IiwiY29sb3JzIiwiaCIsInBhcnNlSW50Iiwic3Vic3RyIiwibGlzdGVuIiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCJdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ0E7Ozs7QUFDQTs7OztBQUNBOzs7O0FBQ0E7Ozs7QUFDQTs7QUFD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QUEsS0FBbkssQ0FEMkM7QUFFeERtRSx3QkFBb0IsSUFGb0M7QUFHeERDLGNBQVUsSUFIOEM7QUFJeERDLFdBQU8zRSxRQUFRMkUsS0FBUixJQUFpQixFQUpnQztBQUtwRDtBQUNBOztBQUVKQyxXQUFPLElBUmlEO0FBU3hEeEQsV0FBTyxFQUFFeUQsUUFBUSxLQUFWO0FBVGlELEdBQXRDLENBQXBCO0FBV0U7QUFDQTtBQUNGLE1BQUlDLElBQUksb0JBQUtqSCxRQUFMLEVBQWVBLFNBQVNwQyxNQUF4QixJQUFpQyxFQUF6QztBQUNBLE1BQUkwQyxPQUFPNkIsUUFBUTdCLElBQVIsSUFBaUIsT0FBTzRHLFNBQVNELEVBQUVFLE1BQUYsQ0FBU0YsRUFBRXJKLE1BQUYsR0FBVyxDQUFwQixDQUFULEVBQWlDLEVBQWpDLENBQW5DO0FBQ0F1QyxnQkFBY2lILE1BQWQsQ0FBcUI5RyxJQUFyQjtBQUNBLDZCQUFZLHNCQUFzQkEsSUFBbEM7QUFDQSxTQUFPLEVBQUVILDRCQUFGLEVBQWlCRCxnQ0FBakIsRUFBa0NJLFVBQWxDLEVBQVA7QUFFRDs7QUFFRCxzQkFBTyxzQ0FBQyxHQUFELE9BQVAsRUFBZStHLFNBQVNDLGNBQVQsQ0FBd0IsTUFBeEIsQ0FBZiIsImZpbGUiOiJhcHAuanMiLCJzb3VyY2VzQ29udGVudCI6WyJcbmltcG9ydCBxcyBmcm9tICdxdWVyeXN0cmluZydcbmltcG9ydCBhdXRvcHJlZml4ZXIgZnJvbSAnYXV0b3ByZWZpeGVyJ1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCdcbmltcG9ydCB7IHJlbmRlciB9IGZyb20gJ3JlYWN0LWRvbSdcbmltcG9ydCAnZ2xhbW9yL3Jlc2V0J1xuaW1wb3J0IGhhc2ggZnJvbSAnZ2xhbW9yL2xpYi9oYXNoJ1xuaW1wb3J0IHByYWdtYXMgZnJvbSAnLi9wcmFnbWFzJ1xuaW1wb3J0IGdsb2IycmVnZXhwIGZyb20gJ2dsb2ItdG8tcmVnZXhwJ1xuaW1wb3J0IG9wZW5Ccm93c2VyIGZyb20gJ3JlYWN0LWRldi11dGlscy9vcGVuQnJvd3NlcidcbmltcG9ydCBPZmZsaW5lUGx1Z2luIGZyb20gJ29mZmxpbmUtcGx1Z2luJ1xuXG5jb25zdCBlbGVjdHJvbiA9IHJlcXVpcmUoJ2VsZWN0cm9uJylcbmNvbnN0IGFwcCA9IGVsZWN0cm9uLmFwcCB8fCBlbGVjdHJvbi5yZW1vdGUuYXBwXG5cblxuaW1wb3J0IFdlYnBhY2tEZXZTZXJ2ZXIgZnJvbSAnd2VicGFjay1kZXYtc2VydmVyJ1xuaW1wb3J0IHdlYnBhY2sgZnJvbSAnd2VicGFjaydcblxuaW1wb3J0IHsgY3NzLCBwcmVzZXRzIH0gZnJvbSAnZ2xhbW9yJ1xuY29uc3QgeWVsbG93ID0gJyNmN2RmMWUnXG5cblxuaW1wb3J0IERhdGFTdG9yZSBmcm9tICduZWRiJ1xubGV0IGRiID0gbmV3IERhdGFTdG9yZSh7XG4gIGZpbGVuYW1lOiBwYXRoLmpvaW4oYXBwLmdldFBhdGgoJ3VzZXJEYXRhJyksICdzdG9yZS5kYicpLFxuICBhdXRvbG9hZDogdHJ1ZVxufSlcbmRiLmZpbmQoeyBfaWQ6ICdyZWNlbnRseScgfSwgKGVyciwgZG9jcykgPT4ge1xuICBpZihkb2NzLmxlbmd0aCA9PT0gMCkge1xuICAgIGRiLmluc2VydCh7IF9pZDogJ3JlY2VudGx5JywgZmlsZXM6IFtdIH0sIGVyciA9PiB7XG4gICAgICBpZihlcnIpIHJldHVybiBjb25zb2xlLmVycm9yKGVycikgLy9lc2xpbnQtZGlzYWJsZS1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUubG9nKCdkYiBpbml0aWFsaXplZCcpIC8vZXNsaW50LWRpc2FibGUtbGluZSBuby1jb25zb2xlXG4gICAgfSlcbiAgfVxuICBlbHNlIGNvbnNvbGUubG9nKCdkYiByZXN0YXJ0ZWQnKSAgLy9lc2xpbnQtZGlzYWJsZS1saW5lIG5vLWNvbnNvbGVcbn0pXG5cbi8vIHRvZG8gLSBtb3ZlIHRoaXMgdG8gbWFpbi5qcyBcbmltcG9ydCBta2RpcnAgZnJvbSAnbWtkaXJwJ1xuaW1wb3J0IHRvdWNoIGZyb20gJ3RvdWNoJ1xuaW1wb3J0IGZzIGZyb20gJ2ZzJ1xuXG5cbi8vIHRvZG8gLSB3aW5kb3dzXG5ta2RpcnAocGF0aC5qb2luKGFwcC5nZXRQYXRoKCdob21lJyksICcucmF0cGFjaycpLCBlcnIgPT4ge1xuICBpZihlcnIpIHtcbiAgICB0aHJvdyBlcnJcbiAgfVxuICBsZXQgcGtqc29uID0gcGF0aC5qb2luKGFwcC5nZXRQYXRoKCdob21lJyksICcucmF0cGFjay9wYWNrYWdlLmpzb24nKVxuICBpZighZnMuZXhpc3RzU3luYyhwa2pzb24pKSB7XG4gICAgdG91Y2guc3luYyhwa2pzb24pXG4gICAgZnMud3JpdGVGaWxlU3luYyhwa2pzb24sIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIG5hbWU6ICdyYXRwYWNrLWxvY2FsJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAndGhlc2UgbW9kdWxlcyBhcmUgYXZhaWxhYmxlIHRvIGFsbCBzY3JpcHRzIGxhdW5jaGVkIGJ5IHJhdHBhY2snXG4gICAgfSkpICBcbiAgfVxuICAvLyBhbW9uZyBvdGhlciB0aGluZ3MsIHRoaXMgbWFrZXMgbG9hZGVycyBkZWZpbmVkIGluIHByYWdtYXMgdG8gd29yayBcbiAgcmVxdWlyZSgnbW9kdWxlJykuZ2xvYmFsUGF0aHMucHVzaChwYXRoLmpvaW4oYXBwLmdldFBhdGgoJ2hvbWUnKSwgJy5yYXRwYWNrL25vZGVfbW9kdWxlcycpKVxuICBcbn0pXG5cbmZ1bmN0aW9uIHRpbWVzKG4sIGZuKSB7XG4gIGxldCBhcnIgPSBbXVxuICBmb3IobGV0IGk9IDA7IGk8IG47IGkrKykge1xuICAgIGFyci5wdXNoKGZuKGkpKVxuICB9XG4gIHJldHVybiBhcnJcbn1cblxuY3NzLmdsb2JhbCgnaHRtbCwgYm9keSwgI3Jvb3QnLCB7IHBvc2l0aW9uOiAncmVsYXRpdmUnLCB3aWR0aDogJzEwMCUnLCBoZWlnaHQ6ICcxMDAlJywgZGlzcGxheTogJ2Jsb2NrJywgYmFja2dyb3VuZENvbG9yOiB5ZWxsb3cgfSlcblxuY29uc3QgTG9nbyA9ICgpID0+IDxkaXYgY3NzPXt7IHdpZHRoOiAxMDAsIGhlaWdodDogMTAwLCBwb3NpdGlvbjogJ2Fic29sdXRlJywgYm90dG9tOiAwLCByaWdodDogMCwgW3ByZXNldHMuUGhhYmxldF06IHsgd2lkdGg6IDIwMCwgaGVpZ2h0OiAyMDAgfSB9fT5cbiAgPHN2ZyB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIgdmlld0JveD1cIjAgMCA2MzAgNjMwXCIgPlxuICAgIDxwYXRoIGQ9XCJtNDIzLjIgNDkyLjE5YzEyLjY5IDIwLjcyIDI5LjIgMzUuOTUgNTguNCAzNS45NSAyNC41MyAwIDQwLjItMTIuMjYgNDAuMi0yOS4yIDAtMjAuMy0xNi4xLTI3LjQ5LTQzLjEtMzkuM2wtMTQuOC02LjM1Yy00Mi43Mi0xOC4yLTcxLjEtNDEtNzEuMS04OS4yIDAtNDQuNCAzMy44My03OC4yIDg2LjctNzguMiAzNy42NCAwIDY0LjcgMTMuMSA4NC4yIDQ3LjRsLTQ2LjEgMjkuNmMtMTAuMTUtMTguMi0yMS4xLTI1LjM3LTM4LjEtMjUuMzctMTcuMzQgMC0yOC4zMyAxMS0yOC4zMyAyNS4zNyAwIDE3Ljc2IDExIDI0Ljk1IDM2LjQgMzUuOTVsMTQuOCA2LjM0YzUwLjMgMjEuNTcgNzguNyA0My41NiA3OC43IDkzIDAgNTMuMy00MS44NyA4Mi41LTk4LjEgODIuNS01NC45OCAwLTkwLjUtMjYuMi0xMDcuODgtNjAuNTR6bS0yMDkuMTMgNS4xM2M5LjMgMTYuNSAxNy43NiAzMC40NSAzOC4xIDMwLjQ1IDE5LjQ1IDAgMzEuNzItNy42MSAzMS43Mi0zNy4ydi0yMDEuM2g1OS4ydjIwMi4xYzAgNjEuMy0zNS45NCA4OS4yLTg4LjQgODkuMi00Ny40IDAtNzQuODUtMjQuNTMtODguODEtNTQuMDc1elwiIC8+XG4gIDwvc3ZnPlxuPC9kaXY+XG5cbmNsYXNzIEFwcCBleHRlbmRzIFJlYWN0LkNvbXBvbmVudCB7XG4gIHN0YXRlID0ge1xuICAgIHRpY2s6IDAsXG4gICAgZmlsZXBhdGg6IHVuZGVmaW5lZCxcbiAgICB3ZWJwYWNrQ29tcGlsZXI6IG51bGwsXG4gICAgd2VicGFja1NlcnZlcjogbnVsbCxcbiAgICByZWNlbnRseTogW10sXG4gICAgZXJyb3JzOiBbXSxcbiAgICBwb3J0OiAwLFxuICAgIHJ1bm5pbmc6IGZhbHNlXG4gIH1cbiAgb25PcGVuRmlsZSA9IChlLCBmaWxlcGF0aCkgPT4ge1xuICAgIHRoaXMubG9hZEZpbGUoZmlsZXBhdGgpXG4gIH1cbiAgcmVmcmVzaFJlY2VudExpc3QoY2IpIHtcbiAgICBkYi5maW5kKHsgX2lkOiAncmVjZW50bHknIH0sIChlcnIsIGRvY3MpID0+IHtcbiAgICAgIGlmKGVycikge1xuICAgICAgICB0aGlzLnNldFN0YXRlKHtcbiAgICAgICAgICBlcnJvcnM6IFsgLi4udGhpcy5zdGF0ZS5lcnJvcnMsIGVyciBdXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVybiBcbiAgICAgIH1cbiAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICByZWNlbnRseTogZG9jc1swXS5maWxlc1xuICAgICAgfSlcbiAgICAgIGlmKGNiKSBjYigpXG4gICAgfSlcbiAgfVxuICBjb21wb25lbnREaWRNb3VudCgpIHtcbiAgICBhcHAub24oJ29wZW4tZmlsZScsIHRoaXMub25PcGVuRmlsZSlcbiAgICB0aGlzLnJlZnJlc2hSZWNlbnRMaXN0KCgpID0+IHtcbiAgICAgIGlmKHdpbmRvdy5sb2NhdGlvbi5zZWFyY2gpIHtcbiAgICAgICAgbGV0IGZpbGVwYXRoID0gcXMucGFyc2Uod2luZG93LmxvY2F0aW9uLnNlYXJjaC5zbGljZSgxKSkuc3RhcnRzV2l0aFxuICAgICAgICBmaWxlcGF0aCAmJiB0aGlzLmxvYWRGaWxlKGZpbGVwYXRoKVxuICAgICAgfVxuICAgIH0pICAgXG4gICAgdGhpcy5pbnRlcnZhbCA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICB0aWNrOiAodGhpcy5zdGF0ZS50aWNrICsgMSkgJSA0IFxuICAgICAgfSkgICAgICBcbiAgICB9LCA0MDApXG4gXG4gIH1cbiAgY29tcG9uZW50V2lsbFVubW91bnQoKSB7XG4gICAgY2xlYXJJbnRlcnZhbCh0aGlzLmludGVydmFsKVxuICAgIHRoaXMuaW50ZXJ2YWwgPSBudWxsICAgIFxuXG4gICAgaWYodGhpcy5zdGF0ZS53ZWJwYWNrU2VydmVyKSB7XG4gICAgICB0aGlzLnN0YXRlLndlYnBhY2tTZXJ2ZXIuY2xvc2UoKSAgXG4gICAgfSAgIFxuICAgIGlmKHRoaXMud2F0Y2hlcikge1xuICAgICAgdGhpcy53YXRjaGVyLmNsb3NlKClcbiAgICAgIHRoaXMud2F0Y2hlciA9IG51bGxcbiAgICB9IFxuICAgIGFwcC5yZW1vdmVFdmVudExpc3RlbmVyKCdvcGVuLWZpbGUnLCB0aGlzLm9uT3BlbkZpbGUpXG4gIH1cbiAgX2xvYWRGaWxlKGZpbGVwYXRoKXtcbiAgICBmcy5yZWFkRmlsZShmaWxlcGF0aCwgJ3V0ZjgnLCAoZXJyLCBzcmMpID0+IHsgICAgICBcbiAgICAgIGlmKGVycikgdGhyb3cgZXJyXG4gICAgICBsZXQgb3B0aW9ucyA9IHByYWdtYXMoc3JjKVxuICAgICAgdGhpcy5zZXRTdGF0ZSh7IC4uLndlYnBhY2tpZnkoZmlsZXBhdGgsIG9wdGlvbnMpLCBmaWxlcGF0aCwgcnVubmluZzogdHJ1ZSwgcHJhZ21hczogb3B0aW9ucyB9KVxuXG4gICAgICAvLyBzaW11bHRhbmVvdXNseSBzdGFydCB3YXRjaGluZyB0aGUgZW50cnkgZmlsZSBcbiAgICAgIHRoaXMud2F0Y2hlciA9IGZzLndhdGNoKGZpbGVwYXRoLCBlID0+IHtcbiAgICAgICAgaWYoZSA9PT0gJ3JlbmFtZScpIHtcbiAgICAgICAgICAvLyA/Pz9cbiAgICAgICAgICByZXR1cm4gICAgICAgICAgXG4gICAgICAgIH0gICAgICAgIFxuICAgICAgICAvLyBpZiBhbnkgb2YgdGhlIHByYWdtYXMgY2hhbmdlLCByZWRvIHRoaXMgc2hpbmRpZyBcbiAgICAgICAgZnMucmVhZEZpbGUoZmlsZXBhdGgsICd1dGY4JywgKGVyciwgc3JjKSA9PiB7XG4gICAgICAgICAgbGV0IG9wdGlvbnMgPSBwcmFnbWFzKHNyYylcbiAgICAgICAgICBpZihKU09OLnN0cmluZ2lmeShvcHRpb25zKSAhPT0gSlNPTi5zdHJpbmdpZnkodGhpcy5zdGF0ZS5wcmFnbWFzKSkge1xuICAgICAgICAgICAgdGhpcy5sb2FkRmlsZShmaWxlcGF0aClcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gdG9kbyAtIHByZXZlbnQgZG91YmxlIHJlYWQgXG4gICAgICAgIH0pXG5cbiAgICAgIH0pXG4gICAgfSkgICAgXG4gIH1cbiAgbG9hZEZpbGUoZmlsZXBhdGgpIHtcblxuICAgIGRiLnVwZGF0ZSh7IF9pZDogJ3JlY2VudGx5JyB9LCB7IF9pZDogJ3JlY2VudGx5JywgZmlsZXM6IFsgeyBwYXRoOiBmaWxlcGF0aCB9LCAuLi50aGlzLnN0YXRlLnJlY2VudGx5LmZpbHRlcih4ID0+IHgucGF0aCAhPT0gZmlsZXBhdGgpIF0uc2xpY2UoMCwgMTApIH0sIHt9LCBlcnIgPT4ge1xuICAgICAgaWYoZXJyKSB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgIGVycm9yczogWyAuLi50aGlzLnN0YXRlLmVycm9ycywgZXJyIF1cbiAgICAgICAgfSlcbiAgICAgICAgcmV0dXJuICAgICAgICAgXG4gICAgICB9IFxuICAgICAgdGhpcy5yZWZyZXNoUmVjZW50TGlzdCgpICAgICAgICAgICBcbiAgICB9KVxuICAgIGlmKHRoaXMud2F0Y2hlcikge1xuICAgICAgdGhpcy53YXRjaGVyLmNsb3NlKClcbiAgICAgIHRoaXMud2F0Y2hlciA9IG51bGxcbiAgICB9XG4gICAgXG4gICAgaWYodGhpcy5zdGF0ZS53ZWJwYWNrU2VydmVyKSB7XG4gICAgICB0aGlzLnN0YXRlLndlYnBhY2tTZXJ2ZXIuY2xvc2UoKVxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHRoaXMuX2xvYWRGaWxlKGZpbGVwYXRoKVxuICAgICAgfSwgNTAwKSAgICAgIFxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHRoaXMuX2xvYWRGaWxlKGZpbGVwYXRoKVxuICAgIH1cbiAgICAgICAgICAgIFxuICB9XG4gIG9uRHJvcCA9IGUgPT4ge1xuICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgIGUuc3RvcFByb3BhZ2F0aW9uKClcbiAgICBsZXQgZmlsZXBhdGggPSBlLm5hdGl2ZUV2ZW50LmRhdGFUcmFuc2Zlci5maWxlc1swXS5wYXRoXG4gICAgdGhpcy5sb2FkRmlsZShmaWxlcGF0aClcbiAgfVxuICBjbGVhclJlY2VudExpc3QoKSB7XG4gICAgZGIudXBkYXRlKHsgX2lkOiAncmVjZW50bHknIH0sIHsgX2lkOiAncmVjZW50bHknLCBmaWxlczogW10gfSwgKCkgPT4ge1xuICAgICAgdGhpcy5yZWZyZXNoUmVjZW50TGlzdCgpXG4gICAgfSlcbiAgfVxuICBcbiAgcmVuZGVyKCkge1xuICAgIHJldHVybiA8ZGl2IFxuICAgICAgY3NzPXt7IHdpZHRoOiAnMTAwJScsIGhlaWdodDogJzEwMCUnLCBmb250RmFtaWx5OiAnaGVsdmV0aWNhJyB9fSBcbiAgICAgIG9uRHJhZ092ZXI9e2UgPT4gZS5wcmV2ZW50RGVmYXVsdCgpfSAvLyBjaHJvbWUgYnVnIFxuICAgICAgb25Ecm9wPXt0aGlzLm9uRHJvcH0+ICAgICAgXG4gICAgICAgIFxuICAgICAgPGRpdiBjc3M9e3sgZm9udFdlaWdodDogJ2JvbGRlcicsIGZvbnRTaXplOiAzMiwgcGFkZGluZzogMjAsIFtwcmVzZXRzLlBoYWJsZXRdOiB7IGZvbnRTaXplOiA2NCB9IH19PlxuICAgICAgeyB0aGlzLnN0YXRlLnJ1bm5pbmcgPyBcbiAgICAgICAgYCR7cGF0aC5iYXNlbmFtZSh0aGlzLnN0YXRlLmZpbGVwYXRoKX0gcnVubmluZyBhdCBcbiAgICAgICAgbG9jYWxob3N0OiR7dGhpcy5zdGF0ZS5wb3J0fSR7dGltZXModGhpcy5zdGF0ZS50aWNrLCAoKSA9PiAnLicpLmpvaW4oJycpfWBcbiAgICAgICAgOiAnRHJvcCBhIC5qcyBmaWxlIGhlcmUgdG8gZ2V0IHN0YXJ0ZWQgJ31cbiAgICAgICAgXG4gICAgICBcbiAgICAgIDwvZGl2PiBcbiAgICAgIHsgdGhpcy5zdGF0ZS5yZWNlbnRseS5sZW5ndGggPiAwICYmIDxkaXYgY3NzPXt7IHBhZGRpbmc6IDIwIH19PlxuICAgICAgICA8aDE+cHJldmlvdXNseS4uLjwvaDE+XG4gICAgICAgIHt0aGlzLnN0YXRlLnJlY2VudGx5Lm1hcCh4ID0+IDxkaXYga2V5PXt4LnBhdGh9IG9uQ2xpY2s9eygpID0+IHRoaXMubG9hZEZpbGUoeC5wYXRoKX0+e3gucGF0aH08L2Rpdj4pfVxuICAgICAgICA8aDQgb25DbGljaz17KCkgPT4gdGhpcy5jbGVhclJlY2VudExpc3QoKX0+Y2xlYXIgbGlzdDwvaDQ+XG4gICAgICA8L2Rpdj59XG4gICAgICBcbiAgICAgIDxMb2dvLz5cbiAgICAgIFxuICAgIDwvZGl2PlxuICAgIFxuICB9XG59XG5cbmZ1bmN0aW9uIHdlYnBhY2tpZnkoZmlsZXBhdGgsIG9wdGlvbnMgPSB7fSkge1xuICBcbiAgbGV0IHdlYnBhY2tDb21waWxlciA9IHdlYnBhY2soe1xuICAgIGRldnRvb2w6IG9wdGlvbnMucHJvZHVjdGlvbiA/IGZhbHNlIDogKG9wdGlvbnMuZGV2dG9vbCB8fCAnY2hlYXAtbW9kdWxlLXNvdXJjZS1tYXAnKSxcbiAgICBlbnRyeTogWyBcbiAgICAgICgob3B0aW9ucy5yZWxvYWQgIT09IGZhbHNlKSB8fCAob3B0aW9ucy5wcm9kdWN0aW9uICE9PSB0cnVlKSApID8gXG4gICAgICAgIHJlcXVpcmUucmVzb2x2ZSgncmVhY3QtZGV2LXV0aWxzL3dlYnBhY2tIb3REZXZDbGllbnQuanMnKSA6IFxuICAgICAgICB1bmRlZmluZWQsIFxuICAgICAgb3B0aW9ucy5zdGF0cyA/IHJlcXVpcmUucmVzb2x2ZSgnLi9zdGF0cy5qcycpIDogdW5kZWZpbmVkLFxuICAgICAgcmVxdWlyZS5yZXNvbHZlKCcuL3BvbHlmaWxscycpLCBcbiAgICAgIG9wdGlvbnMub2ZmbGluZSA/IHJlcXVpcmUucmVzb2x2ZSgnLi9vZmZsaW5lLXBsdWdpbi1ydW50aW1lLmpzJykgOiB1bmRlZmluZWQsXG4gICAgICBmaWxlcGF0aCBcbiAgICBdLmZpbHRlcih4ID0+ICEheCksXG4gICAgb3V0cHV0OiB7XG4gICAgICBwYXRoOiBwYXRoLmpvaW4oX19kaXJuYW1lLCAnLi4vcHVibGljJyksXG4gICAgICBwYXRoaW5mbzogdHJ1ZSxcbiAgICAgIGZpbGVuYW1lOiAnYnVuZGxlLmpzJ1xuICAgIH0sXG4gICAgcGVyZm9ybWFuY2U6IHtcbiAgICAgIGhpbnRzOiBmYWxzZVxuICAgIH0sXG4gICAgbW9kdWxlOiB7XG4gICAgICBydWxlczogWyBcbiAgICAgICAgLi4uKG9wdGlvbnMucnVsZXMgfHwgW10pLm1hcCgoeyBsb2FkZXIsIGZpbGVzLCBvcHRpb25zIH0pID0+ICh7IGxvYWRlcjogcmVxdWlyZS5yZXNvbHZlKGxvYWRlciksIG9wdGlvbnMsIHRlc3Q6IGdsb2IycmVnZXhwKGZpbGVzIHx8ICcqJykgfSkpLCBcbiAgICAgICAge1xuICAgICAgICAgIGVuZm9yY2U6ICdwcmUnLFxuICAgICAgICAgIHRlc3Q6IC9cXC4oanN8anN4KSQvLFxuICAgICAgICAgIGxvYWRlcjogcmVxdWlyZS5yZXNvbHZlKCdlc2xpbnQtbG9hZGVyJyksXG4gICAgICAgICAgZXhjbHVkZTogL25vZGVfbW9kdWxlcy8sXG4gICAgICAgICAgb3B0aW9uczoge1xuICAgICAgICAgICAgY29uZmlnRmlsZTogcGF0aC5qb2luKF9fZGlybmFtZSwgJy4uL3Jlc291cmNlcy8uZXNsaW50cmMnKVxuICAgICAgICAgIH1cbiAgICAgICAgfSwgXG4gICAgICAgIHtcbiAgICAgICAgICBleGNsdWRlOiBbXG4gICAgICAgICAgICAvXFwuaHRtbCQvLFxuICAgICAgICAgICAgL1xcLihqc3xqc3gpJC8sXG4gICAgICAgICAgICAvXFwuY3NzJC8sXG4gICAgICAgICAgICAvXFwuanNvbiQvLFxuICAgICAgICAgICAgL1xcLnN2ZyQvXG4gICAgICAgICAgXSxcbiAgICAgICAgICBsb2FkZXI6IHJlcXVpcmUucmVzb2x2ZSgndXJsLWxvYWRlcicpLFxuICAgICAgICAgIHF1ZXJ5OiB7XG4gICAgICAgICAgICBsaW1pdDogMTAwMDAsXG4gICAgICAgICAgICBuYW1lOiAnc3RhdGljL21lZGlhL1tuYW1lXS5baGFzaDo4XS5bZXh0XSdcbiAgICAgICAgICB9XG4gICAgICAgIH0sIFxuICAgICAgICB7XG4gICAgICAgICAgdGVzdDogL1xcLmpzJC8sXG4gICAgICAgICAgZXhjbHVkZTogL25vZGVfbW9kdWxlcy8sXG4gICAgICAgICAgbG9hZGVyOiByZXF1aXJlLnJlc29sdmUoJ2JhYmVsLWxvYWRlcicpLFxuICAgICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgICdwcmVzZXRzJzogWyBcbiAgICAgICAgICAgICAgWyByZXF1aXJlKCdiYWJlbC1wcmVzZXQtZW52JyksIHsgXG4gICAgICAgICAgICAgICAgJ3RhcmdldHMnOiB7XG4gICAgICAgICAgICAgICAgICAnYnJvd3NlcnMnOiBbICdsYXN0IDIgdmVyc2lvbnMnLCAnc2FmYXJpID49IDcnIF1cbiAgICAgICAgICAgICAgICB9LCBcbiAgICAgICAgICAgICAgICBtb2R1bGVzOiBmYWxzZSBcbiAgICAgICAgICAgICAgfSBdLCBcbiAgICAgICAgICAgICAgcmVxdWlyZSgnYmFiZWwtcHJlc2V0LXN0YWdlLTAnKSwgXG4gICAgICAgICAgICAgIHJlcXVpcmUoJ2JhYmVsLXByZXNldC1yZWFjdCcpLCAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgIC4uLihvcHRpb25zLmJhYmVsIHx8IHt9KS5wcmVzZXRzIHx8IFtdXG4gICAgICAgICAgICBdLFxuICAgICAgICAgICAgJ3BsdWdpbnMnOiBbXG4gICAgICAgICAgICAgIFsgcmVxdWlyZS5yZXNvbHZlKCdiYWJlbC1wbHVnaW4tdHJhbnNmb3JtLXJ1bnRpbWUnKSwge1xuICAgICAgICAgICAgICAgIGhlbHBlcnM6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHBvbHlmaWxsOiBmYWxzZSxcbiAgICAgICAgICAgICAgICByZWdlbmVyYXRvcjogdHJ1ZVxuICAgICAgICAgICAgICAgIC8vIFJlc29sdmUgdGhlIEJhYmVsIHJ1bnRpbWUgcmVsYXRpdmUgdG8gdGhlIGNvbmZpZy5cbiAgICAgICAgICAgICAgICAvLyBtb2R1bGVOYW1lOiBwYXRoLmRpcm5hbWUocmVxdWlyZS5yZXNvbHZlKCdiYWJlbC1ydW50aW1lL3BhY2thZ2UnKSlcbiAgICAgICAgICAgICAgfSBdLFxuICAgICAgICAgICAgICBvcHRpb25zLmpzeCA/IFsgcmVxdWlyZSgnYmFiZWwtcGx1Z2luLXRyYW5zZm9ybS1yZWFjdC1qc3gnKSxcbiAgICAgICAgICAgICAgICB7ICdwcmFnbWEnOiBvcHRpb25zLmpzeCB9IF0gOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIHJlcXVpcmUoJ2JhYmVsLXBsdWdpbi10cmFuc2Zvcm0tZGVjb3JhdG9ycy1sZWdhY3knKS5kZWZhdWx0LFxuICAgICAgICAgICAgICByZXF1aXJlKCdiYWJlbC1wbHVnaW4tdHJhbnNmb3JtLXJlYWN0LXJlcXVpcmUnKS5kZWZhdWx0LFxuICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgLi4uKG9wdGlvbnMuYmFiZWwgfHwge30pLnBsdWdpbnMgfHwgW11cbiAgICAgICAgICAgIF0uZmlsdGVyKHggPT4gISF4KSxcbiAgICAgICAgICAgIGNhY2hlRGlyZWN0b3J5OiBmYWxzZVxuICAgICAgICAgIH1cbiAgICAgICAgfSwgXG4gICAgICAgIHtcbiAgICAgICAgICB0ZXN0OiAvXFwuY3NzJC8sXG4gICAgICAgICAgdXNlOiBbXG4gICAgICAgICAgICByZXF1aXJlLnJlc29sdmUoJ3N0eWxlLWxvYWRlcicpLCBcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbG9hZGVyOiByZXF1aXJlLnJlc29sdmUoJ2Nzcy1sb2FkZXInKSxcbiAgICAgICAgICAgICAgb3B0aW9uczogeyBpbXBvcnRMb2FkZXJzOiAxIH0gXG4gICAgICAgICAgICB9LCBcbiAgICAgICAgICAgIHJlcXVpcmUucmVzb2x2ZSgncG9zdGNzcy1sb2FkZXInKSAgLy8gb3B0aW9ucyBpbiB0aGUgcGx1Z2lucyBzZWN0aW9uIGJlbG93ICAgICAgICAgICAgIFxuICAgICAgICAgIF1cbiAgICAgICAgfSwgXG4gICAgICAgIC8vIHtcbiAgICAgICAgLy8gICB0ZXN0OiAvXFwuanNvbiQvLFxuICAgICAgICAvLyAgIGxvYWRlcjogcmVxdWlyZS5yZXNvbHZlKCdqc29uLWxvYWRlcicpXG4gICAgICAgIC8vIH0sXG4gICAgICAgICB7XG4gICAgICAgICAgdGVzdDogL1xcLnN2ZyQvLFxuICAgICAgICAgIGxvYWRlcjogcmVxdWlyZS5yZXNvbHZlKCdmaWxlLWxvYWRlcicpLFxuICAgICAgICAgIHF1ZXJ5OiB7XG4gICAgICAgICAgICBuYW1lOiAnc3RhdGljL21lZGlhL1tuYW1lXS5baGFzaDo4XS5bZXh0XSdcbiAgICAgICAgICB9XG4gICAgICAgIH0gXG4gICAgICBdXG4gICAgfSxcbiAgICByZXNvbHZlOiB7XG4gICAgICBhbGlhczogb3B0aW9ucy5hbGlhcyB8fCB7fSxcbiAgICAgIGV4dGVuc2lvbnM6IFsgJy5qcycsICcuanNvbicsICcuanN4JyBdLFxuICAgICAgbW9kdWxlczogWyAnbm9kZV9tb2R1bGVzJywgcGF0aC5qb2luKGFwcC5nZXRQYXRoKCdob21lJyksICcucmF0cGFjay9ub2RlX21vZHVsZXMnKSwgIHBhdGguam9pbihfX2Rpcm5hbWUsICcuLi9ub2RlX21vZHVsZXMnKSBdXG4gICAgfSxcbiAgICBwbHVnaW5zOiBbXG4gICAgICBuZXcgd2VicGFjay5EZWZpbmVQbHVnaW4oe1xuICAgICAgICAncHJvY2Vzcy5lbnYuTk9ERV9FTlYnOiBKU09OLnN0cmluZ2lmeSgob3B0aW9ucy5wcm9kdWN0aW9uICYmICdwcm9kdWN0aW9uJykgfHwgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgfHwgJ2RldmVsb3BtZW50JyksXG4gICAgICAgIC4uLk9iamVjdC5rZXlzKG9wdGlvbnMuZGVmaW5lIHx8IHt9KS5yZWR1Y2UoKG8sIGtleSkgPT4gKHsgLi4ubywgW2tleV06IEpTT04uc3RyaW5naWZ5KG9wdGlvbnMuZGVmaW5lW2tleV0pIH0pLCB7fSlcbiAgICAgIH0pLFxuICAgICAgb3B0aW9ucy5vZmZsaW5lID8gbmV3IE9mZmxpbmVQbHVnaW4ob3B0aW9ucy5vZmZsaW5lID09PSB0cnVlID8ge30gOiBvcHRpb25zLm9mZmxpbmUpIDogdW5kZWZpbmVkLFxuICAgICAgbmV3IHdlYnBhY2suUHJvdmlkZVBsdWdpbihvcHRpb25zLnByb3ZpZGUgfHwge30pLFxuICAgICAgbmV3IHdlYnBhY2suTG9hZGVyT3B0aW9uc1BsdWdpbih7XG4gICAgICAgIHRlc3Q6IC9cXC5jc3MkLyxcbiAgICAgICAgZGVidWc6IHRydWUsXG4gICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICBwb3N0Y3NzOiBbXG4gICAgICAgICAgICBhdXRvcHJlZml4ZXIoe1xuICAgICAgICAgICAgICBicm93c2VyczogW1xuICAgICAgICAgICAgICAgICc+MSUnLFxuICAgICAgICAgICAgICAgICdsYXN0IDQgdmVyc2lvbnMnLFxuICAgICAgICAgICAgICAgICdGaXJlZm94IEVTUicsXG4gICAgICAgICAgICAgICAgJ25vdCBpZSA8IDknIC8vIFJlYWN0IGRvZXNuJ3Qgc3VwcG9ydCBJRTggYW55d2F5XG4gICAgICAgICAgICAgIF1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgXVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIF0uZmlsdGVyKHggPT4gISF4KSxcbiAgICBzdGF0czogJ2Vycm9ycy1vbmx5JyxcbiAgICBub2RlOiB7XG4gICAgICBmczogJ2VtcHR5JyxcbiAgICAgIG5ldDogJ2VtcHR5JyxcbiAgICAgIHRsczogJ2VtcHR5J1xuICAgIH1cbiAgfSlcbiAgXG4gIGxldCB3ZWJwYWNrU2VydmVyID0gbmV3IFdlYnBhY2tEZXZTZXJ2ZXIod2VicGFja0NvbXBpbGVyLCB7XG4gICAgY29udGVudEJhc2U6IFsgb3B0aW9ucy5wdWJsaWMgPyBwYXRoLmpvaW4ocGF0aC5kaXJuYW1lKGZpbGVwYXRoKSwgb3B0aW9ucy5wdWJsaWMpIDogJycsIHBhdGguam9pbihwYXRoLmRpcm5hbWUoZmlsZXBhdGgpLCAncHVibGljJyksIHBhdGguam9pbihfX2Rpcm5hbWUsICcuLi9wdWJsaWMnKSBdLmZpbHRlcih4ID0+ICEheCksXG4gICAgaGlzdG9yeUFwaUZhbGxiYWNrOiB0cnVlLFxuICAgIGNvbXByZXNzOiB0cnVlLFxuICAgIHByb3h5OiBvcHRpb25zLnByb3h5IHx8IHt9LFxuICAgICAgICAvLyBzZXR1cCgpXG4gICAgICAgIC8vIHN0YXRpY09wdGlvbnMgXG5cbiAgICBxdWlldDogdHJ1ZSwgICAgICBcbiAgICBzdGF0czogeyBjb2xvcnM6IGZhbHNlIH0gIFxuICB9KVxuICAgIC8vIHRoaXMgaXMgdG8gd29ya2Fyb3VuZCBzb21lIHdlaXJkIGJ1ZyB3aGVyZSB3ZWJwYWNrIGtlZXBzIHRoZSBmaXJzdCBsb2FkZWQgZmlsZSBcbiAgICAvLyBhbHNvIG1ha2VzIGl0IGxvb2sgY29vbCBoYVxuICBsZXQgaCA9IGhhc2goZmlsZXBhdGgsIGZpbGVwYXRoLmxlbmd0aCkrICcnXG4gIGxldCBwb3J0ID0gb3B0aW9ucy5wb3J0IHx8ICgzMDAwICsgcGFyc2VJbnQoaC5zdWJzdHIoaC5sZW5ndGggLSA0KSwgMTApKVxuICB3ZWJwYWNrU2VydmVyLmxpc3Rlbihwb3J0KVxuICBvcGVuQnJvd3NlcignaHR0cDovL2xvY2FsaG9zdDonICsgcG9ydClcbiAgcmV0dXJuIHsgd2VicGFja1NlcnZlciwgd2VicGFja0NvbXBpbGVyLCBwb3J0IH1cblxufVxuIFxucmVuZGVyKDxBcHAvPiwgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Jvb3QnKSlcbiJdfQ==

================================================
FILE: lib/index.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>ratpack</title>
   
</head>
<body>
  <div id='root'/>
  <script>
    require('./app')    
  </script>
</body>
</html>


================================================
FILE: lib/main.js
================================================
'use strict';

var _electron = require('electron');

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'

// app: control application file.
// BrowserWindow: create native browser window.

var isProd = _path2.default.basename(process.argv[0]).indexOf('ratpack') === 0;

var startsWith = require('minimist')(process.argv.slice(1))._[isProd ? 0 : 1];
var startedOnce = false;
var mainWindow = void 0;

_electron.app.on('open-file', function (e, filepath) {
  if (!mainWindow) {
    startsWith = filepath;
    if (!startedOnce) {
      return;
    }
    createWindow();
    return;
  }
});

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.


function createWindow() {
  // Create the browser window.
  // installExtension(REACT_DEVELOPER_TOOLS)
  //   .then((name) => console.log(`Added Extension:  ${name}`))
  //   .catch((err) => console.log('An error occurred: ', err))

  mainWindow = new _electron.BrowserWindow({ width: 800, height: 600, icon: _path2.default.join(__dirname, '../resources/icon.png'), backgroundColor: '#f7df1e', show: false });
  mainWindow.once('ready-to-show', function () {
    mainWindow.show();
  });
  startedOnce = true;
  // and load the index.html of the app.
  mainWindow.loadURL('file://' + __dirname + '/index.html' + (startsWith ? '?startsWith=' + encodeURIComponent(startsWith) : ''));
  startsWith = undefined;
  // Open the DevTools.
  mainWindow.webContents.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
_electron.app.on('ready', createWindow);

// Quit when all windows are closed.
_electron.app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    _electron.app.quit();
  }
});

_electron.app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLmpzIl0sIm5hbWVzIjpbImlzUHJvZCIsImJhc2VuYW1lIiwicHJvY2VzcyIsImFyZ3YiLCJpbmRleE9mIiwic3RhcnRzV2l0aCIsInJlcXVpcmUiLCJzbGljZSIsIl8iLCJzdGFydGVkT25jZSIsIm1haW5XaW5kb3ciLCJvbiIsImUiLCJmaWxlcGF0aCIsImNyZWF0ZVdpbmRvdyIsIndpZHRoIiwiaGVpZ2h0IiwiaWNvbiIsImpvaW4iLCJfX2Rpcm5hbWUiLCJiYWNrZ3JvdW5kQ29sb3IiLCJzaG93Iiwib25jZSIsImxvYWRVUkwiLCJlbmNvZGVVUklDb21wb25lbnQiLCJ1bmRlZmluZWQiLCJ3ZWJDb250ZW50cyIsIm9wZW5EZXZUb29scyIsInBsYXRmb3JtIiwicXVpdCJdLCJtYXBwaW5ncyI6Ijs7QUFLQTs7QUFDQTs7Ozs7O0FBTkE7O0FBRUE7QUFDQTs7QUFLQSxJQUFJQSxTQUFTLGVBQUtDLFFBQUwsQ0FBY0MsUUFBUUMsSUFBUixDQUFhLENBQWIsQ0FBZCxFQUErQkMsT0FBL0IsQ0FBdUMsU0FBdkMsTUFBc0QsQ0FBbkU7O0FBRUEsSUFBSUMsYUFBYUMsUUFBUSxVQUFSLEVBQW9CSixRQUFRQyxJQUFSLENBQWFJLEtBQWIsQ0FBbUIsQ0FBbkIsQ0FBcEIsRUFBMkNDLENBQTNDLENBQThDUixTQUFTLENBQVQsR0FBYSxDQUEzRCxDQUFqQjtBQUNBLElBQUlTLGNBQWMsS0FBbEI7QUFDQSxJQUFJQyxtQkFBSjs7QUFFQSxjQUFJQyxFQUFKLENBQU8sV0FBUCxFQUFvQixVQUFDQyxDQUFELEVBQUlDLFFBQUosRUFBaUI7QUFDbkMsTUFBRyxDQUFDSCxVQUFKLEVBQWlCO0FBQ2ZMLGlCQUFhUSxRQUFiO0FBQ0EsUUFBRyxDQUFDSixXQUFKLEVBQWlCO0FBQ2Y7QUFDRDtBQUNESztBQUNBO0FBQ0Q7QUFDRixDQVREOztBQVdBO0FBQ0E7OztBQUdBLFNBQVNBLFlBQVQsR0FBd0I7QUFDdEI7QUFDQTtBQUNBO0FBQ0E7O0FBRUFKLGVBQWEsNEJBQWtCLEVBQUVLLE9BQU8sR0FBVCxFQUFjQyxRQUFRLEdBQXRCLEVBQTJCQyxNQUFNLGVBQUtDLElBQUwsQ0FBVUMsU0FBVixFQUFxQix1QkFBckIsQ0FBakMsRUFBZ0ZDLGlCQUFpQixTQUFqRyxFQUE0R0MsTUFBTSxLQUFsSCxFQUFsQixDQUFiO0FBQ0FYLGFBQVdZLElBQVgsQ0FBZ0IsZUFBaEIsRUFBaUMsWUFBTTtBQUNyQ1osZUFBV1csSUFBWDtBQUNELEdBRkQ7QUFHQVosZ0JBQWMsSUFBZDtBQUNBO0FBQ0FDLGFBQVdhLE9BQVgsYUFBNkJKLFNBQTdCLG9CQUFvRGQsOEJBQTRCbUIsbUJBQW1CbkIsVUFBbkIsQ0FBNUIsR0FBK0QsRUFBbkg7QUFDQUEsZUFBYW9CLFNBQWI7QUFDQTtBQUNBZixhQUFXZ0IsV0FBWCxDQUF1QkMsWUFBdkI7O0FBRUE7QUFDQWpCLGFBQVdDLEVBQVgsQ0FBYyxRQUFkLEVBQXdCLFlBQVk7QUFDbEM7QUFDQTtBQUNBO0FBQ0FELGlCQUFhLElBQWI7QUFDRCxHQUxEO0FBTUQ7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsY0FBSUMsRUFBSixDQUFPLE9BQVAsRUFBZ0JHLFlBQWhCOztBQUVBO0FBQ0EsY0FBSUgsRUFBSixDQUFPLG1CQUFQLEVBQTRCLFlBQVk7QUFDdEM7QUFDQTtBQUNBLE1BQUlULFFBQVEwQixRQUFSLEtBQXFCLFFBQXpCLEVBQW1DO0FBQ2pDLGtCQUFJQyxJQUFKO0FBQ0Q7QUFDRixDQU5EOztBQVNBLGNBQUlsQixFQUFKLENBQU8sVUFBUCxFQUFtQixZQUFZO0FBQzdCO0FBQ0E7QUFDQSxNQUFJRCxlQUFlLElBQW5CLEVBQXlCO0FBQ3ZCSTtBQUNEO0FBQ0YsQ0FORDs7QUFRQTtBQUNBIiwiZmlsZSI6Im1haW4uanMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBpbXBvcnQgaW5zdGFsbEV4dGVuc2lvbiwgeyBSRUFDVF9ERVZFTE9QRVJfVE9PTFMgfSBmcm9tICdlbGVjdHJvbi1kZXZ0b29scy1pbnN0YWxsZXInXG5cbi8vIGFwcDogY29udHJvbCBhcHBsaWNhdGlvbiBmaWxlLlxuLy8gQnJvd3NlcldpbmRvdzogY3JlYXRlIG5hdGl2ZSBicm93c2VyIHdpbmRvdy5cblxuaW1wb3J0IHsgYXBwLCBCcm93c2VyV2luZG93IH0gIGZyb20gJ2VsZWN0cm9uJ1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCdcblxubGV0IGlzUHJvZCA9IHBhdGguYmFzZW5hbWUocHJvY2Vzcy5hcmd2WzBdKS5pbmRleE9mKCdyYXRwYWNrJykgPT09IDBcblxubGV0IHN0YXJ0c1dpdGggPSByZXF1aXJlKCdtaW5pbWlzdCcpKHByb2Nlc3MuYXJndi5zbGljZSgxKSkuX1sgaXNQcm9kID8gMCA6IDFdXG5sZXQgc3RhcnRlZE9uY2UgPSBmYWxzZVxubGV0IG1haW5XaW5kb3dcblxuYXBwLm9uKCdvcGVuLWZpbGUnLCAoZSwgZmlsZXBhdGgpID0+IHtcbiAgaWYoIW1haW5XaW5kb3cgKSB7XG4gICAgc3RhcnRzV2l0aCA9IGZpbGVwYXRoXG4gICAgaWYoIXN0YXJ0ZWRPbmNlKSB7ICAgICAgXG4gICAgICByZXR1cm5cbiAgICB9ICAgIFxuICAgIGNyZWF0ZVdpbmRvdygpICAgIFxuICAgIHJldHVybiBcbiAgfSAgICBcbn0pXG4gXG4vLyBLZWVwIGEgZ2xvYmFsIHJlZmVyZW5jZSBvZiB0aGUgd2luZG93IG9iamVjdCwgaWYgeW91IGRvbid0LCB0aGUgd2luZG93IHdpbGxcbi8vIGJlIGNsb3NlZCBhdXRvbWF0aWNhbGx5IHdoZW4gdGhlIEphdmFTY3JpcHQgb2JqZWN0IGlzIGdhcmJhZ2UgY29sbGVjdGVkLlxuXG5cbmZ1bmN0aW9uIGNyZWF0ZVdpbmRvdygpIHtcbiAgLy8gQ3JlYXRlIHRoZSBicm93c2VyIHdpbmRvdy5cbiAgLy8gaW5zdGFsbEV4dGVuc2lvbihSRUFDVF9ERVZFTE9QRVJfVE9PTFMpXG4gIC8vICAgLnRoZW4oKG5hbWUpID0+IGNvbnNvbGUubG9nKGBBZGRlZCBFeHRlbnNpb246ICAke25hbWV9YCkpXG4gIC8vICAgLmNhdGNoKChlcnIpID0+IGNvbnNvbGUubG9nKCdBbiBlcnJvciBvY2N1cnJlZDogJywgZXJyKSlcblxuICBtYWluV2luZG93ID0gbmV3IEJyb3dzZXJXaW5kb3coeyB3aWR0aDogODAwLCBoZWlnaHQ6IDYwMCwgaWNvbjogcGF0aC5qb2luKF9fZGlybmFtZSwgJy4uL3Jlc291cmNlcy9pY29uLnBuZycpLCBiYWNrZ3JvdW5kQ29sb3I6ICcjZjdkZjFlJywgc2hvdzogZmFsc2UgfSlcbiAgbWFpbldpbmRvdy5vbmNlKCdyZWFkeS10by1zaG93JywgKCkgPT4ge1xuICAgIG1haW5XaW5kb3cuc2hvdygpXG4gIH0pXG4gIHN0YXJ0ZWRPbmNlID0gdHJ1ZVxuICAvLyBhbmQgbG9hZCB0aGUgaW5kZXguaHRtbCBvZiB0aGUgYXBwLlxuICBtYWluV2luZG93LmxvYWRVUkwoYGZpbGU6Ly8ke19fZGlybmFtZX0vaW5kZXguaHRtbCR7c3RhcnRzV2l0aCA/IGA/c3RhcnRzV2l0aD0ke2VuY29kZVVSSUNvbXBvbmVudChzdGFydHNXaXRoKX1gIDogJyd9YCApXG4gIHN0YXJ0c1dpdGggPSB1bmRlZmluZWRcbiAgLy8gT3BlbiB0aGUgRGV2VG9vbHMuXG4gIG1haW5XaW5kb3cud2ViQ29udGVudHMub3BlbkRldlRvb2xzKClcblxuICAvLyBFbWl0dGVkIHdoZW4gdGhlIHdpbmRvdyBpcyBjbG9zZWQuXG4gIG1haW5XaW5kb3cub24oJ2Nsb3NlZCcsIGZ1bmN0aW9uICgpIHtcbiAgICAvLyBEZXJlZmVyZW5jZSB0aGUgd2luZG93IG9iamVjdCwgdXN1YWxseSB5b3Ugd291bGQgc3RvcmUgd2luZG93c1xuICAgIC8vIGluIGFuIGFycmF5IGlmIHlvdXIgYXBwIHN1cHBvcnRzIG11bHRpIHdpbmRvd3MsIHRoaXMgaXMgdGhlIHRpbWVcbiAgICAvLyB3aGVuIHlvdSBzaG91bGQgZGVsZXRlIHRoZSBjb3JyZXNwb25kaW5nIGVsZW1lbnQuXG4gICAgbWFpbldpbmRvdyA9IG51bGxcbiAgfSlcbn1cblxuLy8gVGhpcyBtZXRob2Qgd2lsbCBiZSBjYWxsZWQgd2hlbiBFbGVjdHJvbiBoYXMgZmluaXNoZWRcbi8vIGluaXRpYWxpemF0aW9uIGFuZCBpcyByZWFkeSB0byBjcmVhdGUgYnJvd3NlciB3aW5kb3dzLlxuLy8gU29tZSBBUElzIGNhbiBvbmx5IGJlIHVzZWQgYWZ0ZXIgdGhpcyBldmVudCBvY2N1cnMuXG5hcHAub24oJ3JlYWR5JywgY3JlYXRlV2luZG93KVxuXG4vLyBRdWl0IHdoZW4gYWxsIHdpbmRvd3MgYXJlIGNsb3NlZC5cbmFwcC5vbignd2luZG93LWFsbC1jbG9zZWQnLCBmdW5jdGlvbiAoKSB7XG4gIC8vIE9uIE9TIFggaXQgaXMgY29tbW9uIGZvciBhcHBsaWNhdGlvbnMgYW5kIHRoZWlyIG1lbnUgYmFyXG4gIC8vIHRvIHN0YXkgYWN0aXZlIHVudGlsIHRoZSB1c2VyIHF1aXRzIGV4cGxpY2l0bHkgd2l0aCBDbWQgKyBRXG4gIGlmIChwcm9jZXNzLnBsYXRmb3JtICE9PSAnZGFyd2luJykge1xuICAgIGFwcC5xdWl0KClcbiAgfVxufSlcblxuXG5hcHAub24oJ2FjdGl2YXRlJywgZnVuY3Rpb24gKCkge1xuICAvLyBPbiBPUyBYIGl0J3MgY29tbW9uIHRvIHJlLWNyZWF0ZSBhIHdpbmRvdyBpbiB0aGUgYXBwIHdoZW4gdGhlXG4gIC8vIGRvY2sgaWNvbiBpcyBjbGlja2VkIGFuZCB0aGVyZSBhcmUgbm8gb3RoZXIgd2luZG93cyBvcGVuLlxuICBpZiAobWFpbldpbmRvdyA9PT0gbnVsbCkge1xuICAgIGNyZWF0ZVdpbmRvdygpXG4gIH1cbn0pXG5cbi8vIEluIHRoaXMgZmlsZSB5b3UgY2FuIGluY2x1ZGUgdGhlIHJlc3Qgb2YgeW91ciBhcHAncyBzcGVjaWZpYyBtYWluIHByb2Nlc3Ncbi8vIGNvZGUuIFlvdSBjYW4gYWxzbyBwdXQgdGhlbSBpbiBzZXBhcmF0ZSBmaWxlcyBhbmQgcmVxdWlyZSB0aGVtIGhlcmUuXG4iXX0=

================================================
FILE: lib/offline-plugin-runtime.js
================================================
'use strict';

require('offline-plugin/runtime').install();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9vZmZsaW5lLXBsdWdpbi1ydW50aW1lLmpzIl0sIm5hbWVzIjpbInJlcXVpcmUiLCJpbnN0YWxsIl0sIm1hcHBpbmdzIjoiOztBQUFBQSxRQUFRLHdCQUFSLEVBQWtDQyxPQUFsQyIsImZpbGUiOiJvZmZsaW5lLXBsdWdpbi1ydW50aW1lLmpzIiwic291cmNlc0NvbnRlbnQiOlsicmVxdWlyZSgnb2ZmbGluZS1wbHVnaW4vcnVudGltZScpLmluc3RhbGwoKVxuIl19

================================================
FILE: lib/polyfills.js
================================================
'use strict';

// @remove-on-eject-begin
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
// @remove-on-eject-end

if (typeof Promise === 'undefined') {
  // Rejection tracking prevents a common issue where React gets into an
  // inconsistent state due to an error, but it gets swallowed by a Promise,
  // and the user has no idea what causes React's erratic future behavior.
  require('promise/lib/rejection-tracking').enable();
  window.Promise = require('promise/lib/es6-extensions.js');
}

// fetch() polyfill for making API calls.
require('whatwg-fetch');

// Object.assign() is commonly used with React.
// It will use the native implementation if it's present and isn't buggy.
Object.assign = require('object-assign');
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9wb2x5ZmlsbHMuanMiXSwibmFtZXMiOlsiUHJvbWlzZSIsInJlcXVpcmUiLCJlbmFibGUiLCJ3aW5kb3ciLCJPYmplY3QiLCJhc3NpZ24iXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTs7Ozs7Ozs7QUFRQTs7QUFFQSxJQUFJLE9BQU9BLE9BQVAsS0FBbUIsV0FBdkIsRUFBb0M7QUFDbEM7QUFDQTtBQUNBO0FBQ0FDLFVBQVEsZ0NBQVIsRUFBMENDLE1BQTFDO0FBQ0FDLFNBQU9ILE9BQVAsR0FBaUJDLFFBQVEsK0JBQVIsQ0FBakI7QUFDRDs7QUFFRDtBQUNBQSxRQUFRLGNBQVI7O0FBRUE7QUFDQTtBQUNBRyxPQUFPQyxNQUFQLEdBQWdCSixRQUFRLGVBQVIsQ0FBaEIiLCJmaWxlIjoicG9seWZpbGxzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHJlbW92ZS1vbi1lamVjdC1iZWdpblxuLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMTUtcHJlc2VudCwgRmFjZWJvb2ssIEluYy5cbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgQlNELXN0eWxlIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuIEFuIGFkZGl0aW9uYWwgZ3JhbnRcbiAqIG9mIHBhdGVudCByaWdodHMgY2FuIGJlIGZvdW5kIGluIHRoZSBQQVRFTlRTIGZpbGUgaW4gdGhlIHNhbWUgZGlyZWN0b3J5LlxuICovXG4vLyBAcmVtb3ZlLW9uLWVqZWN0LWVuZFxuXG5pZiAodHlwZW9mIFByb21pc2UgPT09ICd1bmRlZmluZWQnKSB7XG4gIC8vIFJlamVjdGlvbiB0cmFja2luZyBwcmV2ZW50cyBhIGNvbW1vbiBpc3N1ZSB3aGVyZSBSZWFjdCBnZXRzIGludG8gYW5cbiAgLy8gaW5jb25zaXN0ZW50IHN0YXRlIGR1ZSB0byBhbiBlcnJvciwgYnV0IGl0IGdldHMgc3dhbGxvd2VkIGJ5IGEgUHJvbWlzZSxcbiAgLy8gYW5kIHRoZSB1c2VyIGhhcyBubyBpZGVhIHdoYXQgY2F1c2VzIFJlYWN0J3MgZXJyYXRpYyBmdXR1cmUgYmVoYXZpb3IuXG4gIHJlcXVpcmUoJ3Byb21pc2UvbGliL3JlamVjdGlvbi10cmFja2luZycpLmVuYWJsZSgpXG4gIHdpbmRvdy5Qcm9taXNlID0gcmVxdWlyZSgncHJvbWlzZS9saWIvZXM2LWV4dGVuc2lvbnMuanMnKVxufVxuXG4vLyBmZXRjaCgpIHBvbHlmaWxsIGZvciBtYWtpbmcgQVBJIGNhbGxzLlxucmVxdWlyZSgnd2hhdHdnLWZldGNoJylcblxuLy8gT2JqZWN0LmFzc2lnbigpIGlzIGNvbW1vbmx5IHVzZWQgd2l0aCBSZWFjdC5cbi8vIEl0IHdpbGwgdXNlIHRoZSBuYXRpdmUgaW1wbGVtZW50YXRpb24gaWYgaXQncyBwcmVzZW50IGFuZCBpc24ndCBidWdneS5cbk9iamVjdC5hc3NpZ24gPSByZXF1aXJlKCdvYmplY3QtYXNzaWduJylcbiJdfQ==

================================================
FILE: lib/pragmas.js
================================================
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = parse;

var _babylon = require('babylon');

var babylon = _interopRequireWildcard(_babylon);

var _json = require('json5');

var _json2 = _interopRequireDefault(_json);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function parse(src) {
  var ast = babylon.parse(src, { plugins: ['*'], sourceType: 'module' });

  var config = ast.tokens.filter(function (x) {
    return ['CommentLine', 'CommentBlock'].indexOf(x.type) >= 0;
  }).map(function (x) {
    return x.value;
  }).filter(function (x) {
    return (/^\s*@ratpack/gim.test(x)
    );
  });

  if (config.length > 1) {
    throw new Error('cannot have multiple ratpack configs in one file');
  }
  config = config[0];

  if (!config) return;

  config = _json2.default.parse(config.substr(config.indexOf('@ratpack') + 8));

  Object.keys(config).forEach(function (key) {
    var value = config[key];
    switch (key) {
      case 'devtool':
        {
          var possibles = ['eval', 'cheap-eval-source-map', 'cheap-source-map', 'cheap-module-eval-source-map', 'cheap-module-source-map', 'eval-source-map', 'source-map', 'nosources-source-map'];
          if (!(possibles.indexOf(value) >= 0)) {
            throw new Error('@devtool ' + value + ' needs to be one of ' + possibles.join(', '));
          }
          break;
        }

      case 'target':
        {
          var _possibles = ['async-node', 'electron', 'electron-renderer', 'node', 'node-webkit', 'web', 'webworker'];
          if (!(_possibles.indexOf(value) >= 0)) {
            throw new Error('@target ' + value + ' needs to be one of ' + _possibles.join(', '));
          }
          console.warn('target doesn\'t work yet'); //eslint-disable-line no-console
          break;
        }

      case 'public':
        break; // todo - test if valid dir 

      case 'jsx':
        {
          if (typeof value !== 'string') {
            throw new Error('jsx pragma needs to be a valid string');
          }
          break;
        } // test if string

      case 'offline': // vvv      
      case 'autoinstall':
        {
          if (value !== true && value !== false) {
            throw new Error('@' + key + ' ' + value + ' needs to be true or false');
          }
          console.warn(key + ' doesn\'t work yet'); //eslint-disable-line no-console
          break;
        }
      case 'stats':
      case 'reload':
        {
          if (value !== true && value !== false) {
            throw new Error('@' + key + ' ' + value + ' needs to be true or false');
          }
          break;
        }
      case 'port':
        {
          if (!(value >= 0)) {
            throw new Error('port ' + value + ' needs to be a valid number');
          }
          break;
        }
      case 'proxy':
        break; // vvv    // test shape
      case 'provide':
        break; // vvv  // test shape 
      case 'alias':
        break; // vvv    // test shape
      case 'define':
        break; // test shape
      case 'rules':
        break; // test shape

      case 'plugins':
        {
          console.warn('plugins don\'t work yet'); //eslint-disable-line no-console
          break;
        }
      case 'babel':
        {
          // test shape    
          break;
        }
      default:
        console.warn('not implemented', key, value); //eslint-disable-line no-console
    }
  });
  return config;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9wcmFnbWFzLmpzIl0sIm5hbWVzIjpbInBhcnNlIiwiYmFieWxvbiIsInNyYyIsImFzdCIsInBsdWdpbnMiLCJzb3VyY2VUeXBlIiwiY29uZmlnIiwidG9rZW5zIiwiZmlsdGVyIiwiaW5kZXhPZiIsIngiLCJ0eXBlIiwibWFwIiwidmFsdWUiLCJ0ZXN0IiwibGVuZ3RoIiwiRXJyb3IiLCJzdWJzdHIiLCJPYmplY3QiLCJrZXlzIiwiZm9yRWFjaCIsImtleSIsInBvc3NpYmxlcyIsImpvaW4iLCJjb25zb2xlIiwid2FybiJdLCJtYXBwaW5ncyI6Ijs7Ozs7a0JBR3dCQSxLOztBQUh4Qjs7SUFBWUMsTzs7QUFDWjs7Ozs7Ozs7QUFFZSxTQUFTRCxLQUFULENBQWVFLEdBQWYsRUFBb0I7QUFDakMsTUFBSUMsTUFBTUYsUUFBUUQsS0FBUixDQUFjRSxHQUFkLEVBQW1CLEVBQUVFLFNBQVMsQ0FBRSxHQUFGLENBQVgsRUFBb0JDLFlBQVksUUFBaEMsRUFBbkIsQ0FBVjs7QUFFQSxNQUFJQyxTQUFTSCxJQUFJSSxNQUFKLENBQ1ZDLE1BRFUsQ0FDSDtBQUFBLFdBQUssQ0FBRSxhQUFGLEVBQWlCLGNBQWpCLEVBQWtDQyxPQUFsQyxDQUEwQ0MsRUFBRUMsSUFBNUMsS0FBb0QsQ0FBekQ7QUFBQSxHQURHLEVBRVZDLEdBRlUsQ0FFTjtBQUFBLFdBQUtGLEVBQUVHLEtBQVA7QUFBQSxHQUZNLEVBR1ZMLE1BSFUsQ0FHSDtBQUFBLFdBQUssbUJBQWtCTSxJQUFsQixDQUF1QkosQ0FBdkI7QUFBTDtBQUFBLEdBSEcsQ0FBYjs7QUFLQSxNQUFHSixPQUFPUyxNQUFQLEdBQWdCLENBQW5CLEVBQXNCO0FBQUUsVUFBTSxJQUFJQyxLQUFKLENBQVUsa0RBQVYsQ0FBTjtBQUFxRTtBQUM3RlYsV0FBU0EsT0FBTyxDQUFQLENBQVQ7O0FBRUEsTUFBRyxDQUFDQSxNQUFKLEVBQVk7O0FBRVpBLFdBQVMsZUFBTU4sS0FBTixDQUFZTSxPQUFPVyxNQUFQLENBQWNYLE9B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ZSI6InByYWdtYXMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBiYWJ5bG9uIGZyb20gJ2JhYnlsb24nXG5pbXBvcnQganNvbjUgZnJvbSAnanNvbjUnXG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHBhcnNlKHNyYykge1xuICBsZXQgYXN0ID0gYmFieWxvbi5wYXJzZShzcmMsIHsgcGx1Z2luczogWyAnKicgXSwgc291cmNlVHlwZTogJ21vZHVsZScgfSlcblxuICBsZXQgY29uZmlnID0gYXN0LnRva2Vuc1xuICAgIC5maWx0ZXIoeCA9PiBbICdDb21tZW50TGluZScsICdDb21tZW50QmxvY2snIF0uaW5kZXhPZih4LnR5cGUpID49MClcbiAgICAubWFwKHggPT4geC52YWx1ZSlcbiAgICAuZmlsdGVyKHggPT4gL15cXHMqQHJhdHBhY2svZ2ltLnRlc3QoeCkpXG5cbiAgaWYoY29uZmlnLmxlbmd0aCA+IDEpIHsgdGhyb3cgbmV3IEVycm9yKCdjYW5ub3QgaGF2ZSBtdWx0aXBsZSByYXRwYWNrIGNvbmZpZ3MgaW4gb25lIGZpbGUnKSB9XG4gIGNvbmZpZyA9IGNvbmZpZ1swXVxuXG4gIGlmKCFjb25maWcpIHJldHVyblxuXG4gIGNvbmZpZyA9IGpzb241LnBhcnNlKGNvbmZpZy5zdWJzdHIoY29uZmlnLmluZGV4T2YoJ0ByYXRwYWNrJykgKyA4KSlcblxuXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaChrZXkgPT4ge1xuICAgIGxldCB2YWx1ZSA9IGNvbmZpZ1trZXldXG4gICAgc3dpdGNoKGtleSkge1xuICAgICAgY2FzZSAnZGV2dG9vbCc6IHtcbiAgICAgICAgbGV0IHBvc3NpYmxlcyA9IFsgJ2V2YWwnLCAnY2hlYXAtZXZhbC1zb3VyY2UtbWFwJywgJ2NoZWFwLXNvdXJjZS1tYXAnLCAnY2hlYXAtbW9kdWxlLWV2YWwtc291cmNlLW1hcCcsXG4gICAgICAgICAgJ2NoZWFwLW1vZHVsZS1zb3VyY2UtbWFwJywgJ2V2YWwtc291cmNlLW1hcCcsICdzb3VyY2UtbWFwJywgJ25vc291cmNlcy1zb3VyY2UtbWFwJyBdXG4gICAgICAgIGlmKCEocG9zc2libGVzLmluZGV4T2YodmFsdWUpID49IDApKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBAZGV2dG9vbCAke3ZhbHVlfSBuZWVkcyB0byBiZSBvbmUgb2YgYCArIHBvc3NpYmxlcy5qb2luKCcsICcpKVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrXG4gICAgICB9XG5cbiAgICAgIGNhc2UgJ3RhcmdldCc6IHtcbiAgICAgICAgbGV0IHBvc3NpYmxlcyA9IFsgJ2FzeW5jLW5vZGUnLCAnZWxlY3Ryb24nLCAnZWxlY3Ryb24tcmVuZGVyZXInLCAnbm9kZScsICdub2RlLXdlYmtpdCcsICd3ZWInLCAnd2Vid29ya2VyJyBdIFxuICAgICAgICBpZighKHBvc3NpYmxlcy5pbmRleE9mKHZhbHVlKSA+PSAwKSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQHRhcmdldCAke3ZhbHVlfSBuZWVkcyB0byBiZSBvbmUgb2YgYCArIHBvc3NpYmxlcy5qb2luKCcsICcpKVxuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUud2FybigndGFyZ2V0IGRvZXNuXFwndCB3b3JrIHlldCcpIC8vZXNsaW50LWRpc2FibGUtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG5cbiAgICAgIGNhc2UgJ3B1YmxpYyc6IGJyZWFrIC8vIHRvZG8gLSB0ZXN0IGlmIHZhbGlkIGRpciBcbiAgICAgIFxuICAgICAgY2FzZSAnanN4Jzoge1xuICAgICAgICBpZih0eXBlb2YgdmFsdWUgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdqc3ggcHJhZ21hIG5lZWRzIHRvIGJlIGEgdmFsaWQgc3RyaW5nJylcbiAgICAgICAgfVxuICAgICAgICBicmVhayBcbiAgICAgIH0vLyB0ZXN0IGlmIHN0cmluZ1xuICAgICAgXG4gICAgICBjYXNlICdvZmZsaW5lJzogLy8gdnZ2ICAgICAgXG4gICAgICBjYXNlICdhdXRvaW5zdGFsbCc6IHsgICAgICAgIFxuICAgICAgICBpZigodmFsdWUgIT09IHRydWUpICYmICh2YWx1ZSAhPT0gZmFsc2UpKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBAJHtrZXl9ICR7dmFsdWV9IG5lZWRzIHRvIGJlIHRydWUgb3IgZmFsc2VgKVxuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUud2FybihgJHtrZXl9IGRvZXNuJ3Qgd29yayB5ZXRgKSAvL2VzbGludC1kaXNhYmxlLWxpbmUgbm8tY29uc29sZVxuICAgICAgICBicmVha1xuICAgICAgfSBcbiAgICAgIGNhc2UgJ3N0YXRzJzogXG4gICAgICBjYXNlICdyZWxvYWQnOiB7XG4gICAgICAgIGlmKCh2YWx1ZSAhPT0gdHJ1ZSkgJiYgKHZhbHVlICE9PSBmYWxzZSkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEAke2tleX0gJHt2YWx1ZX0gbmVlZHMgdG8gYmUgdHJ1ZSBvciBmYWxzZWApXG4gICAgICAgIH1cbiAgICAgICAgYnJlYWsgXG4gICAgICB9XG4gICAgICBjYXNlICdwb3J0Jzoge1xuICAgICAgICBpZighKHZhbHVlID49IDApKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBwb3J0ICR7dmFsdWV9IG5lZWRzIHRvIGJlIGEgdmFsaWQgbnVtYmVyYClcbiAgICAgICAgfVxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgICAgY2FzZSAncHJveHknOiBicmVhayAvLyB2dnYgICAgLy8gdGVzdCBzaGFwZVxuICAgICAgY2FzZSAncHJvdmlkZSc6IGJyZWFrIC8vIHZ2diAgLy8gdGVzdCBzaGFwZSBcbiAgICAgIGNhc2UgJ2FsaWFzJzogYnJlYWsgLy8gdnZ2ICAgIC8vIHRlc3Qgc2hhcGVcbiAgICAgIGNhc2UgJ2RlZmluZSc6IGJyZWFrICAgICAgICAgIC8vIHRlc3Qgc2hhcGVcbiAgICAgIGNhc2UgJ3J1bGVzJzogYnJlYWsgICAgICAgICAgIC8vIHRlc3Qgc2hhcGVcbiAgICAgIFxuICAgICAgY2FzZSAncGx1Z2lucyc6IHsgICAgICAgIFxuICAgICAgICBjb25zb2xlLndhcm4oJ3BsdWdpbnMgZG9uXFwndCB3b3JrIHlldCcpIC8vZXNsaW50LWRpc2FibGUtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG4gICAgICBjYXNlICdiYWJlbCc6IHsgICAgIC8vIHRlc3Qgc2hhcGUgICAgXG4gICAgICAgIGJyZWFrXG4gICAgICB9ICAgICAgXG4gICAgICBkZWZhdWx0OiBjb25zb2xlLndhcm4oJ25vdCBpbXBsZW1lbnRlZCcsIGtleSwgdmFsdWUpICAvL2VzbGludC1kaXNhYmxlLWxpbmUgbm8tY29uc29sZVxuICAgIH1cbiAgfSlcbiAgcmV0dXJuIGNvbmZpZ1xufVxuXG4iXX0=

================================================
FILE: lib/stats.js
================================================
'use strict';

var _stats = require('stats.js');

var _stats2 = _interopRequireDefault(_stats);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var stats = new _stats2.default();
document.body.appendChild(stats.dom);

requestAnimationFrame(function loop() {
  stats.update();
  requestAnimationFrame(loop);
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9zdGF0cy5qcyJdLCJuYW1lcyI6WyJzdGF0cyIsImRvY3VtZW50IiwiYm9keSIsImFwcGVuZENoaWxkIiwiZG9tIiwicmVxdWVzdEFuaW1hdGlvbkZyYW1lIiwibG9vcCIsInVwZGF0ZSJdLCJtYXBwaW5ncyI6Ijs7QUFBQTs7Ozs7O0FBQ0EsSUFBSUEsUUFBUSxxQkFBWjtBQUNBQyxTQUFTQyxJQUFULENBQWNDLFdBQWQsQ0FBMEJILE1BQU1JLEdBQWhDOztBQUVBQyxzQkFBc0IsU0FBU0MsSUFBVCxHQUFlO0FBQ25DTixRQUFNTyxNQUFOO0FBQ0FGLHdCQUFzQkMsSUFBdEI7QUFDRCxDQUhEIiwiZmlsZSI6InN0YXRzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFN0YXRzIGZyb20gJ3N0YXRzLmpzJ1xubGV0IHN0YXRzID0gbmV3IFN0YXRzKClcbmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoc3RhdHMuZG9tKVxuXG5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZnVuY3Rpb24gbG9vcCgpe1xuICBzdGF0cy51cGRhdGUoKVxuICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUobG9vcClcbn0pIl19

================================================
FILE: package.json
================================================
{
  "name": "ratpack",
  "version": "1.0.6",
  "description": "ready. set. go!",
  "productName": "ratpack",
  "main": "lib/main.js",
  "scripts": {
    "start": "electron .",
    "package-app": "npm run build && node scripts/package-app.js",
    "init": "mkdirp lib && cp src/index.html lib",
    "build": "npm run init && babel src -d lib -s inline",
    "dev": "npm run build && concurrently \"babel src -d lib --watch -s inline\" \"electron .\" -k",
    "icons": "png2icns resources/icon.png -o resources/icon.icns && png-to-ico resources/icon.png > resources/icon.ico && png-to-ico resources/favicon.png > resources/favicon.ico",
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "npm run package-app"
  },
  "bin": {
    "ratpack": "./bin/ratpack"
  },
  "author": "Sunil Pai <threepointone@gmail.com>",
  "license": "ISC",
  "dependencies": {
    "autoprefixer": "^6.6.1",
    "babel-cli": "^6.18.0",
    "babel-core": "^6.21.0",
    "babel-eslint": "^7.1.1",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-react-jsx": "^6.8.0",
    "babel-plugin-transform-react-require": "^1.0.1",
    "babel-plugin-transform-runtime": "^6.15.0",
    "babel-preset-env": "^1.1.8",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "babel-preset-stage-0": "^6.16.0",
    "babel-runtime": "^6.20.0",
    "case-sensitive-paths-webpack-plugin": "^1.1.4",
    "concurrently": "^3.1.0",
    "create-react-app": "^1.0.3",
    "css-loader": "^0.26.1",
    "electron": "^1.4.14",
    "electron-devtools-installer": "^2.0.1",
    "electron-packager": "^8.5.0",
    "eslint": "^3.13.1",
    "eslint-config-rackt": "^1.1.1",
    "eslint-config-react-app": "^0.5.0",
    "eslint-loader": "^1.6.1",
    "eslint-plugin-flowtype": "^2.29.2",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jsx-a11y": "^3.0.2",
    "eslint-plugin-react": "^6.9.0",
    "file-loader": "^0.9.0",
    "glamor": "^2.20.20",
    "glob-to-regexp": "^0.3.0",
    "json-loader": "^0.5.4",
    "json5": "^0.5.1",
    "minimist": "^1.2.0",
    "mkdirp": "^0.5.1",
    "nedb": "^1.8.0",
    "object-assign": "^4.1.0",
    "offline-plugin": "^4.5.4",
    "opn": "^4.0.2",
    "postcss-loader": "^1.2.2",
    "promise": "^7.1.1",
    "react": "^15.4.2",
    "react-dev-utils": "^0.4.2",
    "react-dom": "^15.4.2",
    "react-router": "^4.0.0-2",
    "stats.js": "^0.17.0",
    "style-loader": "^0.13.1",
    "touch": "^1.0.0",
    "url-loader": "^0.5.7",
    "webpack": "2.2.0-rc.4",
    "webpack-dev-server": "2.2.0-rc.0",
    "whatwg-fetch": "^2.0.1"
  },
  "eslintConfig": {
    "extends": [
      "eslint-config-react-app"
    ],
    "plugins": [
      "react"
    ],
    "rules": {
      "react/jsx-uses-vars": "error",
      "react/jsx-uses-react": "error",
      "react/react-in-jsx-scope": 0,
      "jsx-quotes": 0
    }
  },
  "devDependencies": {}
}


================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title></title>
  <link rel="icon" href="/favicon.ico" type="image/x-icon"/>
</head>
<body>
  <div id='root'></div>
  <script src='/bundle.js'></script>
</body>
</html>

================================================
FILE: readme.md
================================================
ratpack
---

[work in progress]
```
npm install ratpack -g
```
or [download the latest release](https://github.com/threepointone/ratpack/releases)

![ratpack](https://i.imgur.com/eEtI35w.png)

ratpack is for beginners, tinkerers, ents, cylons.

quickstart
---

- write a javascript file, say `script.js`, and save it somewhere. maybe it looks like this - 
  
  ```jsx
    require('react-dom').render(
      <div>hello world</div>, 
      document.getElementById('root'))
  ```
- drag it on to the app, or run `ratpack script.js`
- *magic*


more 
---

ratpack gets rid of all the starting boilerplate for building and running the so-called
'modern' javascript stack, letting you immediately jump into the core of your problem. 

- steals a bunch of code/tools from create-react-app for live reloading, in-browser error messaging, 
file loaders for css, images, etc, and you can load your own loaders with js pragmas
- comes with liberal babel defaults, including future facing extensions (and decorators!). You can also use a 
local `.babelrc` file (experimental).
- preloads libraries like react, react-router@4, and glamor. It'll also use modules from 
your own `node_modules` folder, *and* you can add your own 'globally' by installing with npm/yarn to `~/.ratpack`
- use a local `public` folder to serve static assets that can't be imported (todo - and / or to serve a custom `index.html`)
- use [pragmas](https://github.com/threepointone/ratpack/blob/master/docs/pragmas.md) at the top of your file to configure ratpack
- todo - will autoupdate itself whenever I make a 'release', so you're using the latest versions of presets and packaged libs almost as soon they're out
- todo - a preferences pane to edit 'global' settings; toggle presets, add libs, etc



long, rambly rationale
---

tl;dr - old school flow, new age ride.

So, you're reading a tutorial on requestIdleCallback / render callbacks with react / javascript thoughtlording. Or you've just had an idea for a new algorithm for reversing a binary tree. Or maybe you need to write a shitty benchmark to convince your boss that yes, immutable data is fast enough. In any case, you need to quickly write and run some code. What do you do?

These first 2 minutes are critical. The idea you've just had is ephemeral, and needs to be written down fast. It'll be dirty, and you might just throw it away later. At this point, you don't care about the framework wars and modularity shaming and whatever. This is all about you. 

Back in my day, I would write a javascript file (and a dumb html holder), and just run it in a browser. Sure, I had a bajillion other problems, but I liked the brutal efficiency of being seconds away from taking a random idea and turning it into pixels. Bikeshedding over 'build systems' was never the first step.

Mind you, we still had build systems, and we bikeshedded a lot over them and how we built our apps, but those 'apps' were a different context; built for teams and stability. Indeed, deep inside those app folders it wasn't uncommon to find 'demo' pages and `.md` scratchpads for ideas. 

But 'personal' coding? Totally different rules. For one, you'd usually want the latest versions of libs. You want as little boilerplate as possible, to jump into the meat of the problem immediately. Maybe you'd have a little `/code` folder with all your commonly used libs and personal experiments. Even in the context of a larger project, maybe you want to work on a component in isolation, but don't want / need to use the project's main infrastructure.

Times change.

Transpiled languages rose as a way of papering over some javascript's problems, and to use and experiment with future features in browsers that don't support it yet. And it's been absolutely *fantastic*. The pipeline of new features should keep us busy for a couple of years, if not more. Extensions like JSX and TypeScript make writing code an absolute joy (...well, mostly). I'll be blunt - I can't do UI programming without JSX anymore; having a first class UI construct in your programming language makes soooo much sense. Further, having a React dialect at arm's length means I'm not spending any mindspace on dom mutations. It's the small things, y'know?

The setup process to use transpiled languages though... not so great. You need to install and configure a pipeline of tools, usually cli + node based, in a terminal, and pay this cost almost every time you need to run a new file. nwb, create-react-app, etc get *so* close, but by nature of their being designed for 'apps', don't achieve the lightweight, experimental feel I'm looking for. Also, CLIs, ick.

We're UI developers. We can do better. By building beginner/tinkerer friendly tooling, we can automate some of our own pain away, and increase how inclusive we are as a community and as a technology. Here's my attempt. 

First, some of my 'requirements' - 
- I'd like to be able to write a javascript file, with es6 and jsx and whatnot, and just run it in a browser. This could be on my desktop, my downloads folder, wherever.
- I do *not* want to have to install or configure anything 'build' related every time.
- I'd like some liberal defaults, and include some prepackaged libraries like react, react-router, glamor, polyfills for fetch etc.
- *maybe* I'll have a local node_modules, or in a folder higher up my path 
- *perhaps* I'll have a local `public` folder too, with images and css or whatever I'll need 
- *perchance* if I need to, I'd like a custom babel config to mess with a plugin or preset 


Let's imagine the core experience - I'd like to have an app (like a legit, macOS/windows/linux desktop app), and I'd like to drag and drop my `.js` file into it / onto its dock icon, and it would "just work". It could open a browser tab to that file running in a simple html page, and reload whenever I make file changes. A list of previously opened files would be nice.

Hence... *drum roll*... ratpack. Hope you like it. Lemme know what I could do better, and what you'd like to see!


feature wishlist 
---

- better design
- frictionless mobile web dev
- build / deploy to now / surge / etc
- the service worker treatment 
- end to end encrypted share ala ngrok
- multiple languages; flow, reason, node, whatevs 
- eject config, for carrying on a 'real' project
- a smaller package

the shoulders of giants
---

this couldn't exist without the work of thousands of people in the OSS world. Some projects specifically -

- nwb, create-react-app, and the whole family of CLI apps to bootstrap, run and build your javascript apps
- webpack2, the giant swiss army knife of javascript bundlers 
- babel, for all your javascript transformation needs
- electron, giving a truly desirable target for building javascript tooling 
- and of course, react and its entire ecosystem.


project status
---

This is stil *very* early work, with a large number of todos to get through before it's 'production ready'. 
That said, this is already pretty useful. If you'd like to try it out, either try out the 
prototype mac release, or clone this repo and run `npm run && npm run package-app` to generate the binary for 
your computer. Have fun!



================================================
FILE: resources/.eslintrc
================================================
{
  "extends": [
    "eslint-config-react-app"
  ],
  "plugins": [
    "react"
  ],
  "rules": {
    "react/jsx-uses-vars": "error",
    "react/jsx-uses-react": "error",
    "react/react-in-jsx-scope": 0,
    "jsx-quotes": 0
  }
}

================================================
FILE: resources/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeExtensions</key>
            <array>
                <string>js</string>
                <string>jsx</string>
            </array>
            <key>CFBundleTypeName</key>
            <string>JS</string>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>LSHandlerRank</key>
            <string>Alternate</string>
        </dict>
    </array>
  </dict>
</plist>

================================================
FILE: scripts/package-app.js
================================================
let packager = require('electron-packager')
let path = require('path')

process.chdir(path.join(__dirname, '../'))

packager({
  dir: path.join(__dirname, '../'),
  // afterCopy: [ function () {
  //   console.log('afterCopy', ...arguments)
  // } ],
  // afterExtract: [ function () {
  //   console.log('afterExtract', ...arguments)
  // } ],
  prune: false, // todo - fix
  // mac 
  'app-copyright': 'copyright 2017 Sunil Pai',
  'app-version': require('../package').version,
  asar: false,
  'build-version': require('../package').version,
  icon: path.join(__dirname, '../resources/', process.platform === 'darwin' ? 'icon.icns': 'icon.ico'),
  'out': path.join(__dirname, '../out'),
  overwrite: true,
  'app-bundle-id': 'com.threepointone.ratpack',
  'app-category-type': 'public.app-category.developer-tools',
  'extend-info': path.join(__dirname, '../resources/Info.plist'),
  'helper-bundle-id': 'com.threepointone.ratpack.helper',
  // protocol: 'ratpack'
  // 'protocol-name': []
  
  // windows
  win32metadata: {
    CompanyName: 'threepointone',
    FileDescription: 'ratpack, for justice',
    // OriginalFilename
    ProductName: 'ratpack'
    // InternalName: 
  }

   // nothing for linux? 
}, (err, appPaths) => {
  if(err) {
    console.error(err) //eslint-disable-line no-console
    return
  }
  console.log('done') //eslint-disable-line no-console
  console.log(appPaths) //eslint-disable-line no-console
})


================================================
FILE: spiel.md
================================================
[this has moved to the main readme](readme.md)

================================================
FILE: src/app.js
================================================

import qs from 'querystring'
import autoprefixer from 'autoprefixer'
import React from 'react'
import path from 'path'
import { render } from 'react-dom'
import 'glamor/reset'
import hash from 'glamor/lib/hash'
import pragmas from './pragmas'
import glob2regexp from 'glob-to-regexp'
import openBrowser from 'react-dev-utils/openBrowser'
import OfflinePlugin from 'offline-plugin'

const electron = require('electron')
const app = electron.app || electron.remote.app


import WebpackDevServer from 'webpack-dev-server'
import webpack from 'webpack'

import { css, presets } from 'glamor'
const yellow = '#f7df1e'


import DataStore from 'nedb'
let db = new DataStore({
  filename: path.join(app.getPath('userData'), 'store.db'),
  autoload: true
})
db.find({ _id: 'recently' }, (err, docs) => {
  if(docs.length === 0) {
    db.insert({ _id: 'recently', files: [] }, err => {
      if(err) return console.error(err) //eslint-disable-line no-console
      console.log('db initialized') //eslint-disable-line no-console
    })
  }
  else console.log('db restarted')  //eslint-disable-line no-console
})

// todo - move this to main.js 
import mkdirp from 'mkdirp'
import touch from 'touch'
import fs from 'fs'


// todo - windows
mkdirp(path.join(app.getPath('home'), '.ratpack'), err => {
  if(err) {
    throw err
  }
  let pkjson = path.join(app.getPath('home'), '.ratpack/package.json')
  if(!fs.existsSync(pkjson)) {
    touch.sync(pkjson)
    fs.writeFileSync(pkjson, JSON.stringify({
      name: 'ratpack-local',
      description: 'these modules are available to all scripts launched by ratpack'
    }))  
  }
  // among other things, this makes loaders defined in pragmas to work 
  require('module').globalPaths.push(path.join(app.getPath('home'), '.ratpack/node_modules'))
  
})

function times(n, fn) {
  let arr = []
  for(let i= 0; i< n; i++) {
    arr.push(fn(i))
  }
  return arr
}

css.global('html, body, #root', { position: 'relative', width: '100%', height: '100%', display: 'block', backgroundColor: yellow })

const Logo = () => <div css={{ width: 100, height: 100, position: 'absolute', bottom: 0, right: 0, [presets.Phablet]: { width: 200, height: 200 } }}>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 630 630" >
    <path d="m423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z" />
  </svg>
</div>

class App extends React.Component {
  state = {
    tick: 0,
    filepath: undefined,
    webpackCompiler: null,
    webpackServer: null,
    recently: [],
    errors: [],
    port: 0,
    running: false
  }
  onOpenFile = (e, filepath) => {
    this.loadFile(filepath)
  }
  refreshRecentList(cb) {
    db.find({ _id: 'recently' }, (err, docs) => {
      if(err) {
        this.setState({
          errors: [ ...this.state.errors, err ]
        })
        return 
      }
      this.setState({
        recently: docs[0].files
      })
      if(cb) cb()
    })
  }
  componentDidMount() {
    app.on('open-file', this.onOpenFile)
    this.refreshRecentList(() => {
      if(window.location.search) {
        let filepath = qs.parse(window.location.search.slice(1)).startsWith
        filepath && this.loadFile(filepath)
      }
    })   
    this.interval = setInterval(() => {
      this.setState({
        tick: (this.state.tick + 1) % 4 
      })      
    }, 400)
 
  }
  componentWillUnmount() {
    clearInterval(this.interval)
    this.interval = null    

    if(this.state.webpackServer) {
      this.state.webpackServer.close()  
    }   
    if(this.watcher) {
      this.watcher.close()
      this.watcher = null
    } 
    app.removeEventListener('open-file', this.onOpenFile)
  }
  _loadFile(filepath){
    fs.readFile(filepath, 'utf8', (err, src) => {      
      if(err) throw err
      let options = pragmas(src)
      this.setState({ ...webpackify(filepath, options), filepath, running: true, pragmas: options })

      // simultaneously start watching the entry file 
      this.watcher = fs.watch(filepath, e => {
        if(e === 'rename') {
          // ???
          return          
        }        
        // if any of the pragmas change, redo this shindig 
        fs.readFile(filepath, 'utf8', (err, src) => {
          let options = pragmas(src)
          if(JSON.stringify(options) !== JSON.stringify(this.state.pragmas)) {
            this.loadFile(filepath)
          }
          // todo - prevent double read 
        })

      })
    })    
  }
  loadFile(filepath) {

    db.update({ _id: 'recently' }, { _id: 'recently', files: [ { path: filepath }, ...this.state.recently.filter(x => x.path !== filepath) ].slice(0, 10) }, {}, err => {
      if(err) {
        this.setState({
          errors: [ ...this.state.errors, err ]
        })
        return         
      } 
      this.refreshRecentList()           
    })
    if(this.watcher) {
      this.watcher.close()
      this.watcher = null
    }
    
    if(this.state.webpackServer) {
      this.state.webpackServer.close()
      setTimeout(() => {
        this._loadFile(filepath)
      }, 500)      
    }
    else {
      this._loadFile(filepath)
    }
            
  }
  onDrop = e => {
    e.preventDefault()
    e.stopPropagation()
    let filepath = e.nativeEvent.dataTransfer.files[0].path
    this.loadFile(filepath)
  }
  clearRecentList() {
    db.update({ _id: 'recently' }, { _id: 'recently', files: [] }, () => {
      this.refreshRecentList()
    })
  }
  
  render() {
    return <div 
      css={{ width: '100%', height: '100%', fontFamily: 'helvetica' }} 
      onDragOver={e => e.preventDefault()} // chrome bug 
      onDrop={this.onDrop}>      
        
      <div css={{ fontWeight: 'bolder', fontSize: 32, padding: 20, [presets.Phablet]: { fontSize: 64 } }}>
      { this.state.running ? 
        `${path.basename(this.state.filepath)} running at 
        localhost:${this.state.port}${times(this.state.tick, () => '.').join('')}`
        : 'Drop a .js file here to get started '}
        
      
      </div> 
      { this.state.recently.length > 0 && <div css={{ padding: 20 }}>
        <h1>previously...</h1>
        {this.state.recently.map(x => <div key={x.path} onClick={() => this.loadFile(x.path)}>{x.path}</div>)}
        <h4 onClick={() => this.clearRecentList()}>clear list</h4>
      </div>}
      
      <Logo/>
      
    </div>
    
  }
}

function webpackify(filepath, options = {}) {
  
  let webpackCompiler = webpack({
    devtool: options.production ? false : (options.devtool || 'cheap-module-source-map'),
    entry: [ 
      ((options.reload !== false) || (options.production !== true) ) ? 
        require.resolve('react-dev-utils/webpackHotDevClient.js') : 
        undefined, 
      options.stats ? require.resolve('./stats.js') : undefined,
      require.resolve('./polyfills'), 
      options.offline ? require.resolve('./offline-plugin-runtime.js') : undefined,
      filepath 
    ].filter(x => !!x),
    output: {
      path: path.join(__dirname, '../public'),
      pathinfo: true,
      filename: 'bundle.js'
    },
    performance: {
      hints: false
    },
    module: {
      rules: [ 
        ...(options.rules || []).map(({ loader, files, options }) => ({ loader: require.resolve(loader), options, test: glob2regexp(files || '*') })), 
        {
          enforce: 'pre',
          test: /\.(js|jsx)$/,
          loader: require.resolve('eslint-loader'),
          exclude: /node_modules/,
          options: {
            configFile: path.join(__dirname, '../resources/.eslintrc')
          }
        }, 
        {
          exclude: [
            /\.html$/,
            /\.(js|jsx)$/,
            /\.css$/,
            /\.json$/,
            /\.svg$/
          ],
          loader: require.resolve('url-loader'),
          query: {
            limit: 10000,
            name: 'static/media/[name].[hash:8].[ext]'
          }
        }, 
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: require.resolve('babel-loader'),
          options: {
            'presets': [ 
              [ require('babel-preset-env'), { 
                'targets': {
                  'browsers': [ 'last 2 versions', 'safari >= 7' ]
                }, 
                modules: false 
              } ], 
              require('babel-preset-stage-0'), 
              require('babel-preset-react'),              
              ...(options.babel || {}).presets || []
            ],
            'plugins': [
              [ require.resolve('babel-plugin-transform-runtime'), {
                helpers: false,
                polyfill: false,
                regenerator: true
                // Resolve the Babel runtime relative to the config.
                // moduleName: path.dirname(require.resolve('babel-runtime/package'))
              } ],
              options.jsx ? [ require('babel-plugin-transform-react-jsx'),
                { 'pragma': options.jsx } ] : undefined,
              require('babel-plugin-transform-decorators-legacy').default,
              require('babel-plugin-transform-react-require').default,
              
              ...(options.babel || {}).plugins || []
            ].filter(x => !!x),
            cacheDirectory: false
          }
        }, 
        {
          test: /\.css$/,
          use: [
            require.resolve('style-loader'), 
            {
              loader: require.resolve('css-loader'),
              options: { importLoaders: 1 } 
            }, 
            require.resolve('postcss-loader')  // options in the plugins section below             
          ]
        }, 
        // {
        //   test: /\.json$/,
        //   loader: require.resolve('json-loader')
        // },
         {
          test: /\.svg$/,
          loader: require.resolve('file-loader'),
          query: {
            name: 'static/media/[name].[hash:8].[ext]'
          }
        } 
      ]
    },
    resolve: {
      alias: options.alias || {},
      extensions: [ '.js', '.json', '.jsx' ],
      // todo - windows
      modules: [ 'node_modules', path.join(app.getPath('home'), '.ratpack/node_modules'),  path.join(__dirname, '../node_modules') ]
    },
    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify((options.production && 'production') || process.env.NODE_ENV || 'development'),
        ...Object.keys(options.define || {}).reduce((o, key) => ({ ...o, [key]: JSON.stringify(options.define[key]) }), {})
      }),
      options.offline ? new OfflinePlugin(options.offline === true ? {} : options.offline) : undefined,
      new webpack.ProvidePlugin(options.provide || {}),
      new webpack.LoaderOptionsPlugin({
        test: /\.css$/,
        debug: true,
        options: {
          postcss: [
            autoprefixer({
              browsers: [
                '>1%',
                'last 4 versions',
                'Firefox ESR',
                'not ie < 9' // React doesn't support IE8 anyway
              ]
            })
          ]
        }
      })
    ].filter(x => !!x),
    stats: 'errors-only',
    node: {
      fs: 'empty',
      net: 'empty',
      tls: 'empty'
    }
  })
  
  let webpackServer = new WebpackDevServer(webpackCompiler, {
    // todo - windows
    contentBase: [ options.public ? path.join(path.dirname(filepath), options.public) : '', path.join(path.dirname(filepath), 'public'), path.join(__dirname, '../public') ].filter(x => !!x),
    historyApiFallback: true,
    compress: true,
    proxy: options.proxy || {},
        // setup()
        // staticOptions 

    quiet: true,      
    stats: { colors: false }  
  })
    // this is to workaround some weird bug where webpack keeps the first loaded file 
    // also makes it look cool ha
  let h = hash(filepath, filepath.length)+ ''
  let port = options.port || (3000 + parseInt(h.substr(h.length - 4), 10))
  webpackServer.listen(port)
  openBrowser('http://localhost:' + port)
  return { webpackServer, webpackCompiler, port }

}
 
render(<App/>, document.getElementById('root'))


================================================
FILE: src/index.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>ratpack</title>
   
</head>
<body>
  <div id='root'/>
  <script>
    require('./app')    
  </script>
</body>
</html>


================================================
FILE: src/main.js
================================================
// import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'

// app: control application file.
// BrowserWindow: create native browser window.

import { app, BrowserWindow }  from 'electron'
import path from 'path'

let isProd = path.basename(process.argv[0]).indexOf('ratpack') === 0

let startsWith = require('minimist')(process.argv.slice(1))._[ isProd ? 0 : 1]
let startedOnce = false
let mainWindow

app.on('open-file', (e, filepath) => {
  if(!mainWindow ) {
    startsWith = filepath
    if(!startedOnce) {      
      return
    }    
    createWindow()    
    return 
  }    
})
 
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.


function createWindow() {
  // Create the browser window.
  // installExtension(REACT_DEVELOPER_TOOLS)
  //   .then((name) => console.log(`Added Extension:  ${name}`))
  //   .catch((err) => console.log('An error occurred: ', err))

  mainWindow = new BrowserWindow({ width: 800, height: 600, icon: path.join(__dirname, '../resources/icon.png'), backgroundColor: '#f7df1e', show: false })
  mainWindow.once('ready-to-show', () => {
    mainWindow.show()
  })
  startedOnce = true
  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html${startsWith ? `?startsWith=${encodeURIComponent(startsWith)}` : ''}` )
  startsWith = undefined
  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})


app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.


================================================
FILE: src/offline-plugin-runtime.js
================================================
require('offline-plugin/runtime').install()


================================================
FILE: src/polyfills.js
================================================
// @remove-on-eject-begin
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
// @remove-on-eject-end

if (typeof Promise === 'undefined') {
  // Rejection tracking prevents a common issue where React gets into an
  // inconsistent state due to an error, but it gets swallowed by a Promise,
  // and the user has no idea what causes React's erratic future behavior.
  require('promise/lib/rejection-tracking').enable()
  window.Promise = require('promise/lib/es6-extensions.js')
}

// fetch() polyfill for making API calls.
require('whatwg-fetch')

// Object.assign() is commonly used with React.
// It will use the native implementation if it's present and isn't buggy.
Object.assign = require('object-assign')


================================================
FILE: src/pragmas.js
================================================
import * as babylon from 'babylon'
import json5 from 'json5'

export default function parse(src) {
  let ast = babylon.parse(src, { plugins: [ '*' ], sourceType: 'module' })

  let config = ast.tokens
    .filter(x => [ 'CommentLine', 'CommentBlock' ].indexOf(x.type) >=0)
    .map(x => x.value)
    .filter(x => /^\s*@ratpack/gim.test(x))

  if(config.length > 1) { throw new Error('cannot have multiple ratpack configs in one file') }
  config = config[0]

  if(!config) return

  config = json5.parse(config.substr(config.indexOf('@ratpack') + 8))


  Object.keys(config).forEach(key => {
    let value = config[key]
    switch(key) {
      case 'devtool': {
        let possibles = [ 'eval', 'cheap-eval-source-map', 'cheap-source-map', 'cheap-module-eval-source-map',
          'cheap-module-source-map', 'eval-source-map', 'source-map', 'nosources-source-map' ]
        if(!(possibles.indexOf(value) >= 0)) {
          throw new Error(`@devtool ${value} needs to be one of ` + possibles.join(', '))
        }
        break
      }

      case 'target': {
        let possibles = [ 'async-node', 'electron', 'electron-renderer', 'node', 'node-webkit', 'web', 'webworker' ] 
        if(!(possibles.indexOf(value) >= 0)) {
          throw new Error(`@target ${value} needs to be one of ` + possibles.join(', '))
        }
        console.warn('target doesn\'t work yet') //eslint-disable-line no-console
        break
      }

      case 'public': break // todo - test if valid dir 
      
      case 'jsx': {
        if(typeof value !== 'string') {
          throw new Error('jsx pragma needs to be a valid string')
        }
        break 
      }// test if string
      
      case 'offline': // vvv      
      case 'autoinstall': {        
        if((value !== true) && (value !== false)) {
          throw new Error(`@${key} ${value} needs to be true or false`)
        }
        console.warn(`${key} doesn't work yet`) //eslint-disable-line no-console
        break
      } 
      case 'stats': 
      case 'reload': {
        if((value !== true) && (value !== false)) {
          throw new Error(`@${key} ${value} needs to be true or false`)
        }
        break 
      }
      case 'port': {
        if(!(value >= 0)) {
          throw new Error(`port ${value} needs to be a valid number`)
        }
        break
      }
      case 'proxy': break // vvv    // test shape
      case 'provide': break // vvv  // test shape 
      case 'alias': break // vvv    // test shape
      case 'define': break          // test shape
      case 'rules': break           // test shape
      
      case 'plugins': {        
        console.warn('plugins don\'t work yet') //eslint-disable-line no-console
        break
      }
      case 'babel': {     // test shape    
        break
      }      
      default: console.warn('not implemented', key, value)  //eslint-disable-line no-console
    }
  })
  return config
}



================================================
FILE: src/stats.js
================================================
import Stats from 'stats.js'
let stats = new Stats()
document.body.appendChild(stats.dom)

requestAnimationFrame(function loop(){
  stats.update()
  requestAnimationFrame(loop)
})

================================================
FILE: todo.md
================================================
- x - start webpack, babel shindig 
- x - expose react, react-dom, react-router, glamor
- x - open a browser window 
- x - hot reloading
- x - compilation error overlay
- x - local .babelrc override
- x - implicit react require
- x - drop another file to load that instead 
- x - recently opened files
- x - 'running on port...'
- x - css, image, etc loaders 
- x - special pragmas 
- x - proxy
- x - cli experience
- x - favicon
- x - linting (setup helper?)
- x - open with / dbl click experiencec
- x - dock experience
- x - stats 

- menubar experience
- messaging on webpack error 
- ~ - polyfills
- preferences pane
- configurations / presets 
- ~ - packaged app  
- updates for main app?


- perf / stats / fps measurement helpers 
- do a 'prod build' 
- node?
- offline?
- auto install?
- any language?
- windows?
- redbox?
- flow / typescript / etc 
- implicit React.render for default/App export?
- performance hints 
Download .txt
gitextract___5ovaq3/

├── .babelrc
├── .gitignore
├── bin/
│   └── ratpack
├── docs/
│   └── pragmas.md
├── examples/
│   ├── polyf.js
│   ├── pragmas.js
│   └── scratch.js
├── lib/
│   ├── app.js
│   ├── index.html
│   ├── main.js
│   ├── offline-plugin-runtime.js
│   ├── polyfills.js
│   ├── pragmas.js
│   └── stats.js
├── package.json
├── public/
│   └── index.html
├── readme.md
├── resources/
│   ├── .eslintrc
│   ├── Info.plist
│   └── icon.icns
├── scripts/
│   └── package-app.js
├── spiel.md
├── src/
│   ├── app.js
│   ├── index.html
│   ├── main.js
│   ├── offline-plugin-runtime.js
│   ├── polyfills.js
│   ├── pragmas.js
│   └── stats.js
└── todo.md
Download .txt
SYMBOL INDEX (28 symbols across 7 files)

FILE: lib/app.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _interopRequireDefault (line 73) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function _toConsumableArray (line 75) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...
  function _classCallCheck (line 77) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 79) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 81) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function _defineProperty (line 83) | function _defineProperty(obj, key, value) { if (key in obj) { Object.def...
  function times (line 123) | function times(n, fn) {
  function App (line 151) | function App() {
  function webpackify (line 351) | function webpackify(filepath) {

FILE: lib/main.js
  function _interopRequireDefault (line 9) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function createWindow (line 37) | function createWindow() {

FILE: lib/pragmas.js
  function _interopRequireDefault (line 16) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function _interopRequireWildcard (line 18) | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { ret...
  function parse (line 20) | function parse(src) {

FILE: lib/stats.js
  function _interopRequireDefault (line 7) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...

FILE: src/app.js
  function times (line 64) | function times(n, fn) {
  class App (line 80) | class App extends React.Component {
    method refreshRecentList (line 94) | refreshRecentList(cb) {
    method componentDidMount (line 108) | componentDidMount() {
    method componentWillUnmount (line 123) | componentWillUnmount() {
    method _loadFile (line 136) | _loadFile(filepath){
    method loadFile (line 160) | loadFile(filepath) {
    method clearRecentList (line 193) | clearRecentList() {
    method render (line 199) | render() {
  function webpackify (line 226) | function webpackify(filepath, options = {}) {

FILE: src/main.js
  function createWindow (line 30) | function createWindow() {

FILE: src/pragmas.js
  function parse (line 4) | function parse(src) {
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (118K chars).
[
  {
    "path": ".babelrc",
    "chars": 323,
    "preview": "{\n  \"presets\": [ [ \"babel-preset-env\", {\n    \"targets\": {\n      \"browsers\": [\"last 2 versions\", \"safari >= 7\"]\n    } } ]"
  },
  {
    "path": ".gitignore",
    "chars": 185,
    "preview": "# osx noise\n.DS_Store\nprofile\n\n# xcode noise\nbuild/*\n*.mode1\n*.mode1v3\n*.mode2v3\n*.perspective\n*.perspectivev3\n*.pbxuser"
  },
  {
    "path": "bin/ratpack",
    "chars": 1139,
    "preview": "#!/usr/bin/env node\n// todo - windows\nlet opn = require('opn')\nlet spawn = require('child_process').spawn\nlet fs = requi"
  },
  {
    "path": "docs/pragmas.md",
    "chars": 2334,
    "preview": "magic pragmas\n---\n\nratpack supports some magic incantations you can put at the top of your entry file \nto customize the "
  },
  {
    "path": "examples/polyf.js",
    "chars": 26,
    "preview": "let x = function* () {\n\n}\n"
  },
  {
    "path": "examples/pragmas.js",
    "chars": 376,
    "preview": "/* @ratpack {\n  target: 'node',\n  devtool: 'eval',\n  public: '../public',\n  proxy: {\n    '/yahoo': 'http://www.yahoo.com"
  },
  {
    "path": "examples/scratch.js",
    "chars": 159,
    "preview": "// @ratpack { stats: true }\nconsole.log('here') //eslint-disable-line no-console\n\nrequire('react-dom').render(<div>what "
  },
  {
    "path": "lib/app.js",
    "chars": 51792,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "lib/index.html",
    "chars": 157,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title>ratpack</title>\n   \n</head>\n<body>\n  <div id='root'/>\n  <script>\n    require('./a"
  },
  {
    "path": "lib/main.js",
    "chars": 8887,
    "preview": "'use strict';\n\nvar _electron = require('electron');\n\nvar _path = require('path');\n\nvar _path2 = _interopRequireDefault(_"
  },
  {
    "path": "lib/offline-plugin-runtime.js",
    "chars": 426,
    "preview": "'use strict';\n\nrequire('offline-plugin/runtime').install();\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW"
  },
  {
    "path": "lib/polyfills.js",
    "chars": 2898,
    "preview": "'use strict';\n\n// @remove-on-eject-begin\n/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n *"
  },
  {
    "path": "lib/pragmas.js",
    "chars": 11500,
    "preview": "'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = parse;\n\nvar _babylon ="
  },
  {
    "path": "lib/stats.js",
    "chars": 1127,
    "preview": "'use strict';\n\nvar _stats = require('stats.js');\n\nvar _stats2 = _interopRequireDefault(_stats);\n\nfunction _interopRequir"
  },
  {
    "path": "package.json",
    "chars": 2943,
    "preview": "{\n  \"name\": \"ratpack\",\n  \"version\": \"1.0.6\",\n  \"description\": \"ready. set. go!\",\n  \"productName\": \"ratpack\",\n  \"main\": \""
  },
  {
    "path": "public/index.html",
    "chars": 200,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title></title>\n  <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\"/>\n</head>\n<bo"
  },
  {
    "path": "readme.md",
    "chars": 7175,
    "preview": "ratpack\n---\n\n[work in progress]\n```\nnpm install ratpack -g\n```\nor [download the latest release](https://github.com/three"
  },
  {
    "path": "resources/.eslintrc",
    "chars": 230,
    "preview": "{\n  \"extends\": [\n    \"eslint-config-react-app\"\n  ],\n  \"plugins\": [\n    \"react\"\n  ],\n  \"rules\": {\n    \"react/jsx-uses-var"
  },
  {
    "path": "resources/Info.plist",
    "chars": 668,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "scripts/package-app.js",
    "chars": 1433,
    "preview": "let packager = require('electron-packager')\nlet path = require('path')\n\nprocess.chdir(path.join(__dirname, '../'))\n\npack"
  },
  {
    "path": "spiel.md",
    "chars": 46,
    "preview": "[this has moved to the main readme](readme.md)"
  },
  {
    "path": "src/app.js",
    "chars": 12488,
    "preview": "\nimport qs from 'querystring'\nimport autoprefixer from 'autoprefixer'\nimport React from 'react'\nimport path from 'path'\n"
  },
  {
    "path": "src/index.html",
    "chars": 157,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title>ratpack</title>\n   \n</head>\n<body>\n  <div id='root'/>\n  <script>\n    require('./a"
  },
  {
    "path": "src/main.js",
    "chars": 2662,
    "preview": "// import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'\n\n// app: control application fi"
  },
  {
    "path": "src/offline-plugin-runtime.js",
    "chars": 44,
    "preview": "require('offline-plugin/runtime').install()\n"
  },
  {
    "path": "src/polyfills.js",
    "chars": 969,
    "preview": "// @remove-on-eject-begin\n/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source co"
  },
  {
    "path": "src/pragmas.js",
    "chars": 2926,
    "preview": "import * as babylon from 'babylon'\nimport json5 from 'json5'\n\nexport default function parse(src) {\n  let ast = babylon.p"
  },
  {
    "path": "src/stats.js",
    "chars": 179,
    "preview": "import Stats from 'stats.js'\nlet stats = new Stats()\ndocument.body.appendChild(stats.dom)\n\nrequestAnimationFrame(functio"
  },
  {
    "path": "todo.md",
    "chars": 928,
    "preview": "- x - start webpack, babel shindig \n- x - expose react, react-dom, react-router, glamor\n- x - open a browser window \n- x"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the threepointone/ratpack GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (111.7 KB), approximately 48.8k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!