create-vault-cms 1.0.6 ā 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -91
- package/package.json +2 -2
- package/src/cli.js +204 -173
package/README.md
CHANGED
|
@@ -1,91 +1,47 @@
|
|
|
1
|
-
# Vault CMS
|
|
2
|
-
|
|
3
|
-
Use [Obsidian](https://obsidian.md) as a content management system for your [Astro](https://astro.build) website.
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
# Using pnpm
|
|
50
|
-
pnpm create vault-cms -- --template starlight
|
|
51
|
-
|
|
52
|
-
# Using npm
|
|
53
|
-
npm create vault-cms -- --template starlight
|
|
54
|
-
|
|
55
|
-
# Using yarn
|
|
56
|
-
yarn create vault-cms --template starlight
|
|
57
|
-
```
|
|
58
|
-
*(Replace `starlight` with `slate` or `chiri` as needed).*
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
This will automatically:
|
|
63
|
-
1. Copy the necessary `_bases` and `.obsidian` configuration folders.
|
|
64
|
-
2. Setup a `README.md` for your vault.
|
|
65
|
-
3. Update your `.gitignore` with the recommended Obsidian excludes.
|
|
66
|
-
|
|
67
|
-
### Manual Installation
|
|
68
|
-
|
|
69
|
-
If you prefer to install manually:
|
|
70
|
-
1. Download the [latest release ZIP](https://github.com/davidvkimball/vault-cms/archive/refs/heads/master.zip).
|
|
71
|
-
2. Copy the `_bases` and `.obsidian` folders into your Astro project (e.g., in `src/content`).
|
|
72
|
-
3. Open Obsidian and select "Open folder as vault", then select the folder containing the `.obsidian` directory.
|
|
73
|
-
|
|
74
|
-
## How Auto-Detection Works
|
|
75
|
-
|
|
76
|
-
Vault CMS automatically detects and configures itself based on your Astro project:
|
|
77
|
-
|
|
78
|
-
- **Project Detection**: Automatically finds your Astro project by locating `astro.config.mjs`, `astro.config.ts`, or other Astro config files.
|
|
79
|
-
- **Content Type Detection**: Scans your content folders (like `posts`, `pages`, `docs`, etc.) and automatically identifies them as content types.
|
|
80
|
-
- **Frontmatter Analysis**: Analyzes existing content files to detect frontmatter properties (title, date, description, etc.) and configures the plugin accordingly.
|
|
81
|
-
|
|
82
|
-
When you first open the vault, a setup wizard will guide you through the configuration process.
|
|
83
|
-
|
|
84
|
-
### Recommended .gitignore
|
|
85
|
-
|
|
86
|
-
If you are not using the CLI, add the following to your Astro project's `.gitignore` file:
|
|
87
|
-
```
|
|
88
|
-
# Obsidian
|
|
89
|
-
.obsidian/workspace.json
|
|
90
|
-
.obsidian/workspace-mobile.json
|
|
91
|
-
```
|
|
1
|
+
# Vault CMS
|
|
2
|
+
|
|
3
|
+
Use [Obsidian](https://obsidian.md) as a content management system for your [Astro](https://astro.build) website.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
The fastest way to install Vault CMS into your Astro project is via the CLI:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm create vault-cms
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
*Follow the prompts to install into `src/content` or your desired directory.*
|
|
16
|
+
|
|
17
|
+
## Documentation
|
|
18
|
+
|
|
19
|
+
For full installation guides, plugin details, and customization options, read the [Vault CMS documentation](https://docs.vaultcms.org).
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- Easy integration into Astro website projects
|
|
24
|
+
- **Auto-detection** of your Astro theme and content structure
|
|
25
|
+
- Preconfigured plugins, hotkeys and settings optimized for Astro workflows
|
|
26
|
+
- CMS-like homepage using Obsidian Bases
|
|
27
|
+
- Works with any Astro theme by automatically detecting content types and frontmatter properties
|
|
28
|
+
- Optional instant-publish option via the Git plugin
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
## Video Guide
|
|
33
|
+
|
|
34
|
+
šŗ [Video Guide](https://youtu.be/dSm8aLPdVz0)
|
|
35
|
+
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> To see Vault CMS combined with an Astro site specifically designed with it in mind, check out my theme [Astro Modular](https://github.com/davidvkimball/astro-modular).
|
|
38
|
+
|
|
39
|
+
## Presets
|
|
40
|
+
|
|
41
|
+
If you are using a supported theme like **Starlight**, **Slate**, or **Chiri**, you can use a preconfigured preset:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm create vault-cms -- --template starlight
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See all available presets at the [Presets Repo](https://github.com/davidvkimball/vault-cms-presets).
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,173 +1,204 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { Command } = require('commander');
|
|
4
|
-
const fs = require('fs-extra');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const https = require('https');
|
|
7
|
-
const AdmZip = require('adm-zip');
|
|
8
|
-
const inquirer = require('inquirer');
|
|
9
|
-
|
|
10
|
-
const pkg = require('../package.json');
|
|
11
|
-
|
|
12
|
-
const program = new Command();
|
|
13
|
-
|
|
14
|
-
program
|
|
15
|
-
.name('create-vault-cms')
|
|
16
|
-
.description('Official installer for Vault CMS')
|
|
17
|
-
.version(pkg.version);
|
|
18
|
-
|
|
19
|
-
program
|
|
20
|
-
.argument('[target]', 'target directory')
|
|
21
|
-
.option('-t, --template <name>', 'template to use (from vault-cms-presets)')
|
|
22
|
-
.action(async (target, options) => {
|
|
23
|
-
try {
|
|
24
|
-
console.log('š Initializing Vault CMS Installer...');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
await
|
|
81
|
-
|
|
82
|
-
console.log('
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const AdmZip = require('adm-zip');
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
|
|
10
|
+
const pkg = require('../package.json');
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('create-vault-cms')
|
|
16
|
+
.description('Official installer for Vault CMS')
|
|
17
|
+
.version(pkg.version);
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.argument('[target]', 'target directory')
|
|
21
|
+
.option('-t, --template <name>', 'template to use (from vault-cms-presets)')
|
|
22
|
+
.action(async (target, options) => {
|
|
23
|
+
try {
|
|
24
|
+
console.log('š Initializing Vault CMS Installer...');
|
|
25
|
+
|
|
26
|
+
const availableTemplates = await fetchTemplates();
|
|
27
|
+
|
|
28
|
+
let template = options.template;
|
|
29
|
+
let targetPath = target;
|
|
30
|
+
|
|
31
|
+
if (targetPath && availableTemplates.includes(targetPath.toLowerCase()) && !template) {
|
|
32
|
+
template = targetPath.toLowerCase();
|
|
33
|
+
targetPath = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!template) {
|
|
37
|
+
const { useTemplate } = await inquirer.prompt([{
|
|
38
|
+
type: 'confirm',
|
|
39
|
+
name: 'useTemplate',
|
|
40
|
+
message: 'Would you like to use a preset template (e.g. Starlight, Slate)?',
|
|
41
|
+
default: false
|
|
42
|
+
}]);
|
|
43
|
+
|
|
44
|
+
if (useTemplate) {
|
|
45
|
+
const { selectedTemplate } = await inquirer.prompt([{
|
|
46
|
+
type: 'list',
|
|
47
|
+
name: 'selectedTemplate',
|
|
48
|
+
message: 'Select a template:',
|
|
49
|
+
choices: availableTemplates
|
|
50
|
+
}]);
|
|
51
|
+
template = selectedTemplate;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!targetPath) {
|
|
56
|
+
const answers = await inquirer.prompt([
|
|
57
|
+
{
|
|
58
|
+
type: 'input',
|
|
59
|
+
name: 'path',
|
|
60
|
+
message: 'Where should we install Vault CMS?',
|
|
61
|
+
default: 'src/content',
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
targetPath = answers.path;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const targetDir = path.resolve(targetPath);
|
|
68
|
+
const tempZip = path.join(targetDir, 'vault-cms-temp.zip');
|
|
69
|
+
const extractDir = path.join(targetDir, '.vault-cms-temp-extract');
|
|
70
|
+
|
|
71
|
+
const repoName = template ? 'vault-cms-presets' : 'vault-cms';
|
|
72
|
+
const zipUrl = `https://github.com/davidvkimball/${repoName}/archive/refs/heads/master.zip`;
|
|
73
|
+
|
|
74
|
+
console.log(`\nš Installing Vault CMS${template ? ` (template: ${template})` : ''}...`);
|
|
75
|
+
console.log(` š Target directory: ${targetDir}`);
|
|
76
|
+
|
|
77
|
+
await fs.ensureDir(targetDir);
|
|
78
|
+
|
|
79
|
+
console.log(' š¦ Downloading archive...');
|
|
80
|
+
await downloadFile(zipUrl, tempZip);
|
|
81
|
+
|
|
82
|
+
console.log(' š Extracting files...');
|
|
83
|
+
const zip = new AdmZip(tempZip);
|
|
84
|
+
zip.extractAllTo(extractDir, true);
|
|
85
|
+
|
|
86
|
+
const items = await fs.readdir(extractDir);
|
|
87
|
+
const folders = items.filter(item => fs.statSync(path.join(extractDir, item)).isDirectory());
|
|
88
|
+
|
|
89
|
+
if (folders.length === 0) {
|
|
90
|
+
throw new Error('Could not find content in the downloaded archive.');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const innerFolder = path.join(extractDir, folders[0]);
|
|
94
|
+
const sourcePath = template ? path.join(innerFolder, template) : innerFolder;
|
|
95
|
+
|
|
96
|
+
if (!(await fs.pathExists(sourcePath))) {
|
|
97
|
+
throw new Error(`Template "${template}" not found in presets repository.`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const toKeep = ['_bases', '.obsidian', 'README.md'];
|
|
101
|
+
for (const item of toKeep) {
|
|
102
|
+
const src = path.join(sourcePath, item);
|
|
103
|
+
const dest = path.join(targetDir, item);
|
|
104
|
+
|
|
105
|
+
if (await fs.pathExists(src)) {
|
|
106
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
107
|
+
console.log(` ā Added ${item}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Smart .gitignore logic: Look for project root
|
|
112
|
+
const projectRoot = await findProjectRoot(targetDir);
|
|
113
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
114
|
+
const ignores = '\n# Vault CMS / Obsidian\n.obsidian/workspace.json\n.obsidian/workspace-mobile.json\n.ref/\n';
|
|
115
|
+
|
|
116
|
+
const isExternalRoot = projectRoot !== targetDir && !targetDir.startsWith(projectRoot);
|
|
117
|
+
|
|
118
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
119
|
+
const content = await fs.readFile(gitignorePath, 'utf8');
|
|
120
|
+
if (!content.includes('.obsidian/workspace.json')) {
|
|
121
|
+
await fs.appendFile(gitignorePath, ignores);
|
|
122
|
+
console.log(` ā Updated .gitignore at ${path.relative(process.cwd(), gitignorePath)}`);
|
|
123
|
+
}
|
|
124
|
+
} else if (!isExternalRoot) {
|
|
125
|
+
await fs.writeFile(gitignorePath, ignores.trim() + '\n');
|
|
126
|
+
console.log(` ā Created .gitignore at ${path.relative(process.cwd(), gitignorePath)}`);
|
|
127
|
+
} else {
|
|
128
|
+
console.log(` ā ļø Skipped .gitignore (could not find a safe project root)`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await fs.remove(tempZip);
|
|
132
|
+
await fs.remove(extractDir);
|
|
133
|
+
|
|
134
|
+
if (projectRoot === targetDir) {
|
|
135
|
+
console.log('\n ā ļø Note: No Astro project or package.json found in parent directories.');
|
|
136
|
+
console.log(' Installation completed, but you may need to move these files into your content folder manually.');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log('\n⨠Vault CMS is ready!');
|
|
140
|
+
process.exit(0);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error('\nā Installation failed:', err.message);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
async function findProjectRoot(startDir) {
|
|
148
|
+
let current = startDir;
|
|
149
|
+
// Look up to 6 levels up for a project root (Astro config, package.json, or .git)
|
|
150
|
+
let depth = 0;
|
|
151
|
+
while (current !== path.parse(current).root && depth < 6) {
|
|
152
|
+
const hasPkg = await fs.pathExists(path.join(current, 'package.json'));
|
|
153
|
+
const hasAstro = await fs.pathExists(path.join(current, 'astro.config.mjs')) || await fs.pathExists(path.join(current, 'astro.config.ts'));
|
|
154
|
+
const hasGit = await fs.pathExists(path.join(current, '.git'));
|
|
155
|
+
|
|
156
|
+
if (hasPkg || hasAstro || hasGit) return current;
|
|
157
|
+
|
|
158
|
+
current = path.dirname(current);
|
|
159
|
+
depth++;
|
|
160
|
+
}
|
|
161
|
+
return startDir; // Fallback to target dir
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function downloadFile(url, dest) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
https.get(url, { headers: { 'User-Agent': 'vault-cms-installer' } }, (res) => {
|
|
167
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
168
|
+
return downloadFile(res.headers.location, dest).then(resolve).catch(reject);
|
|
169
|
+
}
|
|
170
|
+
if (res.statusCode !== 200) {
|
|
171
|
+
return reject(new Error(`Failed to download: ${res.statusCode}`));
|
|
172
|
+
}
|
|
173
|
+
const file = fs.createWriteStream(dest);
|
|
174
|
+
res.pipe(file);
|
|
175
|
+
file.on('finish', () => {
|
|
176
|
+
file.close();
|
|
177
|
+
resolve();
|
|
178
|
+
});
|
|
179
|
+
}).on('error', reject);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function fetchTemplates() {
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
const url = 'https://api.github.com/repos/davidvkimball/vault-cms-presets/contents';
|
|
186
|
+
https.get(url, { headers: { 'User-Agent': 'vault-cms-installer' } }, (res) => {
|
|
187
|
+
let data = '';
|
|
188
|
+
res.on('data', (chunk) => data += chunk);
|
|
189
|
+
res.on('end', () => {
|
|
190
|
+
try {
|
|
191
|
+
const contents = JSON.parse(data);
|
|
192
|
+
const dirs = contents
|
|
193
|
+
.filter(item => item.type === 'dir' && !item.name.startsWith('.'))
|
|
194
|
+
.map(item => item.name);
|
|
195
|
+
resolve(dirs);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
resolve(['starlight', 'slate', 'chiri']);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}).on('error', () => resolve(['starlight', 'slate', 'chiri']));
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
program.parse();
|