emdash-emit 0.0.0-stage → 0.1.2
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/LICENSE +21 -0
- package/README.md +798 -2
- package/dist/bin/emit.d.ts +5 -0
- package/dist/bin/emit.d.ts.map +1 -0
- package/dist/bin/emit.js +63 -0
- package/dist/bin/emit.js.map +1 -0
- package/dist/cli/commands/new.d.ts +4 -0
- package/dist/cli/commands/new.d.ts.map +1 -0
- package/dist/cli/commands/new.js +49 -0
- package/dist/cli/commands/new.js.map +1 -0
- package/dist/cli/commands/profile.d.ts +3 -0
- package/dist/cli/commands/profile.d.ts.map +1 -0
- package/dist/cli/commands/profile.js +104 -0
- package/dist/cli/commands/profile.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +4 -0
- package/dist/cli/commands/pull.d.ts.map +1 -0
- package/dist/cli/commands/pull.js +157 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/push.d.ts +4 -0
- package/dist/cli/commands/push.d.ts.map +1 -0
- package/dist/cli/commands/push.js +107 -0
- package/dist/cli/commands/push.js.map +1 -0
- package/dist/cli/commands/status.d.ts +4 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +100 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/context.d.ts +15 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +75 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/client/api.d.ts +19 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +96 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/auth.d.ts +48 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/auth.js +165 -0
- package/dist/client/auth.js.map +1 -0
- package/dist/client/error.d.ts +22 -0
- package/dist/client/error.d.ts.map +1 -0
- package/dist/client/error.js +57 -0
- package/dist/client/error.js.map +1 -0
- package/dist/client/http.d.ts +31 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +97 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/lock.d.ts +40 -0
- package/dist/client/lock.d.ts.map +1 -0
- package/dist/client/lock.js +104 -0
- package/dist/client/lock.js.map +1 -0
- package/dist/client/mapper.d.ts +37 -0
- package/dist/client/mapper.d.ts.map +1 -0
- package/dist/client/mapper.js +142 -0
- package/dist/client/mapper.js.map +1 -0
- package/dist/client/portable-text.d.ts +94 -0
- package/dist/client/portable-text.d.ts.map +1 -0
- package/dist/client/portable-text.js +428 -0
- package/dist/client/portable-text.js.map +1 -0
- package/dist/client/schemas.d.ts +1928 -0
- package/dist/client/schemas.d.ts.map +1 -0
- package/dist/client/schemas.js +123 -0
- package/dist/client/schemas.js.map +1 -0
- package/dist/db/cache.d.ts +60 -0
- package/dist/db/cache.d.ts.map +1 -0
- package/dist/db/cache.js +303 -0
- package/dist/db/cache.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/context.d.ts +14 -0
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/context.js +23 -0
- package/dist/mcp/context.js.map +1 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +106 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/profile.d.ts +6 -0
- package/dist/mcp/tools/profile.d.ts.map +1 -0
- package/dist/mcp/tools/profile.js +81 -0
- package/dist/mcp/tools/profile.js.map +1 -0
- package/dist/mcp/tools/sync-diff.d.ts +4 -0
- package/dist/mcp/tools/sync-diff.d.ts.map +1 -0
- package/dist/mcp/tools/sync-diff.js +115 -0
- package/dist/mcp/tools/sync-diff.js.map +1 -0
- package/dist/mcp/tools/sync-new.d.ts +4 -0
- package/dist/mcp/tools/sync-new.d.ts.map +1 -0
- package/dist/mcp/tools/sync-new.js +54 -0
- package/dist/mcp/tools/sync-new.js.map +1 -0
- package/dist/mcp/tools/sync-pull.d.ts +4 -0
- package/dist/mcp/tools/sync-pull.d.ts.map +1 -0
- package/dist/mcp/tools/sync-pull.js +180 -0
- package/dist/mcp/tools/sync-pull.js.map +1 -0
- package/dist/mcp/tools/sync-push.d.ts +4 -0
- package/dist/mcp/tools/sync-push.d.ts.map +1 -0
- package/dist/mcp/tools/sync-push.js +105 -0
- package/dist/mcp/tools/sync-push.js.map +1 -0
- package/dist/mcp/tools/sync-status.d.ts +4 -0
- package/dist/mcp/tools/sync-status.d.ts.map +1 -0
- package/dist/mcp/tools/sync-status.js +45 -0
- package/dist/mcp/tools/sync-status.js.map +1 -0
- package/dist/sync/bylines.d.ts +23 -0
- package/dist/sync/bylines.d.ts.map +1 -0
- package/dist/sync/bylines.js +112 -0
- package/dist/sync/bylines.js.map +1 -0
- package/dist/sync/diff.d.ts +16 -0
- package/dist/sync/diff.d.ts.map +1 -0
- package/dist/sync/diff.js +68 -0
- package/dist/sync/diff.js.map +1 -0
- package/dist/sync/media.d.ts +37 -0
- package/dist/sync/media.d.ts.map +1 -0
- package/dist/sync/media.js +191 -0
- package/dist/sync/media.js.map +1 -0
- package/dist/sync/merge.d.ts +36 -0
- package/dist/sync/merge.d.ts.map +1 -0
- package/dist/sync/merge.js +112 -0
- package/dist/sync/merge.js.map +1 -0
- package/dist/sync/new.d.ts +24 -0
- package/dist/sync/new.d.ts.map +1 -0
- package/dist/sync/new.js +75 -0
- package/dist/sync/new.js.map +1 -0
- package/dist/sync/orchestrator.d.ts +55 -0
- package/dist/sync/orchestrator.d.ts.map +1 -0
- package/dist/sync/orchestrator.js +222 -0
- package/dist/sync/orchestrator.js.map +1 -0
- package/dist/sync/preflight.d.ts +25 -0
- package/dist/sync/preflight.d.ts.map +1 -0
- package/dist/sync/preflight.js +128 -0
- package/dist/sync/preflight.js.map +1 -0
- package/dist/sync/pull.d.ts +67 -0
- package/dist/sync/pull.d.ts.map +1 -0
- package/dist/sync/pull.js +405 -0
- package/dist/sync/pull.js.map +1 -0
- package/dist/sync/push.d.ts +33 -0
- package/dist/sync/push.d.ts.map +1 -0
- package/dist/sync/push.js +432 -0
- package/dist/sync/push.js.map +1 -0
- package/dist/sync/status.d.ts +24 -0
- package/dist/sync/status.d.ts.map +1 -0
- package/dist/sync/status.js +85 -0
- package/dist/sync/status.js.map +1 -0
- package/dist/sync/taxonomies.d.ts +49 -0
- package/dist/sync/taxonomies.d.ts.map +1 -0
- package/dist/sync/taxonomies.js +220 -0
- package/dist/sync/taxonomies.js.map +1 -0
- package/dist/utils/config.d.ts +18 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +79 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +115 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +147 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/naming.d.ts +28 -0
- package/dist/utils/naming.d.ts.map +1 -0
- package/dist/utils/naming.js +94 -0
- package/dist/utils/naming.js.map +1 -0
- package/dist/utils/profile.d.ts +72 -0
- package/dist/utils/profile.d.ts.map +1 -0
- package/dist/utils/profile.js +180 -0
- package/dist/utils/profile.js.map +1 -0
- package/dist/utils/version.d.ts +5 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +43 -0
- package/dist/utils/version.js.map +1 -0
- package/dist/utils/workspace.d.ts +22 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +74 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +68 -4
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface TaxonomyTerm {
|
|
2
|
+
id: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
parentId?: string | null;
|
|
7
|
+
count?: number;
|
|
8
|
+
locale?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PullTaxonomiesOptions {
|
|
11
|
+
workspaceRoot: string;
|
|
12
|
+
origin: string;
|
|
13
|
+
token: string;
|
|
14
|
+
refreshToken?: string;
|
|
15
|
+
fetchFn?: typeof fetch;
|
|
16
|
+
}
|
|
17
|
+
export interface PullTaxonomiesResult {
|
|
18
|
+
categoriesCount: number;
|
|
19
|
+
tagsCount: number;
|
|
20
|
+
deletedCategoriesCount: number;
|
|
21
|
+
deletedTagsCount: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Pulls categories and tags from EmDash API and saves them to local YAML files:
|
|
25
|
+
* categories/<id>-<sanitizedLabel>.yaml
|
|
26
|
+
* tags/<id>-<sanitizedLabel>.yaml
|
|
27
|
+
*/
|
|
28
|
+
export declare function pullTaxonomies(options: PullTaxonomiesOptions): Promise<PullTaxonomiesResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Loads local taxonomy mapping between term ULID and slug from categories/ and tags/ directories.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadLocalTaxonomyMaps(workspaceRoot: string): {
|
|
33
|
+
slugToId: {
|
|
34
|
+
category: Record<string, string>;
|
|
35
|
+
tag: Record<string, string>;
|
|
36
|
+
};
|
|
37
|
+
idToSlug: {
|
|
38
|
+
category: Record<string, string>;
|
|
39
|
+
tag: Record<string, string>;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Loads local taxonomy candidate names (slugs or labels) from categories/ and tags/ directories.
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadLocalTaxonomyCandidates(workspaceRoot: string): {
|
|
46
|
+
categories: string[];
|
|
47
|
+
tags: string[];
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=taxonomies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taxonomies.d.ts","sourceRoot":"","sources":["../../src/sync/taxonomies.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CA8H/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG;IAC5D,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC5E,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CAC7E,CA0CA;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG;IAClE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CA8BA"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 SPORT-SERVICE RS★R
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import yaml from 'js-yaml';
|
|
25
|
+
import { sanitizeTitleForFilename } from '../utils/frontmatter.js';
|
|
26
|
+
import { ApiError } from '../client/error.js';
|
|
27
|
+
import { createAuthedFetch } from '../client/http.js';
|
|
28
|
+
import { unwrapEnvelope } from '../client/api.js';
|
|
29
|
+
/**
|
|
30
|
+
* Pulls categories and tags from EmDash API and saves them to local YAML files:
|
|
31
|
+
* categories/<id>-<sanitizedLabel>.yaml
|
|
32
|
+
* tags/<id>-<sanitizedLabel>.yaml
|
|
33
|
+
*/
|
|
34
|
+
export async function pullTaxonomies(options) {
|
|
35
|
+
const fetchFn = createAuthedFetch(options);
|
|
36
|
+
const fetchTerms = async (taxName) => {
|
|
37
|
+
const url = new URL(`/_emdash/api/taxonomies/${taxName}/terms`, options.origin).toString();
|
|
38
|
+
const res = await fetchFn(url, {
|
|
39
|
+
method: 'GET',
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${options.token}`,
|
|
42
|
+
Accept: 'application/json',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
throw new ApiError({
|
|
47
|
+
message: `Failed to fetch ${taxName} terms: ${res.statusText}`,
|
|
48
|
+
code: 'TAXONOMY_FETCH_FAILED',
|
|
49
|
+
status: res.status,
|
|
50
|
+
endpoint: url,
|
|
51
|
+
suggestion: 'Verify network connection and that the taxonomies API is enabled.',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const json = await res.json();
|
|
55
|
+
const data = unwrapEnvelope(json);
|
|
56
|
+
const rawTerms = (Array.isArray(data.terms) ? data.terms : Array.isArray(data) ? data : []);
|
|
57
|
+
return rawTerms;
|
|
58
|
+
};
|
|
59
|
+
const [categories, tags] = await Promise.all([
|
|
60
|
+
fetchTerms('category').catch(() => []),
|
|
61
|
+
fetchTerms('tag').catch(() => []),
|
|
62
|
+
]);
|
|
63
|
+
const categoriesDir = path.join(options.workspaceRoot, 'categories');
|
|
64
|
+
const tagsDir = path.join(options.workspaceRoot, 'tags');
|
|
65
|
+
fs.mkdirSync(categoriesDir, { recursive: true });
|
|
66
|
+
fs.mkdirSync(tagsDir, { recursive: true });
|
|
67
|
+
// Save categories and prune deleted ones
|
|
68
|
+
const activeCategoryFileNames = new Set();
|
|
69
|
+
for (const cat of categories) {
|
|
70
|
+
const label = cat.label || cat.slug;
|
|
71
|
+
const sanitized = sanitizeTitleForFilename(label);
|
|
72
|
+
const fileName = `${cat.id}-${sanitized}.yaml`;
|
|
73
|
+
activeCategoryFileNames.add(fileName);
|
|
74
|
+
const filePath = path.join(categoriesDir, fileName);
|
|
75
|
+
const doc = {
|
|
76
|
+
id: cat.id,
|
|
77
|
+
name: label,
|
|
78
|
+
slug: cat.slug,
|
|
79
|
+
taxonomy: 'category',
|
|
80
|
+
parentId: cat.parentId || null,
|
|
81
|
+
count: cat.count ?? 0,
|
|
82
|
+
};
|
|
83
|
+
fs.writeFileSync(filePath, yaml.dump(doc, { indent: 2, quotingType: '"', forceQuotes: false }), 'utf8');
|
|
84
|
+
}
|
|
85
|
+
// Prune local category YAMLs not present on remote
|
|
86
|
+
let deletedCategoriesCount = 0;
|
|
87
|
+
if (fs.existsSync(categoriesDir)) {
|
|
88
|
+
for (const file of fs.readdirSync(categoriesDir)) {
|
|
89
|
+
if (file.endsWith('.yaml') && !activeCategoryFileNames.has(file)) {
|
|
90
|
+
try {
|
|
91
|
+
fs.unlinkSync(path.join(categoriesDir, file));
|
|
92
|
+
deletedCategoriesCount++;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Ignore unlink errors
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Save tags and prune deleted ones
|
|
101
|
+
const activeTagFileNames = new Set();
|
|
102
|
+
for (const tag of tags) {
|
|
103
|
+
const label = tag.label || tag.slug;
|
|
104
|
+
const sanitized = sanitizeTitleForFilename(label);
|
|
105
|
+
const fileName = `${tag.id}-${sanitized}.yaml`;
|
|
106
|
+
activeTagFileNames.add(fileName);
|
|
107
|
+
const filePath = path.join(tagsDir, fileName);
|
|
108
|
+
const doc = {
|
|
109
|
+
id: tag.id,
|
|
110
|
+
name: label,
|
|
111
|
+
slug: tag.slug,
|
|
112
|
+
taxonomy: 'tag',
|
|
113
|
+
parentId: tag.parentId || null,
|
|
114
|
+
count: tag.count ?? 0,
|
|
115
|
+
};
|
|
116
|
+
fs.writeFileSync(filePath, yaml.dump(doc, { indent: 2, quotingType: '"', forceQuotes: false }), 'utf8');
|
|
117
|
+
}
|
|
118
|
+
// Prune local tag YAMLs not present on remote
|
|
119
|
+
let deletedTagsCount = 0;
|
|
120
|
+
if (fs.existsSync(tagsDir)) {
|
|
121
|
+
for (const file of fs.readdirSync(tagsDir)) {
|
|
122
|
+
if (file.endsWith('.yaml') && !activeTagFileNames.has(file)) {
|
|
123
|
+
try {
|
|
124
|
+
fs.unlinkSync(path.join(tagsDir, file));
|
|
125
|
+
deletedTagsCount++;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Ignore unlink errors
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
categoriesCount: categories.length,
|
|
135
|
+
tagsCount: tags.length,
|
|
136
|
+
deletedCategoriesCount,
|
|
137
|
+
deletedTagsCount,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Loads local taxonomy mapping between term ULID and slug from categories/ and tags/ directories.
|
|
142
|
+
*/
|
|
143
|
+
export function loadLocalTaxonomyMaps(workspaceRoot) {
|
|
144
|
+
const categoriesDir = path.join(workspaceRoot, 'categories');
|
|
145
|
+
const tagsDir = path.join(workspaceRoot, 'tags');
|
|
146
|
+
const readDirMap = (dir) => {
|
|
147
|
+
const slugToId = {};
|
|
148
|
+
const idToSlug = {};
|
|
149
|
+
if (!fs.existsSync(dir))
|
|
150
|
+
return { slugToId, idToSlug };
|
|
151
|
+
try {
|
|
152
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml'));
|
|
153
|
+
for (const file of files) {
|
|
154
|
+
try {
|
|
155
|
+
const content = fs.readFileSync(path.join(dir, file), 'utf8');
|
|
156
|
+
const data = yaml.load(content);
|
|
157
|
+
if (data && typeof data.id === 'string' && typeof data.slug === 'string') {
|
|
158
|
+
slugToId[data.slug] = data.id;
|
|
159
|
+
idToSlug[data.id] = data.slug;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Ignore malformed files
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Ignore directory read errors
|
|
169
|
+
}
|
|
170
|
+
return { slugToId, idToSlug };
|
|
171
|
+
};
|
|
172
|
+
const catMap = readDirMap(categoriesDir);
|
|
173
|
+
const tagMap = readDirMap(tagsDir);
|
|
174
|
+
return {
|
|
175
|
+
slugToId: {
|
|
176
|
+
category: catMap.slugToId,
|
|
177
|
+
tag: tagMap.slugToId,
|
|
178
|
+
},
|
|
179
|
+
idToSlug: {
|
|
180
|
+
category: catMap.idToSlug,
|
|
181
|
+
tag: tagMap.idToSlug,
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Loads local taxonomy candidate names (slugs or labels) from categories/ and tags/ directories.
|
|
187
|
+
*/
|
|
188
|
+
export function loadLocalTaxonomyCandidates(workspaceRoot) {
|
|
189
|
+
const categoriesDir = path.join(workspaceRoot, 'categories');
|
|
190
|
+
const tagsDir = path.join(workspaceRoot, 'tags');
|
|
191
|
+
const readDirCandidates = (dir) => {
|
|
192
|
+
if (!fs.existsSync(dir))
|
|
193
|
+
return [];
|
|
194
|
+
try {
|
|
195
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml'));
|
|
196
|
+
const candidates = [];
|
|
197
|
+
for (const file of files) {
|
|
198
|
+
try {
|
|
199
|
+
const content = fs.readFileSync(path.join(dir, file), 'utf8');
|
|
200
|
+
const data = yaml.load(content);
|
|
201
|
+
if (data && typeof data.slug === 'string') {
|
|
202
|
+
candidates.push(data.slug);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Ignore malformed files
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return candidates;
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
categories: readDirCandidates(categoriesDir),
|
|
217
|
+
tags: readDirCandidates(tagsDir),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=taxonomies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taxonomies.js","sourceRoot":"","sources":["../../src/sync/taxonomies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface EmitConfig {
|
|
2
|
+
origin: string;
|
|
3
|
+
defaultCollection: string;
|
|
4
|
+
defaultLocale: string;
|
|
5
|
+
syncLimit?: number;
|
|
6
|
+
postsSyncLimit?: number;
|
|
7
|
+
mediaSyncLimit?: number;
|
|
8
|
+
concurrency?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Initializes a new .emit workspace directory with default config.
|
|
12
|
+
*/
|
|
13
|
+
export declare function initWorkspace(workspaceRoot: string, initialConfig?: Partial<EmitConfig>): void;
|
|
14
|
+
/**
|
|
15
|
+
* Loads the EmitConfig from the active workspace.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadConfig(workspaceRoot?: string): EmitConfig;
|
|
18
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD;;GAEG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAkB9F;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,UAAU,CAyB7D"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 SPORT-SERVICE RS★R
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { getWorkspacePaths, requireWorkspaceRoot } from './workspace.js';
|
|
25
|
+
const DEFAULT_CONFIG = {
|
|
26
|
+
origin: 'https://example.com',
|
|
27
|
+
defaultCollection: 'posts',
|
|
28
|
+
defaultLocale: 'ja',
|
|
29
|
+
postsSyncLimit: 100,
|
|
30
|
+
mediaSyncLimit: 300,
|
|
31
|
+
concurrency: 8,
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Initializes a new .emit workspace directory with default config.
|
|
35
|
+
*/
|
|
36
|
+
export function initWorkspace(workspaceRoot, initialConfig) {
|
|
37
|
+
const paths = getWorkspacePaths(workspaceRoot);
|
|
38
|
+
if (!fs.existsSync(paths.emitDir)) {
|
|
39
|
+
fs.mkdirSync(paths.emitDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
const postsDraftDir = path.join(paths.postsDir, 'draft');
|
|
42
|
+
const postsPublishedDir = path.join(paths.postsDir, 'published');
|
|
43
|
+
fs.mkdirSync(postsDraftDir, { recursive: true });
|
|
44
|
+
fs.mkdirSync(postsPublishedDir, { recursive: true });
|
|
45
|
+
const config = {
|
|
46
|
+
...DEFAULT_CONFIG,
|
|
47
|
+
...initialConfig,
|
|
48
|
+
};
|
|
49
|
+
fs.writeFileSync(paths.configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Loads the EmitConfig from the active workspace.
|
|
53
|
+
*/
|
|
54
|
+
export function loadConfig(workspaceRoot) {
|
|
55
|
+
const root = workspaceRoot || requireWorkspaceRoot();
|
|
56
|
+
const paths = getWorkspacePaths(root);
|
|
57
|
+
if (!fs.existsSync(paths.configPath)) {
|
|
58
|
+
return {
|
|
59
|
+
...DEFAULT_CONFIG,
|
|
60
|
+
origin: process.env.EMDASH_ORIGIN || DEFAULT_CONFIG.origin,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const raw = fs.readFileSync(paths.configPath, 'utf8');
|
|
65
|
+
const parsed = JSON.parse(raw);
|
|
66
|
+
return {
|
|
67
|
+
origin: process.env.EMDASH_ORIGIN || parsed.origin || DEFAULT_CONFIG.origin,
|
|
68
|
+
defaultCollection: parsed.defaultCollection || DEFAULT_CONFIG.defaultCollection,
|
|
69
|
+
defaultLocale: parsed.defaultLocale || DEFAULT_CONFIG.defaultLocale,
|
|
70
|
+
postsSyncLimit: parsed.postsSyncLimit || DEFAULT_CONFIG.postsSyncLimit,
|
|
71
|
+
mediaSyncLimit: parsed.mediaSyncLimit || DEFAULT_CONFIG.mediaSyncLimit,
|
|
72
|
+
concurrency: parsed.concurrency || DEFAULT_CONFIG.concurrency,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return DEFAULT_CONFIG;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAYzE,MAAM,cAAc,GAAe;IACjC,MAAM,EAAE,qBAAqB;IAC7B,iBAAiB,EAAE,OAAO;IAC1B,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,GAAG;IACnB,cAAc,EAAE,GAAG;IACnB,WAAW,EAAE,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,aAAqB,EAAE,aAAmC;IACtF,MAAM,KAAK,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAE/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACjE,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,EAAE,CAAC,SAAS,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAErD,MAAM,MAAM,GAAe;QACzB,GAAG,cAAc;QACjB,GAAG,aAAa;KACjB,CAAC;IAEF,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,aAAsB;IAC/C,MAAM,IAAI,GAAG,aAAa,IAAI,oBAAoB,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,GAAG,cAAc;YACjB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,cAAc,CAAC,MAAM;SAC3D,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QACtD,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM;YAC3E,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,cAAc,CAAC,iBAAiB;YAC/E,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,cAAc,CAAC,aAAa;YACnE,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,cAAc,CAAC,cAAc;YACtE,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,cAAc,CAAC,cAAc;YACtE,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,cAAc,CAAC,WAAW;SAC9D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const FrontmatterSeoSchema: z.ZodObject<{
|
|
3
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
4
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
5
|
+
canonical: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
6
|
+
noIndex: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
title?: string | null | undefined;
|
|
10
|
+
description?: string | null | undefined;
|
|
11
|
+
canonical?: string | null | undefined;
|
|
12
|
+
noIndex?: boolean | undefined;
|
|
13
|
+
image?: string | null | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
title?: string | null | undefined;
|
|
16
|
+
description?: string | null | undefined;
|
|
17
|
+
canonical?: string | null | undefined;
|
|
18
|
+
noIndex?: boolean | undefined;
|
|
19
|
+
image?: string | null | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const FrontmatterSchema: z.ZodObject<{
|
|
22
|
+
id: z.ZodString;
|
|
23
|
+
slug: z.ZodString;
|
|
24
|
+
title: z.ZodString;
|
|
25
|
+
collection: z.ZodDefault<z.ZodString>;
|
|
26
|
+
status: z.ZodDefault<z.ZodEnum<["draft", "published"]>>;
|
|
27
|
+
publishedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
28
|
+
locale: z.ZodDefault<z.ZodString>;
|
|
29
|
+
taxonomies: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>>;
|
|
30
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
31
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
|
+
canonical: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
|
+
noIndex: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
title?: string | null | undefined;
|
|
38
|
+
description?: string | null | undefined;
|
|
39
|
+
canonical?: string | null | undefined;
|
|
40
|
+
noIndex?: boolean | undefined;
|
|
41
|
+
image?: string | null | undefined;
|
|
42
|
+
}, {
|
|
43
|
+
title?: string | null | undefined;
|
|
44
|
+
description?: string | null | undefined;
|
|
45
|
+
canonical?: string | null | undefined;
|
|
46
|
+
noIndex?: boolean | undefined;
|
|
47
|
+
image?: string | null | undefined;
|
|
48
|
+
}>>;
|
|
49
|
+
bylines: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
50
|
+
}, "strip", z.ZodTypeAny, {
|
|
51
|
+
id: string;
|
|
52
|
+
slug: string;
|
|
53
|
+
title: string;
|
|
54
|
+
collection: string;
|
|
55
|
+
status: "draft" | "published";
|
|
56
|
+
publishedAt?: string | null | undefined;
|
|
57
|
+
locale: string;
|
|
58
|
+
taxonomies: Record<string, string[]>;
|
|
59
|
+
seo?: {
|
|
60
|
+
title?: string | null | undefined;
|
|
61
|
+
description?: string | null | undefined;
|
|
62
|
+
canonical?: string | null | undefined;
|
|
63
|
+
noIndex?: boolean | undefined;
|
|
64
|
+
image?: string | null | undefined;
|
|
65
|
+
} | undefined;
|
|
66
|
+
bylines: string[];
|
|
67
|
+
}, {
|
|
68
|
+
id: string;
|
|
69
|
+
slug: string;
|
|
70
|
+
title: string;
|
|
71
|
+
collection?: string | undefined;
|
|
72
|
+
status?: "draft" | "published" | undefined;
|
|
73
|
+
publishedAt?: string | null | undefined;
|
|
74
|
+
locale?: string | undefined;
|
|
75
|
+
taxonomies?: Record<string, string[]> | undefined;
|
|
76
|
+
seo?: {
|
|
77
|
+
title?: string | null | undefined;
|
|
78
|
+
description?: string | null | undefined;
|
|
79
|
+
canonical?: string | null | undefined;
|
|
80
|
+
noIndex?: boolean | undefined;
|
|
81
|
+
image?: string | null | undefined;
|
|
82
|
+
} | undefined;
|
|
83
|
+
bylines?: string[] | undefined;
|
|
84
|
+
}>;
|
|
85
|
+
export type Frontmatter = z.infer<typeof FrontmatterSchema>;
|
|
86
|
+
export interface TaxonomyCandidates {
|
|
87
|
+
categories?: string[];
|
|
88
|
+
tags?: string[];
|
|
89
|
+
bylines?: string[];
|
|
90
|
+
[key: string]: string[] | undefined;
|
|
91
|
+
}
|
|
92
|
+
export interface ParsedMarkdown {
|
|
93
|
+
frontmatter: Frontmatter;
|
|
94
|
+
body: string;
|
|
95
|
+
rawYaml: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Parses markdown text into validated frontmatter and body.
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseMarkdown(content: string): ParsedMarkdown;
|
|
101
|
+
/**
|
|
102
|
+
* Formats a frontmatter object into clean YAML with helpful candidate comments.
|
|
103
|
+
*/
|
|
104
|
+
export declare function formatFrontmatterYaml(data: Frontmatter, candidates?: TaxonomyCandidates): string;
|
|
105
|
+
/**
|
|
106
|
+
* Serializes frontmatter and markdown body into a full document.
|
|
107
|
+
*/
|
|
108
|
+
export declare function serializeMarkdown(frontmatter: Frontmatter, body: string, candidates?: TaxonomyCandidates): string;
|
|
109
|
+
/**
|
|
110
|
+
* Sanitizes an article title for cross-platform safe filenames,
|
|
111
|
+
* preserving Japanese characters while replacing invalid filesystem chars and whitespace with hyphens.
|
|
112
|
+
*/
|
|
113
|
+
export declare function sanitizeTitleForFilename(title: string): string;
|
|
114
|
+
export { formatDatePrefix, extractDatePrefixFromPath, computeArticleRelativePath, } from './naming.js';
|
|
115
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;EAM/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAS7D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAsDhG;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,kBAAkB,GAC9B,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA2B9D;AAED,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 SPORT-SERVICE RS★R
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import matter from 'gray-matter';
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
export const FrontmatterSeoSchema = z.object({
|
|
25
|
+
title: z.string().nullable().optional(),
|
|
26
|
+
description: z.string().nullable().optional(),
|
|
27
|
+
canonical: z.string().url().nullable().optional(),
|
|
28
|
+
noIndex: z.boolean().optional(),
|
|
29
|
+
image: z.string().nullable().optional(),
|
|
30
|
+
});
|
|
31
|
+
export const FrontmatterSchema = z.object({
|
|
32
|
+
id: z.string().length(26, 'ID must be a 26-character ULID'),
|
|
33
|
+
slug: z.string().min(1, 'Slug cannot be empty'),
|
|
34
|
+
title: z.string().min(1, 'Title cannot be empty'),
|
|
35
|
+
collection: z.string().default('posts'),
|
|
36
|
+
status: z.enum(['draft', 'published']).default('draft'),
|
|
37
|
+
publishedAt: z.string().nullable().optional(),
|
|
38
|
+
locale: z.string().default('ja'),
|
|
39
|
+
taxonomies: z.record(z.array(z.string())).optional().default({}),
|
|
40
|
+
seo: FrontmatterSeoSchema.optional(),
|
|
41
|
+
bylines: z.array(z.string()).optional().default([]),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Parses markdown text into validated frontmatter and body.
|
|
45
|
+
*/
|
|
46
|
+
export function parseMarkdown(content) {
|
|
47
|
+
const parsed = matter(content);
|
|
48
|
+
const validated = FrontmatterSchema.parse(parsed.data);
|
|
49
|
+
return {
|
|
50
|
+
frontmatter: validated,
|
|
51
|
+
body: parsed.content,
|
|
52
|
+
rawYaml: matter.stringify('', parsed.data).trim(),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Formats a frontmatter object into clean YAML with helpful candidate comments.
|
|
57
|
+
*/
|
|
58
|
+
export function formatFrontmatterYaml(data, candidates) {
|
|
59
|
+
const lines = ['---'];
|
|
60
|
+
lines.push(`id: ${data.id}`);
|
|
61
|
+
lines.push(`slug: ${data.slug}`);
|
|
62
|
+
lines.push(`title: ${JSON.stringify(data.title)}`);
|
|
63
|
+
lines.push(`collection: ${data.collection}`);
|
|
64
|
+
lines.push(`status: ${data.status}`);
|
|
65
|
+
lines.push(`publishedAt: ${data.publishedAt ? JSON.stringify(data.publishedAt) : 'null'}`);
|
|
66
|
+
lines.push(`locale: ${data.locale}`);
|
|
67
|
+
// Taxonomies
|
|
68
|
+
lines.push('taxonomies:');
|
|
69
|
+
const taxonomies = data.taxonomies || {};
|
|
70
|
+
const allTaxKeys = Array.from(new Set([...Object.keys(taxonomies), 'categories', 'tags']));
|
|
71
|
+
for (const taxKey of allTaxKeys) {
|
|
72
|
+
lines.push(` ${taxKey}:`);
|
|
73
|
+
const terms = taxonomies[taxKey] || [];
|
|
74
|
+
if (terms.length === 0) {
|
|
75
|
+
lines.push(' []');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
for (const term of terms) {
|
|
79
|
+
lines.push(` - ${term}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// SEO
|
|
84
|
+
if (data.seo) {
|
|
85
|
+
lines.push('seo:');
|
|
86
|
+
if (data.seo.title !== undefined)
|
|
87
|
+
lines.push(` title: ${JSON.stringify(data.seo.title)}`);
|
|
88
|
+
if (data.seo.description !== undefined)
|
|
89
|
+
lines.push(` description: ${JSON.stringify(data.seo.description)}`);
|
|
90
|
+
if (data.seo.canonical !== undefined)
|
|
91
|
+
lines.push(` canonical: ${JSON.stringify(data.seo.canonical)}`);
|
|
92
|
+
if (data.seo.noIndex !== undefined)
|
|
93
|
+
lines.push(` noIndex: ${data.seo.noIndex}`);
|
|
94
|
+
if (data.seo.image !== undefined)
|
|
95
|
+
lines.push(` image: ${JSON.stringify(data.seo.image)}`);
|
|
96
|
+
}
|
|
97
|
+
// Bylines
|
|
98
|
+
lines.push('bylines:');
|
|
99
|
+
const bylines = data.bylines || [];
|
|
100
|
+
if (bylines.length === 0) {
|
|
101
|
+
lines.push(' []');
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
for (const byline of bylines) {
|
|
105
|
+
lines.push(` - ${byline}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
lines.push('---');
|
|
109
|
+
return lines.join('\n');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Serializes frontmatter and markdown body into a full document.
|
|
113
|
+
*/
|
|
114
|
+
export function serializeMarkdown(frontmatter, body, candidates) {
|
|
115
|
+
const yaml = formatFrontmatterYaml(frontmatter, candidates);
|
|
116
|
+
const cleanBody = body.startsWith('\n') ? body : `\n${body}`;
|
|
117
|
+
return `${yaml}${cleanBody.endsWith('\n') ? cleanBody : cleanBody + '\n'}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Sanitizes an article title for cross-platform safe filenames,
|
|
121
|
+
* preserving Japanese characters while replacing invalid filesystem chars and whitespace with hyphens.
|
|
122
|
+
*/
|
|
123
|
+
export function sanitizeTitleForFilename(title) {
|
|
124
|
+
if (!title || !title.trim()) {
|
|
125
|
+
return 'untitled';
|
|
126
|
+
}
|
|
127
|
+
// Basic HTML entity decoding (e.g. & -> &, " -> ", – -> -)
|
|
128
|
+
let clean = title
|
|
129
|
+
.replace(/&/g, '&')
|
|
130
|
+
.replace(/"/g, '"')
|
|
131
|
+
.replace(/'/g, "'")
|
|
132
|
+
.replace(/</g, '<')
|
|
133
|
+
.replace(/>/g, '>')
|
|
134
|
+
.replace(/–/g, '-')
|
|
135
|
+
.replace(/—/g, '-');
|
|
136
|
+
// Replace whitespace and filesystem invalid characters (\ / : * ? " < > |) with hyphens
|
|
137
|
+
clean = clean.replace(/[\s/\\:*?"<>|]+/g, '-');
|
|
138
|
+
// Collapse multiple hyphens and trim leading/trailing hyphens
|
|
139
|
+
clean = clean.replace(/-{2,}/g, '-').replace(/^-+|-+$/g, '');
|
|
140
|
+
const maxLen = 100;
|
|
141
|
+
if (clean.length > maxLen) {
|
|
142
|
+
clean = clean.slice(0, maxLen).replace(/-+$/, '');
|
|
143
|
+
}
|
|
144
|
+
return clean || 'untitled';
|
|
145
|
+
}
|
|
146
|
+
export { formatDatePrefix, extractDatePrefixFromPath, computeArticleRelativePath, } from './naming.js';
|
|
147
|
+
//# sourceMappingURL=frontmatter.js.map
|