Skip to content

Allow --copy to handle individual files, not just directories #236

@bcomnes

Description

@bcomnes

This issue was originally identified in voxpelli/voxpelli.github.com#32

Problem

The --copy flag only accepts directories. Individual root-level files that need to end up in the build output require a manual post-build shell command. Any file that lives outside src/ but must appear in the output directory — service workers, Netlify _redirects, _headers, .well-known/* files, etc. — cannot be handled by the DomStack build pipeline alone.

These files cannot live inside src/ because DomStack treats .js files there as page modules, layout modules, or template modules based on naming conventions.

Expected: --copy sw.js copies a single file into the output root, just as --copy images copies a directory.

Actual: --copy calls getCopyDirs() which unconditionally appends /** to every path, treating all arguments as directories. A file path like sw.js becomes the glob sw.js/**, which matches nothing.

Current behavior

// lib/build-copy/index.js
export function getCopyDirs (copy = []) {
  const copyGlobs = copy?.map((dir) => join(dir, '**'))
  return copyGlobs
}

For sw.js, the glob sw.js/** matches nothing — cpx2 silently produces no output.

Real-world workaround

"build": "domstack --copy images --copy media && cp sw.js public/sw.js",
"dev": "domstack --watch --copy images --copy media"

Note: the dev (watch) script does not include the cp sw.js step. This means changes to sw.js during watch mode are never copied — the service worker is stale or missing during local development.

Proposed solution

Option A: Let --copy accept both files and directories (recommended)

Detect whether the argument is a file or directory and handle accordingly:

import { stat } from 'node:fs/promises';

export async function getCopyGlobs (copy = []) {
  const globs = await Promise.all(copy.map(async (entry) => {
    try {
      const stats = await stat(entry);
      if (stats.isDirectory()) {
        return join(entry, '**');
      } else if (stats.isFile()) {
        return entry;
      }
    } catch {
      // Path doesn't exist yet — treat as glob pattern
    }
    return entry;
  }));
  return globs;
}

Usage unchanged:

domstack --copy images --copy media --copy sw.js --copy _redirects

Option B: Add a --copy-file flag for explicit semantics

Option C: Support a static/ directory convention

A static/ directory at the project root whose contents are copied verbatim to the output root, similar to Astro's public/, Vite's public/, or Next.js's public/.

Recommendation: Option A is the smallest useful change. Option C could be added later as a complementary feature.

Common files that would benefit

  • sw.js / service workers
  • _redirects (Netlify/Cloudflare Pages)
  • _headers (Netlify/Cloudflare Pages)
  • .well-known/webfinger (Fediverse discovery)
  • ads.txt, security.txt, browserconfig.xml

See also: issue #34 (Service worker support).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions