Skip to content

Let the generated tags carry other attributes - #6

Merged
katayama8000 merged 3 commits into
mainfrom
tag-attributes
Oct 2, 2026
Merged

katayama8000 merged 3 commits into
mainfrom
tag-attributes

Conversation

@katayama8000

Copy link
Copy Markdown
Collaborator

Closes #2.

The attributes were fixed, so a crossorigin, a nonce, or a data- attribute a CDN or a monitoring tool asks for meant giving up on ViteAssets and building the markup by hand out of tags().

ViteAssets.builder(manifest)
        .scriptAttribute("crossorigin", "anonymous")
        .scriptAttribute("async")
        .styleAttribute("data-turbo-track", "reload")
        .build();
<link rel="stylesheet" href="/assets/main.css" data-turbo-track="reload">
<script type="module" src="/assets/main.js" crossorigin="anonymous" async></script>

The three questions in the issue

Can an attribute be a flag with no value? Yes, through an overload taking the name alone. Laravel spells this 'async' => true, which reads as a value that happens to mean "no value"; a separate method says it directly and cannot be confused with the string "true".

Is anything refused? The attributes vite4j writes itself: src and type on the script, href and rel on the stylesheets. The issue named src and href; type and rel are there for the same reason, since a <script> that is no longer a module, or a <link> that is no longer a stylesheet, is as quietly broken as one pointing somewhere else. It throws from the builder call rather than at render time, so the stack trace names the line that asked for it.

Names are checked too. A value can be escaped on the way out, a name cannot, so anything that is not an attribute name — "one two", "onload=alert(1) x", a name carrying a quote — is refused rather than concatenated into the tag.

Escaping. Values go through Html.attribute, the same escaping the URLs already get.

Also decided

Attributes render in the order they were added, and adding a name twice keeps the last value. Both are asserted, so neither can drift into being accidental.

Not in here

Preload tags take no attributes yet. Laravel has usePreloadTagAttributes, and modulepreload is a tag that plausibly wants a crossorigin. It is left out because the issue named the two tag kinds it wanted and I would rather you decide whether the third belongs than invent API for it. The rendering is shared, so adding it later is a builder method and a map.

#3 has room. Integrity hashes also add attributes to these tags. Nothing here claims integrity or crossorigin, so that work can set them alongside whatever the application added.

Tests

Eleven new in ViteAssetsTest, 17 in the file now: both tag kinds, the bare attribute, order, last-value-wins, escaping, every refused name, and that the script and style sets stay apart. ./gradlew build passes.

README and README.ja.md document it under Options.

katayama8000 and others added 3 commits October 2, 2026 20:21
A crossorigin, a nonce, a data- attribute a CDN or a monitoring tool asks for: wanting any of them
meant giving up on ViteAssets and building the markup by hand out of tags().

A name on its own renders bare, which is what async and defer are. The attributes vite4j writes
itself are refused where they are asked for, because a src or a rel that is quietly overridden
produces a tag that loads nothing and says nothing about why. A name cannot be escaped on the way
out the way a value can, so one that is not a name is refused too.

Closes #2

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hand-written fixtures render one stylesheet per entry, so a style attribute that reached only
the first tag would pass every one of them. The real manifest has an entry needing two, which is the
case worth asserting, and it is also where the preload links can be seen staying bare.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reserved names were already compared without regard to case, so SRC was refused, but the map
collecting the rest was keyed on the name as written. crossorigin and CROSSORIGIN were two entries,
both rendered, and the tag carried one attribute twice - with the browser taking the first, which is
the opposite of the last-one-wins the javadoc promises.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@katayama8000
katayama8000 merged commit 02bc984 into main Oct 2, 2026
2 checks passed
@katayama8000
katayama8000 deleted the tag-attributes branch October 2, 2026 11:30
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.

Allow arbitrary attributes on the generated tags

1 participant