Comparing version 13.0.0 to 14.0.0
@@ -66,2 +66,9 @@ "use strict"; | ||
if (utils.hasCEReactions(this.idl)) { | ||
const processorConfig = { requires }; | ||
getterBody = this.ctx.invokeProcessCEReactions(getterBody, processorConfig); | ||
setterBody = this.ctx.invokeProcessCEReactions(setterBody, processorConfig); | ||
} | ||
addMethod(this.idl.name, [], ` | ||
@@ -68,0 +75,0 @@ ${brandCheck} |
@@ -69,2 +69,3 @@ "use strict"; | ||
this.name = idl.name; | ||
for (const member of this.idl.members) { | ||
@@ -93,2 +94,3 @@ member.definingInterface = this.name; | ||
this.stringifier = null; | ||
this.needsPerGlobalProxyHandler = false; | ||
@@ -233,2 +235,5 @@ this.iterable = null; | ||
this.indexedSetter = member; | ||
if (utils.hasCEReactions(member)) { | ||
this.needsPerGlobalProxyHandler = true; | ||
} | ||
} else if (isNamed(member)) { | ||
@@ -239,2 +244,5 @@ if (this.namedSetter) { | ||
this.namedSetter = member; | ||
if (utils.hasCEReactions(member)) { | ||
this.needsPerGlobalProxyHandler = true; | ||
} | ||
} else { | ||
@@ -259,2 +267,5 @@ throw new Error(msg + "setter is neither indexed nor named"); | ||
this.namedDeleter = member; | ||
if (utils.hasCEReactions(member)) { | ||
this.needsPerGlobalProxyHandler = true; | ||
} | ||
} else { | ||
@@ -590,3 +601,3 @@ throw new Error(msg + "deleter is not named"); | ||
generateLegacyProxy() { | ||
generateLegacyProxyHandler() { | ||
const hasIndexedSetter = this.indexedSetter !== null; | ||
@@ -647,3 +658,3 @@ const hasNamedSetter = this.namedSetter !== null; | ||
let str = ` | ||
const prolog = ` | ||
const index = ${P} >>> 0; | ||
@@ -654,4 +665,5 @@ let indexedValue = ${V}; | ||
let invocation; | ||
if (!this.indexedSetter.name) { | ||
str += ` | ||
invocation = ` | ||
const creating = !(${supportsPropertyIndex(O, "index")}); | ||
@@ -665,3 +677,3 @@ if (creating) { | ||
} else { | ||
str += ` | ||
invocation = ` | ||
${O}[impl].${this.indexedSetter.name}(index, indexedValue); | ||
@@ -671,3 +683,9 @@ `; | ||
return str; | ||
if (utils.hasCEReactions(this.indexedSetter)) { | ||
invocation = this.ctx.invokeProcessCEReactions(invocation, { | ||
requires: this.requires | ||
}); | ||
} | ||
return prolog + invocation; | ||
}; | ||
@@ -683,3 +701,3 @@ | ||
let str = ` | ||
const prolog = ` | ||
let namedValue = ${V}; | ||
@@ -689,4 +707,5 @@ ${conv.body} | ||
let invocation; | ||
if (!this.namedSetter.name) { | ||
str += ` | ||
invocation = ` | ||
const creating = !(${supportsPropertyName(O, P)}); | ||
@@ -700,3 +719,3 @@ if (creating) { | ||
} else { | ||
str += ` | ||
invocation = ` | ||
${O}[impl].${this.namedSetter.name}(${P}, namedValue); | ||
@@ -706,8 +725,26 @@ `; | ||
return str; | ||
if (utils.hasCEReactions(this.namedSetter)) { | ||
invocation = this.ctx.invokeProcessCEReactions(invocation, { | ||
requires: this.requires | ||
}); | ||
} | ||
return prolog + invocation; | ||
}; | ||
this.str += ` | ||
obj = new Proxy(obj, { | ||
`; | ||
let sep = ""; | ||
if (this.needsPerGlobalProxyHandler) { | ||
this.str += ` | ||
const proxyHandlerCache = new WeakMap(); | ||
class ProxyHandler { | ||
constructor(globalObject) { | ||
this._globalObject = globalObject; | ||
} | ||
`; | ||
} else { | ||
this.str += ` | ||
const proxyHandler = { | ||
`; | ||
sep = ","; | ||
} | ||
@@ -736,3 +773,3 @@ // [[Get]] (necessary because of proxy semantics) | ||
return Reflect.apply(getter, receiver, []); | ||
}, | ||
}${sep} | ||
`; | ||
@@ -755,3 +792,3 @@ | ||
return false; | ||
}, | ||
}${sep} | ||
`; | ||
@@ -787,3 +824,3 @@ | ||
return [...keys]; | ||
}, | ||
}${sep} | ||
`; | ||
@@ -862,3 +899,3 @@ | ||
return Reflect.getOwnPropertyDescriptor(target, P); | ||
}, | ||
}${sep} | ||
`; | ||
@@ -875,2 +912,8 @@ | ||
if (this.needsPerGlobalProxyHandler) { | ||
this.str += ` | ||
const globalObject = this._globalObject; | ||
`; | ||
} | ||
if (this.supportsIndexedProperties) { | ||
@@ -972,3 +1015,3 @@ if (hasIndexedSetter) { | ||
return Reflect.defineProperty(receiver, P, valueDesc); | ||
}, | ||
}${sep} | ||
`; | ||
@@ -983,2 +1026,9 @@ | ||
`; | ||
if (this.needsPerGlobalProxyHandler) { | ||
this.str += ` | ||
const globalObject = this._globalObject; | ||
`; | ||
} | ||
if (this.supportsIndexedProperties) { | ||
@@ -1062,3 +1112,3 @@ this.str += ` | ||
this.str += ` | ||
}, | ||
}${sep} | ||
`; | ||
@@ -1073,2 +1123,9 @@ | ||
`; | ||
if (this.needsPerGlobalProxyHandler) { | ||
this.str += ` | ||
const globalObject = this._globalObject; | ||
`; | ||
} | ||
if (this.supportsIndexedProperties) { | ||
@@ -1091,9 +1148,11 @@ this.str += ` | ||
} else { | ||
let invocation; | ||
const func = this.namedDeleter.name ? `.${this.namedDeleter.name}` : "[utils.namedDelete]"; | ||
if (this.namedDeleter.idlType.idlType === "bool") { | ||
this.str += ` | ||
invocation = ` | ||
return target[impl]${func}(P); | ||
`; | ||
} else { | ||
this.str += ` | ||
invocation = ` | ||
target[impl]${func}(P); | ||
@@ -1103,2 +1162,10 @@ return true; | ||
} | ||
if (utils.hasCEReactions(this.namedDeleter)) { | ||
invocation = this.ctx.invokeProcessCEReactions(invocation, { | ||
requires: this.requires | ||
}); | ||
} | ||
this.str += invocation; | ||
} | ||
@@ -1111,3 +1178,3 @@ this.str += ` | ||
return Reflect.deleteProperty(target, P); | ||
}, | ||
}${sep} | ||
`; | ||
@@ -1123,3 +1190,3 @@ | ||
this.str += ` | ||
}); | ||
}; | ||
`; | ||
@@ -1172,3 +1239,18 @@ } | ||
if (this.isLegacyPlatformObj) { | ||
this.generateLegacyProxy(); | ||
if (this.needsPerGlobalProxyHandler) { | ||
this.str += ` | ||
{ | ||
let proxyHandler = proxyHandlerCache.get(globalObject); | ||
if (proxyHandler === undefined) { | ||
proxyHandler = new ProxyHandler(globalObject); | ||
proxyHandlerCache.set(globalObject, proxyHandler); | ||
} | ||
obj = new Proxy(obj, proxyHandler); | ||
} | ||
`; | ||
} else { | ||
this.str += ` | ||
obj = new Proxy(obj, proxyHandler); | ||
`; | ||
} | ||
} | ||
@@ -1221,2 +1303,8 @@ | ||
if (utils.getExtAttr(this.idl.extAttrs, "HTMLConstructor")) { | ||
body = this.ctx.invokeProcessHTMLConstructor(body, { | ||
requires: this.requires | ||
}); | ||
} | ||
this.addMethod("prototype", "constructor", argNames, body, "regular", { enumerable: false }); | ||
@@ -1471,5 +1559,5 @@ } | ||
} | ||
globalObject[ctorRegistry]["${name}"] = ${name}; | ||
globalObject[ctorRegistry][interfaceName] = ${name}; | ||
Object.defineProperty(globalObject, "${name}", { | ||
Object.defineProperty(globalObject, interfaceName, { | ||
configurable: true, | ||
@@ -1484,2 +1572,5 @@ writable: true, | ||
generate() { | ||
this.str += ` | ||
const interfaceName = "${this.name}"; | ||
`; | ||
this.generateIterator(); | ||
@@ -1490,2 +1581,5 @@ | ||
this.generateInstall(); | ||
if (this.isLegacyPlatformObj) { | ||
this.generateLegacyProxyHandler(); | ||
} | ||
@@ -1492,0 +1586,0 @@ this.generateMixins(); |
@@ -42,2 +42,3 @@ "use strict"; | ||
const requires = new utils.RequiresMap(this.ctx); | ||
this.fixUpArgsExtAttrs(); | ||
@@ -82,8 +83,9 @@ let str = ""; | ||
let invocation; | ||
if (overloads.every(overload => conversions[overload.operation.idlType.idlType])) { | ||
str += ` | ||
invocation = ` | ||
return ${callOn}.${implFunc}(${argsSpread}); | ||
`; | ||
} else { | ||
str += ` | ||
invocation = ` | ||
return utils.tryWrapperForImpl(${callOn}.${implFunc}(${argsSpread})); | ||
@@ -93,2 +95,9 @@ `; | ||
if (utils.hasCEReactions(this.idls[0])) { | ||
invocation = this.ctx.invokeProcessCEReactions(invocation, { | ||
requires | ||
}); | ||
} | ||
str += invocation; | ||
if (this.static) { | ||
@@ -95,0 +104,0 @@ this.interface.addStaticMethod(this.name, argNames, str); |
@@ -13,6 +13,18 @@ "use strict"; | ||
function defaultProcessor(code) { | ||
return code; | ||
} | ||
class Context { | ||
constructor({ implSuffix = "", options } = {}) { | ||
constructor({ | ||
implSuffix = "", | ||
processCEReactions = defaultProcessor, | ||
processHTMLConstructor = defaultProcessor, | ||
options | ||
} = {}) { | ||
this.implSuffix = implSuffix; | ||
this.processCEReactions = processCEReactions; | ||
this.processHTMLConstructor = processHTMLConstructor; | ||
this.options = options; | ||
this.initialize(); | ||
@@ -48,4 +60,28 @@ } | ||
} | ||
invokeProcessCEReactions(code, config) { | ||
return this._invokeProcessor(this.processCEReactions, code, config); | ||
} | ||
invokeProcessHTMLConstructor(code, config) { | ||
return this._invokeProcessor(this.processHTMLConstructor, code, config); | ||
} | ||
_invokeProcessor(processor, code, config) { | ||
const { requires } = config; | ||
if (!requires) { | ||
throw new TypeError("Internal error: missing requires object in context"); | ||
} | ||
const context = { | ||
addImport(source, imported) { | ||
return requires.add(source, imported); | ||
} | ||
}; | ||
return processor.call(context, code); | ||
} | ||
} | ||
module.exports = Context; |
@@ -20,2 +20,4 @@ "use strict"; | ||
implSuffix: opts.implSuffix, | ||
processCEReactions: opts.processCEReactions, | ||
processHTMLConstructor: opts.processHTMLConstructor, | ||
options: { | ||
@@ -22,0 +24,0 @@ suppressErrors: Boolean(opts.suppressErrors) |
@@ -35,2 +35,6 @@ "use strict"; | ||
function hasCEReactions(idl) { | ||
return Boolean(getExtAttr(idl.extAttrs, "CEReactions")); | ||
} | ||
function isOnInstance(memberIDL, interfaceIDL) { | ||
@@ -72,8 +76,13 @@ return memberIDL.special !== "static" && (getExtAttr(memberIDL.extAttrs, "Unforgeable") || isGlobal(interfaceIDL)); | ||
add(type, func = "") { | ||
const key = func + type; | ||
let req = `require("./${type}.js")`; | ||
const key = (func + type).replace(/[./-]+/g, " ").trim().replace(/ /g, "_"); | ||
const path = type.startsWith(".") ? type : `./${type}`; | ||
let req = `require("${path}.js")`; | ||
if (func) { | ||
req += `.${func}`; | ||
} | ||
this.addRaw(key, req); | ||
return key; | ||
} | ||
@@ -106,2 +115,3 @@ | ||
isGlobal, | ||
hasCEReactions, | ||
isOnInstance, | ||
@@ -108,0 +118,0 @@ stringifyPropertyKey, |
{ | ||
"name": "webidl2js", | ||
"version": "13.0.0", | ||
"version": "14.0.0", | ||
"description": "Auto-generates class structures for WebIDL specifications", | ||
@@ -14,4 +14,4 @@ "main": "lib/transformer.js", | ||
"devDependencies": { | ||
"eslint": "^6.6.0", | ||
"jest": "^24.9.0" | ||
"eslint": "^6.8.0", | ||
"jest": "^25.1.0" | ||
}, | ||
@@ -30,3 +30,3 @@ "scripts": { | ||
"engines": { | ||
"node": ">=8" | ||
"node": ">=10" | ||
}, | ||
@@ -33,0 +33,0 @@ "author": "Sebastian Mayr <npm@smayr.name>", |
@@ -111,4 +111,5 @@ # JavaScript bindings generator for Web IDL | ||
- `implSuffix`: a suffix used, if any, to find files within the source directory based on the IDL file name. | ||
- `suppressErrors`: set to true to suppress errors during generation. | ||
- `implSuffix`: a suffix used, if any, to find files within the source directory based on the IDL file name | ||
- `suppressErrors`: set to true to suppress errors during generation | ||
- `processCEReactions` and `processHTMLConstructor`: see below | ||
@@ -125,2 +126,62 @@ The `addSource()` method can then be called multiple times to add directories containing `.webidl` IDL files and `.js` implementation class files. | ||
### `[CEReactions]` and `[HTMLConstructor]` | ||
By default webidl2js ignores HTML Standard-defined extended attributes [`[CEReactions]`](https://html.spec.whatwg.org/multipage/custom-elements.html#cereactions) and [`[HTMLConstructor]`](https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor), since they require detailed knowledge of the host environment to implement correctly. The `processCEReactions` and `processHTMLConstructor` hooks provide a way to customize the generation of the wrapper class files when these extended attributes are present. | ||
Both hooks have the signature `(code) => replacementCode`, where: | ||
- `code` is the code generated by webidl2js normally, for calling into the impl class. | ||
- `replacementCode` is the new code that will be output in place of `code` in the wrapper class. | ||
If either hook is omitted, then the code will not be replaced, i.e. the default is equivalent to `(code) => code`. | ||
Both hooks also have some utility methods that are accessible via `this`: | ||
- `addImport(relPath, [importedIdentifier])` utility to require external modules from the generated interface. This method accepts 2 parameters: `relPath` the relative path from the generated interface file, and an optional `importedIdentifier` the identifier to import. This method returns the local identifier from the imported path. | ||
The following variables are available in the scope of the replacement code: | ||
- `globalObject` (object) is the global object associated with the interface | ||
- `interfaceName` (string) is the name of the interface | ||
An example of code that uses these hooks is as follows: | ||
```js | ||
"use strict"; | ||
const WebIDL2JS = require("webidl2js"); | ||
const transformer = new WebIDL2JS({ | ||
implSuffix: "-impl", | ||
processCEReactions(code) { | ||
// Add `require("../ce-reactions")` to generated file. | ||
const ceReactions = this.addImport("../ce-reactions"); | ||
return ` | ||
${ceReactions}.preSteps(globalObject); | ||
try { | ||
${code} | ||
} finally { | ||
${ceReactions}.postSteps(globalObject); | ||
} | ||
`; | ||
}, | ||
processHTMLConstructor(/* code */) { | ||
// Add `require("../HTMLConstructor").HTMLConstructor` to generated file. | ||
const htmlConstructor = this.addImport("../HTMLConstructor", "HTMLConstructor"); | ||
return ` | ||
return ${htmlConstructor}(globalObject, interfaceName); | ||
`; | ||
} | ||
}); | ||
transformer.addSource("idl", "impls"); | ||
transformer.generate("wrappers").catch(err => { | ||
console.error(err.stack); | ||
process.exit(1); | ||
}); | ||
``` | ||
## Generated wrapper class file API | ||
@@ -343,2 +404,7 @@ | ||
Supported Web IDL extensions defined in HTML: | ||
- `[CEReactions]` - behavior can be defined via the `processCEReactions` hook | ||
- `[HTMLConstructor]` - behavior can be defined via the `processHTMLConstructor` hook | ||
Notable missing features include: | ||
@@ -372,3 +438,3 @@ | ||
In the future we may move this extended attribute out into some sort of plugin, since it is more related to HTML than to Web IDL. | ||
In the future we may change this extended attribute to be handled by the caller, similar to `[CEReactions]` and `[HTMLConstructor]`, since it is more related to HTML than to Web IDL. | ||
@@ -375,0 +441,0 @@ ### `[WebIDL2JSValueAsUnsupported=value]` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
145610
3432
444