@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.
@@ -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
+ });