mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Allow capitalized function identifiers to be allowlisted
The current allowlist for capitalized function identifiers only allows stdlib JS modules. This PR introduces a new compiler option to allow passing in a set of allowed capitalized user function identifiers. It's a hack (in the absence of a type system) to let us check that capitalized function calls are only possible for identifiers that aren't bound to a React component. I considered adding a mechanism in fixtures to configure compiler options per-fixture, but opted to keep it simple for now and special case a `ReactForgetSecretInternals` identifier as one allowed user function in transform tests.
This commit is contained in:
@@ -6,15 +6,15 @@
|
||||
*/
|
||||
|
||||
import { PluginOptions } from "@babel/core";
|
||||
import { hasOwnProperty } from "./Common/utils";
|
||||
import {
|
||||
createCompilerFlags,
|
||||
CompilerFlags,
|
||||
createCompilerFlags,
|
||||
parseCompilerFlags,
|
||||
} from "./CompilerFlags";
|
||||
import { isOutputKind, OutputKind } from "./CompilerOutputs";
|
||||
import { Logger, noopLogger } from "./Logger";
|
||||
import { PassName } from "./Pass";
|
||||
import { hasOwnProperty } from "./Common/utils";
|
||||
|
||||
export type CompilerOptions = {
|
||||
outputKinds: OutputKind[];
|
||||
@@ -38,6 +38,11 @@ export type CompilerOptions = {
|
||||
* By default, logs are disabled.
|
||||
*/
|
||||
logger: Logger;
|
||||
|
||||
/**
|
||||
* Capitalized identifier names that can be used in call expressions.
|
||||
*/
|
||||
allowedCapitalizedUserFunctions: Set<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -98,6 +103,17 @@ export function parseCompilerOptions(
|
||||
}
|
||||
resOpts.logger = logger;
|
||||
}
|
||||
if (hasOwnProperty(inputOpts, "allowedCapitalizedUserFunctions")) {
|
||||
const allowedCapitalizedUserFunctions =
|
||||
inputOpts.allowedCapitalizedUserFunctions;
|
||||
if (
|
||||
typeof allowedCapitalizedUserFunctions !== "object" ||
|
||||
!(allowedCapitalizedUserFunctions instanceof Set)
|
||||
) {
|
||||
throw `Invalid value for 'allowedCapitalizedUserFunctions': ${allowedCapitalizedUserFunctions}`;
|
||||
}
|
||||
resOpts.allowedCapitalizedUserFunctions = allowedCapitalizedUserFunctions;
|
||||
}
|
||||
return resOpts;
|
||||
}
|
||||
|
||||
@@ -111,5 +127,6 @@ export function createCompilerOptions(): CompilerOptions {
|
||||
stopPass: PassName.JSGen,
|
||||
optIn: false,
|
||||
logger: noopLogger,
|
||||
allowedCapitalizedUserFunctions: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
run,
|
||||
};
|
||||
|
||||
const ALLOWED_CAPITALIZED_FUNCTIONS = new Set([
|
||||
const ALLOWED_CAPITALIZED_STDLIB_FUNCTIONS = new Set([
|
||||
"AggregateError",
|
||||
"Array",
|
||||
"BigInt",
|
||||
@@ -58,7 +58,10 @@ export function run(
|
||||
const callee = path.get("callee");
|
||||
if (t.isIdentifier(callee.node)) {
|
||||
const name = callee.node.name;
|
||||
if (ALLOWED_CAPITALIZED_FUNCTIONS.has(name)) {
|
||||
if (
|
||||
ALLOWED_CAPITALIZED_STDLIB_FUNCTIONS.has(name) ||
|
||||
context.opts.allowedCapitalizedUserFunctions.has(name)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Allow `Module().method()`;
|
||||
|
||||
@@ -64,6 +64,7 @@ describe("CompilerOptions", () => {
|
||||
optIn: true,
|
||||
stopPass: PassName.JSGen,
|
||||
logger: noopLogger,
|
||||
allowedCapitalizedUserFunctions: new Set(),
|
||||
};
|
||||
expect(parseCompilerOptions(fullInput)).toEqual(fullInput);
|
||||
});
|
||||
|
||||
@@ -116,6 +116,9 @@ describe("React Forget", () => {
|
||||
stopPass,
|
||||
flags,
|
||||
logger: createArrayLogger(logs),
|
||||
allowedCapitalizedUserFunctions: new Set([
|
||||
"ReactForgetSecretInternals",
|
||||
]),
|
||||
},
|
||||
compileOptions
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user