stackscan 0.1.11 → 0.1.14

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 CHANGED
@@ -173,50 +173,4 @@ See `CONTRIBUTING.md` for development workflow and guidelines.
173
173
 
174
174
  ## License
175
175
 
176
- MIT
177
-
178
- ```
179
-
180
- ---
181
-
182
- ### Final notes (important but quick)
183
-
184
- - This README is **npm-ready** and **user-focused**
185
- - Your internal branch/version automation should move to:
186
- - `CONTRIBUTING.md` or `.github/`
187
- - The tone is correct for a free, public dev tool:
188
- *clear, confident, not over-marketed*
189
-
190
- You’re genuinely at the “ship it” point.
191
- If you want, next I can:
192
- - tighten CLI flag descriptions to exactly match `commander`
193
- - write `CONTRIBUTING.md`
194
- - review your npm publish checklist line by line
195
- ```
196
-
197
-
198
- <!-- STACKSYNC_START -->
199
- ## My Projects
200
-
201
- ### asozial
202
- <p>
203
- <img src="public/assets/logos/auth/jsonwebtokens.svg" alt="JWT" height="25" style="margin-right: 10px;" />
204
- <img src="public/assets/logos/frontend/nextdotjs.svg" alt="Auth.js" height="25" style="margin-right: 10px;" />
205
- <img src="public/assets/logos/testing/jest.svg" alt="Jest" height="25" style="margin-right: 10px;" />
206
- </p>
207
-
208
- ### portfolio
209
- <p>
210
- <img src="public/assets/logos/frontend/react.svg" alt="React" height="25" style="margin-right: 10px;" />
211
- <img src="public/assets/logos/frontend/react.svg" alt="ReactDOM" height="25" style="margin-right: 10px;" />
212
- <img src="public/assets/logos/language/typescript.svg" alt="TypeScript" height="25" style="margin-right: 10px;" />
213
- <img src="public/assets/logos/testing/playwright.svg" alt="Playwright" height="25" style="margin-right: 10px;" />
214
- <img src="public/assets/logos/testing/testinglibrary.svg" alt="Testing Library" height="25" style="margin-right: 10px;" />
215
- <img src="public/assets/logos/lint/husky.svg" alt="Husky" height="25" style="margin-right: 10px;" />
216
- <img src="public/assets/logos/testing/jest.svg" alt="Jest" height="25" style="margin-right: 10px;" />
217
- <img src="public/assets/logos/css/postcss.svg" alt="PostCSS" height="25" style="margin-right: 10px;" />
218
- <img src="public/assets/logos/css/tailwindcss.svg" alt="TailwindCSS" height="25" style="margin-right: 10px;" />
219
- <img src="public/assets/logos/build/vite.svg" alt="Vite" height="25" style="margin-right: 10px;" />
220
- </p>
221
-
222
- <!-- STACKSYNC_END -->
176
+ MIT
@@ -0,0 +1,162 @@
1
+ import {
2
+ techMap
3
+ } from "./chunk-LSUI3VI4.mjs";
4
+ import {
5
+ copyAssets,
6
+ generateMarkdown
7
+ } from "./chunk-UJM3S22V.mjs";
8
+ import {
9
+ simple_icons_hex_default
10
+ } from "./chunk-EH2SEQZP.mjs";
11
+
12
+ // src/scan.ts
13
+ import fs from "fs";
14
+ import path from "path";
15
+ var BASE_DIR = path.join(process.cwd(), "public", "stackscan");
16
+ var SKIPPED_TECHS = [
17
+ "react-dom"
18
+ ];
19
+ async function scan(options = {}) {
20
+ if (options.readme === void 0) options.readme = true;
21
+ console.log("\u{1F680} Starting Scan...");
22
+ if (options.color) {
23
+ console.log(`\u{1F3A8} Color mode: ${options.color}`);
24
+ }
25
+ if (!fs.existsSync(BASE_DIR)) {
26
+ console.log(`Creating stackscan directory at: ${BASE_DIR}`);
27
+ fs.mkdirSync(BASE_DIR, { recursive: true });
28
+ console.log('Please place your project folders inside "public/stackscan/" and run this command again.');
29
+ process.exit(0);
30
+ }
31
+ const entries = fs.readdirSync(BASE_DIR, { withFileTypes: true });
32
+ const projectDirs = entries.filter((dirent) => dirent.isDirectory() && dirent.name !== "input" && dirent.name !== "output");
33
+ if (projectDirs.length === 0) {
34
+ console.log('\u26A0\uFE0F No project directories found in "public/stackscan/".');
35
+ return;
36
+ }
37
+ console.log(`Found ${projectDirs.length} projects to process.
38
+ `);
39
+ const allProjects = [];
40
+ const allTechs = [];
41
+ for (const dir of projectDirs) {
42
+ const projectPath = path.join(BASE_DIR, dir.name);
43
+ const packageJsonPath = path.join(projectPath, "package.json");
44
+ if (fs.existsSync(packageJsonPath)) {
45
+ try {
46
+ const content = fs.readFileSync(packageJsonPath, "utf-8");
47
+ const pkg = JSON.parse(content);
48
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
49
+ const detectedTechs = [];
50
+ Object.keys(allDeps).forEach((dep) => {
51
+ if (SKIPPED_TECHS.includes(dep)) return;
52
+ if (techMap[dep]) {
53
+ const tech = techMap[dep];
54
+ let color = null;
55
+ if (options.color === "white") color = "#FFFFFF";
56
+ else if (options.color === "black") color = "#000000";
57
+ else if (options.color && options.color.startsWith("#")) color = options.color;
58
+ else {
59
+ const depSlug = dep.toLowerCase();
60
+ const nameSlug = tech.name.toLowerCase();
61
+ const nameSlugNoSpaces = tech.name.toLowerCase().replace(/\s+/g, "");
62
+ const hex = simple_icons_hex_default[depSlug] || simple_icons_hex_default[nameSlug] || simple_icons_hex_default[nameSlugNoSpaces];
63
+ if (hex) color = `#${hex}`;
64
+ }
65
+ detectedTechs.push({
66
+ name: tech.name,
67
+ slug: dep,
68
+ logo: tech.logo,
69
+ // Raw path for resolution
70
+ type: tech.type,
71
+ color
72
+ });
73
+ }
74
+ });
75
+ let uniqueTechs = Array.from(new Set(detectedTechs.map((t) => t.slug))).map((slug) => detectedTechs.find((t) => t.slug === slug));
76
+ const seenLogos = /* @__PURE__ */ new Set();
77
+ uniqueTechs = uniqueTechs.filter((t) => {
78
+ if (seenLogos.has(t.logo)) {
79
+ return false;
80
+ }
81
+ seenLogos.add(t.logo);
82
+ return true;
83
+ });
84
+ const assetsDir = path.join(process.cwd(), "public", "assets", "logos");
85
+ if (options.copyAssets !== false) {
86
+ await copyAssets(uniqueTechs, assetsDir, { colorMode: options.color });
87
+ }
88
+ const techsWithUrls = uniqueTechs.map((t) => ({
89
+ ...t,
90
+ logo: `https://raw.githubusercontent.com/benjamindotdev/stackscan/main/public/assets/logos/${t.logo}`,
91
+ relativePath: `./public/assets/logos/${t.logo}`
92
+ }));
93
+ allTechs.push(...techsWithUrls);
94
+ fs.writeFileSync(
95
+ path.join(projectPath, "stack.json"),
96
+ JSON.stringify(techsWithUrls, null, 2)
97
+ );
98
+ const mdContent = generateMarkdown(techsWithUrls);
99
+ fs.writeFileSync(path.join(projectPath, "stack.md"), mdContent);
100
+ allProjects.push({
101
+ name: dir.name,
102
+ techs: techsWithUrls
103
+ });
104
+ console.log(`\u2705 ${dir.name.padEnd(20)} -> stack.json (${uniqueTechs.length} techs)`);
105
+ } catch (err) {
106
+ console.error(`\u274C Error processing ${dir.name}:`, err.message);
107
+ }
108
+ } else {
109
+ console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
110
+ }
111
+ }
112
+ if (options.readme && allProjects.length > 0) {
113
+ updateRootReadme(allProjects);
114
+ } else if (!options.readme) {
115
+ console.log("Skipping README update (--no-readme passed).");
116
+ }
117
+ console.log("\n\u2728 Sync complete.");
118
+ }
119
+ function updateRootReadme(projects) {
120
+ const readmePath = path.join(process.cwd(), "README.md");
121
+ if (!fs.existsSync(readmePath)) {
122
+ console.log("\u26A0\uFE0F No root README.md found to update.");
123
+ return;
124
+ }
125
+ let readmeContent = fs.readFileSync(readmePath, "utf-8");
126
+ const startMarker = "<!-- STACKSYNC_START -->";
127
+ const endMarker = "<!-- STACKSYNC_END -->";
128
+ let newSection = `${startMarker}
129
+ ## My Projects
130
+
131
+ `;
132
+ for (const p of projects) {
133
+ newSection += `### ${p.name}
134
+ `;
135
+ newSection += `<p>
136
+ `;
137
+ for (const t of p.techs) {
138
+ const src = t.relativePath || t.logo;
139
+ newSection += ` <img src="${src}" alt="${t.name}" height="25" style="margin-right: 10px;" />
140
+ `;
141
+ }
142
+ newSection += `</p>
143
+
144
+ `;
145
+ }
146
+ newSection += `${endMarker}`;
147
+ if (readmeContent.includes(startMarker) && readmeContent.includes(endMarker)) {
148
+ const regex = new RegExp(`${startMarker}[\\s\\S]*?${endMarker}`);
149
+ readmeContent = readmeContent.replace(regex, newSection);
150
+ console.log(`\u{1F4DD} Updated root README.md with ${projects.length} projects.`);
151
+ } else {
152
+ readmeContent += `
153
+
154
+ ${newSection}`;
155
+ console.log(`\u{1F4DD} Appended projects to root README.md.`);
156
+ }
157
+ fs.writeFileSync(readmePath, readmeContent);
158
+ }
159
+
160
+ export {
161
+ scan
162
+ };
package/dist/cli.js CHANGED
@@ -5576,6 +5576,7 @@ var SKIPPED_TECHS = [
5576
5576
  "react-dom"
5577
5577
  ];
5578
5578
  async function scan(options = {}) {
5579
+ if (options.readme === void 0) options.readme = true;
5579
5580
  console.log("\u{1F680} Starting Scan...");
5580
5581
  if (options.color) {
5581
5582
  console.log(`\u{1F3A8} Color mode: ${options.color}`);
@@ -5667,8 +5668,10 @@ async function scan(options = {}) {
5667
5668
  console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
5668
5669
  }
5669
5670
  }
5670
- if (allProjects.length > 0) {
5671
+ if (options.readme && allProjects.length > 0) {
5671
5672
  updateRootReadme(allProjects);
5673
+ } else if (!options.readme) {
5674
+ console.log("Skipping README update (--no-readme passed).");
5672
5675
  }
5673
5676
  console.log("\n\u2728 Sync complete.");
5674
5677
  }
@@ -5762,7 +5765,7 @@ async function add(sourcePath) {
5762
5765
  // src/cli.ts
5763
5766
  var program = new import_commander.Command();
5764
5767
  program.name("stackscan").description("Auto-detect tech stacks and generate tech.json or markdown.").version("0.1.0");
5765
- program.command("scan", { isDefault: true }).description("Scan stacks from multiple projects in public/stackscan/").option("--color <mode>", "Color mode (brand, white, black, or hex)", "brand").action(async (options) => {
5768
+ program.command("scan", { isDefault: true }).description("Scan stacks from multiple projects in public/stackscan/").option("--color <mode>", "Color mode (brand, white, black, or hex)", "brand").option("--no-readme", "Do not update the root README.md").action(async (options) => {
5766
5769
  await scan(options);
5767
5770
  });
5768
5771
  program.command("add <path>").description("Add a project (folder or package.json) to the public/stackscan workspace").action(async (path4) => {
package/dist/cli.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ACCTMJVS.mjs";
5
5
  import {
6
6
  scan
7
- } from "./chunk-ZPLKDMPP.mjs";
7
+ } from "./chunk-A4WZRPHL.mjs";
8
8
  import "./chunk-LSUI3VI4.mjs";
9
9
  import "./chunk-LB3L25FS.mjs";
10
10
  import "./chunk-UJM3S22V.mjs";
@@ -16,7 +16,7 @@ import "./chunk-EH2SEQZP.mjs";
16
16
  import { Command } from "commander";
17
17
  var program = new Command();
18
18
  program.name("stackscan").description("Auto-detect tech stacks and generate tech.json or markdown.").version("0.1.0");
19
- program.command("scan", { isDefault: true }).description("Scan stacks from multiple projects in public/stackscan/").option("--color <mode>", "Color mode (brand, white, black, or hex)", "brand").action(async (options) => {
19
+ program.command("scan", { isDefault: true }).description("Scan stacks from multiple projects in public/stackscan/").option("--color <mode>", "Color mode (brand, white, black, or hex)", "brand").option("--no-readme", "Do not update the root README.md").action(async (options) => {
20
20
  await scan(options);
21
21
  });
22
22
  program.command("add <path>").description("Add a project (folder or package.json) to the public/stackscan workspace").action(async (path) => {
package/dist/index.js CHANGED
@@ -5663,6 +5663,7 @@ var SKIPPED_TECHS = [
5663
5663
  "react-dom"
5664
5664
  ];
5665
5665
  async function scan(options = {}) {
5666
+ if (options.readme === void 0) options.readme = true;
5666
5667
  console.log("\u{1F680} Starting Scan...");
5667
5668
  if (options.color) {
5668
5669
  console.log(`\u{1F3A8} Color mode: ${options.color}`);
@@ -5754,8 +5755,10 @@ async function scan(options = {}) {
5754
5755
  console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
5755
5756
  }
5756
5757
  }
5757
- if (allProjects.length > 0) {
5758
+ if (options.readme && allProjects.length > 0) {
5758
5759
  updateRootReadme(allProjects);
5760
+ } else if (!options.readme) {
5761
+ console.log("Skipping README update (--no-readme passed).");
5759
5762
  }
5760
5763
  console.log("\n\u2728 Sync complete.");
5761
5764
  }
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ACCTMJVS.mjs";
5
5
  import {
6
6
  scan
7
- } from "./chunk-ZPLKDMPP.mjs";
7
+ } from "./chunk-A4WZRPHL.mjs";
8
8
  import {
9
9
  sync
10
10
  } from "./chunk-XS2LU2MN.mjs";
package/dist/scan.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  interface SyncOptions {
2
2
  color?: string;
3
3
  copyAssets?: boolean;
4
+ readme?: boolean;
4
5
  }
5
6
  declare function scan(options?: SyncOptions): Promise<void>;
6
7
 
package/dist/scan.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  interface SyncOptions {
2
2
  color?: string;
3
3
  copyAssets?: boolean;
4
+ readme?: boolean;
4
5
  }
5
6
  declare function scan(options?: SyncOptions): Promise<void>;
6
7
 
package/dist/scan.js CHANGED
@@ -5584,6 +5584,7 @@ var SKIPPED_TECHS = [
5584
5584
  "react-dom"
5585
5585
  ];
5586
5586
  async function scan(options = {}) {
5587
+ if (options.readme === void 0) options.readme = true;
5587
5588
  console.log("\u{1F680} Starting Scan...");
5588
5589
  if (options.color) {
5589
5590
  console.log(`\u{1F3A8} Color mode: ${options.color}`);
@@ -5675,8 +5676,10 @@ async function scan(options = {}) {
5675
5676
  console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
5676
5677
  }
5677
5678
  }
5678
- if (allProjects.length > 0) {
5679
+ if (options.readme && allProjects.length > 0) {
5679
5680
  updateRootReadme(allProjects);
5681
+ } else if (!options.readme) {
5682
+ console.log("Skipping README update (--no-readme passed).");
5680
5683
  }
5681
5684
  console.log("\n\u2728 Sync complete.");
5682
5685
  }
package/dist/scan.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  scan
3
- } from "./chunk-ZPLKDMPP.mjs";
3
+ } from "./chunk-A4WZRPHL.mjs";
4
4
  import "./chunk-LSUI3VI4.mjs";
5
5
  import "./chunk-LB3L25FS.mjs";
6
6
  import "./chunk-UJM3S22V.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stackscan",
3
- "version": "0.1.11",
3
+ "version": "0.1.14",
4
4
  "description": "Automatically detect tech stacks from repositories and generate logos, JSON, and markdown for portfolios.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",