-
Notifications
You must be signed in to change notification settings - Fork 5.2k
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
Web APIs: File
and FormData
#1056
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts...
js/form_data.ts
Outdated
import { DenoFile } from "./file"; | ||
|
||
export class FormData implements domTypes.FormData { | ||
private data: Array<[string, domTypes.FormDataEntryValue]> = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My personal opinion is that we should follow the convention of prefacing private members and methods with _
. In TypeScript, because these are not really private, they impact the type structure. Which means that something else that has a public data
member is now incompatible structurally, which is undesired. Also, as the TC39 proposal for public and private members becomes a reality, if this is made really private in the future, then there will be a need to refactor and replace the _
sigil with the private member sigil (#
) and so having an existing sigil makes that refactoring easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also why is this not a Map
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My personal opinion is that we should follow the convention of prefacing private members and methods with
_
. In TypeScript, because these are not really private, they impact the type structure. Which means that something else that has a publicdata
member is now incompatible structurally, which is undesired. Also, as the TC39 proposal for public and private members becomes a reality, if this is made really private in the future, then there will be a need to refactor and replace the_
sigil with the private member sigil (#
) and so having an existing sigil makes that refactoring easier.
If anything we would use a symbol instead:
Lines 5 to 8 in c4bddc4
const bytesSymbol = Symbol("bytes"); | |
export class DenoBlob implements domTypes.Blob { | |
private readonly [bytesSymbol]: Uint8Array; |
Also why is this not a
Map
?
Because you can "append" multiple elements under the same name, getAll gets a list of all of them. Also because they're in insertion order:
I know using a Map would be awesome, it's superfast and basically what we'd define as idiomatic JavaScript/TypeScript (since append and getAll give a hint of it), but FormData#forEach would log a 1
, a 3
, b 2
instead of a 1
, b 2
, a 3
like browsers do. It's kinda sad we can't use a Map here, it would definitely boost FormData's internals by a lot, but we can't afford going out of the standard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The symbol makes sense. Regarding the Map of course it could be a Map<string, domTypes.FormDataEntryValue[]>
and on a .get()
you would simply return value[0]
and Array.from(value)
for getAll()
(using Array.from()
to ensure to not leak the reference to real value array)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we were to go performance-wise, I believe Array#slice()
(with no arguments) is much faster.
getAll(name: string): domTypes.FormDataEntryValue[] {
const entries = this[dataSymbol].get(name);
return entries ? entries.slice() : [];
}
But then again using the map would be against the implementation of various browsers where FormData#forEach, URLSearchParams#forEach, and others, call the callback strictly by insertion order, which a Map does not allow.
js/form_data.ts
Outdated
* console.log(key); | ||
* } | ||
*/ | ||
keys(): IterableIterator<string> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should produce iterators in a more idiomatic way... I have raised a PR with the way I feel we should be doing them, see #1062.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to do so, WHATWG defines a series of implementable interfaces like iterable
which is essentially like this:
interface IWHATWGIterable<K, V> {
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
[Symbol.iterator](): IterableIterator<[K, V]>;
forEach((value: V, key: K, parent: this) => void, thisArg?: any): void;
}
We could implement this and use a mixin to implement them, thought that'd need to expose the internal slot, which we cannot do. But if you have a better solution, I'm open to suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kyranet I like the idea... I will change my PR to utilise TypeScript mixin classes. The only problem is that it would need to have whatever the internal data is as an iterator, which means you would have to further abstract your array data structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -0,0 +1,24 @@ | |||
// Copyright 2018 the Deno authors. All rights reserved. MIT license. | |||
import * as domTypes from "./dom_types"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... file
is a bit confusing with files
. I wonder if this should be structured so that Web API stuff is in a sub folder where things are a bit less confusing, and Blob
and Headers
and fetch
, etc. can go in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, we could have js/webapi/
, js/fileapi
, js/denoapi
, and others. But this is out of the scope of this PR. We can do this later once Event and EventTarget are merged too to prevent merge conflicts/issues with other collaborators working on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some feedback...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some feedback...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some feedback...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple thoughts...
js/mixins/dom_iterable.ts
Outdated
yield entry; | ||
} | ||
} | ||
|
||
*keys(): IterableIterator<K> { | ||
for (const key of (this as any)[dataSymbol].keys()) { | ||
yield key; | ||
for (const entry of (this as any)[dataSymbol]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would personally prefer:
for (const entry of (this as any)[dataSymbol]) { | |
for (const [key] of (this as any)[dataSymbol]) { |
js/mixins/dom_iterable.ts
Outdated
} | ||
} | ||
|
||
*values(): IterableIterator<V> { | ||
for (const value of (this as any)[dataSymbol].values()) { | ||
yield value; | ||
for (const entry of (this as any)[dataSymbol]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would personally prefer:
for (const entry of (this as any)[dataSymbol]) { | |
for (const [_key, value] of (this as any)[dataSymbol]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!!!
} | ||
} | ||
|
||
*values(): IterableIterator<V> { | ||
for (const entry of (this as any)[dataSymbol]) { | ||
yield entry[1]; | ||
for (const [, value] of (this as any)[dataSymbol]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oooh, yeah forgot you can omit values on array destructuring... 👍
File
and FormData
File
and FormData
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - thanks!
This PR adds better
Blob
spec compliance (objects, Uint16Array, Uint32Array tests), to pass some of WPT's tests.This also adds
File
, which extendsBlob
, check spec here.And
FormData
, which I first wanted to do (but later noticed it depended onFile
).TODO:
File
andFormData
's tests.ImplementFetchResponse#formData
:deno/js/fetch.ts
Line 159 in c4bddc4