Skip to content

Turbopack disables PropertyLiterals and MemberExpressionLiterals ES3 transforms, breaking Safari 15 strict mode #89808

@jesseproudman

Description

@jesseproudman

Link to the code that reproduces this issue

https://github.com/veniceai/turbopack-safari-reserved-words-repro

To Reproduce

  1. Clone the repo and install dependencies:
    git clone https://github.com/veniceai/turbopack-safari-reserved-words-repro
    cd turbopack-safari-reserved-words-repro
    npm install

  2. Build with Turbopack:
    npm run build

  3. Verify the output chunks contain unquoted reserved words:
    npm run verify
    (This finds 7 violations: {const:, let:, var:} as unquoted object keys in AJV chunks)

  4. Start the production server and open in Safari 15 / iOS 15 (e.g. via BrowserStack):
    npm start
    Open http://localhost:3000

  5. Safari 15 console shows:
    SyntaxError: Unexpected keyword 'var'. Expected '}' to end an object literal.

The bug is in turbopack-ecmascript/src/transform/mod.rs — PropertyLiterals and
MemberExpressionLiterals are excluded from preset_env alongside ReservedWords.
Only ReservedWords needs to be excluded (it breaks module resolution). The other
two are safe and are needed to quote reserved-word property names for Safari 15.

Current vs. Expected behavior

● ## Current behavior

Turbopack production builds (next build --turbo) emit unquoted reserved words
as object property names in JS chunks. For example, AJV's codegen/scope.js:

exports.varKinds = {const: new Name("const"), let: new Name("let"), var: new Name("var")}

Safari 15 / iOS 15 parses ES modules in strict mode, and its parser incorrectly
rejects unquoted reserved words as property names with:

SyntaxError: Unexpected keyword 'var'. Expected '}' to end an object literal.
SyntaxError: Unexpected keyword 'let'. Cannot use 'let' as an identifier name for a LexicalDeclaration.

This affects any dependency that uses reserved words as object keys. Common ones
include ajv, @chakra-ui/styled-system, framer-motion, and mingo.

The root cause is in turbopack-ecmascript/src/transform/mod.rs where three ES3
transforms are excluded from preset_env:

exclude: vec![
    FeatureOrModule::Feature(Feature::ReservedWords),
    FeatureOrModule::Feature(Feature::MemberExpressionLiterals),
    FeatureOrModule::Feature(Feature::PropertyLiterals),
]

ReservedWords must be excluded (it breaks Turbopack's module resolution), but
PropertyLiterals and MemberExpressionLiterals are safe — they only affect
property name quoting, not identifier resolution.

Expected behavior

Turbopack should quote reserved-word property names in output chunks:

exports.varKinds = {"const": new Name("const"), "let": new Name("let"), "var": new Name("var")}

This is what webpack/Babel builds produce via @babel/plugin-transform-property-literals,
and what SWC's swc_ecma_compat_es3::prop_lits transform does when enabled.

The fix is to only exclude ReservedWords from preset_env, leaving PropertyLiterals
and MemberExpressionLiterals enabled:

exclude: vec![
    FeatureOrModule::Feature(Feature::ReservedWords),
]

iOS 15 / Safari 15 still has ~5-8% mobile market share. All Turbopack production
builds are broken for these users if any dependency uses reserved words as
property names.

Provide environment information

Operating System:                                                                                                                                                                                              
    Platform: linux                                                                                                                                                                                              
    Arch: arm64                                                                                                                                                                                                  
    Version: #1 SMP Thu Jan 15 14:58:53 UTC 2026                                                                                                                                                               
    Available memory (MB): 19972
    Available CPU cores: 16
  Binaries:
    Node: 20.20.0
    npm: 10.8.2
    Yarn: 1.22.22
    pnpm: 10.29.2
  Relevant Packages:
    next: 16.2.0-canary.35 // Latest available version is detected (16.2.0-canary.35).
    eslint-config-next: N/A
    react: 19.2.4
    react-dom: 19.2.4
    typescript: 5.9.3
  Next.js Config:
    output: N/A

Which area(s) are affected? (Select all that apply)

Turbopack

Which stage(s) are affected? (Select all that apply)

Vercel (Deployed)

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    TurbopackRelated to Turbopack with Next.js.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions