mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
feat(linter): implement n/no-path-concat rule (#19502)
this PR adds `n/no-path-concat` rule, passes all [test cases](https://github.com/eslint-community/eslint-plugin-n/blob/master/tests/lib/rules/no-path-concat.js) from original rule issue #493 --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Cameron Clark <cameron.clark@hey.com>
This commit is contained in:
5 files changed
+332
-1
No files matched your search
@@ -4274,6 +4274,12 @@ impl RuleRunner for crate::rules::node::no_new_require::NoNewRequire {
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
|
||||
}
|
||||
|
||||
impl RuleRunner for crate::rules::node::no_path_concat::NoPathConcat {
|
||||
const NODE_TYPES: Option<&AstTypesBitset> =
|
||||
Some(&AstTypesBitset::from_types(&[AstType::BinaryExpression, AstType::TemplateLiteral]));
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
|
||||
}
|
||||
|
||||
impl RuleRunner for crate::rules::node::no_process_env::NoProcessEnv {
|
||||
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
|
||||
AstType::ComputedMemberExpression,
|
||||
|
||||
+25
-1
@@ -336,6 +336,7 @@ pub use crate::rules::nextjs::no_unwanted_polyfillio::NoUnwantedPolyfillio as Ne
|
||||
pub use crate::rules::node::global_require::GlobalRequire as NodeGlobalRequire;
|
||||
pub use crate::rules::node::no_exports_assign::NoExportsAssign as NodeNoExportsAssign;
|
||||
pub use crate::rules::node::no_new_require::NoNewRequire as NodeNoNewRequire;
|
||||
pub use crate::rules::node::no_path_concat::NoPathConcat as NodeNoPathConcat;
|
||||
pub use crate::rules::node::no_process_env::NoProcessEnv as NodeNoProcessEnv;
|
||||
pub use crate::rules::oxc::approx_constant::ApproxConstant as OxcApproxConstant;
|
||||
pub use crate::rules::oxc::bad_array_method_on_arguments::BadArrayMethodOnArguments as OxcBadArrayMethodOnArguments;
|
||||
@@ -1379,6 +1380,7 @@ pub enum RuleEnum {
|
||||
NodeGlobalRequire(NodeGlobalRequire),
|
||||
NodeNoExportsAssign(NodeNoExportsAssign),
|
||||
NodeNoNewRequire(NodeNoNewRequire),
|
||||
NodeNoPathConcat(NodeNoPathConcat),
|
||||
NodeNoProcessEnv(NodeNoProcessEnv),
|
||||
VueDefineEmitsDeclaration(VueDefineEmitsDeclaration),
|
||||
VueDefinePropsDeclaration(VueDefinePropsDeclaration),
|
||||
@@ -2152,7 +2154,8 @@ const VITEST_WARN_TODO_ID: usize =
|
||||
const NODE_GLOBAL_REQUIRE_ID: usize = VITEST_WARN_TODO_ID + 1usize;
|
||||
const NODE_NO_EXPORTS_ASSIGN_ID: usize = NODE_GLOBAL_REQUIRE_ID + 1usize;
|
||||
const NODE_NO_NEW_REQUIRE_ID: usize = NODE_NO_EXPORTS_ASSIGN_ID + 1usize;
|
||||
const NODE_NO_PROCESS_ENV_ID: usize = NODE_NO_NEW_REQUIRE_ID + 1usize;
|
||||
const NODE_NO_PATH_CONCAT_ID: usize = NODE_NO_NEW_REQUIRE_ID + 1usize;
|
||||
const NODE_NO_PROCESS_ENV_ID: usize = NODE_NO_PATH_CONCAT_ID + 1usize;
|
||||
const VUE_DEFINE_EMITS_DECLARATION_ID: usize = NODE_NO_PROCESS_ENV_ID + 1usize;
|
||||
const VUE_DEFINE_PROPS_DECLARATION_ID: usize = VUE_DEFINE_EMITS_DECLARATION_ID + 1usize;
|
||||
const VUE_DEFINE_PROPS_DESTRUCTURING_ID: usize = VUE_DEFINE_PROPS_DECLARATION_ID + 1usize;
|
||||
@@ -2949,6 +2952,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NODE_GLOBAL_REQUIRE_ID,
|
||||
Self::NodeNoExportsAssign(_) => NODE_NO_EXPORTS_ASSIGN_ID,
|
||||
Self::NodeNoNewRequire(_) => NODE_NO_NEW_REQUIRE_ID,
|
||||
Self::NodeNoPathConcat(_) => NODE_NO_PATH_CONCAT_ID,
|
||||
Self::NodeNoProcessEnv(_) => NODE_NO_PROCESS_ENV_ID,
|
||||
Self::VueDefineEmitsDeclaration(_) => VUE_DEFINE_EMITS_DECLARATION_ID,
|
||||
Self::VueDefinePropsDeclaration(_) => VUE_DEFINE_PROPS_DECLARATION_ID,
|
||||
@@ -3735,6 +3739,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NodeGlobalRequire::NAME,
|
||||
Self::NodeNoExportsAssign(_) => NodeNoExportsAssign::NAME,
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::NAME,
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::NAME,
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::NAME,
|
||||
Self::VueDefineEmitsDeclaration(_) => VueDefineEmitsDeclaration::NAME,
|
||||
Self::VueDefinePropsDeclaration(_) => VueDefinePropsDeclaration::NAME,
|
||||
@@ -4565,6 +4570,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NodeGlobalRequire::CATEGORY,
|
||||
Self::NodeNoExportsAssign(_) => NodeNoExportsAssign::CATEGORY,
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::CATEGORY,
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::CATEGORY,
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::CATEGORY,
|
||||
Self::VueDefineEmitsDeclaration(_) => VueDefineEmitsDeclaration::CATEGORY,
|
||||
Self::VueDefinePropsDeclaration(_) => VueDefinePropsDeclaration::CATEGORY,
|
||||
@@ -5354,6 +5360,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NodeGlobalRequire::FIX,
|
||||
Self::NodeNoExportsAssign(_) => NodeNoExportsAssign::FIX,
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::FIX,
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::FIX,
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::FIX,
|
||||
Self::VueDefineEmitsDeclaration(_) => VueDefineEmitsDeclaration::FIX,
|
||||
Self::VueDefinePropsDeclaration(_) => VueDefinePropsDeclaration::FIX,
|
||||
@@ -6333,6 +6340,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NodeGlobalRequire::documentation(),
|
||||
Self::NodeNoExportsAssign(_) => NodeNoExportsAssign::documentation(),
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::documentation(),
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::documentation(),
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::documentation(),
|
||||
Self::VueDefineEmitsDeclaration(_) => VueDefineEmitsDeclaration::documentation(),
|
||||
Self::VueDefinePropsDeclaration(_) => VueDefinePropsDeclaration::documentation(),
|
||||
@@ -8259,6 +8267,8 @@ impl RuleEnum {
|
||||
.or_else(|| NodeNoExportsAssign::schema(generator)),
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::config_schema(generator)
|
||||
.or_else(|| NodeNoNewRequire::schema(generator)),
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::config_schema(generator)
|
||||
.or_else(|| NodeNoPathConcat::schema(generator)),
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::config_schema(generator)
|
||||
.or_else(|| NodeNoProcessEnv::schema(generator)),
|
||||
Self::VueDefineEmitsDeclaration(_) => {
|
||||
@@ -8986,6 +8996,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => "node",
|
||||
Self::NodeNoExportsAssign(_) => "node",
|
||||
Self::NodeNoNewRequire(_) => "node",
|
||||
Self::NodeNoPathConcat(_) => "node",
|
||||
Self::NodeNoProcessEnv(_) => "node",
|
||||
Self::VueDefineEmitsDeclaration(_) => "vue",
|
||||
Self::VueDefinePropsDeclaration(_) => "vue",
|
||||
@@ -11163,6 +11174,9 @@ impl RuleEnum {
|
||||
Self::NodeNoNewRequire(_) => {
|
||||
Ok(Self::NodeNoNewRequire(NodeNoNewRequire::from_configuration(value)?))
|
||||
}
|
||||
Self::NodeNoPathConcat(_) => {
|
||||
Ok(Self::NodeNoPathConcat(NodeNoPathConcat::from_configuration(value)?))
|
||||
}
|
||||
Self::NodeNoProcessEnv(_) => {
|
||||
Ok(Self::NodeNoProcessEnv(NodeNoProcessEnv::from_configuration(value)?))
|
||||
}
|
||||
@@ -11895,6 +11909,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.to_configuration(),
|
||||
Self::NodeNoExportsAssign(rule) => rule.to_configuration(),
|
||||
Self::NodeNoNewRequire(rule) => rule.to_configuration(),
|
||||
Self::NodeNoPathConcat(rule) => rule.to_configuration(),
|
||||
Self::NodeNoProcessEnv(rule) => rule.to_configuration(),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.to_configuration(),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.to_configuration(),
|
||||
@@ -12587,6 +12602,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.run(node, ctx),
|
||||
Self::NodeNoExportsAssign(rule) => rule.run(node, ctx),
|
||||
Self::NodeNoNewRequire(rule) => rule.run(node, ctx),
|
||||
Self::NodeNoPathConcat(rule) => rule.run(node, ctx),
|
||||
Self::NodeNoProcessEnv(rule) => rule.run(node, ctx),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.run(node, ctx),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.run(node, ctx),
|
||||
@@ -13279,6 +13295,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.run_once(ctx),
|
||||
Self::NodeNoExportsAssign(rule) => rule.run_once(ctx),
|
||||
Self::NodeNoNewRequire(rule) => rule.run_once(ctx),
|
||||
Self::NodeNoPathConcat(rule) => rule.run_once(ctx),
|
||||
Self::NodeNoProcessEnv(rule) => rule.run_once(ctx),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.run_once(ctx),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.run_once(ctx),
|
||||
@@ -14069,6 +14086,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
Self::NodeNoExportsAssign(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
Self::NodeNoNewRequire(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
Self::NodeNoPathConcat(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
Self::NodeNoProcessEnv(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.run_on_jest_node(jest_node, ctx),
|
||||
@@ -14761,6 +14779,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.should_run(ctx),
|
||||
Self::NodeNoExportsAssign(rule) => rule.should_run(ctx),
|
||||
Self::NodeNoNewRequire(rule) => rule.should_run(ctx),
|
||||
Self::NodeNoPathConcat(rule) => rule.should_run(ctx),
|
||||
Self::NodeNoProcessEnv(rule) => rule.should_run(ctx),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.should_run(ctx),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.should_run(ctx),
|
||||
@@ -15739,6 +15758,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NodeGlobalRequire::IS_TSGOLINT_RULE,
|
||||
Self::NodeNoExportsAssign(_) => NodeNoExportsAssign::IS_TSGOLINT_RULE,
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::IS_TSGOLINT_RULE,
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::IS_TSGOLINT_RULE,
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::IS_TSGOLINT_RULE,
|
||||
Self::VueDefineEmitsDeclaration(_) => VueDefineEmitsDeclaration::IS_TSGOLINT_RULE,
|
||||
Self::VueDefinePropsDeclaration(_) => VueDefinePropsDeclaration::IS_TSGOLINT_RULE,
|
||||
@@ -16598,6 +16618,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(_) => NodeGlobalRequire::HAS_CONFIG,
|
||||
Self::NodeNoExportsAssign(_) => NodeNoExportsAssign::HAS_CONFIG,
|
||||
Self::NodeNoNewRequire(_) => NodeNoNewRequire::HAS_CONFIG,
|
||||
Self::NodeNoPathConcat(_) => NodeNoPathConcat::HAS_CONFIG,
|
||||
Self::NodeNoProcessEnv(_) => NodeNoProcessEnv::HAS_CONFIG,
|
||||
Self::VueDefineEmitsDeclaration(_) => VueDefineEmitsDeclaration::HAS_CONFIG,
|
||||
Self::VueDefinePropsDeclaration(_) => VueDefinePropsDeclaration::HAS_CONFIG,
|
||||
@@ -17292,6 +17313,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.types_info(),
|
||||
Self::NodeNoExportsAssign(rule) => rule.types_info(),
|
||||
Self::NodeNoNewRequire(rule) => rule.types_info(),
|
||||
Self::NodeNoPathConcat(rule) => rule.types_info(),
|
||||
Self::NodeNoProcessEnv(rule) => rule.types_info(),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.types_info(),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.types_info(),
|
||||
@@ -17984,6 +18006,7 @@ impl RuleEnum {
|
||||
Self::NodeGlobalRequire(rule) => rule.run_info(),
|
||||
Self::NodeNoExportsAssign(rule) => rule.run_info(),
|
||||
Self::NodeNoNewRequire(rule) => rule.run_info(),
|
||||
Self::NodeNoPathConcat(rule) => rule.run_info(),
|
||||
Self::NodeNoProcessEnv(rule) => rule.run_info(),
|
||||
Self::VueDefineEmitsDeclaration(rule) => rule.run_info(),
|
||||
Self::VueDefinePropsDeclaration(rule) => rule.run_info(),
|
||||
@@ -18792,6 +18815,7 @@ pub static RULES: std::sync::LazyLock<Vec<RuleEnum>> = std::sync::LazyLock::new(
|
||||
RuleEnum::NodeGlobalRequire(NodeGlobalRequire::default()),
|
||||
RuleEnum::NodeNoExportsAssign(NodeNoExportsAssign::default()),
|
||||
RuleEnum::NodeNoNewRequire(NodeNoNewRequire::default()),
|
||||
RuleEnum::NodeNoPathConcat(NodeNoPathConcat::default()),
|
||||
RuleEnum::NodeNoProcessEnv(NodeNoProcessEnv::default()),
|
||||
RuleEnum::VueDefineEmitsDeclaration(VueDefineEmitsDeclaration::default()),
|
||||
RuleEnum::VueDefinePropsDeclaration(VueDefinePropsDeclaration::default()),
|
||||
|
||||
@@ -715,6 +715,7 @@ pub(crate) mod node {
|
||||
pub mod global_require;
|
||||
pub mod no_exports_assign;
|
||||
pub mod no_new_require;
|
||||
pub mod no_path_concat;
|
||||
pub mod no_process_env;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
use oxc_ast::{
|
||||
AstKind,
|
||||
ast::{Expression, TemplateLiteral},
|
||||
};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::IsGlobalReference;
|
||||
use oxc_span::{Ident, Span};
|
||||
use oxc_syntax::operator::BinaryOperator;
|
||||
|
||||
use crate::{AstNode, context::LintContext, rule::Rule};
|
||||
|
||||
fn no_path_concat_diagnostic(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Use `path.join()` or `path.resolve()` instead of string concatenation")
|
||||
.with_help("Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.")
|
||||
.with_label(span)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoPathConcat;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// Disallows string concatenation with `__dirname` and `__filename`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// In Node.js, the `__dirname` and `__filename` global variables contain the directory path and the file path of the currently executing script file, respectively.
|
||||
/// Sometimes, developers try to use these variables to create paths to other files, such as:
|
||||
///
|
||||
/// ```js
|
||||
/// var fullPath = __dirname + "/foo.js";
|
||||
/// ```
|
||||
///
|
||||
/// However, this is error-prone because it doesn't account for different
|
||||
/// operating systems, which use different path separators. Using `path.join()`
|
||||
/// or `path.resolve()` is the proper way to create cross-platform file paths.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// Examples of **incorrect** code for this rule:
|
||||
/// ```js
|
||||
/// const fullPath1 = __dirname + "/foo.js";
|
||||
/// const fullPath2 = __filename + "/foo.js";
|
||||
/// const fullPath3 = `${__dirname}/foo.js`;
|
||||
/// const fullPath4 = `${__filename}/foo.js`;
|
||||
/// ```
|
||||
///
|
||||
/// Examples of **correct** code for this rule:
|
||||
/// ```js
|
||||
/// const fullPath1 = path.join(__dirname, "foo.js");
|
||||
/// const fullPath2 = path.join(__filename, "foo.js");
|
||||
/// const fullPath3 = __dirname + ".js";
|
||||
/// const fullPath4 = __filename + ".map";
|
||||
/// const fullPath5 = `${__dirname}_foo.js`;
|
||||
/// const fullPath6 = `${__filename}.test.js`;
|
||||
/// ```
|
||||
NoPathConcat,
|
||||
node,
|
||||
restriction
|
||||
);
|
||||
|
||||
impl Rule for NoPathConcat {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
match node.kind() {
|
||||
AstKind::BinaryExpression(bin_expr) => {
|
||||
if bin_expr.operator != BinaryOperator::Addition {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_dirname_or_filename(&bin_expr.left, ctx)
|
||||
&& starts_with_path_separator(&bin_expr.right)
|
||||
{
|
||||
ctx.diagnostic(no_path_concat_diagnostic(bin_expr.span));
|
||||
}
|
||||
}
|
||||
AstKind::TemplateLiteral(temp_lit) => {
|
||||
for (i, expr) in temp_lit.expressions.iter().enumerate() {
|
||||
if is_dirname_or_filename(expr, ctx)
|
||||
&& template_element_starts_with_path_separator(temp_lit, i + 1)
|
||||
{
|
||||
ctx.diagnostic(no_path_concat_diagnostic(temp_lit.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_path_sep(expr: &Expression) -> bool {
|
||||
expr.is_specific_member_access("path", "sep")
|
||||
}
|
||||
|
||||
fn is_path_separator(c: char) -> bool {
|
||||
c == '/' || c == '\\'
|
||||
}
|
||||
|
||||
fn is_dirname_or_filename(expr: &Expression, ctx: &LintContext) -> bool {
|
||||
let Expression::Identifier(ident) = expr else {
|
||||
return false;
|
||||
};
|
||||
ident.is_global_reference_name(Ident::new_const("__dirname"), ctx.scoping())
|
||||
|| ident.is_global_reference_name(Ident::new_const("__filename"), ctx.scoping())
|
||||
}
|
||||
|
||||
fn starts_with_path_separator(expr: &Expression) -> bool {
|
||||
match expr {
|
||||
Expression::StringLiteral(s) => s.value.chars().next().is_some_and(is_path_separator),
|
||||
Expression::TemplateLiteral(temp_lit) => {
|
||||
template_element_starts_with_path_separator(temp_lit, 0)
|
||||
}
|
||||
Expression::BinaryExpression(bin) if bin.operator == BinaryOperator::Addition => {
|
||||
starts_with_path_separator(&bin.left)
|
||||
}
|
||||
Expression::ConditionalExpression(cond) => {
|
||||
starts_with_path_separator(&cond.consequent)
|
||||
|| starts_with_path_separator(&cond.alternate)
|
||||
}
|
||||
Expression::LogicalExpression(logical) => {
|
||||
starts_with_path_separator(&logical.left) || starts_with_path_separator(&logical.right)
|
||||
}
|
||||
Expression::AssignmentExpression(assign) => starts_with_path_separator(&assign.right),
|
||||
Expression::SequenceExpression(seq) => {
|
||||
seq.expressions.last().is_some_and(|last| starts_with_path_separator(last))
|
||||
}
|
||||
Expression::ParenthesizedExpression(paren) => starts_with_path_separator(&paren.expression),
|
||||
_ => is_path_sep(expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn template_element_starts_with_path_separator(temp_lit: &TemplateLiteral, i: usize) -> bool {
|
||||
let Some(quasi) = temp_lit.quasis.get(i) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let Some(c) = quasi.value.cooked.as_ref().and_then(|cooked| cooked.chars().next())
|
||||
&& is_path_separator(c)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
temp_lit.expressions.get(i).is_some_and(|expr| starts_with_path_separator(expr))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
r#"var fullPath = dirname + "foo.js";"#,
|
||||
r#"var fullPath = __dirname == "foo.js";"#,
|
||||
"if (fullPath === __dirname) {}",
|
||||
"if (__dirname === fullPath) {}",
|
||||
r#"var fullPath = "/foo.js" + __filename;"#,
|
||||
r#"var fullPath = "/foo.js" + __dirname;"#,
|
||||
r#"var fullPath = __filename + ".map";"#,
|
||||
"var fullPath = `${__filename}.map`;",
|
||||
r#"var fullPath = __filename + (test ? ".js" : ".ts");"#,
|
||||
r#"var fullPath = __filename + (ext || ".js");"#,
|
||||
r"var fullPath = `${__dirname}\nfoo.js`;",
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r#"var fullPath = __dirname + "/foo.js";"#,
|
||||
r#"var fullPath = __filename + "/foo.js";"#,
|
||||
"var fullPath = `${__dirname}/foo.js`;",
|
||||
"var fullPath = `${__filename}/foo.js`;",
|
||||
r#"var path = require("path"); var fullPath = `${__dirname}${path.sep}foo.js`;"#,
|
||||
r#"var path = require("path"); var fullPath = `${__filename}${path.sep}foo.js`;"#,
|
||||
r#"var path = require("path"); var fullPath = __dirname + path.sep + `foo.js`;"#,
|
||||
r#"var fullPath = __dirname + "/" + "foo.js";"#,
|
||||
r#"var fullPath = __dirname + ("/" + "foo.js");"#,
|
||||
r#"var fullPath = __dirname + (test ? "/foo.js" : "/bar.js");"#,
|
||||
r#"var fullPath = __dirname + (extraPath || "/default.js");"#,
|
||||
r#"var fullPath = __dirname + "\\foo.js";"#,
|
||||
r#"var fullPath = __dirname + "\\${path.sep}foo.js";"#,
|
||||
r#"var fullPath = __filename + "\\${path.sep}foo.js";"#,
|
||||
r"var fullPath = `${__dirname}\\${path.sep}foo.js`;",
|
||||
r"var fullPath = `${__filename}\\${path.sep}foo.js`;",
|
||||
];
|
||||
|
||||
Tester::new(NoPathConcat::NAME, NoPathConcat::PLUGIN, pass, fail).test_and_snapshot();
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
---
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + "/foo.js";
|
||||
· ─────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __filename + "/foo.js";
|
||||
· ──────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = `${__dirname}/foo.js`;
|
||||
· ─────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = `${__filename}/foo.js`;
|
||||
· ──────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:44]
|
||||
1 │ var path = require("path"); var fullPath = `${__dirname}${path.sep}foo.js`;
|
||||
· ───────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:44]
|
||||
1 │ var path = require("path"); var fullPath = `${__filename}${path.sep}foo.js`;
|
||||
· ────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:44]
|
||||
1 │ var path = require("path"); var fullPath = __dirname + path.sep + `foo.js`;
|
||||
· ────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + "/" + "foo.js";
|
||||
· ───────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + ("/" + "foo.js");
|
||||
· ────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + (test ? "/foo.js" : "/bar.js");
|
||||
· ──────────────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + (extraPath || "/default.js");
|
||||
· ────────────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + "\\foo.js";
|
||||
· ──────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __dirname + "\\${path.sep}foo.js";
|
||||
· ─────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = __filename + "\\${path.sep}foo.js";
|
||||
· ──────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = `${__dirname}\\${path.sep}foo.js`;
|
||||
· ─────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
|
||||
⚠ eslint-plugin-node(no-path-concat): Use `path.join()` or `path.resolve()` instead of string concatenation
|
||||
╭─[no_path_concat.tsx:1:16]
|
||||
1 │ var fullPath = `${__filename}\\${path.sep}foo.js`;
|
||||
· ──────────────────────────────────
|
||||
╰────
|
||||
help: Replace string concatenation of `__dirname` or `__filename` with `path.join()` or `path.resolve()`.
|
||||
Reference in new issue
Block a user