Upload to subfolder

Hi there, i am looking for a way to quickly upload my local dev website to my test server
www.mydomain.com/myproject for example.

I am unsure how what I am doing wrong.
What files are required to do so?

I have my .htaccess as

If your homepage is http://yourdomain.com/mysite,

set the RewriteBase to:

RewriteBase /my-project

What other files should be in my dist folder? Can someone give me a full list of the required files for kirby as frontend and panel?

The only way my live server works is when i upload anything in my project folder manually. Which is odd. (its cumbersome as i do not need the . files, the node_modules, so that is why i am looking for a /dist folder way.

I have a script that copies relevant code and folders based on what i see online. but it simply does not work.

#!/usr/bin/env node

const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");

// Configuration
const DIST_FOLDER = "dist";
const SOURCE_DIR = "./";

// Directories to include in production
const INCLUDE_DIRS = ["assets", "content", "kirby", "media", "site"];

// Individual files to include
const INCLUDE_FILES = ["index.php", ".htaccess", "robots.txt"];

// Files and folders to exclude (even if in included directories)
const EXCLUDE_PATTERNS = [
  "node_modules",
  "src",
  "dist",
  "vendor/bin",
  ".git",
  ".gitignore",
  ".gitattributes",
  ".editorconfig",
  ".DS_Store",
  "package.json",
  "package-lock.json",
  "composer.json",
  "composer.lock",
  "build-dist.js",
  "tailwind.config.js",
  "README.md",
  ".env",
];

function log(message, type = "info") {
  const colors = {
    info: "\x1b[36m", // cyan
    success: "\x1b[32m", // green
    warning: "\x1b[33m", // yellow
    error: "\x1b[31m", // red
    reset: "\x1b[0m",
  };

  console.log(`${colors[type]}${message}${colors.reset}`);
}

function shouldExclude(filePath) {
  return EXCLUDE_PATTERNS.some((pattern) => {
    if (pattern.includes("*")) {
      // Simple wildcard matching
      const parts = pattern.split("*");
      if (parts.length === 2) {
        return filePath.startsWith(parts[0]) && filePath.endsWith(parts[1]);
      }
    }
    return filePath.includes(pattern);
  });
}

function copyFileSync(src, dest) {
  const destDir = path.dirname(dest);
  if (!fs.existsSync(destDir)) {
    fs.mkdirSync(destDir, { recursive: true });
  }

  try {
    fs.copyFileSync(src, dest);
  } catch (error) {
    throw new Error(`Failed to copy ${src} to ${dest}: ${error.message}`);
  }
}

function copyDirectory(src, dest, rootPath = "") {
  if (!fs.existsSync(dest)) {
    fs.mkdirSync(dest, { recursive: true });
  }

  let items;
  try {
    items = fs.readdirSync(src);
  } catch (error) {
    log(`Error reading directory ${src}: ${error.message}`, "error");
    return;
  }

  for (const item of items) {
    const srcPath = path.join(src, item);
    const destPath = path.join(dest, item);
    const relativePath = path.join(rootPath, item);

    // Skip if should be excluded
    if (shouldExclude(relativePath)) {
      log(`Skipping: ${relativePath}`, "warning");
      continue;
    }

    let stat;
    try {
      stat = fs.statSync(srcPath);
    } catch (error) {
      log(`Skipping broken link/missing file: ${relativePath}`, "warning");
      continue;
    }

    if (stat.isDirectory()) {
      copyDirectory(srcPath, destPath, relativePath);
    } else {
      try {
        copyFileSync(srcPath, destPath);
        log(`Copied: ${relativePath}`);
      } catch (error) {
        log(`Failed to copy ${relativePath}: ${error.message}`, "error");
      }
    }
  }
}

function buildDist() {
  log("๐Ÿš€ Starting Kirby CMS build process...", "info");

  // Clean dist folder
  if (fs.existsSync(DIST_FOLDER)) {
    log("๐Ÿงน Cleaning existing dist folder...", "warning");
    fs.rmSync(DIST_FOLDER, { recursive: true, force: true });
  }

  // Create dist folder
  fs.mkdirSync(DIST_FOLDER, { recursive: true });

  // Copy included directories
  for (const dirName of INCLUDE_DIRS) {
    if (fs.existsSync(dirName) && fs.statSync(dirName).isDirectory()) {
      const destPath = path.join(DIST_FOLDER, dirName);
      log(`๐Ÿ“ Copying directory: ${dirName}`, "info");
      copyDirectory(dirName, destPath, dirName);
    } else {
      log(`โš ๏ธ  Directory not found: ${dirName}`, "warning");
    }
  }

  // Copy included files
  for (const fileName of INCLUDE_FILES) {
    if (fs.existsSync(fileName) && fs.statSync(fileName).isFile()) {
      const destPath = path.join(DIST_FOLDER, fileName);
      log(`๐Ÿ“„ Copying file: ${fileName}`, "info");
      copyFileSync(fileName, destPath);
    } else {
      log(`โš ๏ธ  File not found: ${fileName}`, "warning");
    }
  }

  // Create a simple deployment info file
  const deployInfo = {
    buildDate: new Date().toISOString(),
    version: process.env.npm_package_version || "1.0.0",
    environment: "production",
    includedDirs: INCLUDE_DIRS,
    includedFiles: INCLUDE_FILES,
  };

  fs.writeFileSync(
    path.join(DIST_FOLDER, "deploy-info.json"),
    JSON.stringify(deployInfo, null, 2)
  );

  log("โœ… Build completed successfully!", "success");
  log(`๐Ÿ“ฆ Production files ready in: ${DIST_FOLDER}/`, "success");

  // Show folder size
  try {
    const stats = execSync(`du -sh ${DIST_FOLDER}`).toString().trim();
    log(`๐Ÿ“Š Dist folder size: ${stats.split("\t")[0]}`, "info");
  } catch (error) {
    // Silently fail on systems without 'du' command
  }

  // List what was copied
  log("\n๐Ÿ“‹ Summary of copied items:", "info");
  const copiedDirs = INCLUDE_DIRS.filter((dir) => fs.existsSync(dir));
  const copiedFiles = INCLUDE_FILES.filter((file) => fs.existsSync(file));

  if (copiedDirs.length > 0) {
    log(`   Directories: ${copiedDirs.join(", ")}`, "success");
  }
  if (copiedFiles.length > 0) {
    log(`   Files: ${copiedFiles.join(", ")}`, "success");
  }
}

// Run the build
try {
  buildDist();
} catch (error) {
  log(`โŒ Build failed: ${error.message}`, "error");
  process.exit(1);
}

Sorry for the bump, but i truly have issues in pulling this off.

If this is what the script does, then some folders to exclude includes folders inside the kirby folder that are needed (e.g. src, dist). You need to be very careful what you exclude.