Repository: stylelint-scss/stylelint-scss (js)

Issue:
Add a lint for an explicit namespace that's the same as the default
If a user writes something like `@use "foo" as foo`, the `as foo` is totally redundant since it's the same as the default namespace that the `@use` rule would provide on its own. It would be nice to have a lint for this case.

Pull request description:
Add at-use-no-redundant-alias rule
Fix #445.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 56f87ea..8e5b55a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 # 5.2.0
 
+- Added: `at-use-no-redundant-alias` rule to disallow redundant namespace aliases (#445).
 - Added: `function-calculation-no-interpolation` rule to forbid interpolation in calc functions (#539).
 
 **Full Changelog**: https://github.com/stylelint-scss/stylelint-scss/compare/v5.1.0...v5.2.0
diff --git a/README.md b/README.md
index 078723f..426e1ec 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,7 @@ Please also see the [example configs](./docs/examples/) for special cases.
 ### `@`-use
 
 - [`at-use-no-unnamespaced`](./src/rules/at-use-no-unnamespaced/README.md): Disallow `@use` without a namespace (i.e. `@use "..." as *`).
+- [`at-use-no-redundant-alias`](./src/rules/at-use-no-redundant-alias/README.md): Disallow redundant namespace aliases (i.e. `@use "foo" as foo`).
 
 ### `$`-variable
 
diff --git a/src/rules/at-use-no-redundant-alias/README.md b/src/rules/at-use-no-redundant-alias/README.md
new file mode 100644
index 0000000..c5ddc45
--- /dev/null
+++ b/src/rules/at-use-no-redundant-alias/README.md
@@ -0,0 +1,44 @@
+# at-use-no-redundant-alias
+
+Disallow redundant namespace aliases.
+
+```scss
+@use "foo" as foo;
+/**           ↑
+ * Disallow this */
+```
+
+By default, the [module's namespace](https://sass-lang.com/documentation/at-rules/use/#loading-members)
+is the last component of the module’s URL.
+
+## Options
+
+### `true`
+
+The following patterns are considered warnings:
+
+```scss
+@use "foo" as foo;
+```
+
+```scss
+@use "sass:math" as math;
+```
+
+```scss
+@use "src/corners" as corners;
+```
+
+The following patterns are _not_ considered warnings:
+
+```scss
+@use "foo" as bar;
+```
+
+```scss
+@use "sass:math";
+```
+
+```scss
+@use "src/corners" as c;
+```
diff --git a/src/rules/at-use-no-redundant-alias/index.js b/src/rules/at-use-no-redundant-alias/index.js
new file mode 100644
index 0000000..fd3f14a
--- /dev/null
+++ b/src/rules/at-use-no-redundant-alias/index.js
@@ -0,0 +1,53 @@
+"use strict";
+
+const { utils } = require("stylelint");
+const namespace = require("../../utils/namespace");
+const ruleUrl = require("../../utils/ruleUrl");
+
+const ruleName = namespace("at-use-no-redundant-alias");
+
+const messages = utils.ruleMessages(ruleName, {
+  rejected: "Unexpected redundant namespace."
+});
+
+const meta = {
+  url: ruleUrl(ruleName)
+};
+
+function getDefaultNamespace(module) {
+  return module.match(/([^/:]+)$/)[1].replace(/\.[^.]|["]+$/, "");
+}
+
+function separateEachParams(paramString) {
+  const parts = paramString.replace(/"/g, "").split(/\s+as\s+|\s+with\s+/);
+  if (parts.length < 2) return;
+  return parts;
+}
+
+function rule(actual) {
+  return (root, result) => {
+    const validOptions = utils.validateOptions(result, ruleName, { actual });
+
+    if (!validOptions) {
+      return;
+    }
+
+    root.walkAtRules("use", decl => {
+      const parts = separateEachParams(decl.params);
+      if (parts && getDefaultNamespace(parts[0]) === parts[1]) {
+        utils.report({
+          message: messages.rejected,
+          node: decl,
+          result,
+          ruleName
+        });
+      }
+    });
+  };
+}
+
+rule.ruleName = ruleName;
+rule.messages = messages;
+rule.meta = meta;
+
+module.exports = rule;
diff --git a/src/rules/index.js b/src/rules/index.js
index b1ac11b..292672c 100644
--- a/src/rules/index.js
+++ b/src/rules/index.js
@@ -23,6 +23,7 @@ const rules = {
   "at-mixin-pattern": require("./at-mixin-pattern"),
   "at-rule-conditional-no-parentheses": require("./at-rule-conditional-no-parentheses"),
   "at-rule-no-unknown": require("./at-rule-no-unknown"),
+  "at-use-no-redundant-alias": require("./at-use-no-redundant-alias"),
   "at-use-no-unnamespaced": require("./at-use-no-unnamespaced"),
   "comment-no-empty": require("./comment-no-empty"),
   "comment-no-loud": require("./comment-no-loud"),


Test patch:
diff --git a/src/rules/at-use-no-redundant-alias/__tests__/index.js b/src/rules/at-use-no-redundant-alias/__tests__/index.js
new file mode 100644
index 0000000..601670a
--- /dev/null
+++ b/src/rules/at-use-no-redundant-alias/__tests__/index.js
@@ -0,0 +1,95 @@
+"use strict";
+
+const { messages, ruleName } = require("..");
+
+testRule({
+  ruleName,
+  config: [true],
+  customSyntax: "postcss-scss",
+
+  accept: [
+    {
+      code: `
+      @use "foo";
+    `,
+      description: "Default namespace"
+    },
+    {
+      code: `
+      @use "foo/bar" as foo;
+    `,
+      description: "Different namespace"
+    },
+    {
+      code: `
+      @use "sass:math" as *;
+    `,
+      description: "Without namespace"
+    },
+    {
+      code: `
+      @use "sass:math";
+    `,
+      description: "No explicit namespace"
+    }
+  ],
+
+  reject: [
+    {
+      code: `
+      @use "foo" as foo;
+    `,
+      line: 2,
+      column: 7,
+      message: messages.rejected,
+      description: "Without url"
+    },
+    {
+      code: `
+      @use "src/corners" as corners;
+    `,
+      line: 2,
+      column: 7,
+      message: messages.rejected,
+      description: "With url"
+    },
+    {
+      code: `
+      @use "sass:math" as math;
+    `,
+      line: 2,
+      column: 7,
+      message: messages.rejected,
+      description: "Built-in module"
+    },
+    {
+      code: `
+      @use "foo"   as   foo;
+    `,
+      line: 2,
+      column: 7,
+      message: messages.rejected,
+      description: "Without space"
+    },
+    {
+      code: `
+      @use "foo" as foo with ($baz: 1px);
+    `,
+      line: 2,
+      column: 7,
+      message: messages.rejected,
+      description: "Configured"
+    },
+    {
+      code: `
+      @use "sass:color" as color with (
+        $baz: 1px
+      );
+    `,
+      line: 2,
+      column: 7,
+      message: messages.rejected,
+      description: "Configured multiline"
+    }
+  ]
+});