◐ Shell
clean mode source ↗

url: align default argument handling for URLPattern with webidl · nodejs/node@2d63638

@@ -202,7 +202,11 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {

202202

// - new URLPattern(input, baseURL)

203203

// - new URLPattern(input, options)

204204

// - new URLPattern(input, baseURL, options)

205-

if (args[0]->IsString()) {

205+

// Per WebIDL, null/undefined for a union type including a dictionary

206+

// uses the default value (empty init).

207+

if (args[0]->IsNullOrUndefined()) {

208+

init = ada::url_pattern_init{};

209+

} else if (args[0]->IsString()) {

206210

BufferValue input_buffer(env->isolate(), args[0]);

207211

CHECK_NOT_NULL(*input_buffer);

208212

input = input_buffer.ToString();

@@ -217,41 +221,55 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {

217221

return;

218222

}

219223220-

// The next argument can be baseURL or options.

221-

if (args.Length() > 1) {

224+

// Per WebIDL overload resolution:

225+

// With 3+ args, it's always overload 1: (input, baseURL, options)

226+

// With 2 args, if arg1 is string, it is overload 1 (baseURL),

227+

// otherwise overload 2 (options)

228+

if (args.Length() >= 3) {

229+

// arg1 is baseURL. Per WebIDL, null/undefined are stringified for

230+

// USVString ("null"/"undefined"), which will be rejected as invalid

231+

// URLs by ada downstream.

222232

if (args[1]->IsString()) {

223233

BufferValue base_url_buffer(env->isolate(), args[1]);

224234

CHECK_NOT_NULL(*base_url_buffer);

225235

base_url = base_url_buffer.ToString();

226-

} else if (args[1]->IsObject()) {

227-

CHECK(!options.has_value());

228-

options = URLPatternOptions::FromJsObject(env, args[1].As<Object>());

229-

if (!options) {

230-

// If options does not have a value, we assume an error was

231-

// thrown and scheduled on the isolate. Return early to

232-

// propagate it.

233-

return;

234-

}

236+

} else if (args[1]->IsNull()) {

237+

base_url = std::string("null");

238+

} else if (args[1]->IsUndefined()) {

239+

base_url = std::string("undefined");

235240

} else {

236-

THROW_ERR_INVALID_ARG_TYPE(env,

237-

"second argument must be a string or object");

241+

THROW_ERR_INVALID_ARG_TYPE(env, "second argument must be a string");

238242

return;

239243

}

240244241-

// Only remaining argument can be options.

242-

if (args.Length() > 2) {

245+

// arg2 is options. Per WebIDL, null/undefined for a dictionary

246+

// uses the default value (empty dict).

247+

if (!args[2]->IsNullOrUndefined()) {

243248

if (!args[2]->IsObject()) {

244249

THROW_ERR_INVALID_ARG_TYPE(env, "options must be an object");

245250

return;

246251

}

247252

CHECK(!options.has_value());

248253

options = URLPatternOptions::FromJsObject(env, args[2].As<Object>());

249-

if (!options) {

250-

// If options does not have a value, we assume an error was

251-

// thrown and scheduled on the isolate. Return early to

252-

// propagate it.

253-

return;

254-

}

254+

if (!options) return;

255+

}

256+

} else if (args.Length() == 2) {

257+

// Overload resolution: string is overload 1 (baseURL),

258+

// otherwise overload 2 (options).

259+

if (args[1]->IsString()) {

260+

BufferValue base_url_buffer(env->isolate(), args[1]);

261+

CHECK_NOT_NULL(*base_url_buffer);

262+

base_url = base_url_buffer.ToString();

263+

} else if (args[1]->IsNullOrUndefined()) {

264+

// Overload 2, options uses default.

265+

} else if (args[1]->IsObject()) {

266+

CHECK(!options.has_value());

267+

options = URLPatternOptions::FromJsObject(env, args[1].As<Object>());

268+

if (!options) return;

269+

} else {

270+

THROW_ERR_INVALID_ARG_TYPE(env,

271+

"second argument must be a string or object");

272+

return;

255273

}

256274

}

257275

@@ -493,11 +511,8 @@ URLPattern::URLPatternOptions::FromJsObject(Environment* env,

493511

Local<Value> ignore_case;

494512

if (obj->Get(env->context(), env->ignore_case_string())

495513

.ToLocal(&ignore_case)) {

496-

if (!ignore_case->IsBoolean()) {

497-

THROW_ERR_INVALID_ARG_TYPE(env, "options.ignoreCase must be a boolean");

498-

return std::nullopt;

499-

}

500-

options.ignore_case = ignore_case->IsTrue();

514+

// Per WebIDL, boolean dictionary members are coerced (not type-checked).

515+

options.ignore_case = ignore_case->BooleanValue(env->isolate());

501516

} else {

502517

// If ToLocal returns false, the assumption is that getting the

503518

// ignore_case_string threw an error, let's propagate that now

@@ -564,7 +579,7 @@ void URLPattern::Exec(const FunctionCallbackInfo<Value>& args) {

564579

ada::url_pattern_input input;

565580

std::optional<std::string> baseURL{};

566581

std::string input_base;

567-

if (args.Length() == 0) {

582+

if (args.Length() == 0 || args[0]->IsNullOrUndefined()) {

568583

input = ada::url_pattern_init{};

569584

} else if (args[0]->IsString()) {

570585

Utf8Value input_value(env->isolate(), args[0].As<String>());

@@ -580,13 +595,16 @@ void URLPattern::Exec(const FunctionCallbackInfo<Value>& args) {

580595

return;

581596

}

582597583-

if (args.Length() > 1) {

584-

if (!args[1]->IsString()) {

598+

if (args.Length() > 1 && !args[1]->IsUndefined()) {

599+

if (args[1]->IsNull()) {

600+

baseURL = std::string("null");

601+

} else if (args[1]->IsString()) {

602+

Utf8Value base_url_value(env->isolate(), args[1].As<String>());

603+

baseURL = base_url_value.ToStringView();

604+

} else {

585605

THROW_ERR_INVALID_ARG_TYPE(env, "baseURL must be a string");

586606

return;

587607

}

588-

Utf8Value base_url_value(env->isolate(), args[1].As<String>());

589-

baseURL = base_url_value.ToStringView();

590608

}

591609592610

Local<Value> result;

@@ -607,7 +625,7 @@ void URLPattern::Test(const FunctionCallbackInfo<Value>& args) {

607625

ada::url_pattern_input input;

608626

std::optional<std::string> baseURL{};

609627

std::string input_base;

610-

if (args.Length() == 0) {

628+

if (args.Length() == 0 || args[0]->IsNullOrUndefined()) {

611629

input = ada::url_pattern_init{};

612630

} else if (args[0]->IsString()) {

613631

Utf8Value input_value(env->isolate(), args[0].As<String>());

@@ -623,13 +641,16 @@ void URLPattern::Test(const FunctionCallbackInfo<Value>& args) {

623641

return;

624642

}

625643626-

if (args.Length() > 1) {

627-

if (!args[1]->IsString()) {

644+

if (args.Length() > 1 && !args[1]->IsUndefined()) {

645+

if (args[1]->IsNull()) {

646+

baseURL = std::string("null");

647+

} else if (args[1]->IsString()) {

648+

Utf8Value base_url_value(env->isolate(), args[1].As<String>());

649+

baseURL = base_url_value.ToStringView();

650+

} else {

628651

THROW_ERR_INVALID_ARG_TYPE(env, "baseURL must be a string");

629652

return;

630653

}

631-

Utf8Value base_url_value(env->isolate(), args[1].As<String>());

632-

baseURL = base_url_value.ToStringView();

633654

}

634655635656

std::optional<std::string_view> baseURL_opt =