Skip to content

Commit aa49744

Browse files
authored
feat: support prefetch and preload in css extract plugin (#12237)
* feat: support prefetch and preload in css extract plugin * feat: support prefetch and preload in css extract plugin * feat: support prefetch and preload in css extract plugin * feat: support prefetch and preload in css extract plugin
1 parent 29d7220 commit aa49744

27 files changed

+591
-249
lines changed

crates/node_binding/napi-binding.d.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,11 @@ export interface JsCreateData {
772772
resource: string
773773
}
774774

775+
export interface JsCreateLinkData {
776+
code: string
777+
chunk: Chunk
778+
}
779+
775780
export interface JsCreateScriptData {
776781
code: string
777782
chunk: Chunk
@@ -2964,13 +2969,14 @@ export declare enum RegisterJsTapKind {
29642969
HtmlPluginBeforeEmit = 39,
29652970
HtmlPluginAfterEmit = 40,
29662971
RuntimePluginCreateScript = 41,
2967-
RuntimePluginLinkPreload = 42,
2968-
RuntimePluginLinkPrefetch = 43,
2969-
RsdoctorPluginModuleGraph = 44,
2970-
RsdoctorPluginChunkGraph = 45,
2971-
RsdoctorPluginModuleIds = 46,
2972-
RsdoctorPluginModuleSources = 47,
2973-
RsdoctorPluginAssets = 48
2972+
RuntimePluginCreateLink = 42,
2973+
RuntimePluginLinkPreload = 43,
2974+
RuntimePluginLinkPrefetch = 44,
2975+
RsdoctorPluginModuleGraph = 45,
2976+
RsdoctorPluginChunkGraph = 46,
2977+
RsdoctorPluginModuleIds = 47,
2978+
RsdoctorPluginModuleSources = 48,
2979+
RsdoctorPluginAssets = 49
29742980
}
29752981

29762982
export interface RegisterJsTaps {
@@ -3016,7 +3022,8 @@ export interface RegisterJsTaps {
30163022
registerHtmlPluginBeforeEmitTaps: (stages: Array<number>) => Array<{ function: ((arg: JsBeforeEmitData) => JsBeforeEmitData); stage: number; }>
30173023
registerHtmlPluginAfterEmitTaps: (stages: Array<number>) => Array<{ function: ((arg: JsAfterEmitData) => JsAfterEmitData); stage: number; }>
30183024
registerRuntimePluginCreateScriptTaps: (stages: Array<number>) => Array<{ function: ((arg: JsCreateScriptData) => String); stage: number; }>
3019-
registerRuntimePluginLinkPreloadTaps: (stages: Array<number>) => Array<{ function: ((arg: JsLinkPreloadData) => String); stage: number; }>
3025+
registerRuntimePluginCreateLinkTaps: (stages: Array<number>) => Array<{ function: ((arg: JsLinkPreloadData) => String); stage: number; }>
3026+
registerRuntimePluginLinkPreloadTaps: (stages: Array<number>) => Array<{ function: ((arg: JsCreateLinkData) => String); stage: number; }>
30203027
registerRuntimePluginLinkPrefetchTaps: (stages: Array<number>) => Array<{ function: ((arg: JsLinkPrefetchData) => String); stage: number; }>
30213028
registerRsdoctorPluginModuleGraphTaps: (stages: Array<number>) => Array<{ function: ((arg: JsRsdoctorModuleGraph) => Promise<boolean | undefined>); stage: number; }>
30223029
registerRsdoctorPluginChunkGraphTaps: (stages: Array<number>) => Array<{ function: ((arg: JsRsdoctorChunkGraph) => Promise<boolean | undefined>); stage: number; }>

crates/rspack_binding_api/src/plugins/interceptor.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ use rspack_plugin_rsdoctor::{
6464
RsdoctorPluginModuleSources, RsdoctorPluginModuleSourcesHook,
6565
};
6666
use rspack_plugin_runtime::{
67-
CreateScriptData, LinkPrefetchData, LinkPreloadData, RuntimePluginCreateScript,
68-
RuntimePluginCreateScriptHook, RuntimePluginLinkPrefetch, RuntimePluginLinkPrefetchHook,
69-
RuntimePluginLinkPreload, RuntimePluginLinkPreloadHook,
67+
CreateLinkData, CreateScriptData, LinkPrefetchData, LinkPreloadData, RuntimePluginCreateLink,
68+
RuntimePluginCreateLinkHook, RuntimePluginCreateScript, RuntimePluginCreateScriptHook,
69+
RuntimePluginLinkPrefetch, RuntimePluginLinkPrefetchHook, RuntimePluginLinkPreload,
70+
RuntimePluginLinkPreloadHook,
7071
};
7172

7273
use crate::{
@@ -92,7 +93,7 @@ use crate::{
9293
},
9394
runtime::{
9495
JsAdditionalTreeRuntimeRequirementsArg, JsAdditionalTreeRuntimeRequirementsResult,
95-
JsCreateScriptData, JsLinkPrefetchData, JsLinkPreloadData, JsRuntimeGlobals,
96+
JsCreateLinkData, JsCreateScriptData, JsLinkPrefetchData, JsLinkPreloadData, JsRuntimeGlobals,
9697
JsRuntimeRequirementInTreeArg, JsRuntimeRequirementInTreeResult,
9798
},
9899
source::JsSourceToJs,
@@ -365,6 +366,7 @@ pub enum RegisterJsTapKind {
365366
HtmlPluginBeforeEmit,
366367
HtmlPluginAfterEmit,
367368
RuntimePluginCreateScript,
369+
RuntimePluginCreateLink,
368370
RuntimePluginLinkPreload,
369371
RuntimePluginLinkPrefetch,
370372
RsdoctorPluginModuleGraph,
@@ -585,6 +587,10 @@ pub struct RegisterJsTaps {
585587
#[napi(
586588
ts_type = "(stages: Array<number>) => Array<{ function: ((arg: JsLinkPreloadData) => String); stage: number; }>"
587589
)]
590+
pub register_runtime_plugin_create_link_taps: RegisterFunction<JsCreateLinkData, Option<String>>,
591+
#[napi(
592+
ts_type = "(stages: Array<number>) => Array<{ function: ((arg: JsCreateLinkData) => String); stage: number; }>"
593+
)]
588594
pub register_runtime_plugin_link_preload_taps:
589595
RegisterFunction<JsLinkPreloadData, Option<String>>,
590596
#[napi(
@@ -930,6 +936,13 @@ define_register!(
930936
kind = RegisterJsTapKind::RuntimePluginCreateScript,
931937
skip = true,
932938
);
939+
define_register!(
940+
RegisterRuntimePluginCreateLinkTaps,
941+
tap = RuntimePluginCreateLinkTap<JsCreateLinkData, Option<String>> @ RuntimePluginCreateLinkHook,
942+
cache = true,
943+
kind = RegisterJsTapKind::RuntimePluginCreateLink,
944+
skip = true,
945+
);
933946
define_register!(
934947
RegisterRuntimePluginLinkPreloadTaps,
935948
tap = RuntimePluginLinkPreloadTap<JsLinkPreloadData, Option<String>> @ RuntimePluginLinkPreloadHook,
@@ -1774,6 +1787,24 @@ impl RuntimePluginCreateScript for RuntimePluginCreateScriptTap {
17741787
}
17751788
}
17761789

1790+
#[async_trait]
1791+
impl RuntimePluginCreateLink for RuntimePluginCreateLinkTap {
1792+
async fn run(&self, mut data: CreateLinkData) -> rspack_error::Result<CreateLinkData> {
1793+
if let Some(code) = self
1794+
.function
1795+
.call_with_sync(JsCreateLinkData::from(data.clone()))
1796+
.await?
1797+
{
1798+
data.code = code;
1799+
}
1800+
Ok(data)
1801+
}
1802+
1803+
fn stage(&self) -> i32 {
1804+
self.stage
1805+
}
1806+
}
1807+
17771808
#[async_trait]
17781809
impl RuntimePluginLinkPreload for RuntimePluginLinkPreloadTap {
17791810
async fn run(&self, mut data: LinkPreloadData) -> rspack_error::Result<LinkPreloadData> {

crates/rspack_binding_api/src/plugins/js_hooks_plugin.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub struct JsHooksAdapterPlugin {
6565
register_html_plugin_before_emit_taps: RegisterHtmlPluginBeforeEmitTaps,
6666
register_html_plugin_after_emit_taps: RegisterHtmlPluginAfterEmitTaps,
6767
register_runtime_plugin_create_script_taps: RegisterRuntimePluginCreateScriptTaps,
68+
register_runtime_plugin_create_link_taps: RegisterRuntimePluginCreateLinkTaps,
6869
register_runtime_plugin_link_preload_taps: RegisterRuntimePluginLinkPreloadTaps,
6970
register_runtime_plugin_link_prefetch_taps: RegisterRuntimePluginLinkPrefetchTaps,
7071
register_rsdoctor_plugin_module_graph_taps: RegisterRsdoctorPluginModuleGraphTaps,
@@ -427,6 +428,9 @@ async fn runtime_hooks_adapter_compilation(
427428
hooks
428429
.create_script
429430
.intercept(self.register_runtime_plugin_create_script_taps.clone());
431+
hooks
432+
.create_link
433+
.intercept(self.register_runtime_plugin_create_link_taps.clone());
430434
hooks
431435
.link_preload
432436
.intercept(self.register_runtime_plugin_link_preload_taps.clone());
@@ -651,6 +655,10 @@ impl JsHooksAdapterPlugin {
651655
register_js_taps.register_runtime_plugin_create_script_taps,
652656
non_skippable_registers.clone(),
653657
),
658+
register_runtime_plugin_create_link_taps: RegisterRuntimePluginCreateLinkTaps::new(
659+
register_js_taps.register_runtime_plugin_create_link_taps,
660+
non_skippable_registers.clone(),
661+
),
654662
register_runtime_plugin_link_preload_taps: RegisterRuntimePluginLinkPreloadTaps::new(
655663
register_js_taps.register_runtime_plugin_link_preload_taps,
656664
non_skippable_registers.clone(),

crates/rspack_binding_api/src/runtime.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use napi::Either;
66
use napi_derive::napi;
77
use rspack_core::RuntimeGlobals;
88
use rspack_plugin_runtime::{
9-
CreateScriptData, LinkPrefetchData, LinkPreloadData, RuntimeModuleChunkWrapper,
9+
CreateLinkData, CreateScriptData, LinkPrefetchData, LinkPreloadData, RuntimeModuleChunkWrapper,
1010
};
1111
use rustc_hash::FxHashMap;
1212

@@ -209,6 +209,22 @@ impl From<CreateScriptData> for JsCreateScriptData {
209209
}
210210
}
211211

212+
#[napi(object, object_from_js = false)]
213+
pub struct JsCreateLinkData {
214+
pub code: String,
215+
#[napi(ts_type = "Chunk")]
216+
pub chunk: ChunkWrapper,
217+
}
218+
219+
impl From<CreateLinkData> for JsCreateLinkData {
220+
fn from(value: CreateLinkData) -> Self {
221+
Self {
222+
code: value.code,
223+
chunk: value.chunk.into(),
224+
}
225+
}
226+
}
227+
212228
#[napi(object, object_from_js = false)]
213229
pub struct JsLinkPreloadData {
214230
pub code: String,

crates/rspack_plugin_css/src/runtime/css_loading.ejs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var uniqueName = "<%- __UNIQUE_NAME__ %>";
1+
var uniqueName = "<%- _unique_name %>";
22
function handleCssComposes(exports, composes) {
33
for (var i = 0; i < composes.length; i += 3) {
44
var moduleId = composes[i];
@@ -8,7 +8,7 @@ function handleCssComposes(exports, composes) {
88
exports[moduleId] = exports[moduleId] + " " + composedId
99
}
1010
}
11-
var loadCssChunkData = <%- __CSS_CHUNK_DATA__ %>
11+
var loadCssChunkData = <%- _css_chunk_data %>
1212
var loadingAttribute = "data-webpack-loading";
1313
var loadStylesheet = <%- basicFunction("chunkId, url, done, hmr, fetchPriority") %> {
1414
var link,
@@ -36,19 +36,7 @@ var loadStylesheet = <%- basicFunction("chunkId, url, done, hmr, fetchPriority")
3636
}
3737
if (!link) {
3838
needAttach = true;
39-
link = document.createElement("link");
40-
if (<%- SCRIPT_NONCE %>) {
41-
link.setAttribute("nonce", <%- SCRIPT_NONCE %>);
42-
}
43-
link.setAttribute("data-webpack", uniqueName + ":" + key);
44-
if (fetchPriority) {
45-
link.setAttribute("fetchpriority", fetchPriority);
46-
}
47-
link.setAttribute(loadingAttribute, 1);
48-
link.rel = "stylesheet";
49-
link.href = url;
50-
51-
<%- __CROSS_ORIGIN_LOADING_PLACEHOLDER__ %>
39+
<%- _create_link %>
5240
}
5341
var onLinkComplete = <%- basicFunction("prev, event") %> {
5442
link.onerror = link.onload = null;
@@ -61,7 +49,7 @@ var loadStylesheet = <%- basicFunction("chunkId, url, done, hmr, fetchPriority")
6149
if (link.getAttribute(loadingAttribute)) {
6250
var timeout = setTimeout(
6351
onLinkComplete.bind(null, undefined, { type: "timeout", target: link }),
64-
<%- __CHUNK_LOAD_TIMEOUT_PLACEHOLDER__ %>
52+
<%- _chunk_load_timeout %>
6553
);
6654
link.onerror = onLinkComplete.bind(null, link.onerror);
6755
link.onload = onLinkComplete.bind(null, link.onload);
@@ -70,4 +58,4 @@ var loadStylesheet = <%- basicFunction("chunkId, url, done, hmr, fetchPriority")
7058
hmr ? document.head.insertBefore(link, hmr) : needAttach && document.head.appendChild(link);
7159
return link;
7260
};
73-
<%- __INITIAL_CSS_CHUNK_DATA__ %>
61+
<%- _initial_css_chunk_data %>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
link = document.createElement("link");
2+
<% if (_charset) { %>
3+
link.charset = 'utf-8';
4+
<% } %>
5+
if (<%- SCRIPT_NONCE %>) {
6+
link.setAttribute("nonce", <%- SCRIPT_NONCE %>);
7+
}
8+
<% if (_unique_name != "") { %>
9+
link.setAttribute("data-webpack", uniqueName + ":" + key);
10+
<% } %>
11+
<% if (_with_fetch_priority) { %>
12+
if (fetchPriority) {
13+
link.setAttribute("fetchpriority", fetchPriority);
14+
}
15+
<% } %>
16+
link.setAttribute(loadingAttribute, 1);
17+
link.rel = "stylesheet";
18+
link.href = url;
19+
<% if (_cross_origin == "use-credentials") { %>
20+
link.crossOrigin = "use-credentials";
21+
<% } else if (_cross_origin != "") { %>
22+
if (link.href.indexOf(window.location.origin + '/') !== 0) {
23+
link.crossOrigin = '<%- _cross_origin %>';
24+
}
25+
<% } %>

crates/rspack_plugin_css/src/runtime/css_loading_with_loading.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
if (installedChunkData) {
1111
promises.push(installedChunkData[2]);
1212
} else {
13-
if (<%- __CSS_MATCHER__ %>) {
13+
if (<%- _css_matcher %>) {
1414
// setup Promise in chunk cache
1515
var promise = new Promise(function (resolve, reject) {
1616
installedChunkData = installedChunks[chunkId] = [resolve, reject];
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
<%- PREFETCH_CHUNK_HANDLERS %>.s =<%- basicFunction("chunkId") %> {
2-
if ((!<%- HAS_OWN_PROPERTY %>(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && <%- __CSS_MATCHER__ %> ) {
2+
if ((!<%- HAS_OWN_PROPERTY %>(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && <%- _css_matcher %> ) {
33
installedChunks[chunkId] = null;
44
if (typeof document === 'undefined') return;
55

6-
var link = document.createElement('link');
7-
<%- __CHARSET_PLACEHOLDER__ %>
8-
<%- __CROSS_ORIGIN_PLACEHOLDER__ %>
9-
10-
if (<%- SCRIPT_NONCE %>) {
11-
link.setAttribute("nonce", <%- SCRIPT_NONCE %>);
12-
}
13-
link.rel = "prefetch";
14-
link.as = "style";
15-
link.href = <%- PUBLIC_PATH %> + <%- GET_CHUNK_CSS_FILENAME %>(chunkId);
6+
<%- _create_prefetch_link %>
167
document.head.appendChild(link);
178
}
189
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
var link = document.createElement('link');
2+
<% if (_charset) { %>
3+
link.charset = 'utf-8';
4+
<% } %>
5+
<% if (_cross_origin != "") { %>
6+
link.crossOrigin = '<%- _cross_origin %>';
7+
<% } %>
8+
if (<%- SCRIPT_NONCE %>) {
9+
link.setAttribute("nonce", <%- SCRIPT_NONCE %>);
10+
}
11+
link.rel = "prefetch";
12+
link.as = "style";
13+
link.href = <%- PUBLIC_PATH %> + <%- GET_CHUNK_CSS_FILENAME %>(chunkId);
Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
11
<%- PRELOAD_CHUNK_HANDLERS %>.s = <%- basicFunction("chunkId") %> {
2-
if ((!<%- HAS_OWN_PROPERTY %>(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && <%- __CSS_MATCHER__ %>) {
2+
if ((!<%- HAS_OWN_PROPERTY %>(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && <%- _css_matcher %>) {
33
installedChunks[chunkId] = null;
44
if (typeof document === 'undefined') return;
55

6-
var link = document.createElement('link');
7-
<%- __CHARSET_PLACEHOLDER__ %>
8-
if (<%- SCRIPT_NONCE %>) {
9-
link.setAttribute("nonce", <%- SCRIPT_NONCE %>);
10-
}
11-
link.rel = "preload";
12-
link.as = "style";
13-
link.href = <%- PUBLIC_PATH %> + <%- GET_CHUNK_CSS_FILENAME %>(chunkId);
14-
<%- __CROSS_ORIGIN_PLACEHOLDER__ %>
6+
<%- _create_preload_link %>
157
document.head.appendChild(link);
168
}
179
};

0 commit comments

Comments
 (0)