fix: type CriticalScript props as script attributes - #39
Conversation
…tter type safety Signed-off-by: gwagjiug <kwjo0228@naver.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR updates ChangesCritical script props
Estimated code review effort: 1 (Trivial) | ~5 minutes Merge Risk: ⚪ Minimal · up to This change widens CriticalScript props to correctly support standard script attributes without changing runtime rendering or generated JavaScript. No actionable merge-blocking risk remains after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
Just a thought — since otherProps is spread onto the <script> tag, passing things like src, children, or dangerouslySetInnerHTML could unintentionally break or override the inlined script. Might it be worth omitting them from the props type? interface CriticalScriptProps
extends Omit<React.ScriptHTMLAttributes<HTMLScriptElement>, 'src' | 'children' | 'dangerouslySetInnerHTML'> {} |
@spoyodevelop |
|
@gwagjiug However, since useful props like fetchPriority only exist in ScriptHTMLAttributes, I agree this PR is valid too. But still, I think we should definitely omit the props that can actually break the inlined script (src, children, dangerouslySetInnerHTML). yup. It may good to update the prop types like i suggested, can make this PR more valid In my opinion. also, sorry for quiet editing in my comment, I am typing this english with my hands and seems LLM brakes my original intention of my suggestion! |
|
It is reasonable for the exported component to expose props appropriate for a For the inline classic scripts currently generated by this plugin:
We could consider supporting
|
|
I also agree with @spoyodevelop’s suggestion. Callers should not be able to override props that define or replace the inline script content, as doing so could break the component’s intended behavior. |
Ah, I was guessing that way a bit too. I also thought that using
I was originally wondering if supporting Thanks for looking into this, it really cleared up my assumptions. Based on what you found, I think sticking with the original code is probably more good for this use case. |
|
Thanks for the detailed explanation. That distinction makes sense to me. I agree that exposing the full Taking both of your comments together, would the following narrower type better match the intended public API? interface CriticalScriptProps extends React.HTMLAttributes<HTMLScriptElement> {
children?: never
dangerouslySetInnerHTML?: never
}This would keep script-specific attributes such as I used explicit If this matches the direction you have in mind, I’ll narrow the PR accordingly and update its type-level checks, description, and related documentation wording. |
Purpose
Modules imported with the
?as-critical-scriptsuffix are exposed as React components that render a<script>element.The documentation states that these components accept the standard HTML
<script>attributes. However, the public type declaration previously extended React’s genericHTMLAttributes:This allowed attributes shared by most HTML elements, such as
id,className,style, andnonce, but omitted attributes specific to<script>elements, including:asyncdefersrctypeintegritycrossOriginnoModulereferrerPolicyfetchPriorityAs a result, valid component usage produced TypeScript errors:
For example, TypeScript reported:
This was a type declaration mismatch rather than a missing runtime capability. The generated component already forwards all supplied props to the rendered
<script>element:JavaScript consumers, or TypeScript consumers bypassing the incorrect declaration, could therefore already pass these attributes at runtime.
Root cause
React.HTMLAttributes<T>represents attributes shared by general HTML elements. PassingHTMLScriptElementas its generic parameter does not add script-specific attributes; the parameter mainly describes the target element used by event-related types.React provides a separate interface for
<script>elements:The previous declaration therefore selected the generic element interface even though the component always renders a
<script>.The existing documentation examples used
idandnonce, both of which are already included in the genericHTMLAttributesinterface. Those examples passed type checking and did not expose the missing script-specific attributes.Main changes
Use React’s script-specific attribute interface
CriticalScriptPropsnow extendsReact.ScriptHTMLAttributes<HTMLScriptElement>:ScriptHTMLAttributesalready extendsHTMLAttributes, so this is a type widening rather than a replacement of existing capabilities.Existing props continue to work:
Script-specific props are now also recognized:
This uses React’s canonical type for a
<script>element instead of maintaining a separate attribute list.Add a compile-time regression check
A small type-check fixture imports an actual
?as-critical-scriptmodule and verifies its inferred component props:asyncwas selected because it clearly distinguishesScriptHTMLAttributesfrom the genericHTMLAttributesinterface.The check demonstrates the regression directly:
The fixture is included in the package’s existing
tsc --noEmitcheck, so no new test framework, script, or configuration is required. It uses a type-only import and is not part of the package’s runtime build output.Why this is not treated as an intentional restriction
It is possible in principle that a critical inline-script component might intentionally restrict attributes such as
src,async, ordefer.However, the repository currently provides no evidence of such a policy:
<script>attributes.otherPropswithout filtering script-specific attributes.Omittype, allowlist, runtime guard, warning, comment, or test describing prohibited attributes.childrenanddangerouslySetInnerHTML, so it did not form a consistent runtime safety boundary.Some script attributes may not be useful for every critical inline script. For example,
srccan change how the browser handles inline content, whileasyncanddeferhave context-dependent behavior.This PR does not introduce a new policy for those attributes. It aligns the public type declaration with the existing documentation and runtime behavior.
If the project later decides to prohibit specific attributes, that should be handled as an explicit design change with:
Omit;User-visible behavior
idandnonceasyncandtypeThe change only corrects the TypeScript representation of behavior that the component already supports.
Compatibility and scope
This PR:
HTMLAttributes;This is a source-compatible type widening. Existing consumers do not need to change their code.
Verification
pnpm --filter @woowabros/vite-plugin-critical-script typecheckTS2353when the previousHTMLAttributesdeclaration is restoredScriptHTMLAttributespnpm --filter @woowabros/vite-plugin-critical-script test— 8 tests passedpnpm --filter @woowabros/vite-plugin-critical-script buildscript-props.typecheck.tsgit diff --check upstream/main...HEADSummary by CodeRabbit
asyncattribute is accepted correctly.