github-desktop/docs/technical/placeholders.md
2018-12-27 15:38:17 -08:00

3.4 KiB

Placeholders and Replacements in Source Code

As GitHub Desktop uses Webpack to transpile, minify and merge our code into unified scripts for each configuration we define, we use some tricks to manage complexity and enable some optimizations.

For example, you might come across this code in app-window.ts:

if (__DARWIN__) {
  windowOptions.titleBarStyle = 'hidden'
} else if (__WIN32__) {
  windowOptions.frame = false
}

You may be inclined to refactor this to be:

if (process.platform === 'darwin') {
  windowOptions.titleBarStyle = 'hidden'
} else if (process.platform === 'win32') {
  windowOptions.frame = false
}

And while both of these are semantically the same, how we bundle Desktop means we get significant benefits from doing it the first way.

Replacements

The replacements defined for Desktop are found in app/app-info.ts as a hash of key-value pairs.

function getReplacements() {
  return {
    __OAUTH_CLIENT_ID__: s(process.env.DESKTOP_OAUTH_CLIENT_ID || devClientId),
    __OAUTH_SECRET__: s(
      process.env.DESKTOP_OAUTH_CLIENT_SECRET || devClientSecret
    ),
    __DARWIN__: process.platform === 'darwin',
    __WIN32__: process.platform === 'win32',
    __LINUX__: process.platform === 'linux',
    __DEV__: channel === 'development',
    __RELEASE_CHANNEL__: s(channel),
    __UPDATES_URL__: s(distInfo.getUpdatesURL()),
    __SHA__: s(gitInfo.getSHA()),
    __CLI_COMMANDS__: s(getCLICommands()),
    'process.platform': s(process.platform),
    'process.env.NODE_ENV': s(process.env.NODE_ENV || 'development'),
    'process.env.TEST_ENV': s(process.env.TEST_ENV),
  }
}

This means we can embed values at build time based on the current platform. Note the values we are embedding for __DARWIN__ and __WIN32__:

__DARWIN__: process.platform === 'darwin',
__WIN32__: process.platform === 'win32',

Because we evaluate these values at build time, the original code snippet will now look like this on macOS:

if (true) {
  windowOptions.titleBarStyle = 'hidden'
} else if (false) {
  windowOptions.frame = false
}

And look like this on Windows:

if (false) {
  windowOptions.titleBarStyle = 'hidden'
} else if (true) {
  windowOptions.frame = false
}

As part of Webpack's minification and bundling, the dead code paths are eliminated, leaving this code for macOS:

windowOptions.titleBarStyle = 'hidden'

And this code for Windows:

windowOptions.frame = false

This means less code is emitted as part of the packaged app, and less JavaScript is interpreted and executed at runtime.

Placeholders

As we are working in TypeScript, we need to define these placeholders as globals under app/src/lib/globals.ts to provide the appropriate type information to the source code.

For example, the values for __DARWIN__ and __WIN32__ are declared as booleans.

/** Is the app being built to run on Darwin? */
declare const __DARWIN__: boolean

/** Is the app being built to run on Win32? */
declare const __WIN32__: boolean

As a convention, globals which should be replaced by Webpack should be prefixed and suffixed with two underscores, e.g. __DEV__.