Files
node/src/json_parser.cc
T
Joyee Cheung ce8f085d26 sea: support embedding assets
With this patch:

Users can now include assets by adding a key-path dictionary
to the configuration as the `assets` field. At build time, Node.js
would read the assets from the specified paths and bundle them into
the preparation blob. In the generated executable, users can retrieve
the assets using the `sea.getAsset()` and `sea.getAssetAsBlob()` API.

```json
{
  "main": "/path/to/bundled/script.js",
  "output": "/path/to/write/the/generated/blob.blob",
  "assets": {
    "a.jpg": "/path/to/a.jpg",
    "b.txt": "/path/to/b.txt"
  }
}
```

The single-executable application can access the assets as follows:

```cjs
const { getAsset } = require('node:sea');
// Returns a copy of the data in an ArrayBuffer
const image = getAsset('a.jpg');
// Returns a string decoded from the asset as UTF8.
const text = getAsset('b.txt', 'utf8');
// Returns a Blob containing the asset.
const blob = getAssetAsBlob('a.jpg');
```

Drive-by: update the  documentation to include a section dedicated
to the injected main script and refer to it as "injected main
script" instead of "injected module" because it's a script, not
a module.

PR-URL: https://github.com/nodejs/node/pull/50960
Refs: https://github.com/nodejs/single-executable/issues/68
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
2024-02-02 15:25:34 +01:00

153 lines
4.5 KiB
C++

#include "json_parser.h"
#include "node_errors.h"
#include "node_v8_platform-inl.h"
#include "util-inl.h"
namespace node {
using v8::Array;
using v8::Context;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
JSONParser::JSONParser() {}
bool JSONParser::Parse(const std::string& content) {
DCHECK(!parsed_);
Isolate* isolate = isolate_.get();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
// It's not a real script, so don't print the source line.
errors::PrinterTryCatch bootstrapCatch(
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
Local<Value> json_string_value;
Local<Value> result_value;
if (!ToV8Value(context, content).ToLocal(&json_string_value) ||
!json_string_value->IsString() ||
!v8::JSON::Parse(context, json_string_value.As<String>())
.ToLocal(&result_value) ||
!result_value->IsObject()) {
return false;
}
context_.Reset(isolate, context);
content_.Reset(isolate, result_value.As<Object>());
parsed_ = true;
return true;
}
std::optional<std::string> JSONParser::GetTopLevelStringField(
std::string_view field) {
Isolate* isolate = isolate_.get();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
Local<Context> context = context_.Get(isolate);
Context::Scope context_scope(context);
Local<Object> content_object = content_.Get(isolate);
Local<Value> value;
// It's not a real script, so don't print the source line.
errors::PrinterTryCatch bootstrapCatch(
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
Local<Value> field_local;
if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) {
return {};
}
if (!content_object->Get(context, field_local).ToLocal(&value) ||
!value->IsString()) {
return {};
}
Utf8Value utf8_value(isolate, value);
return utf8_value.ToString();
}
std::optional<bool> JSONParser::GetTopLevelBoolField(std::string_view field) {
Isolate* isolate = isolate_.get();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
Local<Context> context = context_.Get(isolate);
Context::Scope context_scope(context);
Local<Object> content_object = content_.Get(isolate);
Local<Value> value;
bool has_field;
// It's not a real script, so don't print the source line.
errors::PrinterTryCatch bootstrapCatch(
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
Local<Value> field_local;
if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) {
return {};
}
if (!content_object->Has(context, field_local).To(&has_field)) {
return {};
}
if (!has_field) {
return false;
}
if (!content_object->Get(context, field_local).ToLocal(&value) ||
!value->IsBoolean()) {
return {};
}
return value->BooleanValue(isolate);
}
std::optional<JSONParser::StringDict> JSONParser::GetTopLevelStringDict(
std::string_view field) {
Isolate* isolate = isolate_.get();
v8::HandleScope handle_scope(isolate);
Local<Context> context = context_.Get(isolate);
Local<Object> content_object = content_.Get(isolate);
Local<Value> value;
bool has_field;
// It's not a real script, so don't print the source line.
errors::PrinterTryCatch bootstrapCatch(
isolate, errors::PrinterTryCatch::kDontPrintSourceLine);
Local<Value> field_local;
if (!ToV8Value(context, field, isolate).ToLocal(&field_local)) {
return std::nullopt;
}
if (!content_object->Has(context, field_local).To(&has_field)) {
return std::nullopt;
}
if (!has_field) {
return StringDict();
}
if (!content_object->Get(context, field_local).ToLocal(&value) ||
!value->IsObject()) {
return std::nullopt;
}
Local<Object> dict = value.As<Object>();
Local<Array> keys;
if (!dict->GetOwnPropertyNames(context).ToLocal(&keys)) {
return std::nullopt;
}
std::unordered_map<std::string, std::string> result;
uint32_t length = keys->Length();
for (uint32_t i = 0; i < length; ++i) {
Local<Value> key;
Local<Value> value;
if (!keys->Get(context, i).ToLocal(&key) || !key->IsString())
return StringDict();
if (!dict->Get(context, key).ToLocal(&value) || !value->IsString())
return StringDict();
Utf8Value key_utf8(isolate, key);
Utf8Value value_utf8(isolate, value);
result.emplace(*key_utf8, *value_utf8);
}
return result;
}
} // namespace node