Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add back docs about asar archives #35563

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: we don't really use this README on the website anymore at all!

* [Updates](tutorial/updates.md)
* [Getting Support](tutorial/support.md)

Expand Down
175 changes: 175 additions & 0 deletions docs/tutorial/asar-archives.md
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

@Prinzhorn Prinzhorn Sep 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word "issues" was originally linked to nodejs/node-v0.x-archive#6960 . But hasn't this been fixed years ago (npm doesn't nest node_modules any longer)?

Electron also recommends bundling your code (https://www.electronjs.org/de/docs/latest/tutorial/performance#7-bundle-your-code), which also speeds up require (they're mostly gone) and "conceals" the source (by means of minification and mangling).

The reason why I'm bringing this up is that when I started with Electron I read this paragraph and couldn't find a single compelling reason to use ASAR (and I didn't), only problems (e.g. the Worker constructor didn't seem to be patched to add ASAR support).

Is there actually a real benefit of using ASAR? I'm curious if I'm missing out.

Summary:

  1. Long path names haven't been an issue since 2015 (?)
  2. There's no evidence linked that ASAR is faster than bundling (which is recommended anyway), maybe include a benchmark somewhere?
  3. "conceal your source code from cursory inspection" is a really weak argument, if at all

So maybe this could be rewritten for 2022?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bundlers are not good at handling assets as far as I know. For example you can not bundle image files and then serve them via protocol module, or execute a bundled python script with child_process. Of course you can work around them if your app is designed to work with bundlers from start, but ASAR is designed to work with arbitrary apps and package them into one single file.

But still I agree ASAR does not provide a real benefit than bundling, and this documentation means to list the limitations of ASAR instead of promoting it.

Node.js is going to work on a similar thing https://github.com/nodejs/single-executable, and we might want to use it as the default format in the end.

Copy link
Contributor

@Prinzhorn Prinzhorn Sep 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying. It sounds like you brought up some good points that should belong into the docs (maybe we could explicitly mention bundling and the pros and cons).

you can not bundle image files and then serve them via protocol module

For me they are bundled as part of the client bundle and I point @fastify/static (which is bundled in the server bundle) to the client/dist folder. But I understand not everyone is running an HTTP server. This works as expected for me:

import chromeProxySettings from '~/images/getting-started-chrome-proxy.png';
<img src={chromeProxySettings} alt="Chrome proxy settings" />

I don't see why the protocol module couldn't serve files from my client/dist folder as well.

or execute a bundled python script with child_process

I think ASAR makes that harder, not easier? Because depending on the circumstances you'd have to explicitly do something like asarUnpack (for electron-builder) for it to even work. I'm shipping a Python binary (via PyInstaller) with additional Python scripts and I don't have any problems so far using child_process.spawn.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this document needs an update, and actually the original PR that removed it was planning to move the document to the asar repo but somehow the effort ended nowhere.

This PR means to bring back the deleted page since it contains important information that currently can not be found on Internet, we might delete it later after getting a renewed documentation in the asar repo.

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
<script>
let $ = require('./jquery.min.js')
$.get('file:///path/to/example.asar/file.txt', (data) => {
console.log(data)
})
</script>
```

### 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.