UNPKG

webidl2

Version:
127 lines (119 loc) 3.74 kB
import { validationError } from "../error.js"; import { idlTypeIncludesDictionary, idlTypeIncludesEnforceRange, } from "../validators/helpers.js"; import { Base } from "./base.js"; import { type_with_extended_attributes, unescape, autoParenter, } from "./helpers.js"; export class Attribute extends Base { /** * @param {import("../tokeniser.js").Tokeniser} tokeniser * @param {object} [options] * @param {import("../tokeniser.js").Token} [options.special] * @param {boolean} [options.noInherit] * @param {boolean} [options.readonly] */ static parse( tokeniser, { special, noInherit = false, readonly = false } = {}, ) { const start_position = tokeniser.position; const tokens = { special }; const ret = autoParenter( new Attribute({ source: tokeniser.source, tokens }), ); if (!special && !noInherit) { tokens.special = tokeniser.consume("inherit"); } if (ret.special === "inherit" && tokeniser.probe("readonly")) { tokeniser.error("Inherited attributes cannot be read-only"); } tokens.readonly = tokeniser.consume("readonly"); if (readonly && !tokens.readonly && tokeniser.probe("attribute")) { tokeniser.error("Attributes must be readonly in this context"); } tokens.base = tokeniser.consume("attribute"); if (!tokens.base) { tokeniser.unconsume(start_position); return; } ret.idlType = type_with_extended_attributes(tokeniser, "attribute-type") || tokeniser.error("Attribute lacks a type"); tokens.name = tokeniser.consumeKind("identifier") || tokeniser.consume("async", "required") || tokeniser.error("Attribute lacks a name"); tokens.termination = tokeniser.consume(";") || tokeniser.error("Unterminated attribute, expected `;`"); return ret.this; } get type() { return "attribute"; } get special() { if (!this.tokens.special) { return ""; } return this.tokens.special.value; } get readonly() { return !!this.tokens.readonly; } get name() { return unescape(this.tokens.name.value); } *validate(defs) { yield* this.extAttrs.validate(defs); yield* this.idlType.validate(defs); if ( ["async_sequence", "sequence", "record"].includes(this.idlType.generic) ) { const message = `Attributes cannot accept ${this.idlType.generic} types.`; yield validationError( this.tokens.name, this, "attr-invalid-type", message, ); } { const { reference } = idlTypeIncludesDictionary(this.idlType, defs) || {}; if (reference) { const targetToken = (this.idlType.union ? reference : this.idlType) .tokens.base; const message = "Attributes cannot accept dictionary types."; yield validationError(targetToken, this, "attr-invalid-type", message); } } if (this.readonly) { if (idlTypeIncludesEnforceRange(this.idlType, defs)) { const targetToken = this.idlType.tokens.base; const message = "Readonly attributes cannot accept [EnforceRange] extended attribute."; yield validationError(targetToken, this, "attr-invalid-type", message); } } } /** @param {import("../writer.js").Writer} w */ write(w) { const { parent } = this; return w.ts.definition( w.ts.wrap([ this.extAttrs.write(w), w.token(this.tokens.special), w.token(this.tokens.readonly), w.token(this.tokens.base), w.ts.type(this.idlType.write(w)), w.name_token(this.tokens.name, { data: this, parent }), w.token(this.tokens.termination), ]), { data: this, parent }, ); } }