20
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Atom Editorを改造してReactアプリを作る手順

Posted at

(前回の続き: Atomコード再利用に向けた最新版の仕様調査)

個人で作っているInkdropというMarkdownノートアプリの改良に向けて、Atomのコードを再利用したい。
せっかく内部仕様を理解したし、いっそのことAtomをベースにアプリを組み直したら面白いんじゃないか?と思い立って、Atomを改造してReactが動くようにした。
GitHubにソースを公開したので、手っ取り早く手元で試したい人はそちらをどうぞ:

68747470733a2f2f6769746875622e636f6d2f63726166747a646f672f61746f6d2d72656163742d6170702f626c6f622f6d61737465722f646f63732f72656163742d6170702e706e673f7261773d74727565.png

誰向けなのか: 今から新しいアプリを作ろうとしていて、Atomの高度なパッケージ拡張機能やテーマ機能を組み込みたい、あるいは真似したいという人は覗いてみると参考になるかもしれない。

動かし方は公式ドキュメントと同じ:

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

改造してからしばらく経つので、もし動かなければ自分で解決してください。その技量がなければおすすめしない。

結果的にInkdropをこのプロジェクトに移植するのは諦めた。理由は次のとおり:

  • 全ソースコードがアプリにバンドルされる仕様になっている
    • プロプライエタリなアプリ開発者としては、プライベートなコードを難読化させておきたい
    • そのためにビルド手順を変更するのは結構たいへんそう (途中までやった)
  • 完全にイチからアプリを組み直すのは時間がかかりすぎて頓挫しそう
    • Inkdropもなんだかんだで3年開発しているので、細かなところまでイチから組み直すのは相当大変
    • 既存のコードを改善して部分的に拝借して再利用するのが現実的

ただ、今回の取り組みによってAtomの深いところまで理解できたので良かった。既にその意義は実感している。
例えば起動スピードを高速化するためにv8 snapshotをどのように生成して組み込んでいるのか、とか。
IPCのヘルパーモジュールなんかは一般的なElectronアプリにも広く使える便利なものだと思う。

作業記録

以降は、前回の続きの改造作業メモ。
Inkdropを組む前提でメモっていて、文脈が分かりづらいかもしれないので注意。

やっぱビューは自分で実装した方が良さげ

Atomのエディタは魅力的だが、現在のプラグインとの互換性は無くなるし、ヘディングのフォントサイズを大きくしたりとかは出来なさそうな感じがする。
Reactでレンダリングした方が自分のコード資産を再利用できる。
React Nativeとのコード共用性は保持したい。
Flowも使いたい。
そうすると compile-cache 周りの手入れが必要になってくるな。

とりあえずAtomの標準package群を全て外して空っぽにする。
package.json"packageDependencies":{}と書き換え。
アプリを起動すると空っぽのウインドウが表示されるだけになった。エラーは特にない。良い。

プラグインのインストールUI調査

apmはコマンドラインツールだがAtomはGUIでもパッケージを管理できる。
それはどうやっているのか調べる。

settings-view パッケージが担当しているっぽい。

node_modules/settings-view/lib/install-panel.js にインストールUIがある。

node_modules/settings-view/lib/package-manager.coffee にパッケージの管理ロジックが書かれている。
直接apmコマンドを実行しているっぽい:

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

やっぱそうなってくるか・・
apmコマンドに--jsonオプションを付加して呼び出している。apm install --json とかも出来るっぽい。よいね。

Atom上でReactを動かしてレンダリングする

react react-dom とかをインストール。

--- 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",

babelが古くてトランスパイルが通らないのでbabel-coreをアップデート。設定も変更:

--- 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"
+  ]
 }

atom-environment.jsを以下のように改造する:

--- 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
   }

src/components/app-container.jsを追加:

/** @babel */
import * as React from 'react'

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

上手く動いた。素晴らしい。

スタイルの読み込み

static/atom.less がエントリポイント。less-compile-cacheを通してオンザフライでCSSに変換して読み込んでいる。
ではこれはどこから読み込まれているのか?

src/theme-manager.jsから:

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

という訳で 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;
+}

できた:

Screenshot

前回の開発ではAtomのソースコードの一部を拝借して構築したが、今回はAtomをフォークして開発したほうがいろいろ都合がいいかもしれない。
そして意外にもあっさりと実現可能性が確認できた。

Reactを使用した状態でビルドするとエラーになる

Error: Cannot use `console` functions in the snapshot.
    at Object.consoleNoop (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:98521)
    at Object.../node_modules/scheduler/cjs/scheduler.production.min.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:14:1641204)
    at customRequire (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:99295)
    at Object.../node_modules/scheduler/index.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:14:695932)
    at customRequire (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:99295)
    at Object.../node_modules/react-dom/cjs/react-dom.production.min.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:14:128467)
    at customRequire (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:99295)
    at Object.../node_modules/react-dom/index.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:11:415063)
    at customRequire (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:99295)
    at Object.../src/atom-environment.js (/Users/***/Developments/local/inkdrop/atom/out/startup.js:1:102859)

  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:94:18)
  at <anonymous>:null:null

エラーの箇所を見ると React が原因っぽい: This browser doesn't support requestAnimationFrame.
https://reactjs.org/docs/javascript-environment-requirements.html
なのでsnapshotを生成する時はReactのレンダリング部分を実行しないようにしてみる。
shimが必要・・?

global.requestAnimationFrame = (callback) => {
    setTimeout(callback, 0);
};

ソースコードは隠蔽できるか

現状では app.asarに全てのソースコードが同梱される仕様になっている。
なんとかそれは阻止できないだろうか。

src/main-process/main.js を読んでもソースコードを直接読み込んでるっぽい・・

initialize-application-window.js をwebpackなどでコンパイルすれば良いか。
でもそれだとsnapshotの効力がなくなってしまうのではないか?
いや、external設定で外部依存コードはバンドルしないようにすればいいか。

本番ビルドが立ち上がらない

markdown-preview パッケージが影響しているっぽい。原因は不明。
markdown-previewを含まない場合、不正なasarがoutputされる。asarをunpackしようとすると以下のようなエラーになる:

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)

ためしに markdown-preview パッケージの中身を空っぽにしてみたが、特に問題なくビルドできた。
何が影響しているのかわからない。たぶんasarパッケージャのバグを踏んだのかもしれない。

無事 WindowsでもUbuntuでもビルドできた。

Workspaceの理解

Todo

  • eslint/prettierとかの導入をどうするか
  • webpack
  • demo版のビルド
  • code signingの理解

Webpack

webpackでビルドしたjsがelectron-linkに通らない。

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

正規のビルド方法と比較してみたところ、どうやら生成したjsそのものの問題ではなさそう。
markdown-previewpackage.jsonのmainが違う!

--- /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がcoffeeのままだ。
babelとcoffee-scriptによるトランスパイルを実行していないからではないか。
あたり!無事markdown-previewがrequireできた。
しかし別の問題が見つかった:

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

require.resolveしているやつが必要としているモジュールが、electron-linkに対応していない。
このあたり: src/workspace.js:

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         )

scandal というパッケージを例外リストに入れてみる。
通った!しかし、require.resolve を使っている箇所が沢山見つかるのが気になる・・おそらく実行時にエラーになりそうな予感。
次の問題:

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)

おい・・。ES6に対応していないのでは。
terser-js/terser: JavaScript parser, mangler, optimizer and beautifier toolkit for ES6+を使っている。これはES6に対応しているはずなんだが。
最新版を使うようにしてみる。・・・no luck.
webpackに以下を指定することで解決:

      optimization: {
        nodeEnv: false
      }

次の問題:

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

ちょっと待て。electron-linkもwebpackと同じように依存モジュールをbundleしてるじゃん・・。
という事は、electron-link + minifyで作ったファイルを普通にアプリに含めればいいだけでは。
もちろん、require.resolve問題はまだ残るが。

プラン2

ちょっとこれ以上現状のビルド手順に手を加えると、最新版にキャッチアップする時に死にそうではある。
そこで、自分の書いたコードだけminifyして難読化すると良いのではないか?
copy assetsでは自分のコードはコピーしない。
自分のコードだけをwebpackなどであらかじめパッケージするとか。

あるいは、もう隠すのは諦めるとか。
悪用されたところで対して影響ないし。
セキュリティ上問題なければそれでいいかな。

あるいはコピーしない。

electron-link化されたやつから外部依存コードが取れたらいいんだけど。

electron-linkでバンドルする

electron-linkはnode_modulesにある依存ファイルもバンドルしてくれることが判った。
という事は、もうwebpackを使わずにこれを使ってバンドルすれば良いことになる。
minifyも上手く動くようだし。

問題は、もう一つの作業ディレクトリを用意する必要があることだ。
babelやcoffeeなどをトランスパイルしたファイルを置く場所が必要になる。
一度トランスパイルしたファイルを出力して、それをelectron-linkにかける。
その後に、electron-linkでintermediate dirに出力する。

その他のファイルのコピーをどうするか問題

そもそも src 配下の initialize-application-window.js は要らない

だから以下の手順を取れば良い:

  1. アプリのpackagingの前にelectron-linkでバンドリング
  2. 不要なファイルを削除する
  3. アプリのパッケージ作成

一旦この構成で組んでみることにする。

mainプロセスのソースファイルバンドル問題

dynamic importを多用しているので、エントリポイントを単純にwebpackでバンドルしても上手く動かない。
だからatomのコードはそのままにしといて、inkdropのコードだけをwebpackでバンドルするようにする。
今のところはそれを検証できないので、一旦そうするつもりで進めてみることにする。

別にatomのコードはminifyしなくても良いかもしれない。それは今後どんな変更を加えるかによるが。

atom-workspaceの理解

itemsは表示できる領域すべてを指すようだ。

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     }

たぶん入れ子に出来るようになっている。
しかし明示的な名前は無いような気がする。dockとは何なのだろうか。
たぶんドラッグとかにも対応しているので、かなり機能豊富な印象。
これをわざわざ再利用しなくてもいいんじゃないだろうか。

eslint、prettier関連をどうするか

とりあえずAtomのコードは地道に全て.eslintignoreに加える。

prod用のdependenciesだけコピーする

diff --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})

以上。

20
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?