title: Define browser global in rollup/buble
body: <p>I'm making a Preact SSR application using <a href="https://rollupjs.org" rel="nofollow noreferrer">rollup</a> for bundling and <a href="https://buble.surge.sh/" rel="nofollow noreferrer">buble</a> for ES2015 transformations. Currently, one of the outside modules I'm bundling into my client build, <code>@horizon/client</code>, uses WebSockets. Currently, because bundling is using Node, this is not defined, causing a build error:</p>
  
  <pre><code>/Users/tim/Documents/Tower/node_modules/@horizon/client/lib/index.js:42
  
  
  var WebSocketCtor = _ref$WebSocketCtor === undefined ? WebSocket : _ref$WebSocketCtor;
                                                           ^
  
  ReferenceError: WebSocket is not defined
      at Horizon (/Users/tim/Documents/Tower/node_modules/@horizon/client/lib/index.js:42:58)
      at Object.&lt;anonymous&gt; (/Users/tim/Documents/Tower/build/server.js:309:34)
      at Module._compile (module.js:569:30)
      at Object.Module._extensions..js (module.js:580:10)
      at Module.load (module.js:503:32)
      at tryModuleLoad (module.js:466:12)
      at Function.Module._load (module.js:458:3)
      at Function.Module.runMain (module.js:605:10)
      at startup (bootstrap_node.js:158:16)
      at bootstrap_node.js:575:3
  </code></pre>
  
  <p>Keep in mind that I'm coming from a <a href="https://webpack.github.io" rel="nofollow noreferrer">Webpack</a> and <a href="https://babeljs.io" rel="nofollow noreferrer">babel</a> environment. When using those tools, I would have a plugin to define things which will be present in the output, but not in Node. How would I do this using rollup/buble?</p>
  
  <p>My current build file looks like this:</p>
  
  <pre><code>const rollup = require('rollup').rollup
  const buble = require('rollup-plugin-buble')
  const commonjs = require('rollup-plugin-commonjs')
  const json = require('rollup-plugin-json')
  const nodeResolve = require('rollup-plugin-node-resolve')
  const optimizeJs = require('rollup-plugin-optimize-js')
  const replace = require('rollup-plugin-replace')
  const uglify = require('rollup-plugin-uglify')
  const fs = require('fs-extra-promise')
  const sass = require('node-sass').render
  const cssnano = require('cssnano').process
  const purifycss = require('purify-css')
  const { name, dependencies } = require('./package')
  const swPrecache = require('sw-precache').write
  const nodeRev = require('node-rev').default
  
  const server = () =&gt; rollup({
    entry: 'src/server/server.js',
    external: Object.keys(dependencies).concat(['fs']),
    plugins: [
      replace({ '__CLIENT__': false }),
      json(),
      commonjs({ extensions: [ '.js', '.json' ] }),
      buble({ jsx: 'h', objectAssign: 'Object.assign' })
    ]
  }).then((bundle) =&gt; bundle.write({ sourceMap: true, format: 'cjs', dest: `build/server.js` }))
  
  const client = () =&gt; rollup({
    entry: 'src/app/entry.js',
    context: 'window',
    plugins: [
      nodeResolve({ jsnext: true, browser: true }),
      commonjs(),
      replace({ '__CLIENT__': true, 'process.env.NODE_ENV': JSON.stringify('production') }),
      buble({ jsx: 'h', objectAssign: 'Object.assign' }),
      uglify(require('./uglify')),
      optimizeJs()
    ]
  })
  .then((bundle) =&gt; bundle.write({ sourceMap: true, format: 'iife', dest: `build/public/bundle.js` }))
  
  const css = () =&gt; new Promise((resolve, reject) =&gt; sass({ file: `src/app/styles/entry.scss` }, (err, result) =&gt; err ? reject(err) : resolve(result)))
    .then(({ css }) =&gt; purifycss(['src/app/components/**/*.js'], css.toString()))
    .then((purified) =&gt; cssnano(purified, { autoprefixer: { add: true } }))
    .then(({ css }) =&gt; fs.outputFileAsync(`build/public/bundle.css`, css))
  
  const sw = () =&gt; swPrecache('build/public/sw.js', {
    cacheId: `${name}`,
    directoryIndex: '/',
    staticFileGlobs: [
      '/',
      './build/public/manifest-*.json',
      // './build/public/bundle-*.{css,js}', // depends if we inlineJs, inlineCss or not
      './build/public/*.{gif,png,svg}' // will not preache /icons
    ],
    navigateFallback: '/',
    dynamicUrlToDependencies: {
      '/': ['./src/server/routes/root.js', './build/public/bundle.css', './build/public/bundle.js', './build/public/manifest.json', './package.json'] // bust cache when these change
    },
    skipWaiting: true,
    stripPrefix: './build/public',
    runtimeCaching: [{
      urlPattern: /\/posts/, // handle remote api call
      handler: 'cacheFirst'
    }]
  })
  
  const rev = () =&gt; Promise.resolve(nodeRev({
    files: './build/public/**/*.*',
    outputDir: './build/public/',
    file: './build/public/assets.json',
    hash: true  // depends if we inlineJs, inlineCss or not
  }))
  
  const clean = () =&gt; fs.emptyDirAsync('./build')
  const copy = () =&gt; fs.copyAsync(`src/app/static/`, `./build/public/`)
  
  const tasks = new Map()
  const run = (task) =&gt; {
    const start = new Date()
    return tasks.get(task)().then(
      () =&gt; console.log('\x1b[36m%s\x1b[0m', '[build]', `'${task}' done in ${new Date().getTime() - start.getTime()}ms`),
      (err) =&gt; console.error('\x1b[31m%s\x1b[0m', '[build]', `error running '${task}':`, err.stack)
    )
  }
  
  tasks
    .set('clean', clean)
    .set('client', client)
    .set('css', css)
    .set('copy', copy)
    .set('rev', rev)
    .set('server', server)
    .set('sw', sw)
    .set('build', () =&gt; run('clean')
      .then(() =&gt; Promise.all([run('client'), run('css'), run('copy'), run('server')]))
      .then(() =&gt; run('rev'))
      .then(() =&gt; run('sw'))
    )
  
  run(/^\w/.test(process.argv[2] || '') ? process.argv[2] : 'build')
  </code></pre>