Understanding Atom Editor and Hacking It to Build a React App

My Attempt to Reuse Atom’s Core to Rebuild My Product Called Inkdrop

Understanding Atom Editor and Hacking It to Build a React App

Hello, it’s Takuya here.

I’m developing a Markdown note-taking app called Inkdrop, whose some parts are based on Atom Editor for implementing plugin extensibility, UI theme support and flexible key customizations. Atom Editor is well-designed in code level so I could reuse their codebase easily than I imagined. As I mentioned in our current roadmap, I’m working on the ground-up improvements on Inkdrop. It’s been a while since I’ve read their code, so it would be time to catch it up and to learn from it.

Inkdrop

After I’ve dug into Atom Editor’s core, I thought it would be great if I could rebuild Inkdrop completely based on it from scratch. So I’ve hacked Atom itself to make React able to work on it. I published my work on GitHub so you can try it:

craftzdog/atom-react-app
An empty React app based on Atom Editor. Contribute to craftzdog/atom-react-app development by creating an account on…

You can check the commit to know what has changed from the original code. src/components/app-container.js is the app container component where is a good place to start digging into it.

I guess it would be helpful if you are planning to create new React-based electron app that comes with features like plugin extensibility, UI themes and key customizations like Atom.

How to run it:$ git clone git@github.com:craftzdog/atom-react-app
$ cd atom-react-app
$ script/bootstrap
$ atom --dev --foreground

However, as a result, I ended up stopping to work based on it because:

  • It bundles all source code in the app — I, a proprietary app developer, would like to hide my private code by minifying them. It looks like hard to accomplish that by changing its build process (I have made some changes already for it though).
  • I found that it will be extremely hard to finish rebuilding the whole app from scratch — I admit it’s kind of over-engineering. Like a lot of other products tried to rebuild completely but failed, I could also fail since it will take very long time to finish. It should be more reasonable to borrow only some code as I did before.

But by touching Atom’s deep core, I’ve got to learn a lot from it. It was great. For example, I knew how Atom generates v8 snapshot in order to make the launch speed faster. And I found that their IPC helper module would be also useful for many electron projects.

How I Understood and Hacked Atom Editor

Followings are my progress notes about how I looked into Atom’s core and hacked it for my project, Inkdrop.

Their License

MIT License. Thanks Atom.

Build it

Follow the official documentations:> script/build
Node:   v11.1.0
Npm:    v6.2.0
Installing script dependencies
Installing apm
apm  2.1.3
npm  6.2.0
node 8.9.3 x64
atom unknown
python 2.7.13
git 2.17.2
Installing modules ✓
Wrote Dependencies Fingerprint: /Users/***/inkdrop/atom/node_modules/.dependencies-fingerprint 1a7626f47939d86d09695ab33831091b020a7347
Copying assets to /Users/***/inkdrop/atom/out/app
Transpiling packages with custom transpiler configurations in /Users/***/inkdrop/atom/out/app
transpiling for package github
Installing modules ✓
Transpiling Babel paths in /Users/***/inkdrop/atom/out/app
Transpiling CoffeeScript paths in /Users/***/inkdrop/atom/out/app
Transpiling CSON paths in /Users/***/inkdrop/atom/out/app
Transpiling PEG.js paths in /Users/***/inkdrop/atom/out/app
Generating module cache for /Users/***/inkdrop/atom/out/app
Generating pre-built less cache in /Users/***/inkdrop/atom/out/app/less-compile-cache
Generating metadata for /Users/***/inkdrop/atom/out/app/package.json
Generating API docs at /Users/***/inkdrop/atom/docs/output/atom-api.json
Dumping symbols in /Users/***/inkdrop/atom/out/symbols
Running electron-packager on /Users/***/inkdrop/atom/out/app with app name "Atom Dev"
Downloading electron-v2.0.12-darwin-x64.zip
[============================================>] 100.0% of 48.75 MB (7.5 MB/s)
Packaging app for platform darwin x64 using electron v2.0.12
Setting Atom Helper Version for /Users/***/inkdrop/atom/out/Atom Dev.app/Contents/Frameworks/Atom Helper.app/Contents/Info.plist
Copying non-ASAR resources to /Users/***/inkdrop/atom/out/Atom Dev.app/Contents/Resources
Writing LICENSE.md to /Users/***/inkdrop/atom/out/Atom Dev.app/Contents/Resources
Application bundle created at /Users/***/inkdrop/atom/out/Atom Dev.app
Generating snapshot script at "/Users/***/inkdrop/atom/out/startup.js" (3834)
Minifying startup script
Verifying if snapshot can be executed via `mksnapshot`
Generating startup blob at "/Users/***/inkdrop/atom/out/snapshot_blob.bin"
Moving generated startup blob into "/Users/***/inkdrop/atom/out/Atom Dev.app/Contents/Frameworks/Electron Framework.framework/Resources/snapshot_blob.bin"
Skipping code-signing. Specify the --code-sign option to perform code-signing
Skipping artifacts compression. Specify the --compress-artifacts option to compress Atom binaries (and symbols on macOS)
Skipping installation. Specify the --install option to install Atom

v8 Snapshot

The app launch speed is so fast! It looks like they generate v8 snapshot so that Chrominium can load JS faster.

It installed all dependencies by apm

It gets errors when I installed dependencies with npm because of node version mismatch. script/bootstrap uses apm to install modules. It takes care with node version of Electron. So they fetch apm first. Amazing…

babel configurations

They made a transpiler for themselves. It includes babel-preset-react
so you can write jsx without worrying anything.

how should I adopt flux architecture

Maybe I could put files for redux in exports/ directory.

fetch main process logs

Run like so:$ atom --dev --foreground

Steps to load main process in dev mode

With atom --dev option it launches /Applications/Atom.app with dev mode enabled. In src/main-process/main.js, the main process switches the file to require for dev mode. As a result, <workdir>/src/main-process/start.js is loaded.

Creating windows

src/main-process/atom-window.js:33

They just use BrowserWindow as usual so I can change it to frameless.

Check loading PouchDB

Add:--- a/package.json
+++ b/package.json
@@ -137,6 +137,7 @@
    "pathwatcher": "8.0.1",
    "postcss": "5.2.4",
    "postcss-selector-parser": "2.2.1",
+    "pouchdb": "7.0.0",
    "property-accessors": "^1.1.3",
    "random-words": "0.0.1",
    "resolve": "^1.1.6",

Install it:> script/bootstrap

It failed. Maybe necessary to rebuild the native module:> npm install -g electron-rebuild
> electron-rebuild -v 2.0.12

Got it to work! It is loaded from main-process. Looks good.

Add a sidebar

Now I would like to add a sidebar for showing a list of notebooks. I copied tree-view package to packages/side-bar/package.json and edit it.

Successful. Interesting.

They don’t use React but etch to render UI

I saw a syntax like JSX in a package of welcome but it is not actually react. It seems like etch, which is invented by Atom. So I can’t use React to build UI. Oh. Now, I have to rewrite whole views. How could I reuse Atom’s editor component? Hmm.

Looks like it would better implement views by myself

Atom’s editor is fascinating but it will lose the compatibility with Inkdrop’s existing plugins and I guess it can’t customize font size for headings. To reuse our existing codebase, I need React on Atom. Also I need a resusability on mobile, which is built with React Native. I would like to use Flow. Now, it looks like necessary to change compile-cache.

For now, remove Atom’s default packages. Edit package.json to “packageDependencies”:{}. Then it launched, showing an empty window. Looks good.

Understanding how they provide UI for managing packages

apm is a command-line tool but Atom provides GUI for managing atom packages. Investigate how they accomplish it.

It looks like settings-view is the UI component. node_modules/settings-view/lib/install-panel.js has an implementation of the installation UI.

In node_modules/settings-view/lib/package-manager.coffee, there is a logic for managing packages, executing apm directly:61   runCommand: (args, callback) ->
62     command = atom.packages.getApmPath()
63     outputLines = []
64     stdout = (lines) -> outputLines.push(lines)
65     errorLines = []
66     stderr = (lines) -> errorLines.push(lines)
67     exit = (code) ->
68       callback(code, outputLines.join('\n'), errorLines.join('\n'))
69
70     args.push('--no-color')
71
72     if atom.config.get('core.useProxySettingsWhenCallingApm')
73       bufferedProcess = new BufferedProcess({command, args, stdout, stderr, exit, autoStart: false})
74       if atom.resolveProxy?
75         @setProxyServersAsync -> bufferedProcess.start()
76       else
77         @setProxyServers -> bufferedProcess.start()
78       return bufferedProcess
79     else
80       return new BufferedProcess({command, args, stdout, stderr, exit})
81

That’s what I guessed.. I found that apm command supports --json option so that you can use it programmatically.

Try rendering UI with React on Atom

Install react , react-dom and so on:--- a/package.json
+++ b/package.json
@@ -34,7 +34,9 @@
    "autocomplete-snippets": "https://www.atom.io/api/packages/autocomplete-snippets/versions/1.12.0/tarball",
    "autoflow": "file:packages/autoflow",
    "autosave": "https://www.atom.io/api/packages/autosave/versions/0.24.6/tarball",
-    "babel-core": "5.8.38",
+    "babel-core": "6.26.3",
+    "babel-preset-env": "^1.7.0",
+    "babel-preset-react": "^6.24.1",
    "background-tips": "https://www.atom.io/api/packages/background-tips/versions/0.28.0/tarball",
    "base16-tomorrow-dark-theme": "file:packages/base16-tomorrow-dark-theme",
    "base16-tomorrow-light-theme": "file:packages/base16-tomorrow-light-theme",
@@ -137,8 +139,11 @@
    "pathwatcher": "8.0.1",
    "postcss": "5.2.4",
    "postcss-selector-parser": "2.2.1",
    "pouchdb": "7.0.0",
    "property-accessors": "^1.1.3",
    "random-words": "0.0.1",
+    "react": "^16.6.3",
+    "react-dom": "^16.6.3",
    "resolve": "^1.1.6",
    "scandal": "^3.1.0",
    "scoped-property-store": "^0.17.0",

Update babel-core since transpiling won’t work well because babel is too old. Also change the settings:--- a/static/babelrc.json
+++ b/static/babelrc.json
@@ -1,7 +1,11 @@
{
-  "breakConfig": true,
  "sourceMap": "inline",
-  "blacklist": ["es6.forOf", "useStrict"],
-  "optional": ["asyncToGenerator"],
-  "stage": 0
+  "presets": [
+    ["env", {
+      "targets": { "electron": "2.0.12" },
+      "useBuiltIns": "usage",
+      "debug": false
+    }],
+    "react"
+  ]
}

Hack atom-environment.js like so:--- a/src/atom-environment.js
+++ b/src/atom-environment.js
@@ -43,6 +43,8 @@ const TextEditor = require('./text-editor')
const TextBuffer = require('text-buffer')
const TextEditorRegistry = require('./text-editor-registry')
const AutoUpdateManager = require('./auto-update-manager')
+const React = require('react')
+const { render, unmountComponentAtNode } = require('react-dom')

let nextId = 0

@@ -271,6 +273,15 @@ class AtomEnvironment {
    this.observeAutoHideMenuBar()

    this.disposables.add(this.applicationDelegate.onDidChangeHistoryManager(() => this.history.loadState()))
+
+    const AppContainer = require('./components/app-container').default
+    const root = document.createElement('div')
+    root.classList.add('app-container')
+    document.body.append(root)
+    const element = React.createElement(AppContainer)
+
+    render(element, root)
+    this.element = element
  }

Add a React component in src/components/app-container.js :/** @babel */
import * as React from 'react'

export default function AppContainer () {
 return <div>Inkdrop on Atom!</div>
}

It worked well. Awesome.

Loading styles

The entrypoint is static/atom.less. It transpiles LESS files into CSS on-the-fly through less-compile-cache. Now, where are they loaded from?

From src/theme-manager.js:220   loadBaseStylesheets () {
221     this.reloadBaseStylesheets()
222   }
223
224   reloadBaseStylesheets () {
225     this.requireStylesheet('../static/atom', -2, true)
226   }

Okay, edit atom.less:--- a/static/atom.less
+++ b/static/atom.less
@@ -27,3 +27,12 @@

// Atom UI library
@import "../node_modules/atom-ui/atom-ui.less";
+
+.app-container {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 30px;
+}

It worked:

It would be great to develop new version by forking Atom, instead of just borrowing some code from it like before. I have easily confirmed its feasibility.

I guess React causes the error: This browser doesn’t support requestAnimationFrame. https://reactjs.org/docs/javascript-environment-requirements.html

It is necessary to avoid from running React when generating v8 snapshot. Maybe a shim works?global.requestAnimationFrame = (callback) => {
   setTimeout(callback, 0);
};

Check if it’s possible to hide private code

Currently it bundles all source code in app.asar. How to prevent this?

It looks like it loads the source directly in src/main-process/main.js

I guess I could use webpack to compile initialize-application-window.js? But I suspect that it will become not possible to generate snapshot?

App with production build not launching

I don’t know but markdown-preview causes the problem. If it doesn’t include markdown-preview, it builds invalid asar file. When I tried to unpack the invalid asar, I got following error:asar extract app.asar asarrr
undefined:1
{"files":{"benchmarks":{"files":{"benchmark-runner.js":{"size":2205,"offset":"0"}}},"dot-atom":{"files":{".gitignore":{"size":57,"offset":"2205"},"init.coffee":{"size":386,"offset":"2262"},"keymap.cson":{"size":1333,"offset":"2648"},"packages":{"files":{"README.md":{"size":60,"offset":"3981"}}},"snippets.cson":{"size":710,"offset":"4041"},"styles.less":{"size":712,"offset":"4751"}}},"exports":{"files":{"atom.js":{"size":1258,"offset":"5463"},"clipboard.js":{"size":275,"offset":"6721"},"ipc.js":{"size":273,"offset":"6996"},"remote.js":{"size":266,"offset":"7269"},"shell.js":{"size":263,"offset":"7535"},"web-frame.js":{"size":273,"offset":"7798"}}},"node_modules":{"files":{".dependencies-fingerprint":{"size":40,"offset":"8071"},"@atom":{"files":{"nsfw":{"files":{"build":{"files":{"Release":{"files":{".deps":{"files":{"Release":{"files":{"obj.target":{"files":{"nsfw":{"files":{"src":{"files":{"osx":{"files":{}}}}}}}}}}}},"nsfw.node":{"size":88796,"unpacked":true},"obj.target":{"files":{"nsfw":{"fil

SyntaxError: Unexpected end of JSON input
   at JSON.parse (<anonymous>)
   at Object.module.exports.readArchiveHeaderSync (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/lib/disk.js:90:24)
   at Object.module.exports.readFilesystemSync (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/lib/disk.js:95:25)
   at Object.module.exports.extractAll (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/lib/asar.js:191:27)
   at Command.<anonymous> (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/bin/asar.js:66:15)
   at Command.listener (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/node_modules/commander/index.js:315:8)
   at emitTwo (events.js:106:13)
   at Command.emit (events.js:194:7)
   at Command.parseArgs (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/node_modules/commander/index.js:654:12)
   at Command.parse (/Users/***/.nvm/versions/node/v7.9.0/lib/node_modules/asar/node_modules/commander/index.js:474:21)

If I put an empty package named markdown-preview , it just worked without problems. I don’t know what causes the problem. Maybe a bug of asar?

It also worked fine on Windows and Ubuntu.

Understanding Workspace

Todo

  • Adopt eslint/prettier
  • webpack
  • Build demo version
  • Understand code signing

Webpack

electron-link rejects js built with webpack:Error: ENOENT: no such file or directory, open 'markdown-preview'
 at Object.fs.openSync (fs.js:646:18)
 at Object.fs.readFileSync (fs.js:551:33)
 at /Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/generate-snapshot-script.js:30:27
 at Generator.next (<anonymous>:null:null)
 at step (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/generate-snapshot-script.js:3:191)
 at /Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/generate-snapshot-script.js:3:361
 at <anonymous>:null:null

package.json of markdown-preview was different!--- /Users/***/Developments/local/inkdrop/atom/out.mine/app/node_modules/markdown-preview/package.json 2018-11-24 11:33:39.000000000 +0900
+++ /Users/***/Developments/local/inkdrop/atom/out/app/node_modules/markdown-preview/package.json      2018-11-24 11:22:15.000000000 +0900
@@ -16,20 +16,20 @@
  "name": "markdown-preview",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/atom/markdown-preview.git"
  },
  "version": "0.159.25",
  "_atomModuleCache": {
    "version": 1,
    "dependencies": [],
    "extensions": {
-      ".coffee": [
-        "lib/main.coffee"
+      ".js": [
+        "lib/main.js"
      ],
      ".json": [
        "package.json"
      ]
    },
    "folders": []
  }
}

_atomModuleCache is left as .coffee . I guess transpiling with babel and coffee-script was not being run.

Gotcha! Successfully required markdown-preview . But another problem found:Unable to transform source code for module /Users/***/Developments/local/inkdrop/atom/out/app/node_modules/argparse/lib/argument_parser.js.
Error: /Users/***/Developments/local/inkdrop/atom/out/app/node_modules/argparse/lib/argument_parser.js
Cannot replace with lazy function because the supplied node does not belong to an assignment expression or a variable declaration!
   at FileRequireTransform.replaceAssignmentOrDeclarationWithLazyFunction (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/file-require-transform.js:180:11)
   at Context.visitIdentifier (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/file-require-transform.js:72:18)
   at Context.invokeVisitorMethod (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:344:49)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:196:32)
   at visitChildren (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:246:25)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:204:20)
   at visitChildren (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:246:25)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:204:20)
   at visitChildren (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:246:25)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:204:20)
   at NodePath.each (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path.js:101:26)
   at visitChildren (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:219:18)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:204:20)
   at visitChildren (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:246:25)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:204:20)
   at visitChildren (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:246:25)
   at Visitor.PVp.visitWithoutReset (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:204:20)
   at Visitor.PVp.visit (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:133:29)
   at Object.visit (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/node_modules/ast-types/lib/path-visitor.js:101:55)
   at FileRequireTransform.replaceDeferredRequiresWithLazyFunctions (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/file-require-transform.js:68:20)
   at FileRequireTransform.apply (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/file-require-transform.js:25:10)
   at /Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/generate-snapshot-script.js:57:56
   at Generator.next (<anonymous>:null:null)
   at step (/Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/generate-snapshot-script.js:3:191)
   at /Users/***/Developments/local/inkdrop/atom/script/node_modules/electron-link/lib/generate-snapshot-script.js:3:361
   at <anonymous>:null:null

electron-link doesn’t support modules loaded with require.resolve . Like in src/workspace.js here:1994         const task = Task.once(
1995           require.resolve('./replace-handler'),
1996           outOfProcessPaths,
1997           regex.source,
1998           flags,
1999           replacementText,
2000           () => {
2001             outOfProcessFinished = true
2002             checkFinished()
2003           }
2004         )

Try adding scandal package to the exclude list.

Passed! But I found there are many places where require.resolve is used..that will cause problems when running, I guess.

Next problem:Minifying startup scriptSyntaxError: Invalid assignment
 at JS_Parse_Error.get (<anonymous>:75:23)
 at process.<anonymous> (/Users/***/Developments/local/inkdrop/atom/script/build:56:19)
 at emitTwo (events.js:126:13)
 at process.emit (events.js:214:7)
 at emitPendingUnhandledRejections (internal/process/promises.js:94:22)
 at runMicrotasksCallback (internal/process/next_tick.js:124:9)
 at _combinedTickCallback (internal/process/next_tick.js:131:7)
 at process._tickCallback (internal/process/next_tick.js:180:9)

Oh, does it not support ES6? It uses terser-js/terser: JavaScript parser, mangler, optimizer and beautifier toolkit for ES6+. I guess it should support ES6. It was no luck after having upgraded it to the latest version.

It was solved by setting webpack like so:optimization: {
       nodeEnv: false
     }

Next problem:Verifying if snapshot can be executed via `mksnapshot`
/Users/***/Developments/local/inkdrop/atom/out/startup.js:1
var snapshotAuxiliaryData={};function generateSnapshot(){let process={};function get_process(){return process}function createElement(e){return{innerHTML:"",style:{}}}Object.defineProperties(process,{platform:{value:"darwin",enumerable:!1},argv:{value:[],enumerable:!1},env:{value:{NODE_ENV:"production"},enumerable:!1}});let documentElement={textContent:"",style:{cssFloat:""}},document={};function get_document(){return document}Object.defineProperties(document,{createElement:{value:createElement,enumerable:!1},addEventListener:{value:function(){},enumerable:!1},documentElement:{value:documentElement,enumerable:!1},oninput:{value:{},enumerable:!1},onchange:{value:{},enumerable:!1}});let global={};function get_global(){return global}Object.defineProperties(global,{document:{value:document,enumerable:!1},process:{value:process,enumerable:!1},WeakMap:{value:WeakMap,enumerable:!1},isGeneratingSnapshot:{value:!0,enumerable:!1}});let window={};function get

Error: Cannot require module "crypto".
To use Node's require you need to call `snapshotResult.setGlobals` first!
   at require (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:1662)
   at customRequire (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2113)
   at Object.crypto (/Users/***/Developments/local/inkdrop/atom/out/startup.js:22:174800)
   at __webpack_require__ (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2443)
   at Object.../src/atom-environment.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:13981)
   at __webpack_require__ (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2443)
   at Object.../src/initialize-application-window.coffee (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:212614)
   at __webpack_require__ (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2443)
   at /Users/***/Developments/local/inkdrop/atom/out/startup.js:1:3515
   at Object.../src/initialize-application-window.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:3604)
Error: Command failed: /Users/***/Developments/local/inkdrop/atom/out/Atom Dev.app/Contents/MacOS/Atom Dev /Users/***/Developments/local/inkdrop/atom/script/verify-snapshot-script /Users/***/Developments/local/inkdrop/atom/out/startup.js
/Users/***/Developments/local/inkdrop/atom/out/startup.js:1
var snapshotAuxiliaryData={};function generateSnapshot(){let process={};function get_process(){return process}function createElement(e){return{innerHTML:"",style:{}}}Object.defineProperties(process,{platform:{value:"darwin",enumerable:!1},argv:{value:[],enumerable:!1},env:{value:{NODE_ENV:"production"},enumerable:!1}});let documentElement={textContent:"",style:{cssFloat:""}},document={};function get_document(){return document}Object.defineProperties(document,{createElement:{value:createElement,enumerable:!1},addEventListener:{value:function(){},enumerable:!1},documentElement:{value:documentElement,enumerable:!1},oninput:{value:{},enumerable:!1},onchange:{value:{},enumerable:!1}});let global={};function get_global(){return global}Object.defineProperties(global,{document:{value:document,enumerable:!1},process:{value:process,enumerable:!1},WeakMap:{value:WeakMap,enumerable:!1},isGeneratingSnapshot:{value:!0,enumerable:!1}});let window={};function get

Error: Cannot require module "crypto".
To use Node's require you need to call `snapshotResult.setGlobals` first!
   at require (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:1662)
   at customRequire (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2113)
   at Object.crypto (/Users/***/Developments/local/inkdrop/atom/out/startup.js:22:174800)
   at __webpack_require__ (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2443)
   at Object.../src/atom-environment.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:13981)
   at __webpack_require__ (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2443)
   at Object.../src/initialize-application-window.coffee (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:212614)
   at __webpack_require__ (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:2443)
   at /Users/***/Developments/local/inkdrop/atom/out/startup.js:1:3515
   at Object.../src/initialize-application-window.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:3604)

   at checkExecSyncError (child_process.js:601:13)
   at Object.execFileSync (child_process.js:621:13)
   at electronLink.then (/Users/***/Developments/local/inkdrop/atom/script/lib/generate-startup-snapshot.js:101:18)
   at <anonymous>:null:null

Wait, wait. electron-link does bundle dependencies just like webpack! Well, only what I should do is to bundle the output file generated with electron-link + minify?

Plan B

I think it is dangerous if I continued to hack the current build process because I will not be able to catch up the latest version. Is it possible to minify only my code? Don’t let my code be copied during copy assets step.

Another option would be to allow bundling my code. It is ok if it doesn’t affect any security risk.

Or, just not copy anything.

It would be good if dependencies can be required from electron-link-ized code.

I found that electron-link also bundles dependencies in node_modules . So, I no longer need webpack. It also supports minify.

The problem is that it needs another working directory for storing transpiled files with babel and coffee-script. Run electron-link after transpiling. And output files to intermediate dirs with electron-link.

What about other files..

initialize-application-window.js is not necessary

So a way to build would be:

  1. Bundle dependencies with electron-link before packaging
  2. Delete unnecessary files
  3. Generate app package

Let’s try it.

A problem with bundling the source files of main process

It uses dynamic import at a lot of places, webpack doesn’t work well for bundling the entrypoint. So leave atom’s code as it is, bundle only inkdrop’s code with webpack.

Understanding atom-workspace

items means every pane possible to display:218     this.panelContainers = {
219       top: new PanelContainer({viewRegistry: this.viewRegistry, location: 'top'}),
220       left: new PanelContainer({viewRegistry: this.viewRegistry, location: 'left', dock: this.paneContainers.left}),
221       right: new PanelContainer({viewRegistry: this.viewRegistry, location: 'right', dock: this.paneContainers.right}),
222       bottom: new PanelContainer({viewRegistry: this.viewRegistry, location: 'bottom', dock: this.paneContainers.bottom}),
223       header: new PanelContainer({viewRegistry: this.viewRegistry, location: 'header'}),
224       footer: new PanelContainer({viewRegistry: this.viewRegistry, location: 'footer'}),
225       modal: new PanelContainer({viewRegistry: this.viewRegistry, location: 'modal'})
226     }

Maybe it supports nesting. But I don’t know what is dock. I guess it also supports drag-and-drop to change the layout. It supports a lot of features. But I don’t think I have to use it.

What about eslint and prettier

For now, add every file related to Atom to .eslintignore.

Copy only dependencies for proddiff --git a/script/lib/copy-assets.js b/script/lib/copy-assets.js
index 27b5f086c..8dbf666ee 100644
--- a/script/lib/copy-assets.js
+++ b/script/lib/copy-assets.js
@@ -3,6 +3,7 @@

'use strict'

+const execSync = require('child_process').execSync
const path = require('path')
const fs = require('fs-extra')
const CONFIG = require('../config')
@@ -11,16 +12,23 @@ const includePathInPackagedApp = require('./include-path-in-packaged-app')

module.exports = function () {
  console.log(`Copying assets to ${CONFIG.intermediateAppPath}`)
+
+  let depFiles = execSync('npm ls --prod --parseable')
+    .toString()
+    .split('\n')
+    .slice(1)
+    .slice(0, -1)
+
  let srcPaths = [
    path.join(CONFIG.repositoryRootPath, 'benchmarks', 'benchmark-runner.js'),
    path.join(CONFIG.repositoryRootPath, 'dot-atom'),
    path.join(CONFIG.repositoryRootPath, 'exports'),
-    path.join(CONFIG.repositoryRootPath, 'node_modules'),
    path.join(CONFIG.repositoryRootPath, 'package.json'),
    path.join(CONFIG.repositoryRootPath, 'static'),
    path.join(CONFIG.repositoryRootPath, 'src'),
    path.join(CONFIG.repositoryRootPath, 'vendor')
  ]
+  srcPaths = srcPaths.concat(depFiles)
  srcPaths = srcPaths.concat(glob.sync(path.join(CONFIG.repositoryRootPath, 'spec', '*.*'), {ignore: path.join('**', '*-spec.*')}))
  for (let srcPath of srcPaths) {
    fs.copySync(srcPath, computeDestinationPath(srcPath), {filter: includePathInPackagedApp, dereference: true})

That’s it. Hope that helps.

Homepage: https://inkdrop.app/
Send feedback: https://forum.inkdrop.app/
Contact us: contact@inkdrop.app
Twitter: https://twitter.com/inkdrop_app