Namespaced and TypeScript Enums (#1285)

Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1285

Enums are currently exposed to the JS package as constants (e.g. `import {ERRATA_NONE} from 'yoga-layout'`).

This exports enums in the form of `import {Errata} from 'yoga-layout'` then `Errata.None`.

It would be more ergonomic for these to be string union based enums instead, but right now it is a pretty thin wrapper around the native API, we need ordinal values to do things with bit masks, and folks have wanted to serialize them before.

Reviewed By: yungsters

Differential Revision: D45570417

fbshipit-source-id: dbfd330e939051d0c16460a4d2a996f88f98875c
This commit is contained in:
Nick Gerleman
2023-05-09 22:21:01 -07:00
committed by Facebook GitHub Bot
parent aa812d0e48
commit 104646d8ca
7 changed files with 265 additions and 548 deletions

126
enums.py Normal file → Executable file
View File

@@ -1,13 +1,11 @@
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import absolute_import, division, print_function, unicode_literals
import os
ENUMS = {
"Direction": ["Inherit", "LTR", "RTL"],
"Unit": ["Undefined", "Point", "Percent", "Auto"],
@@ -78,50 +76,44 @@ ENUMS = {
],
}
# Generated Java enums used to emit @DoNotStrip, but D17519844 removed them
# manually from all but YogaLogLevel. TODO: Is it safe to remove from it as
# well?
DO_NOT_STRIP = ["LogLevel"]
BITSET_ENUMS = ["PrintOptions", "Errata"]
def get_license(ext):
prologue = "/**" if ext == "js" else "/*"
return """{}
return f"""{"/**" if ext == "js" else "/*"}
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// @{} by enums.py
// @{"generated"} by enums.py
""".format(
prologue, "generated"
)
"""
def _format_name(symbol, delimiter=None, transform=None):
symbol = str(symbol)
out = ""
for i in range(0, len(symbol)):
c = symbol[i]
if str.istitle(c) and i != 0 and not str.istitle(symbol[i - 1]):
out += delimiter or ""
if transform is None:
out += c
else:
out += getattr(c, transform)()
return out
def to_java_upper(symbol):
symbol = str(symbol)
out = ""
for i in range(0, len(symbol)):
c = symbol[i]
if str.istitle(c) and i is not 0 and not str.istitle(symbol[i - 1]):
out += "_"
out += c.upper()
return out
return _format_name(symbol, "_", "upper")
def to_log_lower(symbol):
symbol = str(symbol)
out = ""
for i in range(0, len(symbol)):
c = symbol[i]
if str.istitle(c) and i is not 0 and not str.istitle(symbol[i - 1]):
out += "-"
out += c.lower()
return out
def to_hyphenated_lower(symbol):
return _format_name(symbol, "-", "lower")
root = os.path.dirname(os.path.abspath(__file__))
@@ -167,10 +159,10 @@ with open(root + "/yoga/YGEnums.cpp", "w") as f:
for value in values:
if isinstance(value, tuple):
f.write(" case YG%s%s:\n" % (name, value[0]))
f.write(' return "%s";\n' % to_log_lower(value[0]))
f.write(' return "%s";\n' % to_hyphenated_lower(value[0]))
else:
f.write(" case YG%s%s:\n" % (name, value))
f.write(' return "%s";\n' % to_log_lower(value))
f.write(' return "%s";\n' % to_hyphenated_lower(value))
f.write(" }\n")
f.write(' return "unknown";\n')
f.write("}\n")
@@ -246,61 +238,27 @@ for name, values in sorted(ENUMS.items()):
f.write(" }\n")
f.write("}\n")
# write out javascript file
with open(root + "/javascript/src/generated/YGEnums.js", "w") as f:
# write out TypeScript file
with open(root + "/javascript/src/generated/YGEnums.ts", "w") as f:
f.write(get_license("js"))
items = sorted(ENUMS.items())
for name, values in items:
base = 0
for value in values:
value_arg = value[0] if isinstance(value, tuple) else value
ordinal_arg = value[1] if isinstance(value, tuple) else base
enums = sorted(ENUMS.items())
for enum_name, ordinals in enums:
f.write(f"export enum {enum_name} {{\n")
for ordinal_index, ordinal in enumerate(ordinals):
ordinal_name = ordinal[0] if isinstance(ordinal, tuple) else ordinal
ordinal_value = ordinal[1] if isinstance(ordinal, tuple) else ordinal_index
f.write(f" {ordinal_name} = {ordinal_value},\n")
f.write("}\n\n")
f.write("const constants = {\n")
for enum_name, ordinals in enums:
for ordinal in ordinals:
ordinal_name = ordinal[0] if isinstance(ordinal, tuple) else ordinal
ordinal_value = ordinal[1] if isinstance(ordinal, tuple) else ordinal_index
f.write(
"exports.%s_%s = %d;\n"
% (to_java_upper(name), to_java_upper(value_arg), ordinal_arg)
)
base = ordinal_arg + 1
if name != items[-1][0]:
f.write("\n")
with open(root + "/javascript/src/generated/YGEnums.d.ts", "w") as f:
f.write(get_license("js"))
for name, values in sorted(ENUMS.items()):
base = 0
for value in values:
value_arg = value[0] if isinstance(value, tuple) else value
ordinal_arg = value[1] if isinstance(value, tuple) else base
f.write(
(
"type {name}_{value} = {ordinal} & ['{name}']\n"
+ "export declare const {name}_{value}: {name}_{value};\n\n"
).format(
name=to_java_upper(name),
value=to_java_upper(value_arg),
ordinal=ordinal_arg,
)
f" {to_java_upper(enum_name)}_{to_java_upper(ordinal_name)}: {enum_name}.{ordinal_name},\n"
)
base = ordinal_arg + 1
f.write("\n")
for name, values in sorted(ENUMS.items()):
f.write("export type {} =\n".format(name))
for value in values:
unpackedValue = value[0] if isinstance(value, tuple) else value
f.write(
" | typeof {}_{}".format(
to_java_upper(name), to_java_upper(unpackedValue)
)
)
if values[-1] == value:
f.write(";\n")
else:
f.write("\n")
f.write("\n")
f.write("}\n")
f.write("export default constants")