mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 12:33:47 +08:00
This change makes it easier to run the script from directly `roll_py.deps` without having to fetch the dependencies first. This also makes it easier to track which dependencies our tooling have. Change-Id: I1ce55074ee6a1fffa6f2a2f05a56d0d74b4aca90 Bug: 1255619 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3431346 Reviewed-by: Mathias Bynens <mathias@chromium.org> Commit-Queue: Johan Bay <jobay@chromium.org>
176 lines
5.4 KiB
JavaScript
176 lines
5.4 KiB
JavaScript
import { Container } from "./container.js";
|
|
import { Attribute } from "./attribute.js";
|
|
import { Operation } from "./operation.js";
|
|
import { Constant } from "./constant.js";
|
|
import { IterableLike } from "./iterable.js";
|
|
import {
|
|
stringifier,
|
|
autofixAddExposedWindow,
|
|
getMemberIndentation,
|
|
getLastIndentation,
|
|
getFirstToken,
|
|
findLastIndex,
|
|
autoParenter,
|
|
} from "./helpers.js";
|
|
import { validationError } from "../error.js";
|
|
import { checkInterfaceMemberDuplication } from "../validators/interface.js";
|
|
import { Constructor } from "./constructor.js";
|
|
import { Tokeniser } from "../tokeniser.js";
|
|
import { ExtendedAttributes } from "./extended-attributes.js";
|
|
|
|
/**
|
|
* @param {import("../tokeniser").Tokeniser} tokeniser
|
|
*/
|
|
function static_member(tokeniser) {
|
|
const special = tokeniser.consume("static");
|
|
if (!special) return;
|
|
const member =
|
|
Attribute.parse(tokeniser, { special }) ||
|
|
Operation.parse(tokeniser, { special }) ||
|
|
tokeniser.error("No body in static member");
|
|
return member;
|
|
}
|
|
|
|
export class Interface extends Container {
|
|
/**
|
|
* @param {import("../tokeniser").Tokeniser} tokeniser
|
|
*/
|
|
static parse(tokeniser, base, { partial = null } = {}) {
|
|
const tokens = { partial, base };
|
|
return Container.parse(
|
|
tokeniser,
|
|
new Interface({ source: tokeniser.source, tokens }),
|
|
{
|
|
inheritable: !partial,
|
|
allowedMembers: [
|
|
[Constant.parse],
|
|
[Constructor.parse],
|
|
[static_member],
|
|
[stringifier],
|
|
[IterableLike.parse],
|
|
[Attribute.parse],
|
|
[Operation.parse],
|
|
],
|
|
}
|
|
);
|
|
}
|
|
|
|
get type() {
|
|
return "interface";
|
|
}
|
|
|
|
*validate(defs) {
|
|
yield* this.extAttrs.validate(defs);
|
|
if (
|
|
!this.partial &&
|
|
this.extAttrs.every((extAttr) => extAttr.name !== "Exposed")
|
|
) {
|
|
const message = `Interfaces must have \`[Exposed]\` extended attribute. \
|
|
To fix, add, for example, \`[Exposed=Window]\`. Please also consider carefully \
|
|
if your interface should also be exposed in a Worker scope. Refer to the \
|
|
[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \
|
|
for more information.`;
|
|
yield validationError(
|
|
this.tokens.name,
|
|
this,
|
|
"require-exposed",
|
|
message,
|
|
{
|
|
autofix: autofixAddExposedWindow(this),
|
|
}
|
|
);
|
|
}
|
|
const oldConstructors = this.extAttrs.filter(
|
|
(extAttr) => extAttr.name === "Constructor"
|
|
);
|
|
for (const constructor of oldConstructors) {
|
|
const message = `Constructors should now be represented as a \`constructor()\` operation on the interface \
|
|
instead of \`[Constructor]\` extended attribute. Refer to the \
|
|
[WebIDL spec section on constructor operations](https://heycam.github.io/webidl/#idl-constructors) \
|
|
for more information.`;
|
|
yield validationError(
|
|
constructor.tokens.name,
|
|
this,
|
|
"constructor-member",
|
|
message,
|
|
{
|
|
autofix: autofixConstructor(this, constructor),
|
|
}
|
|
);
|
|
}
|
|
|
|
const isGlobal = this.extAttrs.some((extAttr) => extAttr.name === "Global");
|
|
if (isGlobal) {
|
|
const factoryFunctions = this.extAttrs.filter(
|
|
(extAttr) => extAttr.name === "LegacyFactoryFunction"
|
|
);
|
|
for (const named of factoryFunctions) {
|
|
const message = `Interfaces marked as \`[Global]\` cannot have factory functions.`;
|
|
yield validationError(
|
|
named.tokens.name,
|
|
this,
|
|
"no-constructible-global",
|
|
message
|
|
);
|
|
}
|
|
|
|
const constructors = this.members.filter(
|
|
(member) => member.type === "constructor"
|
|
);
|
|
for (const named of constructors) {
|
|
const message = `Interfaces marked as \`[Global]\` cannot have constructors.`;
|
|
yield validationError(
|
|
named.tokens.base,
|
|
this,
|
|
"no-constructible-global",
|
|
message
|
|
);
|
|
}
|
|
}
|
|
|
|
yield* super.validate(defs);
|
|
if (!this.partial) {
|
|
yield* checkInterfaceMemberDuplication(defs, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
function autofixConstructor(interfaceDef, constructorExtAttr) {
|
|
interfaceDef = autoParenter(interfaceDef);
|
|
return () => {
|
|
const indentation = getLastIndentation(
|
|
interfaceDef.extAttrs.tokens.open.trivia
|
|
);
|
|
const memberIndent = interfaceDef.members.length
|
|
? getLastIndentation(getFirstToken(interfaceDef.members[0]).trivia)
|
|
: getMemberIndentation(indentation);
|
|
const constructorOp = Constructor.parse(
|
|
new Tokeniser(`\n${memberIndent}constructor();`)
|
|
);
|
|
constructorOp.extAttrs = new ExtendedAttributes({});
|
|
autoParenter(constructorOp).arguments = constructorExtAttr.arguments;
|
|
|
|
const existingIndex = findLastIndex(
|
|
interfaceDef.members,
|
|
(m) => m.type === "constructor"
|
|
);
|
|
interfaceDef.members.splice(existingIndex + 1, 0, constructorOp);
|
|
|
|
const { close } = interfaceDef.tokens;
|
|
if (!close.trivia.includes("\n")) {
|
|
close.trivia += `\n${indentation}`;
|
|
}
|
|
|
|
const { extAttrs } = interfaceDef;
|
|
const index = extAttrs.indexOf(constructorExtAttr);
|
|
const removed = extAttrs.splice(index, 1);
|
|
if (!extAttrs.length) {
|
|
extAttrs.tokens.open = extAttrs.tokens.close = undefined;
|
|
} else if (extAttrs.length === index) {
|
|
extAttrs[index - 1].tokens.separator = undefined;
|
|
} else if (!extAttrs[index].tokens.name.trivia.trim()) {
|
|
extAttrs[index].tokens.name.trivia = removed[0].tokens.name.trivia;
|
|
}
|
|
};
|
|
}
|