File FoundryFile Foundry is a VS Code extension for turning reusable directory blueprints into real project files. A blueprint can contain nested folders, empty folders, dotfiles, text files, and binary files. File Foundry replaces context-aware placeholders in folder names, file names, and UTF-8 text content while copying binary content unchanged. The extension validates the entire output before it writes anything. Unknown placeholders, unsafe paths, symlinks, duplicate destinations, and unreadable sources stop the forge operation before generation begins.
Start hereNew to File Foundry? Follow this shortest path:
The directory-only format is intentionally useful: a blueprint does not need a manifest until it needs interactive behavior. Table of contentsFeature referenceFeature indexUse this index when you know the outcome you want but not the File Foundry term for it.
Set up and forgeThe forge workflow is dependency-aware: output selection happens before prompts, only active dependencies are resolved, and the complete write plan is validated before filesystem changes begin.
Configure the blueprint directoryRun File Foundry: Configure Blueprint Directory from the Command Palette. Choose a local directory, then save it to either Workspace Settings or User Settings. You can also edit File Foundry › Blueprints Directory in the Settings UI. The setting ID is:
The path may be:
A relative path is resolved against the workspace folder containing the selected target. If there is no matching folder, File Foundry uses the first workspace folder. Relative paths require an open workspace. Run File Foundry: Open Blueprint Directory to reveal the resolved directory in your operating system. Blueprint discoveryEvery immediate child directory of the configured directory is one blueprint. Its directory name is the default Quick Pick label; a manifest may override it.
When selected, the contents of the blueprint directory are copied directly into the target. File Foundry does not create an extra directory named after the blueprint. A working demonstration is included in the repository at Most Selected blueprintsMost Selected is enabled by default. File Foundry records a tiny workspace-local usage count when a valid blueprint is chosen and moves up to three frequent choices into a starred Most Selected section above the alphabetical list. A blueprint appears only once in the picker. Membership is count-first. When candidates have the same count, the three most recently selected candidates remain in the section, displayed oldest-to-newest within that tie. This gives the list predictable queue behavior:
The history uses VS Code workspace state rather than project files or settings synchronization. It is capped at 100 compact records, keeps only three displayed entries, performs one storage update per blueprint selection, and has no timers, watchers, polling, or background work. Set Author a blueprintA blueprint is ordinary files plus File Foundry's
The examples below are independent and copyable. The complete manifest shows how the pieces compose. Blueprint manifestsA blueprint may contain an optional
Set An invalid manifest is shown using its directory name without disrupting other blueprints. Selecting it reports the manifest error. A blueprint without a manifest retains the original behavior: its directory name is shown, no custom prompts appear, and all source contents are generated. Custom placeholdersManifest-defined custom values use identifier keys beginning with a letter and containing letters, numbers, or underscores:
Use them in directory names, file names, and text content:
Custom values may reference built-in placeholders, prompt placeholders, or other custom placeholders. File Foundry resolves them through an explicit dependency graph, then applies any transform at the usage location. Circular dependencies are rejected with their chain, such as Interactive promptsPrompt definitions live in the ordered Only prompts needed by selected output names, selected text content, or required custom-placeholder dependencies are shown. Prompts referenced only by unselected files are skipped. Pressing Escape during output selection or any prompt cancels the entire forge without writing files or displaying an alarming error.
Every prompt uses
Picker options support Input prompts
Input defaults may contain built-in context placeholders, but not prompt or custom placeholders. WordPress package blueprints may resolve a namespace from a workspace
File Foundry prefers the Pick and multi-pick promptsPick options require distinct
A
Confirm promptsConfirm prompts default to labels
File and folder promptsFile and folder prompts use the native open dialog and support these
File prompts can provide VS Code filters:
A folder prompt uses the same shape with CollectionsCollections are named arrays of structured records. A collection can discover files and folders or extract data from one text file. Templates can render every record directly, or a
Collection names start with a letter and contain only letters, numbers, and underscores. Collections are resolved only when a selected output references them, and each named collection is scanned once per forge operation. An unused collection—even one referenced by an unselected optional file—is not resolved.
Filesystem collections
Hidden entries are excluded when
Symlinks are skipped and logged by default. Filesystem sorting supports Source scopes and pathsEvery collection has a
Source paths may use built-in placeholders and custom placeholders that ultimately depend only on built-ins:
Collection source paths may also depend on an earlier scalar prompt or on a field from an earlier single-record collection selection:
File Foundry stages this as component discovery → component selection → prop extraction → prop checklist. Multi-selection prompts cannot feed source paths, record selections must name a field, and collection/prompt dependency cycles are rejected during manifest validation. Filesystem record fieldsFilesystem records expose only portable, relative metadata—never absolute source paths:
Empty collection behavior
Use
Do not confuse collection-level Extract collectionsAn extract collection reads one UTF-8 text file and runs it through the central extractor registry. Binary sources are rejected.
Extract collections preserve extractor source order by default. They can also sort by a top-level scalar result field.
|
| Value | Syntax |
|---|---|
| Built-in target context | FolderName, FolderLetter, DirName, DirLetter |
| Scalar or confirm prompt | Prompt:Key |
| Single selected record field | Prompt:Key.field returns one scalar |
| Multi-selected records | Prompt:Key as a collection truthy check |
| Multi-selected record fields | Prompt:Key.field returns the selected scalar values for contains |
| Named collection | Collection:Key |
| Custom placeholder | Custom:Key |
| Active loop record field | Alias:field |
| Loop metadata | Alias:@index, @number, @first, @last, or @count |
Missing references and fields are errors rather than false values. Loop aliases remain scoped to their loop, including conditionals nested inside that loop.
Literals and comparisons
The safe condition language supports:
- Single- and double-quoted strings, with escaped matching quotes and backslashes
- Integers and decimals, including negative values
- The case-sensitive literals
true,false, andnull - Strict equality and inequality with
==and!= - Relational comparison with
>,>=,<, and<= - Case-sensitive membership and substring checks with
contains - Parentheses
Equality never coerces types: "1" == 1 and true == "true" are false. String equality is case-sensitive. Relational operators require two numbers or two strings of the same type; booleans, null, records, and collections are rejected. contains searches for a case-sensitive substring in a string. With a multi-select prompt field such as Prompt:Props.name, it succeeds when any selected field value contains the scalar value. With other scalar collections it checks strict membership.
[[#if Prompt:Props.name contains "Button" or Prompt:Props.name contains "Link"]]
import { getButtonField } from '@lib/contentful/utils/field';
[[/if]]
Test a collection directly for emptiness rather than comparing it with a scalar:
[[#if Collection:ComponentFiles]]
Components were found.
[[#else]]
No components were found.
[[/if]]
Template literals, regex literals, function calls, arithmetic, assignment, indexing, object/array literals, and arbitrary JavaScript are not supported or executed.
Logical operators and precedence
Use the keyword operators not, and, and or. Symbolic !, &&, and || are deliberately rejected.
Precedence from highest to lowest is:
- Parentheses
not- Comparisons
andor
Therefore Prompt:A or Prompt:B and Prompt:C means Prompt:A or (Prompt:B and Prompt:C). Parentheses can override that order.
Truthiness and confirm prompts
False values are false, null, numeric zero, an empty or whitespace-only string, and an empty collection. Non-zero numbers, non-empty strings, non-empty collections, and valid records are true. The string "false" is a non-empty string and is therefore true; compare it explicitly when that is the intended test.
Confirm prompts retain two internal forms. Conditions use the raw selected boolean, while ordinary placeholders preserve the configured output string:
{
"key": "IncludeStyles",
"type": "confirm",
"trueValue": "with-styles",
"falseValue": "without-styles"
}
With a false selection, [[#if Prompt:IncludeStyles]] is false while [[Prompt:IncludeStyles]] still renders without-styles.
An optional stylesheet import can therefore be written as:
[[#if Prompt:IncludeStyles]]
import './[[FolderName>PascalCase]].scss';
[[/if]]
Conditions and loops
Conditions may appear inside loops, and active conditional branches may contain loops. Loop fields and metadata keep their native boolean and numeric types during evaluation:
[[#each Prompt:SelectedProps as Prop]]
[[Prop:name]][[#if not Prop:@last]], [[/if]]
[[/each]]
[[#each Prompt:SelectedProps as Prop]]
[[#if Prop:hasDefault]]
[[Prop:name]]: [[Prop:defaultValue]],
[[#else]]
[[Prop:name]]: undefined,
[[/if]]
[[/each]]
Branch-specific collections work without unnecessary scans:
[[#if Prompt:IncludeStories]]
[[#each Collection:Stories as Story]]
export { [[Story:name>PascalCase]] } from './[[Story:relativePath]]';
[[/each]]
[[/if]]
File Foundry resolves the first reachable condition dependencies, evaluates it, and then resolves only the selected branch body. Later elseif expressions are staged sequentially, so once a branch matches, later conditions are not prompted or resolved. Inactive bodies do not request prompts, scan collections, run extractors, validate semantic placeholders, or render loops. Their block and expression syntax is still parsed and must be structurally valid.
Selected file groups are available to conditions as Output:<optionKey>. For example, [[#if Output:setup]]...[[/if]] renders only when the setup file-selection option was chosen. Output selections are known before blueprint prompts and do not create additional questions.
Conditional and loop blocks are supported only in UTF-8 file contents. They are rejected in file and directory names, blueprint.json, custom definitions, collection paths, prompt text, option templates, and binary files. Errors identify the blueprint, relative source path, and line and column where available.
The condition engine tokenizes and parses this deliberately limited grammar into an internal syntax tree. It never uses eval, new Function, the Node VM, or source-code execution.
Selectable output
Set fileSelection.enabled to show a multi-selection Quick Pick before blueprint prompts:
{
"fileSelection": {
"enabled": true,
"title": "Choose component files",
"placeholder": "Select the features to forge.",
"includeUnlisted": true,
"options": [
{
"key": "component",
"label": "Component",
"description": "Primary component file",
"files": ["[[FolderName>PascalCase]].js"],
"required": true,
"defaultSelected": true
},
{
"key": "stories",
"label": "Storybook",
"files": ["stories", { "glob": "**/*.stories.js" }],
"defaultSelected": false
}
]
}
}
Option keys follow the same identifier rules as prompt keys. Required options cannot remain deselected. defaultSelected preselects an optional option. Overlapping options are safe: each source is generated once.
If any file belonging to an initially selected option already exists in the target, File Foundry clears every initial checklist selection. This prevents a partially existing scaffold from silently carrying forward the remaining defaults; the user explicitly chooses which outputs to attempt.
String entries in files are literal blueprint-relative source paths before placeholder replacement. A literal directory includes its complete subtree, and placeholder-containing source names must use this form. Paths are separator-normalized for portability and must exist inside the blueprint.
Objects with glob use minimatch against normalized source paths. A glob must be safe, contain no File Foundry placeholders, and match at least one source. Use literal paths when the source name itself contains [[...]].
includeUnlisted defaults to true, automatically generating sources not matched by any option, such as index.js. When false, only selected or required options are generated. If selection resolves to no files or directories, File Foundry reports that nothing was selected and writes nothing.
Output formatters and workspace edits
An alignAssignments formatter lines up consecutive $this->name = value statements in selected generated source files after placeholders and conditions render:
{
"formatters": [
{
"type": "alignAssignments",
"sourceFiles": ["class-[[Prompt:ModuleName>KebabCase]].php"]
}
]
}
Use alignPhpOperators for ordinary PHP variable assignments and associative-array arrows. It aligns each consecutive group independently and leaves unrelated lines untouched:
{
"formatters": [
{
"type": "alignPhpOperators",
"sourceFiles": ["[[FolderName]].block.php", "[[FolderName]].stories.php"]
}
]
}
$id = get_block_id($block);
$custom_class = get_block_class($block);
'id' => $id,
'class' => $custom_class,
sourceFiles always names blueprint-relative source paths, before placeholders are replaced. A formatter runs only on the listed UTF-8 sources and runs after loops, conditions, and ordinary placeholders have rendered.
WordPress package blueprints can register their selected parent module and functions file in a containing useful-group/functions.php:
{
"workspaceEdits": [
{
"type": "usefulGroupPhpRegistry",
"moduleNamePrompt": "ModuleName",
"parentModuleOption": "parentModule",
"functionsOption": "functions"
}
]
}
The three reference keys default to the values shown. File Foundry updates the registry only when the matching output is selected, the generated files are inside a workspace folder named useful-group, and that folder has a root functions.php. It adds the public module property, aligns all Includes constructor assignments, appends the module-array entry, and adds the generated functions dependency. Existing entries are not duplicated. The root file is read and validated during preflight, and forging stops before any output is written if it changes or its expected PHP structure cannot be recognized.
The WordPress component blueprint uses an output route for its Template Block option:
{
"outputRoutes": [
{
"type": "wordpressTemplateBlock",
"option": "templateBlock",
"legacySource": "_page-builder/[[FolderName]].php",
"modernSource": "_page-builder/[[FolderName]].block.php"
}
]
}
When that option is selected, File Foundry asks between the legacy Template Blocks Folder destination and the modern Component Folder destination. The legacy route searches the containing useful-group/template-blocks tree for a real folder named page-builder; a missing template-blocks or page-builder directory produces a warning and skips only that routed file. The modern route writes [[FolderName]].block.php directly in the forge target. Route metadata directories are never generated in the target.
Complete manifest example
{
"version": 1,
"name": "Atomic React Component",
"description": "Creates a component with optional styles and tests.",
"placeholders": {
"ComponentName": { "value": "[[FolderName>PascalCase]]" },
"ClassBase": { "value": "[[DirLetter>LowerCase]]-[[FolderName>KebabCase]]" },
"DisplayName": { "value": "[[Prompt:ComponentLabel>TitleCase]]" }
},
"prompts": [
{
"key": "ComponentLabel",
"type": "input",
"title": "Component label",
"default": "[[FolderName>TitleCase]]",
"required": true
},
{
"key": "RootElement",
"type": "pick",
"title": "Root element",
"options": [
{ "label": "Div", "value": "div" },
{ "label": "Section", "value": "section" },
{ "label": "Article", "value": "article" }
],
"default": "div"
}
],
"fileSelection": {
"enabled": true,
"includeUnlisted": true,
"options": [
{
"key": "component",
"label": "Component",
"files": ["[[FolderName>PascalCase]].js"],
"required": true,
"defaultSelected": true
},
{
"key": "styles",
"label": "Stylesheet",
"files": ["[[FolderName>PascalCase]].scss"],
"defaultSelected": true
},
{
"key": "tests",
"label": "Tests",
"files": [{ "glob": "**/*.test.js" }],
"defaultSelected": false
}
]
}
}
Selected text may then use all three placeholder families:
const [[Custom:ComponentName]] = ({
tag: Tag = '[[Prompt:RootElement]]'
}) => (
<Tag className="[[Custom:ClassBase]]">
[[Custom:DisplayName]]
</Tag>
);
Forge a blueprint
- Right-click a folder in the VS Code Explorer.
- Select File Foundry: Forge Blueprint Here.
- Pick a blueprint.
- Choose optional outputs when the manifest enables file selection.
- Answer only the prompts required by those outputs.
- If destination files already exist, File Foundry lists and skips them while generating the remaining files.
The forge command is also available from the Command Palette. When it is run there, File Foundry opens a folder picker for the target.
After a successful forge, File Foundry opens the generated file when exactly one new file was created. Multi-file results stay focused on the current editor. The result notification reports created folders and files plus any skipped files, and its Reveal Target Folder action selects the target in the Explorer. Detailed activity is available in the File Foundry output channel.
Built-in placeholders
Placeholder and transformation names are case-sensitive. Whitespace and transform chaining are not supported.
| Placeholder | Value for target /components/molecules/reading-time |
|---|---|
[[FolderName]] |
reading-time |
[[FolderLetter]] |
r |
[[DirName]] |
molecules |
[[DirLetter]] |
m |
Add one optional transformation after >:
[[FolderName>PascalCase]]
Transformations
For the input menu-link:
| Transformation | Result |
|---|---|
UpperCase |
MENU LINK |
LowerCase |
menu link |
SentenceCase |
Menu link |
TitleCase |
Menu Link |
SingularTitleCase |
Menu Link |
CamelCase |
menuLink |
PascalCase |
MenuLink |
SnakeCase |
menu_link |
UpperSnakeCase |
MENU_LINK |
KebabCase |
menu-link |
TrainCase |
Menu-Link |
FlatCase |
menulink |
The correct spelling is KebabCase, not KababCase. SingularTitleCase conservatively singularizes the final display word, such as component-types → Component Type. All transforms share the same tokenizer and handle spaces, hyphens, underscores, dots, camelCase, PascalCase, acronyms such as HTMLParser, and values such as version2-item.
Names and nested paths
Placeholders are evaluated independently in every path segment. This blueprint path:
[[FolderName>PascalCase]]/[[DirLetter>LowerCase]]-[[FolderName>KebabCase]].test.js
becomes this path for a reading-time target inside molecules:
ReadingTime/m-reading-time.test.js
Multiple placeholders can appear in one name. A generated name may not be empty, absolute, invalid for the current platform, or escape the selected target.
Text content
For a target named reading-time, this blueprint content:
const name = '[[FolderName>PascalCase]]';
const value = `${someValue}`;
becomes:
const name = 'ReadingTime';
const value = `${someValue}`;
Only File Foundry's double-square-bracket syntax is interpreted. JavaScript template expressions and ordinary square brackets remain unchanged.
Reading-time example
For the target /components/molecules/reading-time, File Foundry derives:
FolderName = reading-time
FolderLetter = r
DirName = molecules
DirLetter = m
A file named [[FolderName>PascalCase]].js becomes ReadingTime.js. Inside that file:
const ReadingTime = () => {
const classBase = 'm-reading-time';
return <div className={classBase} />;
};
export default ReadingTime;
can be produced from:
const [[FolderName>PascalCase]] = () => {
const classBase = '[[DirLetter>LowerCase]]-[[FolderName>KebabCase]]';
return <div className={classBase} />;
};
export default [[FolderName>PascalCase]];
Existing files
File Foundry never overwrites generated destination files. When one or more files already exist, it shows one alert with a bulleted skipped-file list. After the user confirms the alert, File Foundry preserves those files and generates every non-conflicting file. Prompts used only by skipped files are not shown.
Directories are merged; a file/directory type collision is a validation error.
Commands
Open these from the Command Palette unless a location is noted.
| Command | What it does |
|---|---|
| File Foundry: Forge Blueprint Here | Chooses a target when necessary, then runs the complete forge workflow. Also appears on Explorer folders. |
| File Foundry: Configure Blueprint Directory | Picks a local blueprint root and saves it to Workspace or User Settings. |
| File Foundry: Open Blueprint Directory | Reveals the resolved blueprint root in the operating system. |
| File Foundry: Open Custom Extractors Directory | Reveals the configured trusted extractor-module directory. |
| File Foundry: Reload Custom Extractors | Clears the extractor cache and reloads presets and custom modules without restarting VS Code. |
| File Foundry: List Registered Extractors | Shows every built-in, preset, and custom extractor with its source. |
| File Foundry: Clear Most Selected Blueprints | Deletes the bounded Most Selected history for the current workspace. |
Settings
| Setting | Default | Purpose |
|---|---|---|
fileFoundry.blueprintsDirectory |
Empty | Required blueprint root; accepts absolute, ~-relative, or workspace-relative paths. |
fileFoundry.extractorsFile |
Empty | Optional version 1 JSON file containing declarative extractor presets. |
fileFoundry.customExtractorsDirectory |
Empty | Optional trusted directory containing CommonJS extractor modules. |
fileFoundry.mostSelectedBlueprints |
true |
Enables the workspace-local starred Most Selected section; may be overridden globally or per workspace. |
Settings are resource-scoped, so a multi-root workspace may use a different blueprint or extractor configuration for each folder. Custom JavaScript extractors are disabled in untrusted workspaces; built-in extractors and declarative presets remain available.
Performance and resource use
File Foundry is command-activated and does not run while idle. It registers commands on activation but creates no filesystem watchers, timers, polling loops, language services, or background indexes.
The forge path is intentionally bounded and lazy:
- Blueprint manifests are read with an eight-operation concurrency ceiling, preventing both slow serial discovery and unbounded I/O bursts.
- The manifest selected in the picker is reused rather than read and parsed a second time.
- Source buffers inspected for early prompts are reused during final planning instead of read twice.
- Extractor registries are cached by configuration and trust signature, then rebuilt only when configuration changes or the reload command is used.
- Collections, extractors, prompts, conditions, and loops resolve only when selected output depends on them, and each collection resolves once per forge.
- Most Selected retains at most 100 small workspace records and computes only a three-item view when the picker opens.
- Complete preflight data exists only for the duration of one forge operation and becomes collectible when the command finishes.
- The published extension is bundled into one extension-host entry file, avoiding hundreds of small runtime module loads and excluding build dependencies from the VSIX.
These constraints keep idle CPU at zero, background memory effectively constant, and active work proportional to the blueprint files and project data the user explicitly selected.
Troubleshooting
The blueprint directory is missing or invalid
Use the action in the error notification to open fileFoundry.blueprintsDirectory, or run File Foundry: Configure Blueprint Directory again. Confirm that the path exists, is a readable local directory, and—if it is relative—that a workspace is open.
An unknown placeholder or transformation is reported
The error includes the invalid expression and the blueprint-relative source path. Compare it with the exact names documented above. Names are case-sensitive, transforms cannot be chained, internal whitespace is invalid, and the correct spelling is KebabCase.
A blueprint is rejected before generation
Inspect the File Foundry output channel for details. Preflight intentionally rejects symlinks, duplicate generated paths, unreadable files, path traversal, unsafe destination ancestors, invalid file-system names, and malformed placeholders before it writes output.
A manifest is rejected
Confirm that blueprint.json is strict JSON with "version": 1. Errors identify invalid prompt defaults, duplicate keys, unknown prompt types, unsafe file selectors, undefined references, and circular custom-placeholder chains. Unknown top-level properties are warnings rather than errors and appear in the File Foundry output channel.
Development
The extension uses JavaScript, minimatch for production-grade glob matching, and @babel/parser for AST-based JavaScript/TypeScript prop extraction.
Development and packaging require Node.js 18 or newer. The installed extension uses VS Code's extension host and ships a single esbuild-generated runtime bundle; source modules remain separate for tests and maintenance.
npm test
npm run lint
npm run check
npm run build
npm run test:bundle
npm run package
Press F5 in VS Code to launch an Extension Development Host using the included launch configuration.