Skip to content

feat(cli): add auto derive to t() function - #1141

Merged
eoinest merged 11 commits into
mainfrom
e/react/auto-derive-for-t-function
Mar 21, 2026
Merged

feat(cli): add auto derive to t() function#1141
eoinest merged 11 commits into
mainfrom
e/react/auto-derive-for-t-function

Conversation

@eoinest

@eoinest eoinest commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

For the new t() function, you no longer need to invoke derive() to derive dynamic content. This feature is enabled automatically for the t() function, and can be disabled in the gt.config.json by setting files.gt.parsingFlags.autoDerive: false.

Before

const noun = cond ? "boy" : "girl"
const result = t("The " + derive(noun))

After

const noun = cond ? "boy" : "girl"
const result = t("The " + noun)

This does not affect the t macro.

Greptile Summary

This PR adds auto-derive support to the t() function in the CLI parser, allowing dynamic content (variables, ternary expressions, function return values) to be statically resolved without requiring an explicit derive() wrapper. The feature is enabled by default and can be disabled via files.gt.parsingFlags.autoDerive: false in gt.config.json.

Key changes:

  • A new enableAutoDerive: 'ENABLED' | 'DISABLED' | 'AUTO' field is added to ParsingConfig; the t() function gets ENABLED while all other string registration functions (msg, tagged templates, prop-drilled callbacks) stay at DISABLED
  • handleDerivation gains a skipDeriveInvocation flag that routes non-static expressions directly through resolveCallStringVariants (the same resolution logic used inside derive())
  • GTParsingFlags is introduced in types/parsing.ts to carry both autoDerive and includeSourceCodeContext together, replacing the old ad-hoc includeSourceCodeContext boolean parameter throughout the call chain (createInlineUpdates, createUpdates, stage.ts, validate.ts)
  • A warnAutoDeriveNoResultsSync message with context-appropriate wording is added — improving on the feedback from a prior review comment
  • The previously disabled processCallExpression.ts compiler pass is cleaned up by deleting the file entirely

Issues found:

  • The @deprecated migration path in FilesOptions.gt.includeSourceCodeContext JSDoc says files.gtJson.parsingFlags... but should say files.gt.parsingFlags... — inconsistent with the correct runtime warning in generateSettings.ts
  • When auto-derive fails to resolve an expression, both warnAutoDeriveNoResultsSync (from handleDerivation) and warnNonStringSync (from deriveExpression) are pushed to output.errors, surfacing duplicate messages to the user — more visible now since auto-derive is on by default
  • Minor JSDoc typo in parseStringFunction.ts: example uses gt('hello') instead of t('hello')

Confidence Score: 4/5

  • Safe to merge — no runtime regressions; issues are limited to a misleading deprecation JSDoc path and duplicate error messages on failure.
  • The core logic is well-structured: skipDeriveInvocation is correctly threaded through binary, template-literal, and parenthesized expression branches; the enableAutoDerive tristate is cleanly mapped at each decision point; and the refactoring of includeSourceCodeContext into GTParsingFlags fixes the pre-existing silent-ignore regression. Tests cover basic auto-derive success and failure cases. Score is 4 rather than 5 due to the duplicate error messages on auto-derive failure (more impactful now that it's the default) and the misleading @deprecated JSDoc path that would send users to a non-existent config key.
  • packages/cli/src/react/jsx/utils/stringParsing/derivation/index.ts (duplicate error) and packages/cli/src/types/index.ts (incorrect @deprecated path).

Important Files Changed

Filename Overview
packages/cli/src/react/jsx/utils/stringParsing/derivation/handleDerivation.ts Core derivation logic extended with skipDeriveInvocation flag that routes non-static expressions through resolveCallStringVariants without requiring a derive() wrapper. Logic is sound; propagation through binary/template/parenthesized branches is correctly threaded.
packages/cli/src/react/jsx/utils/stringParsing/derivation/index.ts Entry point for derivation; correctly maps enableAutoDerive === 'ENABLED' to skipDeriveInvocation: true, but the existing warnNonStringSync fallback creates duplicate error messages when handleDerivation already reported a specific auto-derive error.
packages/cli/src/react/jsx/utils/parseStringFunction.ts Auto-derive routing is well implemented: T_REGISTRATION_FUNCTION direct calls get ENABLED, tagged templates and prop-drilled paths get DISABLED, and the global t macro forces DISABLED as stated in the PR description. Has a minor JSDoc typo (gt instead of t).
packages/cli/src/types/index.ts Types updated correctly (GTParsingFlags added, Partial used for user config, parsingFlags added to Settings.files). The @deprecated migration path in the FilesOptions.gt.includeSourceCodeContext JSDoc is incorrect (files.gtJson... should be files.gt...).
packages/cli/src/types/parsing.ts New types GTParsingFlags, BaseParsingFlags, and ParseFlagsByFileType introduced cleanly. GTParsingFlags requiring both autoDerive and includeSourceCodeContext is appropriate since it's only used internally (the user-facing type uses Partial<GTParsingFlags>).
packages/cli/src/config/generateSettings.ts Deprecation warning for files.gt.includeSourceCodeContext is correctly placed and uses the accurate replacement path. Default parsing flags object is correctly initialized when files config is absent.
packages/cli/src/fs/config/parseFilesConfig.ts Return type simplified to Settings['files']. GT-specific flags flow into gtJson.parsingFlags with full defaults applied; other file type flags flow into parsingFlags[fileType] with BASE_PARSING_FLAGS_DEFAULT. Logic is correct.
packages/cli/src/react/parse/createInlineUpdates.ts Signature updated to accept GTParsingFlags directly; includeSourceCodeContext and enableAutoDerive are both now correctly read from parsingFlags, fixing the previously reported silent-ignore regression.
packages/compiler/src/processing/macro-expansion/processCallExpression.ts File deleted as the call-expression compiler pass was already disabled (commented out in macroExpansionPass.ts). Clean removal with no dangling references.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["parseStrings(name, originalName, ...)"] --> B{originalName?}
    B -->|T_GLOBAL_MARKER| C["processTaggedTemplateCall\nenableAutoDerive: AUTO→DISABLED"]
    B -->|t / msg / etc| D{"Direct call?\nrefPath.parent = CallExpression"}
    D -->|No - tagged template| E["processTaggedTemplateCall\nenableAutoDerive: AUTO→DISABLED"]
    D -->|No - prop drilled| F["handleFunctionCall\nenableAutoDerive: AUTO→DISABLED"]
    D -->|Yes| G{originalName === T_REGISTRATION_FUNCTION?}
    G -->|No - msg etc| H["processTranslationCall\nenableAutoDerive: DISABLED"]
    G -->|Yes - t| I{config.enableAutoDerive === AUTO?}
    I -->|No| J["processTranslationCall\nenableAutoDerive: DISABLED"]
    I -->|Yes| K["processTranslationCall\nenableAutoDerive: ENABLED"]
    K --> L["deriveExpression\nskipDeriveInvocation: true"]
    H --> M["deriveExpression\nskipDeriveInvocation: false"]
    L --> N["handleDerivation\nskipDeriveInvocation=true"]
    M --> O["handleDerivation\nskipDeriveInvocation=false"]
    N --> P{Non-static expr?}
    P -->|Yes| Q["resolveCallStringVariants(expr)\nno derive() wrapper needed"]
    Q -->|resolved| R["StringNode ✓"]
    Q -->|null| S["warnAutoDeriveNoResultsSync ✗"]
    O --> T{Non-static expr?}
    T -->|Yes, runtimeInterp| U["{n} placeholder"]
    T -->|Yes, no runtimeInterp| V["return null (warnNonString)"]
Loading

Comments Outside Diff (2)

  1. packages/cli/src/fs/config/parseFilesConfig.ts, line 184-192 (link)

    P1 Deprecated includeSourceCodeContext value silently dropped

    The deprecated files.gt.includeSourceCodeContext flag is warned about in generateSettings.ts, but its value is never migrated into the new parsingFlags.includeSourceCodeContext. Any user with includeSourceCodeContext: true in their config will silently lose this behavior after updating — the CLI will emit the deprecation warning yet continue running with the wrong value.

    A backward-compatible fix is to read the old field as a fallback:

    gtJson: {
      publish: files.gt?.publish,
      parsingFlags: {
        ...GT_PARSING_FLAGS_DEFAULT,
        // Backward-compat: migrate the old top-level flag if the new field is absent
        ...(files.gt?.includeSourceCodeContext != null && !files.gt?.parsingFlags?.includeSourceCodeContext
          ? { includeSourceCodeContext: files.gt.includeSourceCodeContext }
          : {}),
        ...(files.gt?.parsingFlags || {}),
      },
    },

    Without this, the deprecation warning is misleading: the user sees "use the new field instead" but has no indication that the old setting is already being ignored.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/cli/src/fs/config/parseFilesConfig.ts
    Line: 184-192
    
    Comment:
    **Deprecated `includeSourceCodeContext` value silently dropped**
    
    The deprecated `files.gt.includeSourceCodeContext` flag is warned about in `generateSettings.ts`, but its value is **never migrated** into the new `parsingFlags.includeSourceCodeContext`. Any user with `includeSourceCodeContext: true` in their config will silently lose this behavior after updating — the CLI will emit the deprecation warning yet continue running with the wrong value.
    
    A backward-compatible fix is to read the old field as a fallback:
    
    ```ts
    gtJson: {
      publish: files.gt?.publish,
      parsingFlags: {
        ...GT_PARSING_FLAGS_DEFAULT,
        // Backward-compat: migrate the old top-level flag if the new field is absent
        ...(files.gt?.includeSourceCodeContext != null && !files.gt?.parsingFlags?.includeSourceCodeContext
          ? { includeSourceCodeContext: files.gt.includeSourceCodeContext }
          : {}),
        ...(files.gt?.parsingFlags || {}),
      },
    },
    ```
    
    Without this, the deprecation warning is misleading: the user sees "use the new field instead" but has no indication that the old setting is *already being ignored*.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. packages/cli/src/react/jsx/utils/stringParsing/derivation/index.ts, line 60-69 (link)

    P2 Duplicate error messages when auto-derive fails

    When skipDeriveInvocation is true and handleDerivation cannot resolve the expression, two separate errors are pushed to output.errors:

    1. warnAutoDeriveNoResultsSync — pushed inside handleDerivation before it returns null
    2. warnNonStringSync — pushed here in deriveExpression when !stringNode

    Since auto-derive is now the default behavior (enabled by autoDerive: true in GT_PARSING_FLAGS_DEFAULT), every unresolvable expression in a t() call will generate a duplicate error, which is confusing to users. Consider guarding warnNonStringSync so it only fires when no more-specific error was already added — e.g. check if output.errors.length grew before calling handleDerivation:

    const prevErrorCount = output.errors.length;
    const stringNode = handleDerivation({ ... });
    if (!stringNode) {
      // Only add fallback error if handleDerivation didn't already report one
      if (output.errors.length === prevErrorCount) {
        output.errors.push(warnNonStringSync(...));
      }
      return;
    }
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/cli/src/react/jsx/utils/stringParsing/derivation/index.ts
    Line: 60-69
    
    Comment:
    **Duplicate error messages when auto-derive fails**
    
    When `skipDeriveInvocation` is `true` and `handleDerivation` cannot resolve the expression, two separate errors are pushed to `output.errors`:
    
    1. `warnAutoDeriveNoResultsSync` — pushed inside `handleDerivation` before it returns `null`
    2. `warnNonStringSync` — pushed here in `deriveExpression` when `!stringNode`
    
    Since auto-derive is now the **default** behavior (enabled by `autoDerive: true` in `GT_PARSING_FLAGS_DEFAULT`), every unresolvable expression in a `t()` call will generate a duplicate error, which is confusing to users. Consider guarding `warnNonStringSync` so it only fires when no more-specific error was already added — e.g. check if `output.errors.length` grew before calling `handleDerivation`:
    
    ```ts
    const prevErrorCount = output.errors.length;
    const stringNode = handleDerivation({ ... });
    if (!stringNode) {
      // Only add fallback error if handleDerivation didn't already report one
      if (output.errors.length === prevErrorCount) {
        output.errors.push(warnNonStringSync(...));
      }
      return;
    }
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/cli/src/react/jsx/utils/parseStringFunction.ts
Line: 118-119

Comment:
**JSDoc example uses wrong function name**

The comment was changed from `t('hello')` / `getData(t)` to `gt('hello')` / `getData(gt)`. `gt` is a different package identifier — the function being described here is the translation callback `t` (or `msg`), not `gt`. This is a misleading documentation regression.

```suggestion
 * This covers both direct translation calls (t('hello')) and prop drilling
 * where the translation callback is passed to other functions (getData(t)).
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/cli/src/types/index.ts
Line: 198-201

Comment:
**Incorrect `@deprecated` migration path in JSDoc**

The comment says `files.gtJson.parsingFlags.includeSourceCodeContext`, but `gtJson` is an internal resolved key in `Settings['files']`, not the user-facing config key. Users write their config as `FilesOptions`, so the correct replacement path is `files.gt.parsingFlags.includeSourceCodeContext`. This matches what the runtime warning in `generateSettings.ts` correctly reports:

```ts
warnDeprecatedField(
  'files.gt.includeSourceCodeContext',
  'files.gt.parsingFlags.includeSourceCodeContext' // ← correct
);
```

```suggestion
     * use `files.gt.parsingFlags.includeSourceCodeContext` instead
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/cli/src/react/jsx/utils/stringParsing/derivation/index.ts
Line: 60-69

Comment:
**Duplicate error messages when auto-derive fails**

When `skipDeriveInvocation` is `true` and `handleDerivation` cannot resolve the expression, two separate errors are pushed to `output.errors`:

1. `warnAutoDeriveNoResultsSync` — pushed inside `handleDerivation` before it returns `null`
2. `warnNonStringSync` — pushed here in `deriveExpression` when `!stringNode`

Since auto-derive is now the **default** behavior (enabled by `autoDerive: true` in `GT_PARSING_FLAGS_DEFAULT`), every unresolvable expression in a `t()` call will generate a duplicate error, which is confusing to users. Consider guarding `warnNonStringSync` so it only fires when no more-specific error was already added — e.g. check if `output.errors.length` grew before calling `handleDerivation`:

```ts
const prevErrorCount = output.errors.length;
const stringNode = handleDerivation({ ... });
if (!stringNode) {
  // Only add fallback error if handleDerivation didn't already report one
  if (output.errors.length === prevErrorCount) {
    output.errors.push(warnNonStringSync(...));
  }
  return;
}
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "chore: satisfy grept..."

Greptile also left 2 inline comments on this PR.

@eoinest
eoinest requested a review from a team as a code owner March 20, 2026 22:19
Comment thread packages/cli/src/react/parse/createInlineUpdates.ts
Comment thread packages/cli/src/types/index.ts
Comment thread packages/cli/src/react/jsx/utils/stringParsing/derivation/handleDerivation.ts Outdated
@eoinest

eoinest commented Mar 20, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai plz re-review

Comment thread packages/cli/src/react/jsx/utils/stringParsing/derivation/handleDerivation.ts Outdated
Comment thread packages/cli/src/types/index.ts
Comment thread packages/cli/src/types/index.ts
@eoinest

eoinest commented Mar 21, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai plz re-review

Comment thread packages/cli/src/react/jsx/utils/parseStringFunction.ts
Comment thread packages/cli/src/types/index.ts
@eoinest
eoinest enabled auto-merge (squash) March 21, 2026 01:50
@eoinest
eoinest merged commit 4820643 into main Mar 21, 2026
26 checks passed
@eoinest
eoinest deleted the e/react/auto-derive-for-t-function branch March 21, 2026 01:55
@github-actions github-actions Bot mentioned this pull request Mar 21, 2026
eoinest pushed a commit that referenced this pull request Mar 21, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## gt@2.13.0

### Minor Changes

- [#1141](#1141)
[`4820643`](4820643)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - feat: auto
derive for the t() function

## @generaltranslation/compiler@1.1.34

### Patch Changes

- [#1141](#1141)
[`4820643`](4820643)
Thanks [@ErnestM1234](https://github.com/ErnestM1234)! - feat: auto
derive for the t() function

## gtx-cli@2.13.0

### Patch Changes

- Updated dependencies
\[[`4820643`](4820643)]:
    -   gt@2.13.0

## locadex@1.0.131

### Patch Changes

- Updated dependencies
\[[`4820643`](4820643)]:
    -   gt@2.13.0

## gt-next@6.14.5

### Patch Changes

- Updated dependencies
\[[`4820643`](4820643)]:
    -   @generaltranslation/compiler@1.1.34

## @generaltranslation/gt-next-lint@12.0.5

### Patch Changes

-   Updated dependencies \[]:
    -   gt-next@6.14.5

## gt-next-middleware-e2e@0.1.17

### Patch Changes

-   Updated dependencies \[]:
    -   gt-next@6.14.5

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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

Successfully merging this pull request may close these issues.

2 participants