diff --git a/docs/README.md b/docs/README.md index e346d985d755d..40ae2be10c18c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -68,6 +68,7 @@ an issue: * [Mac App Store](tutorial/mac-app-store-submission-guide.md) * [Windows Store](tutorial/windows-store-guide.md) * [Snapcraft](tutorial/snapcraft.md) + * [ASAR Archives](tutorial/asar-archives.md) * [Updates](tutorial/updates.md) * [Getting Support](tutorial/support.md) diff --git a/docs/tutorial/asar-archives.md b/docs/tutorial/asar-archives.md new file mode 100644 index 0000000000000..23b987f42ee50 --- /dev/null +++ b/docs/tutorial/asar-archives.md @@ -0,0 +1,175 @@ +--- +title: ASAR Archives +description: What is ASAR archive and how does it affect the application. +slug: asar-archives +hide_title: false +--- + +After creating an [application distribution](application-distribution.md), the +app's source code are usually bundled into an [ASAR +archive](https://github.com/electron/asar), which is a simple extensive archive +format designed for Electron apps. By bundling the app we can mitigate issues +around long path names on Windows, speed up `require` and conceal your source +code from cursory inspection. + +The bundled app runs in a virtual file system and most APIs would just work +normally, but for some cases you might want to work on ASAR archives explicitly +due to a few caveats. + +## Using ASAR Archives + +In Electron there are two sets of APIs: Node APIs provided by Node.js and Web +APIs provided by Chromium. Both APIs support reading files from ASAR archives. + +### Node API + +With special patches in Electron, Node APIs like `fs.readFile` and `require` +treat ASAR archives as virtual directories, and the files in it as normal +files in the filesystem. + +For example, suppose we have an `example.asar` archive under `/path/to`: + +```sh +$ asar list /path/to/example.asar +/app.js +/file.txt +/dir/module.js +/static/index.html +/static/main.css +/static/jquery.min.js +``` + +Read a file in the ASAR archive: + +```javascript +const fs = require('fs') +fs.readFileSync('/path/to/example.asar/file.txt') +``` + +List all files under the root of the archive: + +```javascript +const fs = require('fs') +fs.readdirSync('/path/to/example.asar') +``` + +Use a module from the archive: + +```javascript +require('./path/to/example.asar/dir/module.js') +``` + +You can also display a web page in an ASAR archive with `BrowserWindow`: + +```javascript +const { BrowserWindow } = require('electron') +const win = new BrowserWindow() + +win.loadURL('file:///path/to/example.asar/static/index.html') +``` + +### Web API + +In a web page, files in an archive can be requested with the `file:` protocol. +Like the Node API, ASAR archives are treated as directories. + +For example, to get a file with `$.get`: + +```html + +``` + +### Treating an ASAR archive as a Normal File + +For some cases like verifying the ASAR archive's checksum, we need to read the +content of an ASAR archive as a file. For this purpose you can use the built-in +`original-fs` module which provides original `fs` APIs without `asar` support: + +```javascript +const originalFs = require('original-fs') +originalFs.readFileSync('/path/to/example.asar') +``` + +You can also set `process.noAsar` to `true` to disable the support for `asar` in +the `fs` module: + +```javascript +const fs = require('fs') +process.noAsar = true +fs.readFileSync('/path/to/example.asar') +``` + +## Limitations of the Node API + +Even though we tried hard to make ASAR archives in the Node API work like +directories as much as possible, there are still limitations due to the +low-level nature of the Node API. + +### Archives Are Read-only + +The archives can not be modified so all Node APIs that can modify files will not +work with ASAR archives. + +### Working Directory Can Not Be Set to Directories in Archive + +Though ASAR archives are treated as directories, there are no actual +directories in the filesystem, so you can never set the working directory to +directories in ASAR archives. Passing them as the `cwd` option of some APIs +will also cause errors. + +### Extra Unpacking on Some APIs + +Most `fs` APIs can read a file or get a file's information from ASAR archives +without unpacking, but for some APIs that rely on passing the real file path to +underlying system calls, Electron will extract the needed file into a +temporary file and pass the path of the temporary file to the APIs to make them +work. This adds a little overhead for those APIs. + +APIs that requires extra unpacking are: + +* `child_process.execFile` +* `child_process.execFileSync` +* `fs.open` +* `fs.openSync` +* `process.dlopen` - Used by `require` on native modules + +### Fake Stat Information of `fs.stat` + +The `Stats` object returned by `fs.stat` and its friends on files in `asar` +archives is generated by guessing, because those files do not exist on the +filesystem. So you should not trust the `Stats` object except for getting file +size and checking file type. + +### Executing Binaries Inside ASAR archive + +There are Node APIs that can execute binaries like `child_process.exec`, +`child_process.spawn` and `child_process.execFile`, but only `execFile` is +supported to execute binaries inside ASAR archive. + +This is because `exec` and `spawn` accept `command` instead of `file` as input, +and `command`s are executed under shell. There is no reliable way to determine +whether a command uses a file in asar archive, and even if we do, we can not be +sure whether we can replace the path in command without side effects. + +## Adding Unpacked Files to ASAR archives + +As stated above, some Node APIs will unpack the file to the filesystem when +called. Apart from the performance issues, various anti-virus scanners might +be triggered by this behavior. + +As a workaround, you can leave various files unpacked using the `--unpack` option. +In the following example, shared libraries of native Node.js modules will not be +packed: + +```sh +$ asar pack app app.asar --unpack *.node +``` + +After running the command, you will notice that a folder named `app.asar.unpacked` +was created together with the `app.asar` file. It contains the unpacked files +and should be shipped together with the `app.asar` archive.