Skip to content

Commit ec7546d

Browse files
authored
fix(css,assets): wrong "publicPath: auto" asset path in windows (#9239)
chore: update
1 parent b104095 commit ec7546d

File tree

9 files changed

+136
-134
lines changed

9 files changed

+136
-134
lines changed

crates/rspack_core/src/options/output.rs

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use rspack_cacheable::cacheable;
66
use rspack_hash::RspackHash;
77
pub use rspack_hash::{HashDigest, HashFunction, HashSalt};
88
use rspack_macros::MergeFrom;
9-
use rspack_paths::{AssertUtf8, Utf8Path, Utf8PathBuf};
10-
use sugar_path::SugarPath;
9+
use rspack_paths::Utf8PathBuf;
1110

1211
use super::CleanOptions;
1312
use crate::{Chunk, ChunkGroupByUkey, ChunkKind, Compilation, Filename, FilenameTemplate};
@@ -315,6 +314,54 @@ pub enum PublicPath {
315314
Auto,
316315
}
317316

317+
//https://github.com/webpack/webpack/blob/001cab14692eb9a833c6b56709edbab547e291a1/lib/util/identifier.js#L378
318+
pub fn get_undo_path(filename: &str, output_path: String, enforce_relative: bool) -> String {
319+
let mut depth: i32 = -1;
320+
let mut append = String::new();
321+
let mut p = output_path;
322+
if p.ends_with('/') || p.ends_with('\\') {
323+
p.pop();
324+
}
325+
for part in filename.split(&['/', '\\']) {
326+
if part == ".." {
327+
if depth > -1 {
328+
depth -= 1
329+
} else {
330+
let pos = match (p.rfind('/'), p.rfind('\\')) {
331+
(None, None) => {
332+
p.push('/');
333+
return p;
334+
}
335+
(None, Some(j)) => j,
336+
(Some(i), None) => i,
337+
(Some(i), Some(j)) => usize::max(i, j),
338+
};
339+
append = format!("{}/{append}", &p[pos + 1..]);
340+
p = p[0..pos].to_string();
341+
}
342+
} else if part != "." {
343+
depth += 1;
344+
}
345+
}
346+
347+
if depth > 0 {
348+
format!("{}{append}", "../".repeat(depth as usize))
349+
} else if enforce_relative {
350+
format!("./{append}")
351+
} else {
352+
append
353+
}
354+
}
355+
356+
#[test]
357+
fn test_get_undo_path() {
358+
assert_eq!(get_undo_path("a", "/a/b/c".to_string(), true), "./");
359+
assert_eq!(
360+
get_undo_path("static/js/a.js", "/a/b/c".to_string(), false),
361+
"../../"
362+
);
363+
}
364+
318365
impl PublicPath {
319366
pub fn render(&self, compilation: &Compilation, filename: &str) -> String {
320367
match self {
@@ -342,26 +389,7 @@ impl PublicPath {
342389
}
343390

344391
pub fn render_auto_public_path(compilation: &Compilation, filename: &str) -> String {
345-
let public_path = match Utf8Path::new(filename).parent() {
346-
None => String::new(),
347-
Some(dirname) => compilation
348-
.options
349-
.output
350-
.path
351-
.as_std_path()
352-
.relative(
353-
compilation
354-
.options
355-
.output
356-
.path
357-
.join(dirname)
358-
.into_std_path_buf()
359-
.absolutize(),
360-
)
361-
.assert_utf8()
362-
.as_str()
363-
.to_owned(),
364-
};
392+
let public_path = get_undo_path(filename, compilation.options.output.path.to_string(), false);
365393
Self::ensure_ends_with_slash(public_path)
366394
}
367395
}

crates/rspack_plugin_extract_css/src/plugin.rs

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::sync::LazyLock;
2-
use std::{borrow::Cow, cmp::max, hash::Hash, sync::Arc};
2+
use std::{borrow::Cow, hash::Hash, sync::Arc};
33

44
use cow_utils::CowUtils;
55
use regex::Regex;
66
use rspack_cacheable::cacheable;
77
use rspack_collections::{DatabaseItem, IdentifierMap, IdentifierSet, UkeySet};
88
use rspack_core::rspack_sources::{BoxSource, CachedSource, SourceExt};
9+
use rspack_core::{get_undo_path, AssetInfo, ChunkGraph};
910
use rspack_core::{
1011
rspack_sources::{
1112
ConcatSource, RawStringSource, SourceMap, SourceMapSource, WithoutOriginalOptions,
@@ -16,7 +17,6 @@ use rspack_core::{
1617
ModuleIdentifier, ModuleType, NormalModuleFactoryParser, ParserAndGenerator, ParserOptions,
1718
PathData, Plugin, PluginContext, RenderManifestEntry, RuntimeGlobals, SourceType,
1819
};
19-
use rspack_core::{AssetInfo, ChunkGraph};
2020
use rspack_error::{Diagnostic, Result};
2121
use rspack_hash::RspackHash;
2222
use rspack_hook::{plugin, plugin_hook};
@@ -433,7 +433,7 @@ despite it was not able to fulfill desired ordering with these modules:
433433
// different from webpack, add `enforce_relative` to preserve './'
434434
let undo_path = get_undo_path(
435435
filename,
436-
compilation.options.output.path.as_str(),
436+
compilation.options.output.path.to_string(),
437437
self.options.enforce_relative,
438438
);
439439

@@ -721,54 +721,3 @@ impl Plugin for PluginCssExtract {
721721
Ok(())
722722
}
723723
}
724-
725-
#[allow(clippy::unwrap_used)]
726-
fn get_undo_path(filename: &str, output_path: &str, enforce_relative: bool) -> String {
727-
let mut depth: isize = -1;
728-
let mut append = "".into();
729-
730-
let output_path = output_path.strip_suffix('\\').unwrap_or(output_path);
731-
let mut output_path = output_path
732-
.strip_suffix('/')
733-
.unwrap_or(output_path)
734-
.to_string();
735-
736-
static PATH_SEP: LazyLock<Regex> =
737-
LazyLock::new(|| Regex::new(r#"[\\/]+"#).expect("should compile"));
738-
739-
for part in PATH_SEP.split(filename) {
740-
if part == ".." {
741-
if depth > -1 {
742-
depth -= 1;
743-
} else {
744-
let i = output_path.find('/');
745-
let j = output_path.find('\\');
746-
let pos = if i.is_none() {
747-
j
748-
} else if j.is_none() {
749-
i
750-
} else {
751-
max(i, j)
752-
};
753-
754-
if pos.is_none() {
755-
return format!("{output_path}/");
756-
}
757-
758-
append = format!("{}/{append}", &output_path[pos.unwrap() + 1..]);
759-
760-
output_path = output_path[0..pos.unwrap()].to_string();
761-
}
762-
} else if part != "." {
763-
depth += 1;
764-
}
765-
}
766-
767-
if depth > 0 {
768-
format!("{}{append}", "../".repeat(depth as usize))
769-
} else if enforce_relative {
770-
format!("./{append}")
771-
} else {
772-
append
773-
}
774-
}

crates/rspack_plugin_javascript/src/runtime.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use rspack_core::rspack_sources::{
44
BoxSource, ConcatSource, RawStringSource, ReplaceSource, Source, SourceExt,
55
};
66
use rspack_core::{
7-
to_normal_comment, BoxModule, ChunkGraph, ChunkInitFragments, ChunkUkey,
8-
CodeGenerationPublicPathAutoReplace, Compilation, PublicPath, RuntimeGlobals, SourceType,
7+
get_undo_path, to_normal_comment, BoxModule, ChunkGraph, ChunkInitFragments, ChunkUkey,
8+
CodeGenerationPublicPathAutoReplace, Compilation, RuntimeGlobals, SourceType,
99
};
1010
use rspack_error::{error, Result};
1111
use rspack_util::diff_mode::is_diff_mode;
@@ -109,10 +109,11 @@ pub fn render_module(
109109
if !auto_public_path_matches.is_empty() {
110110
let mut replace = ReplaceSource::new(origin_source.clone());
111111
for (start, end) in auto_public_path_matches {
112-
let mut relative = PublicPath::render_auto_public_path(compilation, output_path);
113-
if relative.is_empty() {
114-
relative = String::from("./");
115-
}
112+
let relative = get_undo_path(
113+
output_path,
114+
compilation.options.output.path.to_string(),
115+
true,
116+
);
116117
replace.replace(start as u32, end as u32, &relative, None);
117118
}
118119
RenderSource {

crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use rspack_collections::Identifier;
22
use rspack_core::{
3-
get_js_chunk_filename_template, impl_runtime_module,
3+
get_js_chunk_filename_template, get_undo_path, impl_runtime_module,
44
rspack_sources::{BoxSource, RawStringSource, SourceExt},
55
ChunkUkey, Compilation, OutputOptions, PathData, RuntimeGlobals, RuntimeModule,
66
RuntimeModuleStage, SourceType,
77
};
88

9-
use super::utils::get_undo_path;
10-
119
#[impl_runtime_module]
1210
#[derive(Debug)]
1311
pub struct AutoPublicPathRuntimeModule {

crates/rspack_plugin_runtime/src/runtime_module/utils.rs

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use cow_utils::CowUtils;
22
use itertools::Itertools;
33
use rspack_collections::{UkeyIndexMap, UkeyIndexSet};
44
use rspack_core::{
5-
chunk_graph_chunk::ChunkId, get_js_chunk_filename_template, Chunk, ChunkLoading, ChunkUkey,
6-
Compilation, PathData, SourceType,
5+
chunk_graph_chunk::ChunkId, get_js_chunk_filename_template, get_undo_path, Chunk, ChunkLoading,
6+
ChunkUkey, Compilation, PathData, SourceType,
77
};
88
use rspack_util::test::{
99
HOT_TEST_ACCEPT, HOT_TEST_DISPOSE, HOT_TEST_OUTDATED, HOT_TEST_RUNTIME, HOT_TEST_UPDATED,
@@ -79,44 +79,6 @@ pub fn chunk_has_css(chunk: &ChunkUkey, compilation: &Compilation) -> bool {
7979
.is_empty()
8080
}
8181

82-
pub fn get_undo_path(filename: &str, p: String, enforce_relative: bool) -> String {
83-
let mut depth: i32 = -1;
84-
let mut append = String::new();
85-
let mut p = p;
86-
if p.ends_with('/') || p.ends_with('\\') {
87-
p.pop();
88-
}
89-
for part in filename.split(&['/', '\\']) {
90-
if part == ".." {
91-
if depth > -1 {
92-
depth -= 1
93-
} else {
94-
let pos = match (p.rfind('/'), p.rfind('\\')) {
95-
(None, None) => {
96-
p.push('/');
97-
return p;
98-
}
99-
(None, Some(j)) => j,
100-
(Some(i), None) => i,
101-
(Some(i), Some(j)) => usize::max(i, j),
102-
};
103-
append = format!("{}/{append}", &p[pos + 1..]);
104-
p = p[0..pos].to_string();
105-
}
106-
} else if part != "." {
107-
depth += 1;
108-
}
109-
}
110-
111-
if depth > 0 {
112-
format!("{}{append}", "../".repeat(depth as usize))
113-
} else if enforce_relative {
114-
format!("./{append}")
115-
} else {
116-
append
117-
}
118-
}
119-
12082
pub fn get_output_dir(
12183
chunk: &Chunk,
12284
compilation: &Compilation,
@@ -277,15 +239,6 @@ fn stringify_map<T: std::fmt::Display>(map: &HashMap<&str, T>) -> String {
277239
)
278240
}
279241

280-
#[test]
281-
fn test_get_undo_path() {
282-
assert_eq!(get_undo_path("a", "/a/b/c".to_string(), true), "./");
283-
assert_eq!(
284-
get_undo_path("static/js/a.js", "/a/b/c".to_string(), false),
285-
"../../"
286-
);
287-
}
288-
289242
pub fn generate_javascript_hmr_runtime(method: &str) -> String {
290243
include_str!("runtime/javascript_hot_module_replacement.js")
291244
.cow_replace("$key$", method)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "./style.css";
Lines changed: 18 additions & 0 deletions
Loading
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const { rspack } = require("@rspack/core");
2+
3+
class Plugin {
4+
apply(compiler) {
5+
compiler.hooks.thisCompilation.tap("MyPlugin", compilation => {
6+
compilation.hooks.processAssets.tap(
7+
{
8+
name: "MyPlugin",
9+
stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH
10+
},
11+
() => {
12+
const cssFilename = compilation
13+
.getAssets()
14+
.find(asset => asset.name.endsWith(".css")).name;
15+
16+
compilation.updateAsset(cssFilename, old => {
17+
const oldSource = old.source().toString();
18+
expect(
19+
oldSource.includes("url(../../static/svg/logo.svg)")
20+
).toBeTruthy();
21+
return old;
22+
});
23+
}
24+
);
25+
});
26+
}
27+
}
28+
29+
/** @type {import("@rspack/core").Configuration} */
30+
module.exports = {
31+
entry: "./index.js",
32+
target: "web",
33+
plugins: [new Plugin()],
34+
output: {
35+
cssFilename: "static/initial/index.css"
36+
},
37+
module: {
38+
rules: [
39+
{
40+
test: /\.svg$/,
41+
type: "asset/resource",
42+
generator: {
43+
filename: "static/svg/[name][ext]"
44+
}
45+
}
46+
]
47+
},
48+
experiments: {
49+
css: true
50+
}
51+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.example {
2+
background: url('./logo.svg');
3+
}

0 commit comments

Comments
 (0)