mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
sea: implement sea.getAssetKeys()
This adds a new API to allow the bundled script in SEA to query the list of assets. PR-URL: https://github.com/nodejs/node/pull/59661 Refs: https://github.com/nodejs/single-executable/discussions/112 Reviewed-By: Darshan Sen <raisinten@gmail.com>
This commit is contained in:
parent
66fcca4328
commit
6428e2e4ca
|
|
@ -221,7 +221,10 @@ executable, users can retrieve the assets using the [`sea.getAsset()`][] and
|
||||||
The single-executable application can access the assets as follows:
|
The single-executable application can access the assets as follows:
|
||||||
|
|
||||||
```cjs
|
```cjs
|
||||||
const { getAsset, getAssetAsBlob, getRawAsset } = require('node:sea');
|
const { getAsset, getAssetAsBlob, getRawAsset, getAssetKeys } = require('node:sea');
|
||||||
|
// Get all asset keys.
|
||||||
|
const keys = getAssetKeys();
|
||||||
|
console.log(keys); // ['a.jpg', 'b.txt']
|
||||||
// Returns a copy of the data in an ArrayBuffer.
|
// Returns a copy of the data in an ArrayBuffer.
|
||||||
const image = getAsset('a.jpg');
|
const image = getAsset('a.jpg');
|
||||||
// Returns a string decoded from the asset as UTF8.
|
// Returns a string decoded from the asset as UTF8.
|
||||||
|
|
@ -232,8 +235,8 @@ const blob = getAssetAsBlob('a.jpg');
|
||||||
const raw = getRawAsset('a.jpg');
|
const raw = getRawAsset('a.jpg');
|
||||||
```
|
```
|
||||||
|
|
||||||
See documentation of the [`sea.getAsset()`][], [`sea.getAssetAsBlob()`][] and [`sea.getRawAsset()`][]
|
See documentation of the [`sea.getAsset()`][], [`sea.getAssetAsBlob()`][],
|
||||||
APIs for more information.
|
[`sea.getRawAsset()`][] and [`sea.getAssetKeys()`][] APIs for more information.
|
||||||
|
|
||||||
### Startup snapshot support
|
### Startup snapshot support
|
||||||
|
|
||||||
|
|
@ -429,6 +432,19 @@ writes to the returned array buffer is likely to result in a crash.
|
||||||
`assets` field in the single-executable application configuration.
|
`assets` field in the single-executable application configuration.
|
||||||
* Returns: {ArrayBuffer}
|
* Returns: {ArrayBuffer}
|
||||||
|
|
||||||
|
### `sea.getAssetKeys()`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Returns {string\[]} An array containing all the keys of the assets
|
||||||
|
embedded in the executable. If no assets are embedded, returns an empty array.
|
||||||
|
|
||||||
|
This method can be used to retrieve an array of all the keys of assets
|
||||||
|
embedded into the single-executable application.
|
||||||
|
An error is thrown when not running inside a single-executable application.
|
||||||
|
|
||||||
### `require(id)` in the injected main script is not file based
|
### `require(id)` in the injected main script is not file based
|
||||||
|
|
||||||
`require()` in the injected main script is not the same as the [`require()`][]
|
`require()` in the injected main script is not the same as the [`require()`][]
|
||||||
|
|
@ -503,6 +519,7 @@ to help us document them.
|
||||||
[`require.main`]: modules.md#accessing-the-main-module
|
[`require.main`]: modules.md#accessing-the-main-module
|
||||||
[`sea.getAsset()`]: #seagetassetkey-encoding
|
[`sea.getAsset()`]: #seagetassetkey-encoding
|
||||||
[`sea.getAssetAsBlob()`]: #seagetassetasblobkey-options
|
[`sea.getAssetAsBlob()`]: #seagetassetasblobkey-options
|
||||||
|
[`sea.getAssetKeys()`]: #seagetassetkeys
|
||||||
[`sea.getRawAsset()`]: #seagetrawassetkey
|
[`sea.getRawAsset()`]: #seagetrawassetkey
|
||||||
[`v8.startupSnapshot.setDeserializeMainFunction()`]: v8.md#v8startupsnapshotsetdeserializemainfunctioncallback-data
|
[`v8.startupSnapshot.setDeserializeMainFunction()`]: v8.md#v8startupsnapshotsetdeserializemainfunctioncallback-data
|
||||||
[`v8.startupSnapshot` API]: v8.md#startup-snapshot-api
|
[`v8.startupSnapshot` API]: v8.md#startup-snapshot-api
|
||||||
|
|
|
||||||
16
lib/sea.js
16
lib/sea.js
|
|
@ -3,7 +3,7 @@ const {
|
||||||
ArrayBufferPrototypeSlice,
|
ArrayBufferPrototypeSlice,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
const { isSea, getAsset: getAssetInternal } = internalBinding('sea');
|
const { isSea, getAsset: getAssetInternal, getAssetKeys: getAssetKeysInternal } = internalBinding('sea');
|
||||||
const { TextDecoder } = require('internal/encoding');
|
const { TextDecoder } = require('internal/encoding');
|
||||||
const { validateString } = require('internal/validators');
|
const { validateString } = require('internal/validators');
|
||||||
const {
|
const {
|
||||||
|
|
@ -68,9 +68,23 @@ function getAssetAsBlob(key, options) {
|
||||||
return new Blob([asset], options);
|
return new Blob([asset], options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all the keys of assets embedded into the
|
||||||
|
* single-executable application.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
function getAssetKeys() {
|
||||||
|
if (!isSea()) {
|
||||||
|
throw new ERR_NOT_IN_SINGLE_EXECUTABLE_APPLICATION();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAssetKeysInternal() || [];
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isSea,
|
isSea,
|
||||||
getAsset,
|
getAsset,
|
||||||
getRawAsset,
|
getRawAsset,
|
||||||
getAssetAsBlob,
|
getAssetAsBlob,
|
||||||
|
getAssetKeys,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using node::ExitCode;
|
using node::ExitCode;
|
||||||
|
using v8::Array;
|
||||||
using v8::ArrayBuffer;
|
using v8::ArrayBuffer;
|
||||||
using v8::BackingStore;
|
using v8::BackingStore;
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
|
|
@ -807,6 +808,25 @@ void GetAsset(const FunctionCallbackInfo<Value>& args) {
|
||||||
args.GetReturnValue().Set(ab);
|
args.GetReturnValue().Set(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAssetKeys(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
CHECK_EQ(args.Length(), 0);
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
SeaResource sea_resource = FindSingleExecutableResource();
|
||||||
|
|
||||||
|
Local<Context> context = isolate->GetCurrentContext();
|
||||||
|
LocalVector<Value> keys(isolate);
|
||||||
|
keys.reserve(sea_resource.assets.size());
|
||||||
|
for (const auto& [key, _] : sea_resource.assets) {
|
||||||
|
Local<Value> key_str;
|
||||||
|
if (!ToV8Value(context, key).ToLocal(&key_str)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
keys.push_back(key_str);
|
||||||
|
}
|
||||||
|
Local<Array> result = Array::New(isolate, keys.data(), keys.size());
|
||||||
|
args.GetReturnValue().Set(result);
|
||||||
|
}
|
||||||
|
|
||||||
MaybeLocal<Value> LoadSingleExecutableApplication(
|
MaybeLocal<Value> LoadSingleExecutableApplication(
|
||||||
const StartExecutionCallbackInfo& info) {
|
const StartExecutionCallbackInfo& info) {
|
||||||
// Here we are currently relying on the fact that in NodeMainInstance::Run(),
|
// Here we are currently relying on the fact that in NodeMainInstance::Run(),
|
||||||
|
|
@ -858,12 +878,14 @@ void Initialize(Local<Object> target,
|
||||||
"isExperimentalSeaWarningNeeded",
|
"isExperimentalSeaWarningNeeded",
|
||||||
IsExperimentalSeaWarningNeeded);
|
IsExperimentalSeaWarningNeeded);
|
||||||
SetMethod(context, target, "getAsset", GetAsset);
|
SetMethod(context, target, "getAsset", GetAsset);
|
||||||
|
SetMethod(context, target, "getAssetKeys", GetAssetKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||||
registry->Register(IsSea);
|
registry->Register(IsSea);
|
||||||
registry->Register(IsExperimentalSeaWarningNeeded);
|
registry->Register(IsExperimentalSeaWarningNeeded);
|
||||||
registry->Register(GetAsset);
|
registry->Register(GetAsset);
|
||||||
|
registry->Register(GetAssetKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sea
|
} // namespace sea
|
||||||
|
|
|
||||||
9
test/fixtures/sea/get-asset-keys.js
vendored
Normal file
9
test/fixtures/sea/get-asset-keys.js
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { isSea, getAssetKeys } = require('node:sea');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
|
||||||
|
assert(isSea());
|
||||||
|
|
||||||
|
const keys = getAssetKeys();
|
||||||
|
console.log('Asset keys:', JSON.stringify(keys.sort()));
|
||||||
11
test/parallel/test-sea-get-asset-keys.js
Normal file
11
test/parallel/test-sea-get-asset-keys.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
|
||||||
|
const { getAssetKeys } = require('node:sea');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
|
||||||
|
// Test that getAssetKeys throws when not in SEA
|
||||||
|
assert.throws(() => getAssetKeys(), {
|
||||||
|
code: 'ERR_NOT_IN_SINGLE_EXECUTABLE_APPLICATION'
|
||||||
|
});
|
||||||
|
|
@ -57,6 +57,8 @@ test-watch-mode-inspect: SKIP
|
||||||
test-single-executable-application: SKIP
|
test-single-executable-application: SKIP
|
||||||
test-single-executable-application-assets: SKIP
|
test-single-executable-application-assets: SKIP
|
||||||
test-single-executable-application-assets-raw: SKIP
|
test-single-executable-application-assets-raw: SKIP
|
||||||
|
test-single-executable-application-asset-keys-empty: SKIP
|
||||||
|
test-single-executable-application-asset-keys: SKIP
|
||||||
test-single-executable-application-disable-experimental-sea-warning: SKIP
|
test-single-executable-application-disable-experimental-sea-warning: SKIP
|
||||||
test-single-executable-application-empty: SKIP
|
test-single-executable-application-empty: SKIP
|
||||||
test-single-executable-application-exec-argv: SKIP
|
test-single-executable-application-exec-argv: SKIP
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// This test verifies that the `getAssetKeys()` function works correctly
|
||||||
|
// in a single executable application without any assets.
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
|
||||||
|
const {
|
||||||
|
generateSEA,
|
||||||
|
skipIfSingleExecutableIsNotSupported,
|
||||||
|
} = require('../common/sea');
|
||||||
|
|
||||||
|
skipIfSingleExecutableIsNotSupported();
|
||||||
|
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
|
||||||
|
const { copyFileSync, writeFileSync, existsSync } = require('fs');
|
||||||
|
const {
|
||||||
|
spawnSyncAndExitWithoutError,
|
||||||
|
spawnSyncAndAssert,
|
||||||
|
} = require('../common/child_process');
|
||||||
|
const assert = require('assert');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
|
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
|
||||||
|
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
copyFileSync(fixtures.path('sea', 'get-asset-keys.js'), tmpdir.resolve('sea.js'));
|
||||||
|
|
||||||
|
writeFileSync(tmpdir.resolve('sea-config.json'), `
|
||||||
|
{
|
||||||
|
"main": "sea.js",
|
||||||
|
"output": "sea-prep.blob"
|
||||||
|
}
|
||||||
|
`, 'utf8');
|
||||||
|
|
||||||
|
spawnSyncAndExitWithoutError(
|
||||||
|
process.execPath,
|
||||||
|
['--experimental-sea-config', 'sea-config.json'],
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
NODE_DEBUG_NATIVE: 'SEA',
|
||||||
|
...process.env,
|
||||||
|
},
|
||||||
|
cwd: tmpdir.path
|
||||||
|
},
|
||||||
|
{});
|
||||||
|
|
||||||
|
assert(existsSync(seaPrepBlob));
|
||||||
|
|
||||||
|
generateSEA(outputFile, process.execPath, seaPrepBlob);
|
||||||
|
|
||||||
|
spawnSyncAndAssert(
|
||||||
|
outputFile,
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
NODE_DEBUG_NATIVE: 'SEA',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdout: /Asset keys: \[\]/,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// This test verifies that the `getAssetKeys()` function works correctly
|
||||||
|
// in a single executable application with assets.
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
|
||||||
|
const {
|
||||||
|
generateSEA,
|
||||||
|
skipIfSingleExecutableIsNotSupported,
|
||||||
|
} = require('../common/sea');
|
||||||
|
|
||||||
|
skipIfSingleExecutableIsNotSupported();
|
||||||
|
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
|
||||||
|
const { copyFileSync, writeFileSync, existsSync } = require('fs');
|
||||||
|
const {
|
||||||
|
spawnSyncAndExitWithoutError,
|
||||||
|
spawnSyncAndAssert,
|
||||||
|
} = require('../common/child_process');
|
||||||
|
const assert = require('assert');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
|
const configFile = tmpdir.resolve('sea-config.json');
|
||||||
|
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
|
||||||
|
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
copyFileSync(fixtures.path('sea', 'get-asset-keys.js'), tmpdir.resolve('sea.js'));
|
||||||
|
writeFileSync(tmpdir.resolve('asset-1.txt'), 'This is asset 1');
|
||||||
|
writeFileSync(tmpdir.resolve('asset-2.txt'), 'This is asset 2');
|
||||||
|
writeFileSync(tmpdir.resolve('asset-3.txt'), 'This is asset 3');
|
||||||
|
|
||||||
|
writeFileSync(configFile, `
|
||||||
|
{
|
||||||
|
"main": "sea.js",
|
||||||
|
"output": "sea-prep.blob",
|
||||||
|
"assets": {
|
||||||
|
"asset-1.txt": "asset-1.txt",
|
||||||
|
"asset-2.txt": "asset-2.txt",
|
||||||
|
"asset-3.txt": "asset-3.txt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, 'utf8');
|
||||||
|
|
||||||
|
spawnSyncAndExitWithoutError(
|
||||||
|
process.execPath,
|
||||||
|
['--experimental-sea-config', 'sea-config.json'],
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
NODE_DEBUG_NATIVE: 'SEA',
|
||||||
|
...process.env,
|
||||||
|
},
|
||||||
|
cwd: tmpdir.path
|
||||||
|
},
|
||||||
|
{});
|
||||||
|
|
||||||
|
assert(existsSync(seaPrepBlob));
|
||||||
|
|
||||||
|
generateSEA(outputFile, process.execPath, seaPrepBlob);
|
||||||
|
|
||||||
|
spawnSyncAndAssert(
|
||||||
|
outputFile,
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
NODE_DEBUG_NATIVE: 'SEA',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdout: /Asset keys: \["asset-1\.txt","asset-2\.txt","asset-3\.txt"\]/,
|
||||||
|
}
|
||||||
|
);
|
||||||
Loading…
Reference in New Issue
Block a user