stackscan 0.1.24 → 0.1.29
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.
- package/README.md +10 -0
- package/dist/add.mjs +3 -2
- package/dist/chunk-2ZANNQYS.mjs +162 -0
- package/dist/chunk-5AYPCRZA.mjs +21 -0
- package/dist/chunk-CFGUYUPP.mjs +42 -0
- package/dist/chunk-DF3YGYKJ.mjs +2241 -0
- package/dist/chunk-GFZMRHRT.mjs +181 -0
- package/dist/chunk-HT4RZGLE.mjs +267 -0
- package/dist/chunk-IFB4PCXR.mjs +28 -0
- package/dist/chunk-MFJXW5RR.mjs +6 -0
- package/dist/chunk-SECL5E42.mjs +23 -0
- package/dist/chunk-STCTH5AY.mjs +15619 -0
- package/dist/chunk-ZYFKPOWH.mjs +54 -0
- package/dist/cli.js +34 -7
- package/dist/cli.mjs +11 -7
- package/dist/defaults.mjs +3 -2
- package/dist/defaults.test.d.mts +2 -0
- package/dist/defaults.test.d.ts +2 -0
- package/dist/defaults.test.js +17007 -0
- package/dist/defaults.test.mjs +30 -0
- package/dist/index.js +34 -7
- package/dist/index.mjs +18 -12
- package/dist/magic-string.es-WH4FGKAB.mjs +1315 -0
- package/dist/output.mjs +4 -3
- package/dist/output.test.d.mts +2 -0
- package/dist/output.test.d.ts +2 -0
- package/dist/output.test.js +20687 -0
- package/dist/output.test.mjs +160 -0
- package/dist/scan.js +34 -7
- package/dist/scan.mjs +7 -6
- package/dist/scan.test.d.mts +2 -0
- package/dist/scan.test.d.ts +2 -0
- package/dist/scan.test.js +23184 -0
- package/dist/scan.test.mjs +183 -0
- package/dist/simple-icons-hex.mjs +1 -0
- package/dist/sync.js +1 -2
- package/dist/sync.mjs +7 -6
- package/dist/techDefinitions.js +1 -2
- package/dist/techDefinitions.mjs +3 -2
- package/dist/techMap.js +1 -2
- package/dist/techMap.mjs +4 -3
- package/dist/techMap.test.d.mts +2 -0
- package/dist/techMap.test.d.ts +2 -0
- package/dist/techMap.test.js +19240 -0
- package/dist/techMap.test.mjs +38 -0
- package/dist/types.mjs +3 -2
- package/package.json +9 -4
- package/public/assets/logos/defaults/package.svg +18 -18
- package/public/assets/logos/defaults/terminal.svg +16 -16
- package/public/assets/logos/defaults/wrench.svg +15 -15
- package/public/assets/logos/testing/vitest.svg +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
beforeEach,
|
|
3
|
+
describe,
|
|
4
|
+
globalExpect,
|
|
5
|
+
it,
|
|
6
|
+
vi
|
|
7
|
+
} from "./chunk-STCTH5AY.mjs";
|
|
8
|
+
import {
|
|
9
|
+
copyAssets,
|
|
10
|
+
generateMarkdown,
|
|
11
|
+
writeOutput
|
|
12
|
+
} from "./chunk-GFZMRHRT.mjs";
|
|
13
|
+
import "./chunk-EH2SEQZP.mjs";
|
|
14
|
+
import "./chunk-IFB4PCXR.mjs";
|
|
15
|
+
import {
|
|
16
|
+
init_esm_shims
|
|
17
|
+
} from "./chunk-5AYPCRZA.mjs";
|
|
18
|
+
import "./chunk-CFGUYUPP.mjs";
|
|
19
|
+
|
|
20
|
+
// src/output.test.ts
|
|
21
|
+
init_esm_shims();
|
|
22
|
+
import fs from "fs-extra";
|
|
23
|
+
vi.mock("fs-extra", () => {
|
|
24
|
+
return {
|
|
25
|
+
default: {
|
|
26
|
+
ensureDir: vi.fn(),
|
|
27
|
+
writeJSON: vi.fn(),
|
|
28
|
+
writeFile: vi.fn(),
|
|
29
|
+
pathExists: vi.fn(),
|
|
30
|
+
readFile: vi.fn(),
|
|
31
|
+
copy: vi.fn()
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
describe("output module", () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.clearAllMocks();
|
|
38
|
+
});
|
|
39
|
+
describe("generateMarkdown", () => {
|
|
40
|
+
it("should generate markdown with categorized sections", () => {
|
|
41
|
+
const techs = [
|
|
42
|
+
{ name: "TypeScript", type: "language", logo: "ts.svg", slug: "typescript" },
|
|
43
|
+
{ name: "React", type: "frontend", logo: "react.svg", slug: "react" },
|
|
44
|
+
{ name: "Unknown", type: "custom", logo: "u.svg", slug: "u" }
|
|
45
|
+
];
|
|
46
|
+
const md = generateMarkdown(techs);
|
|
47
|
+
globalExpect(md).toContain("# Tech Stack");
|
|
48
|
+
globalExpect(md).toContain("## Language");
|
|
49
|
+
globalExpect(md).toContain("**TypeScript**");
|
|
50
|
+
globalExpect(md).toContain("## Frontend");
|
|
51
|
+
globalExpect(md).toContain("**React**");
|
|
52
|
+
globalExpect(md).toContain("## Custom");
|
|
53
|
+
globalExpect(md).toContain("**Unknown**");
|
|
54
|
+
});
|
|
55
|
+
it("should include images when assetsPath is provided and logo is available", () => {
|
|
56
|
+
const techs = [
|
|
57
|
+
{ name: "TypeScript", type: "language", logo: "ts.svg", slug: "typescript" }
|
|
58
|
+
];
|
|
59
|
+
const availableLogos = /* @__PURE__ */ new Set(["ts.svg"]);
|
|
60
|
+
const md = generateMarkdown(techs, "./assets", availableLogos);
|
|
61
|
+
globalExpect(md).toContain('<img src="./assets/ts.svg"');
|
|
62
|
+
});
|
|
63
|
+
it("should handle http logos correctly", () => {
|
|
64
|
+
const techs = [
|
|
65
|
+
{ name: "Remote", type: "language", logo: "https://example.com/logo.svg", slug: "remote" }
|
|
66
|
+
];
|
|
67
|
+
const md = generateMarkdown(techs);
|
|
68
|
+
globalExpect(md).toContain('<img src="https://example.com/logo.svg"');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("writeOutput", () => {
|
|
72
|
+
it("should write JSON output by default", async () => {
|
|
73
|
+
const techs = [{ name: "Test", type: "misc", logo: "test.svg", slug: "t" }];
|
|
74
|
+
const outPath = "out.json";
|
|
75
|
+
fs.writeJSON.mockResolvedValue(void 0);
|
|
76
|
+
await writeOutput(outPath, techs, {}, "json");
|
|
77
|
+
globalExpect(fs.writeJSON).toHaveBeenCalledWith(outPath, globalExpect.anything(), globalExpect.anything());
|
|
78
|
+
});
|
|
79
|
+
it("should write Markdown output and handle assets", async () => {
|
|
80
|
+
const techs = [{ name: "Test", type: "misc", logo: "test.svg", slug: "t" }];
|
|
81
|
+
const outPath = "out.json";
|
|
82
|
+
const assetsPath = "public/assets";
|
|
83
|
+
fs.pathExists.mockResolvedValue(true);
|
|
84
|
+
fs.readFile.mockResolvedValue("<svg></svg>");
|
|
85
|
+
fs.writeFile.mockResolvedValue(void 0);
|
|
86
|
+
await writeOutput(outPath, techs, {}, "markdown", assetsPath);
|
|
87
|
+
globalExpect(fs.writeFile).toHaveBeenCalledWith("out.md", globalExpect.stringContaining("# Tech Stack"));
|
|
88
|
+
globalExpect(fs.readFile).toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("copyAssets", () => {
|
|
92
|
+
it("should copy existing assets", async () => {
|
|
93
|
+
const techs = [
|
|
94
|
+
{ name: "Test", type: "misc", logo: "test.svg", slug: "t" }
|
|
95
|
+
];
|
|
96
|
+
const dest = "public/assets";
|
|
97
|
+
const config = { colorMode: "brand" };
|
|
98
|
+
fs.pathExists.mockResolvedValue(true);
|
|
99
|
+
fs.readFile.mockResolvedValue("<svg></svg>");
|
|
100
|
+
const copied = await copyAssets(techs, dest, config);
|
|
101
|
+
globalExpect(fs.readFile).toHaveBeenCalled();
|
|
102
|
+
globalExpect(fs.writeFile).toHaveBeenCalled();
|
|
103
|
+
globalExpect(copied.has("test.svg")).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
it("should apply color to svg (white)", async () => {
|
|
106
|
+
const techs = [
|
|
107
|
+
{ name: "Test", type: "misc", logo: "test.svg", slug: "t" }
|
|
108
|
+
];
|
|
109
|
+
const dest = "public/assets";
|
|
110
|
+
const config = { colorMode: "white" };
|
|
111
|
+
fs.pathExists.mockResolvedValue(true);
|
|
112
|
+
fs.readFile.mockResolvedValue('<svg fill="#000"></svg>');
|
|
113
|
+
await copyAssets(techs, dest, config);
|
|
114
|
+
globalExpect(fs.writeFile).toHaveBeenCalledWith(
|
|
115
|
+
globalExpect.any(String),
|
|
116
|
+
globalExpect.stringContaining('fill="#FFFFFF"')
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
it("should apply color to svg (black)", async () => {
|
|
120
|
+
const techs = [{ name: "Test", type: "misc", logo: "test.svg", slug: "t" }];
|
|
121
|
+
const dest = "public/assets";
|
|
122
|
+
const config = { colorMode: "black" };
|
|
123
|
+
fs.pathExists.mockResolvedValue(true);
|
|
124
|
+
fs.readFile.mockResolvedValue('<svg fill="#FFF"></svg>');
|
|
125
|
+
await copyAssets(techs, dest, config);
|
|
126
|
+
globalExpect(fs.writeFile).toHaveBeenCalledWith(
|
|
127
|
+
globalExpect.any(String),
|
|
128
|
+
globalExpect.stringContaining('fill="#000000"')
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
it("should apply custom hex color", async () => {
|
|
132
|
+
const techs = [{ name: "Test", type: "misc", logo: "test.svg", slug: "t" }];
|
|
133
|
+
const dest = "public/assets";
|
|
134
|
+
const config = { colorMode: "custom", customColor: "#123456" };
|
|
135
|
+
fs.pathExists.mockResolvedValue(true);
|
|
136
|
+
fs.readFile.mockResolvedValue('<svg fill="#000"></svg>');
|
|
137
|
+
await copyAssets(techs, dest, config);
|
|
138
|
+
globalExpect(fs.writeFile).toHaveBeenCalledWith(
|
|
139
|
+
globalExpect.any(String),
|
|
140
|
+
globalExpect.stringContaining('fill="#123456"')
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
it("should fallback to default icon if logo missing", async () => {
|
|
144
|
+
const techs = [{ name: "Test", type: "auth", logo: "missing.svg", slug: "t" }];
|
|
145
|
+
const dest = "public/assets";
|
|
146
|
+
fs.pathExists.mockImplementation(async (p) => {
|
|
147
|
+
if (p.includes("missing.svg")) return false;
|
|
148
|
+
if (p.includes("lock.svg")) return true;
|
|
149
|
+
return false;
|
|
150
|
+
});
|
|
151
|
+
fs.readFile.mockResolvedValue('<svg stroke="currentColor"></svg>');
|
|
152
|
+
const copied = await copyAssets(techs, dest, { colorMode: "white" });
|
|
153
|
+
globalExpect(fs.writeFile).toHaveBeenCalledWith(
|
|
154
|
+
globalExpect.stringContaining("lock.svg"),
|
|
155
|
+
globalExpect.stringContaining('stroke="#FFFFFF"')
|
|
156
|
+
// Lucide uses stroke
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
package/dist/scan.js
CHANGED
|
@@ -581,7 +581,7 @@ var techDefinitions = [
|
|
|
581
581
|
"id": "rtkquery",
|
|
582
582
|
"name": "RTK Query",
|
|
583
583
|
"aliases": [
|
|
584
|
-
"@
|
|
584
|
+
"@rtk-query/graphql-request-base-query"
|
|
585
585
|
],
|
|
586
586
|
"category": "state",
|
|
587
587
|
"logo": "state/redux.svg",
|
|
@@ -1625,7 +1625,6 @@ var techDefinitions = [
|
|
|
1625
1625
|
"aliases": [
|
|
1626
1626
|
"aws-sdk",
|
|
1627
1627
|
"@aws-sdk/client-s3",
|
|
1628
|
-
"@aws-sdk/client-dynamodb",
|
|
1629
1628
|
"@aws-sdk/client-lambda"
|
|
1630
1629
|
],
|
|
1631
1630
|
"category": "cloud",
|
|
@@ -5840,6 +5839,36 @@ var BASE_DIR = import_path2.default.join(process.cwd(), "public", "stackscan");
|
|
|
5840
5839
|
var SKIPPED_TECHS = [
|
|
5841
5840
|
"react-dom"
|
|
5842
5841
|
];
|
|
5842
|
+
function getPackageJson(projectPath) {
|
|
5843
|
+
const pkgPath = import_path2.default.join(projectPath, "package.json");
|
|
5844
|
+
const pkgPathUnderscore = import_path2.default.join(projectPath, "_package.json");
|
|
5845
|
+
const isInsideStackScanDir = projectPath.includes(import_path2.default.join("public", "stackscan"));
|
|
5846
|
+
if (import_fs.default.existsSync(pkgPath) && isInsideStackScanDir) {
|
|
5847
|
+
try {
|
|
5848
|
+
console.log(`Renaming ${pkgPath} to ${pkgPathUnderscore}`);
|
|
5849
|
+
import_fs.default.renameSync(pkgPath, pkgPathUnderscore);
|
|
5850
|
+
} catch (e) {
|
|
5851
|
+
console.warn(`Failed to rename package.json to _package.json: ${e.message}`);
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
if (import_fs.default.existsSync(pkgPathUnderscore)) {
|
|
5855
|
+
try {
|
|
5856
|
+
const content = import_fs.default.readFileSync(pkgPathUnderscore, "utf-8");
|
|
5857
|
+
return JSON.parse(content);
|
|
5858
|
+
} catch (e) {
|
|
5859
|
+
throw new Error(`Failed to read _package.json: ${e.message}`);
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
if (import_fs.default.existsSync(pkgPath)) {
|
|
5863
|
+
try {
|
|
5864
|
+
const content = import_fs.default.readFileSync(pkgPath, "utf-8");
|
|
5865
|
+
return JSON.parse(content);
|
|
5866
|
+
} catch (e) {
|
|
5867
|
+
throw new Error(`Failed to read package.json: ${e.message}`);
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
return null;
|
|
5871
|
+
}
|
|
5843
5872
|
var CATEGORY_PRIORITY = [
|
|
5844
5873
|
"language",
|
|
5845
5874
|
"framework",
|
|
@@ -5876,12 +5905,10 @@ var getCategoryPriority = (cat) => {
|
|
|
5876
5905
|
return idx === -1 ? 999 : idx;
|
|
5877
5906
|
};
|
|
5878
5907
|
async function analyzeProject(projectPath, options) {
|
|
5879
|
-
const
|
|
5880
|
-
if (!
|
|
5881
|
-
throw new Error(`No package.json found at ${
|
|
5908
|
+
const pkg = getPackageJson(projectPath);
|
|
5909
|
+
if (!pkg) {
|
|
5910
|
+
throw new Error(`No package.json or _package.json found at ${projectPath}`);
|
|
5882
5911
|
}
|
|
5883
|
-
const content = import_fs.default.readFileSync(packageJsonPath, "utf-8");
|
|
5884
|
-
const pkg = JSON.parse(content);
|
|
5885
5912
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5886
5913
|
const detectedTechs = [];
|
|
5887
5914
|
Object.keys(allDeps).forEach((dep) => {
|
package/dist/scan.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
scan
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-NGEKE4DQ.mjs";
|
|
8
|
-
import "./chunk-EOKQCSHI.mjs";
|
|
3
|
+
} from "./chunk-HT4RZGLE.mjs";
|
|
4
|
+
import "./chunk-SECL5E42.mjs";
|
|
5
|
+
import "./chunk-DF3YGYKJ.mjs";
|
|
6
|
+
import "./chunk-GFZMRHRT.mjs";
|
|
9
7
|
import "./chunk-EH2SEQZP.mjs";
|
|
8
|
+
import "./chunk-IFB4PCXR.mjs";
|
|
9
|
+
import "./chunk-5AYPCRZA.mjs";
|
|
10
|
+
import "./chunk-CFGUYUPP.mjs";
|
|
10
11
|
export {
|
|
11
12
|
scan
|
|
12
13
|
};
|