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.
Files changed (2) hide show
  1. package/bin/stackpatch.ts +103 -33
  2. 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: Remove added files
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.0",
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",