nuxt-skill-hub 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const noRedundantImport: Rule.RuleModule;
@@ -0,0 +1,114 @@
1
+ const SETTINGS_KEY = "skill-hub/autoImports";
2
+ export const noRedundantImport = {
3
+ meta: {
4
+ type: "suggestion",
5
+ docs: {
6
+ description: "Disallow explicit imports of auto-imported identifiers in Nuxt"
7
+ },
8
+ fixable: "code",
9
+ schema: [],
10
+ messages: {
11
+ redundant: "'{{ name }}' is auto-imported by Nuxt. Remove this explicit import."
12
+ }
13
+ },
14
+ create(context) {
15
+ const entries = context.settings[SETTINGS_KEY];
16
+ if (!entries?.length)
17
+ return {};
18
+ const lookup = /* @__PURE__ */ new Map();
19
+ for (const entry of entries) {
20
+ const localName = entry.as || entry.name;
21
+ const existing = lookup.get(entry.from) || { type: /* @__PURE__ */ new Map(), value: /* @__PURE__ */ new Map() };
22
+ addAutoImportName(existing[entry.type ? "type" : "value"], localName, entry.name);
23
+ lookup.set(entry.from, existing);
24
+ }
25
+ return {
26
+ ImportDeclaration(node) {
27
+ const importNode = node;
28
+ if (!importNode.specifiers.length) return;
29
+ const source = typeof importNode.source.value === "string" ? importNode.source.value : void 0;
30
+ if (!source) return;
31
+ const autoImported = lookup.get(source);
32
+ if (!autoImported) return;
33
+ if (isDeclarationFile(context.filename)) return;
34
+ const specInfos = [];
35
+ for (const spec of importNode.specifiers) {
36
+ if (spec.type !== "ImportSpecifier") {
37
+ specInfos.push({ spec, localName: "", isRedundant: false });
38
+ continue;
39
+ }
40
+ const localName = spec.local.name;
41
+ const names = isTypeOnlyImport(importNode, spec) ? autoImported.type : autoImported.value;
42
+ const importedName = getImportedName(spec);
43
+ specInfos.push({ spec, localName, isRedundant: importedName ? names.get(localName)?.has(importedName) === true : false });
44
+ }
45
+ const redundant = specInfos.filter((s) => s.isRedundant);
46
+ if (!redundant.length) return;
47
+ const kept = specInfos.filter((s) => !s.isRedundant);
48
+ const sourceText = context.sourceCode.getText();
49
+ if (!kept.length) {
50
+ context.report({
51
+ node: importNode,
52
+ messageId: "redundant",
53
+ data: { name: redundant.map((s) => s.localName).join(", ") },
54
+ fix: (fixer) => fixer.removeRange(importRemovalRange(sourceText, importNode.range))
55
+ });
56
+ return;
57
+ }
58
+ context.report({
59
+ node: importNode,
60
+ messageId: "redundant",
61
+ data: { name: redundant.map((s) => s.localName).join(", ") },
62
+ fix: (fixer) => fixer.replaceText(importNode, rebuildImportDeclaration(context, importNode, kept))
63
+ });
64
+ }
65
+ };
66
+ }
67
+ };
68
+ function isDeclarationFile(filename) {
69
+ return /\.d\.(?:cts|mts|ts)$/.test(filename);
70
+ }
71
+ function importRemovalRange(text, range) {
72
+ let end = range[1];
73
+ while (text[end] === " " || text[end] === " ") {
74
+ end++;
75
+ }
76
+ if (text.startsWith("\r\n", end)) {
77
+ return [range[0], end + 2];
78
+ }
79
+ if (text[end] === "\n") {
80
+ return [range[0], end + 1];
81
+ }
82
+ return [range[0], end];
83
+ }
84
+ function isTypeOnlyImport(node, spec) {
85
+ return node.importKind === "type" || spec.importKind === "type";
86
+ }
87
+ function addAutoImportName(lookup, localName, importedName) {
88
+ const importedNames = lookup.get(localName) || /* @__PURE__ */ new Set();
89
+ importedNames.add(importedName);
90
+ lookup.set(localName, importedNames);
91
+ }
92
+ function getImportedName(spec) {
93
+ return typeof spec.imported.name === "string" ? spec.imported.name : typeof spec.imported.value === "string" ? spec.imported.value : void 0;
94
+ }
95
+ function rebuildImportDeclaration(context, node, kept) {
96
+ const defaultSpecifiers = kept.filter(({ spec }) => spec.type === "ImportDefaultSpecifier");
97
+ const namespaceSpecifiers = kept.filter(({ spec }) => spec.type === "ImportNamespaceSpecifier");
98
+ const namedSpecifiers = kept.filter(({ spec }) => spec.type === "ImportSpecifier").map(({ spec }) => spec);
99
+ const useTypeKeyword = node.importKind !== "type" && !defaultSpecifiers.length && !namespaceSpecifiers.length && namedSpecifiers.length > 0 && namedSpecifiers.every((spec) => spec.importKind === "type");
100
+ const specifierTexts = [
101
+ ...defaultSpecifiers.map(({ spec }) => context.sourceCode.getText(spec)),
102
+ ...namespaceSpecifiers.map(({ spec }) => context.sourceCode.getText(spec))
103
+ ];
104
+ if (namedSpecifiers.length) {
105
+ const namedTexts = namedSpecifiers.map((spec) => {
106
+ const text = context.sourceCode.getText(spec);
107
+ return useTypeKeyword ? text.replace(/^type\s+/, "") : text;
108
+ });
109
+ specifierTexts.push(`{ ${namedTexts.join(", ")} }`);
110
+ }
111
+ const sourceRange = node.source.range;
112
+ const suffix = context.sourceCode.text.slice(sourceRange[1], node.range[1]);
113
+ return `import${node.importKind === "type" || useTypeKeyword ? " type" : ""} ${specifierTexts.join(", ")} from ${context.sourceCode.getText(node.source)}${suffix}`;
114
+ }
@@ -0,0 +1,9 @@
1
+ declare const _default: {
2
+ meta: {
3
+ name: string;
4
+ };
5
+ rules: {
6
+ 'no-redundant-import': import("eslint").Rule.RuleModule;
7
+ };
8
+ };
9
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { noRedundantImport } from "./no-redundant-import.js";
2
+ export default {
3
+ meta: { name: "skill-hub" },
4
+ rules: { "no-redundant-import": noRedundantImport }
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-skill-hub",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Teach your AI agent the Nuxt way with best practices and module guidance.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,6 +13,9 @@
13
13
  "types": "./dist/types.d.mts",
14
14
  "import": "./dist/module.mjs",
15
15
  "default": "./dist/module.mjs"
16
+ },
17
+ "./eslint-plugin": {
18
+ "import": "./dist/runtime/eslint/plugin.js"
16
19
  }
17
20
  },
18
21
  "main": "./dist/module.mjs",
@@ -58,11 +61,13 @@
58
61
  "dev:build": "nuxt-module-build build --stub --watch",
59
62
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare",
60
63
  "release": "bumpp --push --no-push-all",
64
+ "release:check-version": "node ./scripts/check-npm-version.mjs",
61
65
  "lint": "eslint .",
62
- "test": "pnpm run dev:prepare && vitest run && pnpm run test:pack",
66
+ "test": "pnpm run dev:prepare && pnpm run docs:prepare && vitest run && pnpm run test:pack",
63
67
  "test:pack": "node ./scripts/check-packlist.mjs",
64
68
  "test:watch": "vitest watch",
65
69
  "test:types": "pnpm run dev:prepare && vue-tsc --noEmit",
70
+ "docs:prepare": "pnpm --dir docs exec nuxt prepare",
66
71
  "docs:typecheck": "pnpm --dir docs typecheck",
67
72
  "typecheck": "pnpm run test:types && pnpm run docs:typecheck"
68
73
  }