Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not wrap keys in unnecessary quotes when using ES2016 #4106

Open
floratmin opened this issue Mar 6, 2025 · 1 comment
Open

Do not wrap keys in unnecessary quotes when using ES2016 #4106

floratmin opened this issue Mar 6, 2025 · 1 comment

Comments

@floratmin
Copy link

When I have an object with valid unicode keys, sometimes the characters get quoted.

I have e.g. the following object:

export default {
  ꁈꄼሕᣎᠳ勞: 'test',
};

When I build it with esbuild specifying the tsconfig.json file with target: 'esnext' and the --charset=utf8 option, it is transformed to

export default {
  "ꁈꄼሕᣎᠳ勞": "test",
};

Is there a way to get rid of these unnecessary quotes?

@evanw
Copy link
Owner

evanw commented Mar 6, 2025

It looks like the specific character here is a.k.a. U+18CE a.k.a. Canadian Syllabics Rwee. It appears to have been added in Unicode 5.2 in the Unified Canadian Aboriginal Syllabics Extended block.

The JavaScript language standard doesn't require a specific version of Unicode other than saying the following:

A conforming implementation of ECMAScript must interpret source text input in conformance with the latest version of the Unicode Standard and ISO/IEC 10646.

It also contains the following footnote:

In ECMAScript 2016, Unicode 8.0.0 or higher is mandated, as opposed to ECMAScript 2015 which mandated Unicode 5.1. In particular, this caused U+180E MONGOLIAN VOWEL SEPARATOR, which was in the Space_Separator (Zs) category and thus treated as whitespace in ECMAScript 2015, to be moved to the Format (Cf) category (as of Unicode 6.3.0). This causes whitespace-sensitive methods to behave differently. For example, "\u180E".trim().length was 0 in previous editions, but 1 in ECMAScript 2016 and later. Additionally, ECMAScript 2017 mandated always using the latest version of the Unicode Standard.

That makes the Unicode version support matrix look roughly like this:

ECMAScript version Unicode version
ES5 Unicode 3.0+
ES2015 Unicode 5.1+
ES2016 Unicode 8.0+
ES2017+ Latest

So you're right that technically a target of ES2016 means that anything consuming should treat it as an identifier. But this whole thing is kind of a mess. It means that for ECMAScript 2017 and beyond, it's not clear what ECMAScript version maps to what Unicode version. There are also edge cases where the mapping isn't straightforward, such as when Unicode version 4.1 to 15.0 accidentally removed some characters that were previously considered to be identifiers (the mistake was finally corrected in Unicode 15.1). So some identifiers went from being valid to not valid and back again.

It's hard to commit to emitting JavaScript that is guaranteed to be parsed successfully by all of the various JavaScript processing tools in the ecosystem without quoting things conservatively. The approach that esbuild uses is to parse all identifiers that are/were considered to be valid identifiers in any past version of ECMAScript, but to quote identifiers that aren't considered to be a valid identifier in all past versions of ECMAScript. So esbuild naturally works with all JavaScript input, and esbuild's JavaScript output naturally works with everything else. You can read more about the details of esbuild's approach here.

I personally like the conceptual simplicity of this approach and would be very hesitant to change it as I imagine it could easily break interoperability with the ecosystem in hard-to-anticipate ways. Any change to this would have to be carefully considered and would need to come with a lot of research into compatibility with other tools and engines (including past versions). And all of that doesn't seem worth it to save on generating a few quotation marks. So I'm not inclined to do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants