Skip to content

Commit 94ed3e3

Browse files
committed
Escape semantic @as values in GenType
Signed-off-by: Christoph Knittel <ck@cca.io>
1 parent f96987f commit 94ed3e3

13 files changed

Lines changed: 71 additions & 7 deletions

File tree

compiler/gentype/import_path.ml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,24 @@ let to_cmt ~(config : Config.t) ~output_file_relative (dir, s) =
2929
| Some name -> "-" ^ name)
3030
^ ".cmt"
3131

32-
let emit (dir, s) = (dir, s) |> dump
32+
(* Import paths are emitted inside single-quoted JavaScript/TypeScript string
33+
literals. The AST stores their semantic value, so restore source escapes at
34+
this final output boundary. *)
35+
let escape_for_single_quotes s =
36+
let buf = Buffer.create (String.length s) in
37+
String.iter
38+
(function
39+
| '\'' -> Buffer.add_string buf "\\'"
40+
| '\\' -> Buffer.add_string buf "\\\\"
41+
| '\b' -> Buffer.add_string buf "\\b"
42+
| '\012' -> Buffer.add_string buf "\\f"
43+
| '\n' -> Buffer.add_string buf "\\n"
44+
| '\r' -> Buffer.add_string buf "\\r"
45+
| '\t' -> Buffer.add_string buf "\\t"
46+
| c when Char.code c < 0x20 || Char.code c = 0x7f ->
47+
Buffer.add_string buf (Printf.sprintf "\\x%02x" (Char.code c))
48+
| c -> Buffer.add_char buf c)
49+
s;
50+
Buffer.contents buf
51+
52+
let emit path = path |> dump |> escape_for_single_quotes

compiler/gentype/import_path.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ type t
55
val bs_curry_path : config:Config.t -> t
66
val chop_extension_safe : t -> t [@@live]
77
val dump : t -> string
8+
9+
(* Escape a semantic import path for a single-quoted JavaScript/TypeScript
10+
string literal. The returned string does not include the quotes. *)
811
val emit : t -> string
912
val from_module : dir:string -> import_extension:string -> Module_name.t -> t
1013
val from_string_unsafe : string -> t

compiler/gentype/translate_core_type.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ and translateCoreType_ ~config ~type_vars_gen
183183
let label_js =
184184
if as_string then
185185
match attributes |> Annotation.get_as_string with
186-
| Some label_renamed -> StringLabel label_renamed
186+
| Some label_renamed ->
187+
StringLabel (String.escaped label_renamed)
187188
| None ->
188189
if is_number label then IntLabel label else StringLabel label
189190
else if as_int then (

compiler/gentype/translate_type_declarations.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ let create_polyvariant_case (label, attributes) =
3535
| Some (_, BoolPayload b) -> BoolLabel b
3636
| Some (_, FloatPayload s) -> FloatLabel s
3737
| Some (_, IntPayload i) -> IntLabel i
38-
| Some (_, StringPayload as_label) -> StringLabel as_label
38+
| Some (_, StringPayload as_label) ->
39+
StringLabel (String.escaped as_label)
3940
| _ -> if is_number label then IntLabel label else StringLabel label);
4041
}
4142

4243
let create_variant_case label = function
43-
| Some (Variant_runtime.String label) -> {label_js = StringLabel label}
44+
| Some (Variant_runtime.String label) ->
45+
{label_js = StringLabel (String.escaped label)}
4446
| Some (Variant_runtime.Int label) ->
4547
{label_js = IntLabel (string_of_int label)}
4648
| Some (Variant_runtime.Float label) -> {label_js = FloatLabel label}

tests/gentype_tests/typescript-react-example/src/ImportJsValue.gen.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {returnMixedArray as returnMixedArrayNotChecked} from './MyMath';
1313

1414
import {useColor as useColorNotChecked} from './MyMath';
1515

16+
import {useEscapedInlineVariant as useEscapedInlineVariantNotChecked} from './MyMath';
17+
1618
import {higherOrder as higherOrderNotChecked} from './MyMath';
1719

1820
import {convertVariant as convertVariantNotChecked} from './MyMath';
@@ -51,6 +53,12 @@ export const useColorTypeChecked: (_1:color) => number = useColorNotChecked as a
5153
// Export 'useColor' early to allow circular import from the '.bs.js' file.
5254
export const useColor: unknown = useColorTypeChecked as (_1:color) => number as any;
5355

56+
// In case of type error, check the type of 'useEscapedInlineVariant' in 'ImportJsValue.res' and './MyMath'.
57+
export const useEscapedInlineVariantTypeChecked: (_1:"Illegal\"Name") => number = useEscapedInlineVariantNotChecked as any;
58+
59+
// Export 'useEscapedInlineVariant' early to allow circular import from the '.bs.js' file.
60+
export const useEscapedInlineVariant: unknown = useEscapedInlineVariantTypeChecked as (_1:"Illegal\"Name") => number as any;
61+
5462
// In case of type error, check the type of 'higherOrder' in 'ImportJsValue.res' and './MyMath'.
5563
export const higherOrderTypeChecked: (_1:((_1:number, _2:number) => number)) => number = higherOrderNotChecked as any;
5664

tests/gentype_tests/typescript-react-example/src/ImportJsValue.res

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ type stringFunction
5757

5858
@genType.import("./MyMath") external useColor: color => int = "useColor"
5959

60+
@genType.import("./MyMath")
61+
external useEscapedInlineVariant: @string [@as("Illegal\"Name") #illegalName] => int =
62+
"useEscapedInlineVariant"
63+
6064
@genType.import("./MyMath") external higherOrder: ((int, int) => int) => int = "higherOrder"
6165

6266
@genType let returnedFromHigherOrder = higherOrder(\"+")

tests/gentype_tests/typescript-react-example/src/ImportJsValue.res.js

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/gentype_tests/typescript-react-example/src/MyMath.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export type stringFunction = (_: string) => string;
2323

2424
export const useColor = (_x: "tomato" | "gray"): number => 0;
2525

26+
export const useEscapedInlineVariant = (_x: 'Illegal"Name'): number => 0;
27+
2628
export const higherOrder = (foo: (_1: number, _2: number) => number) =>
2729
foo(3, 4);
2830

tests/gentype_tests/typescript-react-example/src/Records.gen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export type myRecBsAs = {
5252
readonly jsValid0: string;
5353
readonly type: string;
5454
readonly "the-key": string;
55-
readonly "with\\\"dquote": string;
55+
readonly "with\"dquote": string;
5656
readonly "with'squote": string;
5757
readonly "1number": string
5858
};

tests/gentype_tests/typescript-react-example/src/Records.res.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)