Skip to content

Commit

Permalink
fix: dynamic import can not resolve part of filename tpl (#1224)
Browse files Browse the repository at this point in the history
* fix: dynamic import can not resolve part of filename tpl

* fix: dir without index dont generator require()

* chore: delete code

* fix: use == instead of startWith

* fix: should be extension
  • Loading branch information
Jinbao1001 authored Jun 20, 2024
1 parent 0be15ca commit a35f648
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 28 deletions.
2 changes: 1 addition & 1 deletion crates/mako/src/build/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum LoadError {
CompileMdError { path: String, reason: String },
}

const JS_EXTENSIONS: [&str; 6] = ["js", "jsx", "ts", "tsx", "cjs", "mjs"];
pub const JS_EXTENSIONS: [&str; 6] = ["js", "jsx", "ts", "tsx", "cjs", "mjs"];
const CSS_EXTENSIONS: [&str; 1] = ["css"];
const JSON_EXTENSIONS: [&str; 2] = ["json", "json5"];
const YAML_EXTENSIONS: [&str; 2] = ["yaml", "yml"];
Expand Down
63 changes: 48 additions & 15 deletions crates/mako/src/plugins/context_module.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fs;
use std::path::Path;
use std::sync::Arc;

use mako_core::anyhow::Result;
Expand All @@ -11,6 +13,7 @@ use mako_core::swc_ecma_visit::{VisitMut, VisitMutWith};

use crate::ast::file::{Content, JsContent};
use crate::ast::utils::{is_commonjs_require, is_dynamic_import};
use crate::build::load::JS_EXTENSIONS;
use crate::compiler::Context;
use crate::plugin::{Plugin, PluginLoadParam};
use crate::resolve::get_module_extensions;
Expand Down Expand Up @@ -41,8 +44,14 @@ impl Plugin for ContextModulePlugin {
let rlt_path = path.strip_prefix(&param.file.pathname)?;

// full path `./i18n/zh_CN.json`
let mut keys = vec![format!("./{}", rlt_path.to_string_lossy())];

let mut keys: Vec<String> = vec![];
let metadata = fs::metadata(&path);
if let Ok(md) = metadata {
if md.is_dir() && !has_index_file_in_directory(&path) {
continue;
}
}
keys.push(format!("./{}", rlt_path.to_string_lossy()));
// omit ext `./i18n/zh_CN`
if let Some(ext) = rlt_path.extension() {
if get_module_extensions().contains(&format!(".{}", ext.to_string_lossy())) {
Expand Down Expand Up @@ -219,23 +228,37 @@ fn try_replace_context_arg(

// handle prefix of `'./foo/' + bar + '.ext'`
Expr::Lit(Lit::Str(str)) => {
let prefix = str.value.to_string();

let mut prefix = str.value.to_string();
// replace first str with relative prefix
str.value = get_relative_prefix(prefix.clone()).into();
let (pre_quasis, remainder) = if let Some(pos) = prefix.rfind('/') {
(prefix[..=pos].to_string(), prefix[pos + 1..].to_string())
} else {
(prefix.clone(), String::new())
};
str.value = format!("./{}", remainder).into();
str.raw = None;

if !prefix.ends_with('/') {
prefix = pre_quasis;
}
Some((prefix, None))
}

// handle `./foo/${bar}.ext`
Expr::Tpl(tpl) => {
if !tpl.exprs.is_empty() {
let prefix = tpl.quasis.first().unwrap().raw.to_string();
let pre_quasis = tpl.quasis.first().unwrap().raw.to_string();
let (prefix, remainder) = if let Some(pos) = pre_quasis.rfind('/') {
(
pre_quasis[..=pos].to_string(),
pre_quasis[pos + 1..].to_string(),
)
} else {
(pre_quasis.clone(), String::new())
};
let mut suffix = None;

// replace first quasi with relative prefix
tpl.quasis[0].raw = get_relative_prefix(tpl.quasis[0].raw.to_string()).into();
tpl.quasis[0].raw = format!("./{}", remainder).into();
tpl.quasis[0].cooked = None;

// extract suffix
Expand All @@ -244,7 +267,6 @@ fn try_replace_context_arg(
suffix = Some(raw.to_string());
}
}

Some((prefix, suffix))
} else {
None
Expand All @@ -255,10 +277,21 @@ fn try_replace_context_arg(
}
}

fn get_relative_prefix(path: String) -> String {
if path.ends_with('/') {
"./".to_string()
} else {
".".to_string()
}
fn has_index_file_in_directory(dir_path: &Path) -> bool {
fs::read_dir(dir_path)
.map(|entries| {
entries.filter_map(Result::ok).any(|entry| {
let path = entry.path();
path.is_file()
&& path
.file_stem()
.and_then(|n| n.to_str())
.map_or(false, |fname| fname == "index")
&& path
.extension()
.and_then(|e| e.to_str())
.map_or(false, |extension: &str| JS_EXTENSIONS.contains(&extension))
})
})
.unwrap_or(false)
}
41 changes: 32 additions & 9 deletions e2e/fixtures/javascript.require-dynamic/expect.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,28 @@ const asyncContent = names.filter((name) => name.startsWith("src_i18n_")).reduce
assert.match(
content,
moduleReg(
"src/i18n\\?context&glob=\\*\\*/\\*",
"src\\?context&glob=\\*\\*/\\*",
[
"'./zh-CN.json': ()=>__mako_require__(\"src/i18n/zh-CN.json\")",
"'./zh-CN': ()=>__mako_require__(\"src/i18n/zh-CN.json\")",
"'./ext/json.ts': ()=>__mako_require__(\"src/ext/json.ts\")",
"'./ext/json': ()=>__mako_require__(\"src/ext/json.ts\")",
"'./fake.js': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./fake': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./fake.js/a.js': ()=>__mako_require__(\"src/fake.js/a.js\")",
"'./fake.js/a': ()=>__mako_require__(\"src/fake.js/a.js\")",
"'./fake.js/aa.js': ()=>__mako_require__(\"src/fake.js/aa.js\")",
"'./fake.js/aa': ()=>__mako_require__(\"src/fake.js/aa.js\")",
"'./fake.js/index.js': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./fake.js/index': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./fake.js': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./fake.js/': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./i18n/en-US.json': ()=>__mako_require__(\"src/i18n/en-US.json\")",
"'./i18n/en-US': ()=>__mako_require__(\"src/i18n/en-US.json\")",
"'./i18n/zh-CN.json': ()=>__mako_require__(\"src/i18n/zh-CN.json\")",
"'./i18n/zh-CN': ()=>__mako_require__(\"src/i18n/zh-CN.json\")",
"'./index.ts': ()=>__mako_require__(\"src/index.ts\")",
"'./index': ()=>__mako_require__(\"src/index.ts\")",
"'.': ()=>__mako_require__(\"src/index.ts\")",
"'./': ()=>__mako_require__(\"src/index.ts\")"
].join(',\n\\s+'),
true,
),
Expand All @@ -24,7 +42,10 @@ assert.match(
moduleReg(
"src/fake.js\\?context&glob=\\*\\*/\\*",
[
"'./a.js': ()=>__mako_require__(\"src/fake.js/a.js\")",
"'./a': ()=>__mako_require__(\"src/fake.js/a.js\")",
"'./aa.js': ()=>__mako_require__(\"src/fake.js/aa.js\")",
"'./aa': ()=>__mako_require__(\"src/fake.js/aa.js\")",
"'./index.js': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'./index': ()=>__mako_require__(\"src/fake.js/index.js\")",
"'.': ()=>__mako_require__(\"src/fake.js/index.js\")",
Expand Down Expand Up @@ -79,22 +100,24 @@ assert.match(

assert.match(
content,
moduleReg("src/index.ts", '__mako_require__("src/i18n\\?context&glob=\\*\\*/\\*")', true),
"should generate sync require for require dynamic module",
moduleReg("src/index.ts", "`./zh-\\${lang}.json`", true),
"prefix should match the last one '/' ",
);


assert.match(
content,
moduleReg("src/index.ts", '__mako_require__("src/ext\\?context&glob=\\*\\*/\\*")', true),
"should generate nested sync require in dynamic require/import args",
moduleReg("src/index.ts", '__mako_require__("src\\?context&glob=\\*\\*/\\*")', true),
"should generate sync require for require dynamic module",
);

assert.match(
content,
moduleReg("src/index.ts", '"." \\+ file', true),
"should replace bin left string @/i18n with .",
moduleReg("src/index.ts", '__mako_require__("src/ext\\?context&glob=\\*\\*/\\*")', true),
"should generate nested sync require in dynamic require/import args",
);


assert.doesNotMatch(
content,
// /*.../glob=**/**/ should be escaped to /*.../glob=**\/**/
Expand Down
2 changes: 1 addition & 1 deletion e2e/fixtures/javascript.require-dynamic/src/fake.js/a.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default 1;
export default 'a';
1 change: 1 addition & 0 deletions e2e/fixtures/javascript.require-dynamic/src/fake.js/aa.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'aa';
17 changes: 15 additions & 2 deletions e2e/fixtures/javascript.require-dynamic/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,35 @@ function loadLang(lang) {
return import(`./i18n/${lang}.json`);
}

function loadLang2(lang) {
return import(`./i18n/zh-${lang}.json`);
}

function loadLangExt(lang, ext) {
// nested dynamic require + with then callback
return import(`./i18n/${lang}.${(require(`./ext/${ext}`)).default}`)

.then(m => m);
}


function loadFile(file) {
return require('@/i18n' + file);
}

function loadFile2(file) {
return require('./fake.js/' + file);
return require('./fake.js' + file);
}

function loadFile3(file) {
return require('./fake.js/a' + file);
}
loadLang('zh-CN').then(console.log);
loadLang2('CN').then(console.log);

loadLangExt('zh-CN', 'json').then(console.log);

console.log(loadFile('/zh-CN.json'));
console.log(loadFile2('a.js'));
console.log(loadFile2('/a.js'));
console.log(loadFile3('a.js'));

0 comments on commit a35f648

Please sign in to comment.