WritedocsWritedocs

Leaving files out

A docs project often shares its folder with things that aren’t the site: a package.json, a .env, an export with customer data. Writedocs leaves a list of such files out by default, and a .wdignore file at the project root lets you add your own - or bring a default one back.

A file that’s left out is never published and never read as content: it isn’t published at its own path, it isn’t a page, its CSS or JavaScript isn’t loaded, and an image in it isn’t shown.

The .wdignore file

Create a file named .wdignore next to writedocs.json, with one pattern per line. The syntax is the one .gitignore uses:

.wdignore
# Drafts and internal material
drafts/
internal-notes.md

# Every CSV, except the public ones
*.csv
!data/public/*.csv
PatternWhat it leaves out
drafts/Every folder named drafts, at any depth, and everything in it.
/drafts/Only the drafts folder at the project root.
internal-notes.mdEvery file (or folder) with that name, at any depth.
docs/guides/old.mdxThat one file. A pattern with a / in the middle starts at the project root.
*.csvEvery file ending in .csv. * doesn’t cross a /.
data/**/*.csvEvery .csv under data/, at any depth.
!data/public/*.csvBrings back what an earlier line left out.
# ...A comment. Blank lines are skipped too.

Later lines win over earlier ones, and upper and lower case are treated as the same.

A file can't come back while its folder is left out#

As in .gitignore: once a folder is left out, a ! line for a file inside it does nothing. Bring the folder back (!drafts/), then leave out what you don’t want in it.

writedocs dev picks up a change to .wdignore without a restart.

What’s left out by default

These apply to every project, with or without a .wdignore.

Never published

Secrets. No .wdignore line brings these back, and they’re left out of the public/ folder too:

PatternWhat it is
.env, .env.*Environment files - API keys and passwords.
.git/The repository’s history.
.npmrcPackage manager configuration, which can hold an access token.
*.pem, *.key, *.p12, *.pfxPrivate keys, and the files that carry them.
id_rsa*, id_dsa*, id_ecdsa*, id_ed25519*SSH keys.

Left out unless you bring them back

Tooling and data that a docs repository holds but a site rarely means to publish. A ! line in .wdignore brings one back:

PatternWhat it is
.*Every file and folder whose name starts with a dot: .gitignore, .gitmodules, .gitattributes, .github/, .vscode/, .DS_Store, .wdignore itself, .writedocs/ …
package.json, package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, composer.json, composer.lockPackage manifests and lockfiles.
node_modules/, dist/Dependencies and build output, at any depth.
tsconfig.json, jsconfig.json, vercel.json, netlify.json, netlify.toml, wrangler.json, wrangler.jsonc, wrangler.toml, Dockerfile, docker-compose*.yml, docker-compose*.yamlTooling and hosting configuration.
*.crt, *.jks, *.keystoreCertificates and key stores.
*.sqlite, *.sqlite3, *.db, *.sqlDatabases and dumps.
*.logLogs.

Each one applies at any depth: examples/app/package.json is left out like the one at the root.

Bringing a default back

Some of these are sometimes real content - a package.json a guide walks through, a SQL file to download, a .well-known/ folder. Add a ! line:

.wdignore
# The example app's manifest, for readers to download
!examples/app/package.json

# A folder whose name starts with a dot
!.well-known/

# The built example, zipped
!examples/app/dist/

Bringing a file back makes it a file like any other, so the usual rule still decides whether it’s published: it is when it’s one of the kinds of file Writedocs publishes. !.well-known/ publishes .well-known/security.txt, and !examples/app/package.json publishes that file. A .sql file isn’t one of those kinds - to offer one for download, put it in public/, where it’s published without a .wdignore line.

Three things don’t come back:

  • The files under Never published.
  • node_modules/, dist/ and .writedocs/ at the project root.
  • Pages, CSS and JavaScript inside a folder whose name starts with a dot. !.drafts/ publishes the PDFs and images in .drafts/, but an .mdx file there still isn’t a page.

The public folder

Everything in a public/ folder is published as it is - putting a file there already says “publish this”. So the defaults under Left out unless you bring them back don’t apply inside it: public/.well-known/security.txt and public/downloads/schema.sql are published with no .wdignore at all.

Two things still apply in public/:

  • The files under Never published. A public/.env or public/server.key is not published.
  • Your own .wdignore lines. A pattern matches the file’s path from the project root, so public/old/ leaves that folder out.

A page can still name a file that’s left out - link to it, or show it as an image. The build doesn’t fail: the file just isn’t on the site, so the link leads nowhere and the image doesn’t load.

writedocs broken-links finds these, and says why:

index.mdx
  12  /internal/plan.pdf - internal/plan.pdf is in the project, but the ignore rules leave it out, so it isn't published.
      To publish it, bring it back in .wdignore - a line with !internal/plan.pdf, or its folder's (!folder/) when the whole folder is left out.

What to keep in mind

  • Don’t leave out a file the site is built from. A snippet a page imports, or the OpenAPI spec an API reference reads, has to be there when the site is built - with one of them left out, the build fails. writedocs broken-links reports a page that imports a snippet that’s left out. writedocs.json is always read and never published, whatever .wdignore says.
  • A page that’s left out has no address. If writedocs.json’s navigation still lists it, writedocs validate reports the missing page.
  • It decides what’s published, not what’s in your repository. A secret that’s committed is still in the repository’s history. .wdignore keeps it off the site.
  • .mintignore still works. A project that came from Mintlify keeps its .mintignore, which lists files that aren’t pages. .wdignore does that too, and also covers every other file - use it for anything new.