Skip to content

feat(metro): surface compiler warnings during a build - #430

Merged
danstepanov merged 2 commits into
nativewind:mainfrom
YevheniiKotyrlo:feat/metro-compiler-warnings
Oct 31, 2026
Merged

danstepanov merged 2 commits into
nativewind:mainfrom
YevheniiKotyrlo:feat/metro-compiler-warnings

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Aug 15, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Closes #424.

On main, compile(css).warnings() collects everything the compiler could not translate, but src/metro/metro-transformer.ts calls .stylesheet() and discards the rest of the compile result. So a dropped float or z-index: auto is silent in every build.

lightningcss also warns about input it cannot parse, such as an unknown at-rule or an unrecognised pseudo-element. lightningcss passes the rule through, the visitor finds nothing in it, and the rule disappears. The compiler never reads those warnings.

Solution

  • The transformer hands .warnings() to reportCompilerWarnings, which prints one block per stylesheet:

    react-native-css: src/global.css - 3 declarations dropped, no React Native equivalent
      properties: columns, float
      values: z-index: auto
    

    A file's block prints when its set of warnings changes, so an incremental rebuild that changes nothing about them stays quiet. Warnings never fail a build.

  • A warnings option on withReactNativeCSS sets how much is printed. Issue compile().warnings() is unreachable from a real build: no Metro consumer #424 offered three shapes, and this PR implements option 1 with option 2's escape hatch:

    • "summary" (the default) caps each channel at ten entries;
    • "verbose" lists everything;
    • "none" prints nothing.

    An opt-in default would leave the channel unseen by anyone who doesn't read the docs. If you'd rather it defaulted to "none", that's a one-word change.

  • lightningcss's parse warnings from the first pass go to a new syntax channel. It is kept apart from what React Native cannot express, because the fix for it is in the author's stylesheet. The block counts them as syntax warnings and quotes each message, since a warning does not say what became of its rule: one message covers every repeat of a mistake, and with fix(compiler): recover from a rule lightningcss cannot parse #493's error recovery a media query whose feature value lightningcss refuses still compiles. This package's own @nativeMapping is not reported.

  • Every recorded value is text, so CompilerWarnings.values is Record<string, string[]>. The one number the compiler recorded, a percentage line-height, is recorded as the ratio it resolved to.

  • The README documents the option and the syntax warnings.

main refuses @react-native blocks outright, so the filter for this package's own at-rules covers only @nativeMapping.

Tests

  • src/__tests__/metro/warnings.test.ts builds its fixtures from real compile() results and covers:
    • the formatting, with value entries in the same code-unit order as every other channel;
    • the caps;
    • the syntax clause, which counts warnings rather than the rules they name;
    • re-printing only on a change;
    • a file that stops warning and warns again;
    • paths inside and outside the project root.
  • src/__tests__/metro/metro-transformer.test.ts drives the transformer with a stand-in for Expo's transform worker, and checks that the level and the file reach the report. It also checks that web and non-CSS files never print.
  • src/__tests__/compiler/warnings.test.ts covers the syntax channel: an unknown at-rule and an unrecognised pseudo-element, each dropping its rule; a correctly spelled control; this package's own at-rule, and a misspelling of it, which is reported; and a repeated mistake.
  • On main, 7 of the 15 cases in the files that load there fail; warnings.test.ts exercises the new module. Each of 33 mutations fails its own cases.
  • yarn example expo export --platform android prints the block for the example's global.css: 42 declarations from Tailwind's preflight.
  • On a 76 KB Tailwind stylesheet whose compile drops 441 declarations, a native transform takes 102.4 ms with the report, between two runs of main at 101.5 ms and 103.2 ms.

Verification

Every ci.yml job, with this branch merged into main: the Linux jobs on Ubuntu with Node 22.18.0, yarn test at 1379 passed, and build-ios-dev on macOS with Xcode 27.1, where ci.yml pins 26.6.

This PR's cases, run on main and here: #430 (comment)

Merge order

#379 changes the same lines of metro/metro-transformer.ts; whichever lands second rebases.

#493 is stacked on this branch.

@YevheniiKotyrlo

YevheniiKotyrlo commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

No device frame

The change is terminal output, and the description shows the block a build prints.

This PR's cases — main against this PR

This PR's test files, run on main (2421ea77) and here, merged into main:

suite fail on main fail here
compiler/warnings.test.ts, new 4 of 8 0 of 8
metro/metro-transformer.test.ts, new 3 of 7 0 of 7
metro/warnings.test.ts, new does not load: src/metro/warnings.ts is not on main 0 of 23

Run through the repo's jest on Ubuntu with Node 22.18.0.

YevheniiKotyrlo added a commit to YevheniiKotyrlo/react-native-css that referenced this pull request Sep 22, 2026
This branch's first commit added a local logWarnings to the metro
transformer, using picocolors directly. PR nativewind#430 adds the same feature to
the same file through an extracted ./warnings module, so the two
conflict and whichever lands second rewrites the other.

Nothing here needs it: the compiler suites assert compiled.warnings()
directly, and all 277 tests across the three border suites pass without
it. It also left this branch changing a metro source with no metro test,
which is the only reason that plane looked uncovered.
@YevheniiKotyrlo
YevheniiKotyrlo force-pushed the feat/metro-compiler-warnings branch from 156af1b to 61f8ef1 Compare October 11, 2026 12:36
@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as draft October 17, 2026 07:38
@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as ready for review October 17, 2026 12:05
@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as draft October 27, 2026 21:19
@YevheniiKotyrlo
YevheniiKotyrlo force-pushed the feat/metro-compiler-warnings branch from edf60a3 to d23edad Compare October 27, 2026 21:44
@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as ready for review October 27, 2026 22:44

@sammoore sammoore left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a solid improvement and default for consumers of react-native-css, but probably not something we want to ship in nativewind by default.

We should follow this up with a PR to nativewind (once a new version of react-native-css is published, so that it has the new warnings config key defined) that updates its call to withReactNativeCSS to configure warnings: 'none' by default.

cc @danstepanov

The compiler records every declaration it could not translate, but nothing
read compile().warnings() during a build, so a dropped float or
z-index: auto was silent. The Metro transformer now prints one block per CSS
file: capped in summary mode, listed in full in verbose mode, and printed
again only when the file's warnings change. The warnings option sets the
level, and none silences it.

lightningcss also warns about input it cannot parse, such as an unknown
at-rule or pseudo-element. Those warnings reach a new syntax channel, kept
apart from what React Native cannot express, and the block counts them as
syntax warnings; this package's own @nativeMapping is not reported.

Every recorded value is text, so CompilerWarnings.values is
Record<string, string[]>; the one number, a percentage line-height, is
recorded as the ratio it resolved to.
The section showed only dropped declarations, while the block also counts
the warnings lightningcss gives for CSS it cannot parse and lists them
under syntax:.
@danstepanov
danstepanov force-pushed the feat/metro-compiler-warnings branch from 8f005fa to ffe65c8 Compare October 31, 2026 01:11
@danstepanov
danstepanov merged commit 47d8c9f into nativewind:main Oct 31, 2026
6 checks passed
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.

compile().warnings() is unreachable from a real build: no Metro consumer

3 participants