@millstone/synapse-site 0.1.0
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 +194 -0
- package/decap/build-config.ts +435 -0
- package/decap/build-vault-index.ts +178 -0
- package/decap/validate-browser.ts +638 -0
- package/package.json +67 -0
- package/plugins/CustomHeaderFooter.ts +387 -0
- package/quartz.config.ts +107 -0
- package/quartz.layout.ts +124 -0
- package/setup-quartz.ts +151 -0
- package/static/edit/body-grammars/index.json +1532 -0
- package/static/edit/config.yml +1393 -0
- package/static/edit/index.html +402 -0
- package/static/edit/schemas/index.json +1477 -0
- package/static/edit/validate.bundle.js +22720 -0
- package/static/edit/vault-index.json +78 -0
- package/sync-plugins.ts +126 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env npx ts-node
|
|
2
|
+
/**
|
|
3
|
+
* Build-time script to generate vault-index.json
|
|
4
|
+
*
|
|
5
|
+
* This script scans the content directory and creates an index of all
|
|
6
|
+
* valid wikilink targets for browser-side validation.
|
|
7
|
+
*
|
|
8
|
+
* Usage: npx ts-node packages/site/decap/build-vault-index.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import glob from 'fast-glob';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
|
|
19
|
+
interface VaultIndex {
|
|
20
|
+
/** Generated timestamp */
|
|
21
|
+
generatedAt: string;
|
|
22
|
+
/** Total file count */
|
|
23
|
+
fileCount: number;
|
|
24
|
+
/** All valid wikilink targets (basenames without .md) */
|
|
25
|
+
targets: string[];
|
|
26
|
+
/** Full relative paths for folder validation */
|
|
27
|
+
paths: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function buildVaultIndex(contentDir: string, outputPath: string): Promise<void> {
|
|
31
|
+
console.log(`Scanning content directory: ${contentDir}`);
|
|
32
|
+
|
|
33
|
+
// Find all markdown files
|
|
34
|
+
const files = await glob('**/*.md', {
|
|
35
|
+
cwd: contentDir,
|
|
36
|
+
absolute: false,
|
|
37
|
+
ignore: [
|
|
38
|
+
'**/node_modules/**',
|
|
39
|
+
'**/.git/**',
|
|
40
|
+
'**/templates/**',
|
|
41
|
+
'index.md'
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log(`Found ${files.length} markdown files`);
|
|
46
|
+
|
|
47
|
+
// Extract unique targets
|
|
48
|
+
const targets = new Set<string>();
|
|
49
|
+
const paths: string[] = [];
|
|
50
|
+
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
// Add full relative path
|
|
53
|
+
paths.push(file);
|
|
54
|
+
|
|
55
|
+
// Add basename without extension
|
|
56
|
+
const basename = path.basename(file, '.md');
|
|
57
|
+
targets.add(basename);
|
|
58
|
+
|
|
59
|
+
// Also add normalized version (lowercase, dashes)
|
|
60
|
+
const normalized = basename.toLowerCase().replace(/\s+/g, '-');
|
|
61
|
+
targets.add(normalized);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const index: VaultIndex = {
|
|
65
|
+
generatedAt: new Date().toISOString(),
|
|
66
|
+
fileCount: files.length,
|
|
67
|
+
targets: Array.from(targets).sort(),
|
|
68
|
+
paths: paths.sort()
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Ensure output directory exists
|
|
72
|
+
const outputDir = path.dirname(outputPath);
|
|
73
|
+
if (!fs.existsSync(outputDir)) {
|
|
74
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Write index
|
|
78
|
+
fs.writeFileSync(outputPath, JSON.stringify(index, null, 2));
|
|
79
|
+
console.log(`Wrote vault index to: ${outputPath}`);
|
|
80
|
+
console.log(` - ${index.targets.length} unique targets`);
|
|
81
|
+
console.log(` - ${index.paths.length} file paths`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function copySchemas(schemasDir: string, outputDir: string): Promise<void> {
|
|
85
|
+
console.log(`\nCopying schemas from: ${schemasDir}`);
|
|
86
|
+
|
|
87
|
+
// Copy frontmatter schemas
|
|
88
|
+
const frontmatterDir = path.join(schemasDir, 'frontmatter');
|
|
89
|
+
const frontmatterOutput = path.join(outputDir, 'schemas');
|
|
90
|
+
|
|
91
|
+
if (!fs.existsSync(frontmatterOutput)) {
|
|
92
|
+
fs.mkdirSync(frontmatterOutput, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const schemaFiles = await glob('*.schema.json', { cwd: frontmatterDir });
|
|
96
|
+
const schemas: Record<string, any> = {};
|
|
97
|
+
|
|
98
|
+
for (const file of schemaFiles) {
|
|
99
|
+
const content = fs.readFileSync(path.join(frontmatterDir, file), 'utf-8');
|
|
100
|
+
const schema = JSON.parse(content);
|
|
101
|
+
const type = file.replace('.schema.json', '');
|
|
102
|
+
|
|
103
|
+
// Resolve base schema references
|
|
104
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
105
|
+
const basePath = path.join(frontmatterDir, 'base.schema.json');
|
|
106
|
+
const baseContent = fs.readFileSync(basePath, 'utf-8');
|
|
107
|
+
const baseSchema = JSON.parse(baseContent);
|
|
108
|
+
|
|
109
|
+
// Merge base with type-specific
|
|
110
|
+
schemas[type] = {
|
|
111
|
+
...schema,
|
|
112
|
+
properties: {
|
|
113
|
+
...baseSchema.properties,
|
|
114
|
+
...schema.properties
|
|
115
|
+
},
|
|
116
|
+
required: [
|
|
117
|
+
...(baseSchema.required || []),
|
|
118
|
+
...(schema.required || [])
|
|
119
|
+
].filter((v, i, a) => a.indexOf(v) === i)
|
|
120
|
+
};
|
|
121
|
+
delete schemas[type].allOf;
|
|
122
|
+
} else if (type !== 'base') {
|
|
123
|
+
schemas[type] = schema;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fs.writeFileSync(
|
|
128
|
+
path.join(frontmatterOutput, 'index.json'),
|
|
129
|
+
JSON.stringify(schemas, null, 2)
|
|
130
|
+
);
|
|
131
|
+
console.log(` Wrote ${Object.keys(schemas).length} schemas to schemas/index.json`);
|
|
132
|
+
|
|
133
|
+
// Copy body grammars
|
|
134
|
+
const grammarsDir = path.join(schemasDir, 'body-grammars');
|
|
135
|
+
const grammarsOutput = path.join(outputDir, 'body-grammars');
|
|
136
|
+
|
|
137
|
+
if (!fs.existsSync(grammarsOutput)) {
|
|
138
|
+
fs.mkdirSync(grammarsOutput, { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const grammarFiles = await glob('*.body-grammar.json', { cwd: grammarsDir });
|
|
142
|
+
const grammars: Record<string, any> = {};
|
|
143
|
+
|
|
144
|
+
for (const file of grammarFiles) {
|
|
145
|
+
const content = fs.readFileSync(path.join(grammarsDir, file), 'utf-8');
|
|
146
|
+
const grammar = JSON.parse(content);
|
|
147
|
+
grammars[grammar.type] = grammar;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fs.writeFileSync(
|
|
151
|
+
path.join(grammarsOutput, 'index.json'),
|
|
152
|
+
JSON.stringify(grammars, null, 2)
|
|
153
|
+
);
|
|
154
|
+
console.log(` Wrote ${Object.keys(grammars).length} grammars to body-grammars/index.json`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function main(): Promise<void> {
|
|
158
|
+
// Determine paths
|
|
159
|
+
const projectRoot = path.resolve(__dirname, '../../..');
|
|
160
|
+
const contentDir = path.join(projectRoot, 'content');
|
|
161
|
+
const schemasDir = path.join(projectRoot, 'schemas');
|
|
162
|
+
const outputDir = path.join(__dirname, '../static/edit');
|
|
163
|
+
|
|
164
|
+
console.log('=== Building Decap CMS Validation Assets ===\n');
|
|
165
|
+
|
|
166
|
+
// Build vault index
|
|
167
|
+
await buildVaultIndex(contentDir, path.join(outputDir, 'vault-index.json'));
|
|
168
|
+
|
|
169
|
+
// Copy and process schemas
|
|
170
|
+
await copySchemas(schemasDir, outputDir);
|
|
171
|
+
|
|
172
|
+
console.log('\n=== Build Complete ===');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
main().catch(error => {
|
|
176
|
+
console.error('Build failed:', error);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
});
|