Skip to content

Commit

Permalink
fix: less/sass url rebase and url publicPath (#1071)
Browse files Browse the repository at this point in the history
* chore: stash code

* chore: use deepmerge to merge config

* chore: support url rebase for sass and less js plugins

* chore: add rust plugin example

* chore: support rust plugin sass rebase urls

* feat: Support alias resolve and url rebase for import.meta.glob, sass and less plugins

---------

Co-authored-by: brightwwu <[email protected]>
  • Loading branch information
wre232114 and brightwwu authored Mar 28, 2024
1 parent 63abace commit cb7df71
Show file tree
Hide file tree
Showing 77 changed files with 1,497 additions and 260 deletions.
9 changes: 9 additions & 0 deletions .changeset/new-ravens-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@farmfe/js-plugin-postcss': patch
'@farmfe/plugin-sass': patch
'@farmfe/js-plugin-less': patch
'@farmfe/js-plugin-sass': patch
'@farmfe/core': patch
---

Support alias resolve and url rebase for import.meta.glob, sass and less plugins
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions configs/farm-js-plugin.base.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function createFarmJsPluginBuildConfig(plugins, options = {}) {
format
},
external: [
'@farmfe/core',
...builtinModules.map((m) => `^${m}$`),
...builtinModules.map((m) => `^node:${m}$`),
...(options.external || [])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,23 @@ fn handle_enforce_resource_pots(
let mut un_enforced_modules = HashSet::new();
let mut affected_resource_pot_ids = HashSet::new();

let is_module_external = |module_id: &ModuleId| {
let module = if let Some(module) = removed_modules.get(module_id) {
module
} else {
module_graph.module(module_id).unwrap()
};

module.external
};

let mut handle_changed_modules = |module_ids: &HashSet<ModuleId>, ty: ChangedModuleType| {
for module_id in module_ids {
// ignore external module
if is_module_external(module_id) {
continue;
}

if let Some(name) = get_enforce_resource_name_for_module(
module_id,
&context.config.partial_bundling.enforce_resources,
Expand Down Expand Up @@ -152,6 +167,10 @@ fn handle_enforce_resource_pots(

// Filter out the modules that are not in any enforce resource pot
for module_id in affected_modules {
if is_module_external(module_id) {
continue;
}

if let Some(name) = get_enforce_resource_name_for_module(
module_id,
&context.config.partial_bundling.enforce_resources,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/cache/cache_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{

use crate::config::Mode;

const FARM_CACHE_VERSION: &str = "0.3.1";
const FARM_CACHE_VERSION: &str = "0.3.2";
const FARM_CACHE_MANIFEST_FILE: &str = "farm-cache.json";

// TODO make CacheStore a trait and implement DiskCacheStore or RemoteCacheStore or more.
Expand Down
3 changes: 2 additions & 1 deletion crates/plugin_css/src/dep_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ impl Visit for DepAnalyzer {
}

pub fn is_source_ignored(source: &str) -> bool {
source.starts_with("http")
source.starts_with("http://")
|| source.starts_with("https://")
|| source.starts_with('/')
|| source.starts_with("data:")
|| source.starts_with('#')
Expand Down
5 changes: 4 additions & 1 deletion crates/plugin_css/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ impl Plugin for FarmPluginCss {
&module.id,
&module_graph,
&resources_map,
context.config.output.public_path.clone(),
);

if minify_enabled {
Expand Down Expand Up @@ -768,7 +769,9 @@ pub fn source_replace(
module_id: &ModuleId,
module_graph: &ModuleGraph,
resources_map: &HashMap<String, Resource>,
public_path: String,
) {
let mut source_replacer = SourceReplacer::new(module_id.clone(), module_graph, resources_map);
let mut source_replacer =
SourceReplacer::new(module_id.clone(), module_graph, resources_map, public_path);
stylesheet.visit_mut_with(&mut source_replacer);
}
17 changes: 16 additions & 1 deletion crates/plugin_css/src/source_replacer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ pub struct SourceReplacer<'a> {
module_id: ModuleId,
module_graph: &'a ModuleGraph,
resources_map: &'a HashMap<String, Resource>,
public_path: String,
}

impl<'a> SourceReplacer<'a> {
pub fn new(
module_id: ModuleId,
module_graph: &'a ModuleGraph,
resources_map: &'a HashMap<String, Resource>,
public_path: String,
) -> Self {
Self {
module_id,
module_graph,
resources_map,
public_path,
}
}
}
Expand All @@ -45,7 +48,19 @@ impl<'a> VisitMut for SourceReplacer<'a> {
for resource in self.resources_map.values() {
if let ResourceOrigin::Module(m_id) = &resource.origin {
if &dep_module == m_id {
return format!("/{}", resource.name);
// fix #1076. url prefixed by publicPath
let normalized_public_path = if self.public_path.is_empty() {
""
} else {
self.public_path.trim_matches('/')
};
let normalized_public_path = if normalized_public_path.is_empty() {
"/".to_string()
} else {
format!("/{normalized_public_path}/")
};

return format!("{normalized_public_path}{}", resource.name);
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion crates/plugin_css/src/transform_css_to_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@ pub fn transform_css_stylesheet(
};

let resources_map = context.resources_map.lock();
source_replace(&mut stylesheet, module_id, &module_graph, &resources_map);
source_replace(
&mut stylesheet,
module_id,
&module_graph,
&resources_map,
context.config.output.public_path.clone(),
);

stylesheet
}
Expand Down
5 changes: 4 additions & 1 deletion crates/plugin_resolve/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ impl Resolver {
};

if let Some(result) = self.resolve_cache.lock().get(&cache_key) {
return result.clone();
// None result should not be cached
if let Some(result) = result {
return Some(result.clone());
}
}

let result = self._resolve(source, base_dir, kind, context);
Expand Down
7 changes: 6 additions & 1 deletion crates/plugin_script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,12 @@ impl Plugin for FarmPluginScript {
.to_string_lossy()
.to_string()
};
transform_import_meta_glob(ast, context.config.root.clone(), cur_dir)?;
transform_import_meta_glob(
ast,
context.config.root.clone(),
cur_dir,
&context.config.resolve.alias,
)?;
}

Ok(Some(()))
Expand Down
77 changes: 70 additions & 7 deletions crates/swc_transformer_import_glob/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::collections::HashMap;
use std::path::Component;
use std::path::PathBuf;

use farmfe_core::regex;
use farmfe_core::relative_path::RelativePath;
use farmfe_core::swc_common::DUMMY_SP;
use farmfe_core::swc_ecma_ast::{
Expand All @@ -30,12 +31,15 @@ use farmfe_core::wax::Glob;
use farmfe_toolkit::swc_ecma_visit::{VisitMut, VisitMutWith};
use farmfe_utils::relative;

const REGEX_PREFIX: &str = "$__farm_regex:";

pub fn transform_import_meta_glob(
ast: &mut SwcModule,
root: String,
cur_dir: String,
alias: &HashMap<String, String>,
) -> farmfe_core::error::Result<()> {
let mut visitor = ImportGlobVisitor::new(cur_dir, root);
let mut visitor = ImportGlobVisitor::new(cur_dir, root, alias);
ast.visit_mut_with(&mut visitor);

if visitor.errors.len() > 0 {
Expand Down Expand Up @@ -220,20 +224,22 @@ pub struct ImportMetaGlobInfo {
pub query: Option<String>,
}

pub struct ImportGlobVisitor {
pub struct ImportGlobVisitor<'a> {
import_globs: Vec<ImportMetaGlobInfo>,
cur_dir: String,
root: String,
alias: &'a HashMap<String, String>,
pub errors: Vec<String>,
}

impl ImportGlobVisitor {
pub fn new(cur_dir: String, root: String) -> Self {
impl<'a> ImportGlobVisitor<'a> {
pub fn new(cur_dir: String, root: String, alias: &'a HashMap<String, String>) -> Self {
Self {
import_globs: vec![],
cur_dir,
root,
errors: vec![],
alias,
}
}

Expand Down Expand Up @@ -275,6 +281,56 @@ impl ImportGlobVisitor {
import_glob_info
}

fn try_alias(&self, source: &str) -> String {
let (source, negative) = if source.starts_with('!') {
(&source[1..], true)
} else {
(source, false)
};
let mut result = source.to_string();
// sort the alias by length, so that the longest alias will be matched first
let mut alias_list: Vec<_> = self.alias.keys().collect();
alias_list.sort_by(|a, b| b.len().cmp(&a.len()));

for alias in alias_list {
let replaced = self.alias.get(alias).unwrap();

// try regex alias first
if let Some(alias) = alias.strip_prefix(REGEX_PREFIX) {
let regex = regex::Regex::new(alias).unwrap();
if regex.is_match(source) {
let replaced = regex.replace(source, replaced.as_str()).to_string();
result = replaced;
break;
}
}

if alias.ends_with('$') && source == alias.trim_end_matches('$') {
result = replaced.to_string();
break;
} else if !alias.ends_with('$') && source.starts_with(alias) {
let source_left = RelativePath::new(source.trim_start_matches(alias));
let new_source = source_left.to_logical_path(replaced);

result = if new_source.is_absolute() {
format!(
"/{}",
relative(&self.root, &new_source.to_string_lossy().to_string())
)
} else {
new_source.to_string_lossy().to_string()
};
break;
}
}

if negative {
format!("!{}", result)
} else {
result
}
}

/// Glob the sources and filter negative sources, return globs relative paths
fn glob_and_filter_sources(&mut self, sources: &Vec<String>) -> HashMap<String, String> {
let mut paths = vec![];
Expand All @@ -288,6 +344,7 @@ impl ImportGlobVisitor {
} else {
&source[..]
};

let source = if !source.starts_with('.') && !source.starts_with('/') {
format!("./{}", source)
} else {
Expand Down Expand Up @@ -520,7 +577,7 @@ impl ImportGlobVisitor {
}
}

impl VisitMut for ImportGlobVisitor {
impl<'a> VisitMut for ImportGlobVisitor<'a> {
fn visit_mut_expr(&mut self, expr: &mut Expr) {
match expr {
Expr::Call(CallExpr {
Expand All @@ -538,8 +595,12 @@ impl VisitMut for ImportGlobVisitor {
..
}) => {
if *sym == *"glob" && !args.is_empty() {
if let Some(sources) = get_string_literal(&args[0]) {
for source in &sources {
if let Some(original_sources) = get_string_literal(&args[0]) {
let mut sources = vec![];

for source in original_sources {
let source = self.try_alias(&source);

if !source.starts_with('.')
&& !source.starts_with('/')
&& !source.starts_with('!')
Expand All @@ -550,6 +611,8 @@ impl VisitMut for ImportGlobVisitor {
.push(format!("Error when glob {source}: source must be relative path. e.g. './dir/*.js' or '/dir/*.js'(relative to root) or '!/dir/*.js'(exclude) or '!**/bar.js'(exclude) or '**/*.js'(relative to current dir)"));
return;
}

sources.push(source);
}

let cur_index = self.import_globs.len();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'bar';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'foo';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const modules = {
"/dir/bar.js": ()=>import("./dir/bar.js")
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const modules = import.meta.glob(['@/*.js', '!@/foo.js'])
19 changes: 17 additions & 2 deletions crates/swc_transformer_import_glob/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};

use farmfe_core::{swc_common::SourceMap, swc_ecma_ast::EsVersion, swc_ecma_parser::Syntax};
use farmfe_swc_transformer_import_glob::transform_import_meta_glob;
Expand All @@ -25,7 +25,22 @@ fn test_import_meta_glob() {
dir
};

transform_import_meta_glob(&mut ast, root.to_string(), dir.to_string()).unwrap();
transform_import_meta_glob(
&mut ast,
root.to_string(),
dir.to_string(),
&HashMap::from([(
"@".to_string(),
file
.parent()
.unwrap()
.to_path_buf()
.join("dir")
.to_string_lossy()
.to_string(),
)]),
)
.unwrap();

let code = codegen_module(&ast, EsVersion::EsNext, cm, None, false, None).unwrap();
let code = String::from_utf8(code).unwrap();
Expand Down
4 changes: 3 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@
"srcset",
"shulandmimi",
"concurrentify",
"pageerror"
"pageerror",
"deepmerge",
"Mergeable"
],
"ignorePaths": [
"pnpm-lock.yaml",
Expand Down
Loading

0 comments on commit cb7df71

Please sign in to comment.