stackscan 0.1.9 → 0.1.13
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/dist/chunk-A4WZRPHL.mjs +162 -0
- package/dist/chunk-EL53M5P5.mjs +159 -0
- package/dist/chunk-ZPLKDMPP.mjs +159 -0
- package/dist/cli.js +6 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.js +5 -1
- package/dist/index.mjs +1 -1
- package/dist/scan.d.mts +1 -0
- package/dist/scan.d.ts +1 -0
- package/dist/scan.js +5 -1
- package/dist/scan.mjs +1 -1
- package/package.json +1 -1
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
console.log("\u{1F680} Starting Scan...");
|
|
21
|
+
if (options.color) {
|
|
22
|
+
console.log(`\u{1F3A8} Color mode: ${options.color}`);
|
|
23
|
+
}
|
|
24
|
+
if (!fs.existsSync(BASE_DIR)) {
|
|
25
|
+
console.log(`Creating stackscan directory at: ${BASE_DIR}`);
|
|
26
|
+
fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
27
|
+
console.log('Please place your project folders inside "public/stackscan/" and run this command again.');
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
const entries = fs.readdirSync(BASE_DIR, { withFileTypes: true });
|
|
31
|
+
const projectDirs = entries.filter((dirent) => dirent.isDirectory() && dirent.name !== "input" && dirent.name !== "output");
|
|
32
|
+
if (projectDirs.length === 0) {
|
|
33
|
+
console.log('\u26A0\uFE0F No project directories found in "public/stackscan/".');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(`Found ${projectDirs.length} projects to process.
|
|
37
|
+
`);
|
|
38
|
+
const allProjects = [];
|
|
39
|
+
const allTechs = [];
|
|
40
|
+
for (const dir of projectDirs) {
|
|
41
|
+
const projectPath = path.join(BASE_DIR, dir.name);
|
|
42
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
43
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
44
|
+
try {
|
|
45
|
+
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
46
|
+
const pkg = JSON.parse(content);
|
|
47
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
48
|
+
const detectedTechs = [];
|
|
49
|
+
Object.keys(allDeps).forEach((dep) => {
|
|
50
|
+
if (SKIPPED_TECHS.includes(dep)) return;
|
|
51
|
+
if (techMap[dep]) {
|
|
52
|
+
const tech = techMap[dep];
|
|
53
|
+
let color = null;
|
|
54
|
+
if (options.color === "white") color = "#FFFFFF";
|
|
55
|
+
else if (options.color === "black") color = "#000000";
|
|
56
|
+
else if (options.color && options.color.startsWith("#")) color = options.color;
|
|
57
|
+
else {
|
|
58
|
+
const depSlug = dep.toLowerCase();
|
|
59
|
+
const nameSlug = tech.name.toLowerCase();
|
|
60
|
+
const nameSlugNoSpaces = tech.name.toLowerCase().replace(/\s+/g, "");
|
|
61
|
+
const hex = simple_icons_hex_default[depSlug] || simple_icons_hex_default[nameSlug] || simple_icons_hex_default[nameSlugNoSpaces];
|
|
62
|
+
if (hex) color = `#${hex}`;
|
|
63
|
+
}
|
|
64
|
+
detectedTechs.push({
|
|
65
|
+
name: tech.name,
|
|
66
|
+
slug: dep,
|
|
67
|
+
logo: tech.logo,
|
|
68
|
+
// Raw path for resolution
|
|
69
|
+
type: tech.category,
|
|
70
|
+
color
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
let uniqueTechs = Array.from(new Set(detectedTechs.map((t) => t.slug))).map((slug) => detectedTechs.find((t) => t.slug === slug));
|
|
75
|
+
const seenLogos = /* @__PURE__ */ new Set();
|
|
76
|
+
uniqueTechs = uniqueTechs.filter((t) => {
|
|
77
|
+
if (seenLogos.has(t.logo)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
seenLogos.add(t.logo);
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
const assetsDir = path.join(process.cwd(), "public", "assets", "logos");
|
|
84
|
+
if (options.copyAssets !== false) {
|
|
85
|
+
await copyAssets(uniqueTechs, assetsDir, { colorMode: options.color });
|
|
86
|
+
}
|
|
87
|
+
const techsWithUrls = uniqueTechs.map((t) => ({
|
|
88
|
+
...t,
|
|
89
|
+
logo: `https://raw.githubusercontent.com/benjamindotdev/stackscan/main/public/assets/logos/${t.logo}`,
|
|
90
|
+
relativePath: `./public/assets/logos/${t.logo}`
|
|
91
|
+
}));
|
|
92
|
+
allTechs.push(...techsWithUrls);
|
|
93
|
+
fs.writeFileSync(
|
|
94
|
+
path.join(projectPath, "stack.json"),
|
|
95
|
+
JSON.stringify(techsWithUrls, null, 2)
|
|
96
|
+
);
|
|
97
|
+
const mdContent = generateMarkdown(techsWithUrls);
|
|
98
|
+
fs.writeFileSync(path.join(projectPath, "stack.md"), mdContent);
|
|
99
|
+
allProjects.push({
|
|
100
|
+
name: dir.name,
|
|
101
|
+
techs: techsWithUrls
|
|
102
|
+
});
|
|
103
|
+
console.log(`\u2705 ${dir.name.padEnd(20)} -> stack.json (${uniqueTechs.length} techs)`);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error(`\u274C Error processing ${dir.name}:`, err.message);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (allProjects.length > 0) {
|
|
112
|
+
updateRootReadme(allProjects);
|
|
113
|
+
}
|
|
114
|
+
console.log("\n\u2728 Sync complete.");
|
|
115
|
+
}
|
|
116
|
+
function updateRootReadme(projects) {
|
|
117
|
+
const readmePath = path.join(process.cwd(), "README.md");
|
|
118
|
+
if (!fs.existsSync(readmePath)) {
|
|
119
|
+
console.log("\u26A0\uFE0F No root README.md found to update.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let readmeContent = fs.readFileSync(readmePath, "utf-8");
|
|
123
|
+
const startMarker = "<!-- STACKSYNC_START -->";
|
|
124
|
+
const endMarker = "<!-- STACKSYNC_END -->";
|
|
125
|
+
let newSection = `${startMarker}
|
|
126
|
+
## My Projects
|
|
127
|
+
|
|
128
|
+
`;
|
|
129
|
+
for (const p of projects) {
|
|
130
|
+
newSection += `### ${p.name}
|
|
131
|
+
`;
|
|
132
|
+
newSection += `<p>
|
|
133
|
+
`;
|
|
134
|
+
for (const t of p.techs) {
|
|
135
|
+
const src = t.relativePath || t.logo;
|
|
136
|
+
newSection += ` <img src="${src}" alt="${t.name}" height="25" style="margin-right: 10px;" />
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
newSection += `</p>
|
|
140
|
+
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
newSection += `${endMarker}`;
|
|
144
|
+
if (readmeContent.includes(startMarker) && readmeContent.includes(endMarker)) {
|
|
145
|
+
const regex = new RegExp(`${startMarker}[\\s\\S]*?${endMarker}`);
|
|
146
|
+
readmeContent = readmeContent.replace(regex, newSection);
|
|
147
|
+
console.log(`\u{1F4DD} Updated root README.md with ${projects.length} projects.`);
|
|
148
|
+
} else {
|
|
149
|
+
readmeContent += `
|
|
150
|
+
|
|
151
|
+
${newSection}`;
|
|
152
|
+
console.log(`\u{1F4DD} Appended projects to root README.md.`);
|
|
153
|
+
}
|
|
154
|
+
fs.writeFileSync(readmePath, readmeContent);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
scan
|
|
159
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
console.log("\u{1F680} Starting Scan...");
|
|
21
|
+
if (options.color) {
|
|
22
|
+
console.log(`\u{1F3A8} Color mode: ${options.color}`);
|
|
23
|
+
}
|
|
24
|
+
if (!fs.existsSync(BASE_DIR)) {
|
|
25
|
+
console.log(`Creating stackscan directory at: ${BASE_DIR}`);
|
|
26
|
+
fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
27
|
+
console.log('Please place your project folders inside "public/stackscan/" and run this command again.');
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
const entries = fs.readdirSync(BASE_DIR, { withFileTypes: true });
|
|
31
|
+
const projectDirs = entries.filter((dirent) => dirent.isDirectory() && dirent.name !== "input" && dirent.name !== "output");
|
|
32
|
+
if (projectDirs.length === 0) {
|
|
33
|
+
console.log('\u26A0\uFE0F No project directories found in "public/stackscan/".');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(`Found ${projectDirs.length} projects to process.
|
|
37
|
+
`);
|
|
38
|
+
const allProjects = [];
|
|
39
|
+
const allTechs = [];
|
|
40
|
+
for (const dir of projectDirs) {
|
|
41
|
+
const projectPath = path.join(BASE_DIR, dir.name);
|
|
42
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
43
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
44
|
+
try {
|
|
45
|
+
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
46
|
+
const pkg = JSON.parse(content);
|
|
47
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
48
|
+
const detectedTechs = [];
|
|
49
|
+
Object.keys(allDeps).forEach((dep) => {
|
|
50
|
+
if (SKIPPED_TECHS.includes(dep)) return;
|
|
51
|
+
if (techMap[dep]) {
|
|
52
|
+
const tech = techMap[dep];
|
|
53
|
+
let color = null;
|
|
54
|
+
if (options.color === "white") color = "#FFFFFF";
|
|
55
|
+
else if (options.color === "black") color = "#000000";
|
|
56
|
+
else if (options.color && options.color.startsWith("#")) color = options.color;
|
|
57
|
+
else {
|
|
58
|
+
const depSlug = dep.toLowerCase();
|
|
59
|
+
const nameSlug = tech.name.toLowerCase();
|
|
60
|
+
const nameSlugNoSpaces = tech.name.toLowerCase().replace(/\s+/g, "");
|
|
61
|
+
const hex = simple_icons_hex_default[depSlug] || simple_icons_hex_default[nameSlug] || simple_icons_hex_default[nameSlugNoSpaces];
|
|
62
|
+
if (hex) color = `#${hex}`;
|
|
63
|
+
}
|
|
64
|
+
detectedTechs.push({
|
|
65
|
+
name: tech.name,
|
|
66
|
+
slug: dep,
|
|
67
|
+
logo: tech.logo,
|
|
68
|
+
// Raw path for resolution
|
|
69
|
+
type: tech.type,
|
|
70
|
+
color
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
let uniqueTechs = Array.from(new Set(detectedTechs.map((t) => t.slug))).map((slug) => detectedTechs.find((t) => t.slug === slug));
|
|
75
|
+
const seenLogos = /* @__PURE__ */ new Set();
|
|
76
|
+
uniqueTechs = uniqueTechs.filter((t) => {
|
|
77
|
+
if (seenLogos.has(t.logo)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
seenLogos.add(t.logo);
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
const assetsDir = path.join(process.cwd(), "public", "assets", "logos");
|
|
84
|
+
if (options.copyAssets !== false) {
|
|
85
|
+
await copyAssets(uniqueTechs, assetsDir, { colorMode: options.color });
|
|
86
|
+
}
|
|
87
|
+
const techsWithUrls = uniqueTechs.map((t) => ({
|
|
88
|
+
...t,
|
|
89
|
+
logo: `https://raw.githubusercontent.com/benjamindotdev/stackscan/main/public/assets/logos/${t.logo}`,
|
|
90
|
+
relativePath: `./public/assets/logos/${t.logo}`
|
|
91
|
+
}));
|
|
92
|
+
allTechs.push(...techsWithUrls);
|
|
93
|
+
fs.writeFileSync(
|
|
94
|
+
path.join(projectPath, "stack.json"),
|
|
95
|
+
JSON.stringify(techsWithUrls, null, 2)
|
|
96
|
+
);
|
|
97
|
+
const mdContent = generateMarkdown(techsWithUrls);
|
|
98
|
+
fs.writeFileSync(path.join(projectPath, "stack.md"), mdContent);
|
|
99
|
+
allProjects.push({
|
|
100
|
+
name: dir.name,
|
|
101
|
+
techs: techsWithUrls
|
|
102
|
+
});
|
|
103
|
+
console.log(`\u2705 ${dir.name.padEnd(20)} -> stack.json (${uniqueTechs.length} techs)`);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error(`\u274C Error processing ${dir.name}:`, err.message);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (allProjects.length > 0) {
|
|
112
|
+
updateRootReadme(allProjects);
|
|
113
|
+
}
|
|
114
|
+
console.log("\n\u2728 Sync complete.");
|
|
115
|
+
}
|
|
116
|
+
function updateRootReadme(projects) {
|
|
117
|
+
const readmePath = path.join(process.cwd(), "README.md");
|
|
118
|
+
if (!fs.existsSync(readmePath)) {
|
|
119
|
+
console.log("\u26A0\uFE0F No root README.md found to update.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let readmeContent = fs.readFileSync(readmePath, "utf-8");
|
|
123
|
+
const startMarker = "<!-- STACKSYNC_START -->";
|
|
124
|
+
const endMarker = "<!-- STACKSYNC_END -->";
|
|
125
|
+
let newSection = `${startMarker}
|
|
126
|
+
## My Projects
|
|
127
|
+
|
|
128
|
+
`;
|
|
129
|
+
for (const p of projects) {
|
|
130
|
+
newSection += `### ${p.name}
|
|
131
|
+
`;
|
|
132
|
+
newSection += `<p>
|
|
133
|
+
`;
|
|
134
|
+
for (const t of p.techs) {
|
|
135
|
+
const src = t.relativePath || t.logo;
|
|
136
|
+
newSection += ` <img src="${src}" alt="${t.name}" height="25" style="margin-right: 10px;" />
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
newSection += `</p>
|
|
140
|
+
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
newSection += `${endMarker}`;
|
|
144
|
+
if (readmeContent.includes(startMarker) && readmeContent.includes(endMarker)) {
|
|
145
|
+
const regex = new RegExp(`${startMarker}[\\s\\S]*?${endMarker}`);
|
|
146
|
+
readmeContent = readmeContent.replace(regex, newSection);
|
|
147
|
+
console.log(`\u{1F4DD} Updated root README.md with ${projects.length} projects.`);
|
|
148
|
+
} else {
|
|
149
|
+
readmeContent += `
|
|
150
|
+
|
|
151
|
+
${newSection}`;
|
|
152
|
+
console.log(`\u{1F4DD} Appended projects to root README.md.`);
|
|
153
|
+
}
|
|
154
|
+
fs.writeFileSync(readmePath, readmeContent);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
scan
|
|
159
|
+
};
|
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}`);
|
|
@@ -5625,6 +5626,7 @@ async function scan(options = {}) {
|
|
|
5625
5626
|
slug: dep,
|
|
5626
5627
|
logo: tech.logo,
|
|
5627
5628
|
// Raw path for resolution
|
|
5629
|
+
type: tech.type,
|
|
5628
5630
|
color
|
|
5629
5631
|
});
|
|
5630
5632
|
}
|
|
@@ -5666,8 +5668,10 @@ async function scan(options = {}) {
|
|
|
5666
5668
|
console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
|
|
5667
5669
|
}
|
|
5668
5670
|
}
|
|
5669
|
-
if (allProjects.length > 0) {
|
|
5671
|
+
if (options.readme && allProjects.length > 0) {
|
|
5670
5672
|
updateRootReadme(allProjects);
|
|
5673
|
+
} else if (!options.readme) {
|
|
5674
|
+
console.log("Skipping README update (--no-readme passed).");
|
|
5671
5675
|
}
|
|
5672
5676
|
console.log("\n\u2728 Sync complete.");
|
|
5673
5677
|
}
|
|
@@ -5761,7 +5765,7 @@ async function add(sourcePath) {
|
|
|
5761
5765
|
// src/cli.ts
|
|
5762
5766
|
var program = new import_commander.Command();
|
|
5763
5767
|
program.name("stackscan").description("Auto-detect tech stacks and generate tech.json or markdown.").version("0.1.0");
|
|
5764
|
-
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) => {
|
|
5765
5769
|
await scan(options);
|
|
5766
5770
|
});
|
|
5767
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-
|
|
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}`);
|
|
@@ -5712,6 +5713,7 @@ async function scan(options = {}) {
|
|
|
5712
5713
|
slug: dep,
|
|
5713
5714
|
logo: tech.logo,
|
|
5714
5715
|
// Raw path for resolution
|
|
5716
|
+
type: tech.type,
|
|
5715
5717
|
color
|
|
5716
5718
|
});
|
|
5717
5719
|
}
|
|
@@ -5753,8 +5755,10 @@ async function scan(options = {}) {
|
|
|
5753
5755
|
console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
|
|
5754
5756
|
}
|
|
5755
5757
|
}
|
|
5756
|
-
if (allProjects.length > 0) {
|
|
5758
|
+
if (options.readme && allProjects.length > 0) {
|
|
5757
5759
|
updateRootReadme(allProjects);
|
|
5760
|
+
} else if (!options.readme) {
|
|
5761
|
+
console.log("Skipping README update (--no-readme passed).");
|
|
5758
5762
|
}
|
|
5759
5763
|
console.log("\n\u2728 Sync complete.");
|
|
5760
5764
|
}
|
package/dist/index.mjs
CHANGED
package/dist/scan.d.mts
CHANGED
package/dist/scan.d.ts
CHANGED
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}`);
|
|
@@ -5633,6 +5634,7 @@ async function scan(options = {}) {
|
|
|
5633
5634
|
slug: dep,
|
|
5634
5635
|
logo: tech.logo,
|
|
5635
5636
|
// Raw path for resolution
|
|
5637
|
+
type: tech.type,
|
|
5636
5638
|
color
|
|
5637
5639
|
});
|
|
5638
5640
|
}
|
|
@@ -5674,8 +5676,10 @@ async function scan(options = {}) {
|
|
|
5674
5676
|
console.warn(`\u26A0\uFE0F Skipping "${dir.name}": No package.json found.`);
|
|
5675
5677
|
}
|
|
5676
5678
|
}
|
|
5677
|
-
if (allProjects.length > 0) {
|
|
5679
|
+
if (options.readme && allProjects.length > 0) {
|
|
5678
5680
|
updateRootReadme(allProjects);
|
|
5681
|
+
} else if (!options.readme) {
|
|
5682
|
+
console.log("Skipping README update (--no-readme passed).");
|
|
5679
5683
|
}
|
|
5680
5684
|
console.log("\n\u2728 Sync complete.");
|
|
5681
5685
|
}
|
package/dist/scan.mjs
CHANGED
package/package.json
CHANGED