stackpatch 1.1.0 โ 1.1.1
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/bin/stackpatch.ts +103 -33
- package/package.json +1 -1
package/bin/stackpatch.ts
CHANGED
|
@@ -303,53 +303,56 @@ async function copyFiles(src: string, dest: string): Promise<{ success: boolean;
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
// Track files from SOURCE (boilerplate) before copying
|
|
307
|
+
// This ensures we only track files that are actually from StackPatch
|
|
308
|
+
function trackSourceFiles(srcDir: string, baseDir: string, targetBase: string): void {
|
|
309
|
+
if (!fs.existsSync(srcDir)) return;
|
|
310
|
+
|
|
311
|
+
const files = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
312
|
+
for (const file of files) {
|
|
313
|
+
const srcFilePath = path.join(srcDir, file.name);
|
|
314
|
+
if (file.isDirectory()) {
|
|
315
|
+
trackSourceFiles(srcFilePath, baseDir, targetBase);
|
|
316
|
+
} else {
|
|
317
|
+
const relativePath = path.relative(baseDir, srcFilePath);
|
|
318
|
+
const targetPath = targetBase
|
|
319
|
+
? path.join(targetBase, relativePath).replace(/\\/g, "/")
|
|
320
|
+
: relativePath.replace(/\\/g, "/");
|
|
321
|
+
addedFiles.push(targetPath);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
306
326
|
// Copy files with smart app directory handling
|
|
307
327
|
for (const entry of entries) {
|
|
308
328
|
const srcPath = path.join(src, entry.name);
|
|
309
329
|
|
|
310
330
|
if (entry.name === "app") {
|
|
331
|
+
// Track files from SOURCE boilerplate before copying
|
|
332
|
+
trackSourceFiles(srcPath, srcPath, appDir);
|
|
333
|
+
|
|
311
334
|
// Copy app directory contents to the detected app directory location
|
|
312
335
|
await fse.ensureDir(appDirPath);
|
|
313
336
|
await fse.copy(srcPath, appDirPath, { overwrite: true });
|
|
314
|
-
|
|
315
|
-
// Track all files in app directory
|
|
316
|
-
function trackFiles(dir: string, baseDir: string) {
|
|
317
|
-
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
318
|
-
for (const file of files) {
|
|
319
|
-
const filePath = path.join(dir, file.name);
|
|
320
|
-
if (file.isDirectory()) {
|
|
321
|
-
trackFiles(filePath, baseDir);
|
|
322
|
-
} else {
|
|
323
|
-
const relativePath = path.relative(baseDir, filePath);
|
|
324
|
-
addedFiles.push(path.join(appDir, relativePath).replace(/\\/g, "/"));
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
trackFiles(appDirPath, appDirPath);
|
|
329
337
|
} else if (entry.name === "components") {
|
|
338
|
+
// Track files from SOURCE boilerplate before copying
|
|
339
|
+
trackSourceFiles(srcPath, srcPath, componentsDir);
|
|
340
|
+
|
|
330
341
|
// Copy components directory to the detected components directory location
|
|
331
342
|
await fse.ensureDir(componentsDirPath);
|
|
332
343
|
await fse.copy(srcPath, componentsDirPath, { overwrite: true });
|
|
333
|
-
|
|
334
|
-
// Track all files in components directory
|
|
335
|
-
function trackFiles(dir: string, baseDir: string) {
|
|
336
|
-
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
337
|
-
for (const file of files) {
|
|
338
|
-
const filePath = path.join(dir, file.name);
|
|
339
|
-
if (file.isDirectory()) {
|
|
340
|
-
trackFiles(filePath, baseDir);
|
|
341
|
-
} else {
|
|
342
|
-
const relativePath = path.relative(baseDir, filePath);
|
|
343
|
-
addedFiles.push(path.join(componentsDir, relativePath).replace(/\\/g, "/"));
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
trackFiles(componentsDirPath, componentsDirPath);
|
|
348
344
|
} else {
|
|
345
|
+
// For root-level files/directories, track from source
|
|
346
|
+
const srcStat = fs.statSync(srcPath);
|
|
347
|
+
if (srcStat.isDirectory()) {
|
|
348
|
+
trackSourceFiles(srcPath, srcPath, "");
|
|
349
|
+
} else {
|
|
350
|
+
addedFiles.push(entry.name);
|
|
351
|
+
}
|
|
352
|
+
|
|
349
353
|
// Copy other files/directories (middleware, etc.) to root
|
|
350
354
|
const destPath = path.join(dest, entry.name);
|
|
351
355
|
await fse.copy(srcPath, destPath, { overwrite: true });
|
|
352
|
-
addedFiles.push(entry.name);
|
|
353
356
|
}
|
|
354
357
|
}
|
|
355
358
|
|
|
@@ -1790,9 +1793,66 @@ async function main() {
|
|
|
1790
1793
|
let failedRestorations: string[] = [];
|
|
1791
1794
|
const directoriesToCheck: Set<string> = new Set();
|
|
1792
1795
|
|
|
1793
|
-
// Step 1:
|
|
1796
|
+
// Step 1: Get list of valid StackPatch files from boilerplate
|
|
1797
|
+
// This ensures we only remove files that are actually from StackPatch
|
|
1798
|
+
const boilerplatePath = path.join(BOILERPLATE_ROOT, manifest.patchName === "auth-ui" ? "auth" : manifest.patchName);
|
|
1799
|
+
const validStackPatchFiles = new Set<string>();
|
|
1800
|
+
|
|
1801
|
+
function collectBoilerplateFiles(srcDir: string, baseDir: string, targetBase: string): void {
|
|
1802
|
+
if (!fs.existsSync(srcDir)) return;
|
|
1803
|
+
|
|
1804
|
+
const files = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
1805
|
+
for (const file of files) {
|
|
1806
|
+
const srcFilePath = path.join(srcDir, file.name);
|
|
1807
|
+
if (file.isDirectory()) {
|
|
1808
|
+
collectBoilerplateFiles(srcFilePath, baseDir, targetBase);
|
|
1809
|
+
} else {
|
|
1810
|
+
const relativePath = path.relative(baseDir, srcFilePath);
|
|
1811
|
+
const targetPath = targetBase
|
|
1812
|
+
? path.join(targetBase, relativePath).replace(/\\/g, "/")
|
|
1813
|
+
: relativePath.replace(/\\/g, "/");
|
|
1814
|
+
validStackPatchFiles.add(targetPath);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// Collect files from boilerplate app directory
|
|
1820
|
+
const appDir = detectAppDirectory(target);
|
|
1821
|
+
const componentsDir = detectComponentsDirectory(target);
|
|
1822
|
+
const boilerplateAppPath = path.join(boilerplatePath, "app");
|
|
1823
|
+
const boilerplateComponentsPath = path.join(boilerplatePath, "components");
|
|
1824
|
+
|
|
1825
|
+
if (fs.existsSync(boilerplateAppPath)) {
|
|
1826
|
+
collectBoilerplateFiles(boilerplateAppPath, boilerplateAppPath, appDir);
|
|
1827
|
+
}
|
|
1828
|
+
if (fs.existsSync(boilerplateComponentsPath)) {
|
|
1829
|
+
collectBoilerplateFiles(boilerplateComponentsPath, boilerplateComponentsPath, componentsDir);
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// Collect root-level files
|
|
1833
|
+
if (fs.existsSync(boilerplatePath)) {
|
|
1834
|
+
const entries = fs.readdirSync(boilerplatePath, { withFileTypes: true });
|
|
1835
|
+
for (const entry of entries) {
|
|
1836
|
+
if (entry.name !== "app" && entry.name !== "components") {
|
|
1837
|
+
const srcPath = path.join(boilerplatePath, entry.name);
|
|
1838
|
+
if (entry.isDirectory()) {
|
|
1839
|
+
collectBoilerplateFiles(srcPath, srcPath, "");
|
|
1840
|
+
} else {
|
|
1841
|
+
validStackPatchFiles.add(entry.name);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// Step 1: Remove added files (only if they're actually from StackPatch boilerplate)
|
|
1794
1848
|
console.log(chalk.white("๐ Removing added files..."));
|
|
1795
1849
|
for (const filePath of manifest.files.added) {
|
|
1850
|
+
// Only remove if this file is actually in the boilerplate
|
|
1851
|
+
if (!validStackPatchFiles.has(filePath)) {
|
|
1852
|
+
console.log(chalk.gray(` โ Skipped (not in boilerplate): ${filePath}`));
|
|
1853
|
+
continue;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1796
1856
|
const fullPath = path.join(target, filePath);
|
|
1797
1857
|
if (fs.existsSync(fullPath)) {
|
|
1798
1858
|
try {
|
|
@@ -1884,23 +1944,33 @@ async function main() {
|
|
|
1884
1944
|
}
|
|
1885
1945
|
}
|
|
1886
1946
|
|
|
1887
|
-
// Step 5: Clean up empty directories
|
|
1947
|
+
// Step 5: Clean up empty directories (only if they only contained StackPatch files)
|
|
1888
1948
|
console.log(chalk.white("\n๐งน Cleaning up empty directories..."));
|
|
1889
1949
|
const sortedDirs = Array.from(directoriesToCheck).sort((a, b) => b.length - a.length); // Sort by depth (deepest first)
|
|
1950
|
+
let removedDirCount = 0;
|
|
1951
|
+
|
|
1890
1952
|
for (const dir of sortedDirs) {
|
|
1891
1953
|
if (fs.existsSync(dir)) {
|
|
1892
1954
|
try {
|
|
1893
1955
|
const entries = fs.readdirSync(dir);
|
|
1894
1956
|
if (entries.length === 0) {
|
|
1957
|
+
// Only remove if directory is empty
|
|
1958
|
+
// We know it was created by StackPatch because we're tracking it
|
|
1895
1959
|
fs.rmdirSync(dir);
|
|
1960
|
+
removedDirCount++;
|
|
1896
1961
|
console.log(chalk.green(` โ Removed empty directory: ${path.relative(target, dir)}`));
|
|
1897
1962
|
}
|
|
1963
|
+
// If directory has other files, we don't remove it (silently skip)
|
|
1898
1964
|
} catch {
|
|
1899
1965
|
// Ignore errors
|
|
1900
1966
|
}
|
|
1901
1967
|
}
|
|
1902
1968
|
}
|
|
1903
1969
|
|
|
1970
|
+
if (removedDirCount === 0) {
|
|
1971
|
+
console.log(chalk.gray(" โ No empty directories to remove"));
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1904
1974
|
// Step 6: Remove manifest and backups
|
|
1905
1975
|
console.log(chalk.white("\n๐๏ธ Removing StackPatch tracking files..."));
|
|
1906
1976
|
const stackpatchDir = path.join(target, ".stackpatch");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stackpatch",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Composable frontend features for modern React & Next.js apps - Add authentication, UI components, and more with zero configuration",
|
|
5
5
|
"main": "bin/stackpatch.ts",
|
|
6
6
|
"type": "module",
|