stackscan 0.1.23 → 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 +16 -8
- package/dist/add.mjs +3 -2
- package/dist/chunk-24PM76MV.mjs +235 -0
- 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-E75XPZ2U.mjs +2237 -0
- package/dist/chunk-GFZMRHRT.mjs +181 -0
- package/dist/chunk-HKCVFKM4.mjs +19 -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-XNTLNSF6.mjs +158 -0
- package/dist/chunk-ZYFKPOWH.mjs +54 -0
- package/dist/cli.js +48 -15
- 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.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +50 -17
- package/dist/index.mjs +18 -12
- package/dist/magic-string.es-WH4FGKAB.mjs +1315 -0
- package/dist/output.d.mts +3 -3
- package/dist/output.d.ts +3 -3
- 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 +48 -15
- 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 +15 -10
- package/dist/sync.mjs +7 -6
- package/dist/techDefinitions.js +13 -8
- package/dist/techDefinitions.mjs +3 -2
- package/dist/techMap.js +13 -8
- 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.d.mts +2 -2
- package/dist/types.d.ts +2 -2
- 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",
|
|
@@ -876,7 +876,8 @@ var techDefinitions = [
|
|
|
876
876
|
"id": "figma",
|
|
877
877
|
"name": "Figma",
|
|
878
878
|
"aliases": [
|
|
879
|
-
"figma"
|
|
879
|
+
"figma",
|
|
880
|
+
"figma-api"
|
|
880
881
|
],
|
|
881
882
|
"category": "utility",
|
|
882
883
|
"logo": "utility/figma.svg",
|
|
@@ -886,7 +887,8 @@ var techDefinitions = [
|
|
|
886
887
|
"id": "notion",
|
|
887
888
|
"name": "Notion",
|
|
888
889
|
"aliases": [
|
|
889
|
-
"notion"
|
|
890
|
+
"notion",
|
|
891
|
+
"@notionhq/client"
|
|
890
892
|
],
|
|
891
893
|
"category": "utility",
|
|
892
894
|
"logo": "utility/notion.svg",
|
|
@@ -896,7 +898,8 @@ var techDefinitions = [
|
|
|
896
898
|
"id": "slack",
|
|
897
899
|
"name": "Slack",
|
|
898
900
|
"aliases": [
|
|
899
|
-
"slack"
|
|
901
|
+
"slack",
|
|
902
|
+
"@slack/web-api"
|
|
900
903
|
],
|
|
901
904
|
"category": "utility",
|
|
902
905
|
"logo": "utility/slack.svg",
|
|
@@ -916,7 +919,8 @@ var techDefinitions = [
|
|
|
916
919
|
"id": "githubcopilot",
|
|
917
920
|
"name": "GitHub Copilot",
|
|
918
921
|
"aliases": [
|
|
919
|
-
"github-copilot"
|
|
922
|
+
"github-copilot",
|
|
923
|
+
"github-copilot-extension"
|
|
920
924
|
],
|
|
921
925
|
"category": "ai",
|
|
922
926
|
"logo": "ai/githubcopilot.svg",
|
|
@@ -1548,7 +1552,8 @@ var techDefinitions = [
|
|
|
1548
1552
|
"id": "docker",
|
|
1549
1553
|
"name": "Docker",
|
|
1550
1554
|
"aliases": [
|
|
1551
|
-
"docker"
|
|
1555
|
+
"docker",
|
|
1556
|
+
"dockerode"
|
|
1552
1557
|
],
|
|
1553
1558
|
"category": "container",
|
|
1554
1559
|
"logo": "container/docker.svg",
|
|
@@ -1620,7 +1625,6 @@ var techDefinitions = [
|
|
|
1620
1625
|
"aliases": [
|
|
1621
1626
|
"aws-sdk",
|
|
1622
1627
|
"@aws-sdk/client-s3",
|
|
1623
|
-
"@aws-sdk/client-dynamodb",
|
|
1624
1628
|
"@aws-sdk/client-lambda"
|
|
1625
1629
|
],
|
|
1626
1630
|
"category": "cloud",
|
|
@@ -1661,7 +1665,8 @@ var techDefinitions = [
|
|
|
1661
1665
|
"id": "netlify",
|
|
1662
1666
|
"name": "Netlify",
|
|
1663
1667
|
"aliases": [
|
|
1664
|
-
"netlify"
|
|
1668
|
+
"netlify",
|
|
1669
|
+
"netlify-cli"
|
|
1665
1670
|
],
|
|
1666
1671
|
"category": "hosting",
|
|
1667
1672
|
"logo": "hosting/netlify.svg",
|
|
@@ -5834,6 +5839,36 @@ var BASE_DIR = import_path2.default.join(process.cwd(), "public", "stackscan");
|
|
|
5834
5839
|
var SKIPPED_TECHS = [
|
|
5835
5840
|
"react-dom"
|
|
5836
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
|
+
}
|
|
5837
5872
|
var CATEGORY_PRIORITY = [
|
|
5838
5873
|
"language",
|
|
5839
5874
|
"framework",
|
|
@@ -5870,12 +5905,10 @@ var getCategoryPriority = (cat) => {
|
|
|
5870
5905
|
return idx === -1 ? 999 : idx;
|
|
5871
5906
|
};
|
|
5872
5907
|
async function analyzeProject(projectPath, options) {
|
|
5873
|
-
const
|
|
5874
|
-
if (!
|
|
5875
|
-
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}`);
|
|
5876
5911
|
}
|
|
5877
|
-
const content = import_fs.default.readFileSync(packageJsonPath, "utf-8");
|
|
5878
|
-
const pkg = JSON.parse(content);
|
|
5879
5912
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5880
5913
|
const detectedTechs = [];
|
|
5881
5914
|
Object.keys(allDeps).forEach((dep) => {
|
|
@@ -6014,8 +6047,8 @@ function updateRootReadme(projects) {
|
|
|
6014
6047
|
return;
|
|
6015
6048
|
}
|
|
6016
6049
|
let readmeContent = import_fs.default.readFileSync(readmePath, "utf-8");
|
|
6017
|
-
const startMarker = "<!--
|
|
6018
|
-
const endMarker = "<!--
|
|
6050
|
+
const startMarker = "<!-- STACKSCAN_START -->";
|
|
6051
|
+
const endMarker = "<!-- STACKSCAN_END -->";
|
|
6019
6052
|
let newSection = `${startMarker}
|
|
6020
6053
|
## My Projects
|
|
6021
6054
|
|
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
|
};
|