Neovim plugin that creates directories and files from JSON snippet templates.
- 📁 Create directory structures from templates
- ✨ FZF-powered snippet selection
- 🔄 Smart case transformations (camelCase, PascalCase, etc.)
- 📚 Built-in snippets included
- 🔌 Integration with mini.files
Using packer.nvim:
use {
'yourusername/scaffolder.nvim',
requires = {
'ibhagwan/fzf-lua',
'echasnovski/mini.files', -- optional
}
}
Using lazy.nvim:
{
'yourusername/scaffolder.nvim',
dependencies = {
'ibhagwan/fzf-lua',
'echasnovski/mini.files', -- optional
},
config = function()
require('scaffolder').setup({})
end
}
- Run the
:MultiFileSnippet
command in Neovim - Select a snippet from the FZF window
- Enter a name when prompted
- Files and directories will be created relative to your current directory
When using mini.files:
- Open mini.files with your preferred method (e.g.,
:lua MiniFiles.open()
) - Navigate to the directory where you want to create your new structure
- Press
<C-m>
or run:MiniFilesSnippet
- Select a snippet and enter a name
- Files will be created relative to your current directory in mini.files
The plugin looks for snippets in two locations:
- User snippets:
~/.config/nvim/snippets/multi-file/
- Built-in snippets: Included in the plugin (no setup required)
require('scaffolder').setup({
snippet_dir = '~/my-snippets', -- Optional custom path for user snippets
show_version_on_startup = true, -- Show full version info at startup
})
{
"name": "Example Snippet",
"description": "Creates a basic directory structure",
"folder_case": "kebab", // Optional: How to format folder names (kebab, snake, camel, pascal, upper, lower, original)
"files": [
{
"path": "${folder_name}/index.ts",
"content": "export * from './${name:kebab}.service';\n"
},
{
"path": "${folder_name}/${name:kebab}.service.ts",
"content": "console.log('This is the ${name:pascal} service');\n"
},
{
"path": "${folder_name}/__tests__/",
"content": ""
},
{
"path": "${folder_name}/types/${name:kebab}.type.ts",
// Multiline content using array of strings (each item is a line)
"content": [
"export type ${name:pascal} = {",
" id: string;",
" name: string;",
" createdAt: Date;",
"};"
]
}
]
}
You can use format specifiers to apply case transformations to variables:
${name}
- As entered (e.g., "test")${folder_name}
- Uses case format specified infolder_case
(or original if not specified)${name:pascal}
- PascalCase (e.g., "TestName" from any input format)${name:camel}
- camelCase (e.g., "testName" from any input format)${name:snake}
- snake_case (e.g., "test_name" from any input format)${name:kebab}
- kebab-case (e.g., "test-name" from any input format)${name:upper}
- UPPER_CASE (e.g., "TEST_NAME" from any input format)
Case transformations handle any input format consistently. For example, all of these inputs:
test-file
, TESTFILE
, Test_File
, testFile
, TestFile
will produce the same consistent output
in each format.
You can set a specific case format for folder names in your template using the folder_case
field:
{
"name": "My Template",
"folder_case": "kebab", // Will create folders in kebab-case
"files": [
// ...
]
}
Valid options for folder_case
are:
"original"
- Use the name exactly as entered (default)"kebab"
- Use kebab-case (e.g., "my-component")"snake"
- Use snake_case (e.g., "my_component")"camel"
- Use camelCase (e.g., "myComponent")"pascal"
- Use PascalCase (e.g., "MyComponent")"upper"
- Use UPPER_CASE (e.g., "MY_COMPONENT")"lower"
- Use lowercase (e.g., "mycomponent")
The ${folder_name}
variable in paths will automatically use the specified case format.
You can define multiline file content in two ways:
-
Using escaped newlines (traditional approach)
{ "path": "file.txt", "content": "Line 1\nLine 2\nLine 3" }
-
Using arrays of strings (recommended for readability)
{ "path": "file.txt", "content": [ "Line 1", "Line 2", "Line 3" ] }
Using arrays of strings makes templates more readable, especially for large code files. Variable substitution works in both formats, and you can mix both approaches in the same template.
When running :MultiFileSnippet
and selecting the "Multi-File Test" template, entering "user-profile" will create:
user-profile/
├── __tests__/
├── index.ts // exports from user-profile.service and user-profile.hooks
├── user-profile.hooks.ts // Has exports like useUserProfile()
├── user-profile.service.ts // Has exports like userProfileService
└── types/
└── user-profile.type.ts // Has types like UserProfile
The plugin comes with several built-in snippets:
- Multi-File Test - Basic TypeScript module structure with service, hooks, and types
- React Component - React component with styles and tests
- NestJS Module - Complete NestJS module with controller, service, DTOs and tests
:MultiFileSnippet
- Open FZF to select a snippet template:MiniFilesSnippet
- Same as above, but specifically for mini.files:ScaffolderVersion
- Display the current plugin version
MIT