sequoia-cli 0.1.1 → 0.1.2-beta.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/dist/index.js +158 -49
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -67266,10 +67266,18 @@ function parseFrontmatter(content, mapping) {
|
|
|
67266
67266
|
const raw = {};
|
|
67267
67267
|
const lines = frontmatterStr.split(`
|
|
67268
67268
|
`);
|
|
67269
|
-
|
|
67269
|
+
let i = 0;
|
|
67270
|
+
while (i < lines.length) {
|
|
67271
|
+
const line = lines[i];
|
|
67272
|
+
if (line === undefined) {
|
|
67273
|
+
i++;
|
|
67274
|
+
continue;
|
|
67275
|
+
}
|
|
67270
67276
|
const sepIndex = line.indexOf(separator);
|
|
67271
|
-
if (sepIndex === -1)
|
|
67277
|
+
if (sepIndex === -1) {
|
|
67278
|
+
i++;
|
|
67272
67279
|
continue;
|
|
67280
|
+
}
|
|
67273
67281
|
const key = line.slice(0, sepIndex).trim();
|
|
67274
67282
|
let value = line.slice(sepIndex + 1).trim();
|
|
67275
67283
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
@@ -67278,6 +67286,36 @@ function parseFrontmatter(content, mapping) {
|
|
|
67278
67286
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
67279
67287
|
const arrayContent = value.slice(1, -1);
|
|
67280
67288
|
raw[key] = arrayContent.split(",").map((item) => item.trim().replace(/^["']|["']$/g, ""));
|
|
67289
|
+
} else if (value === "" && !isToml) {
|
|
67290
|
+
const arrayItems = [];
|
|
67291
|
+
let j2 = i + 1;
|
|
67292
|
+
while (j2 < lines.length) {
|
|
67293
|
+
const nextLine = lines[j2];
|
|
67294
|
+
if (nextLine === undefined) {
|
|
67295
|
+
j2++;
|
|
67296
|
+
continue;
|
|
67297
|
+
}
|
|
67298
|
+
const listMatch = nextLine.match(/^\s+-\s*(.*)$/);
|
|
67299
|
+
if (listMatch && listMatch[1] !== undefined) {
|
|
67300
|
+
let itemValue = listMatch[1].trim();
|
|
67301
|
+
if (itemValue.startsWith('"') && itemValue.endsWith('"') || itemValue.startsWith("'") && itemValue.endsWith("'")) {
|
|
67302
|
+
itemValue = itemValue.slice(1, -1);
|
|
67303
|
+
}
|
|
67304
|
+
arrayItems.push(itemValue);
|
|
67305
|
+
j2++;
|
|
67306
|
+
} else if (nextLine.trim() === "") {
|
|
67307
|
+
j2++;
|
|
67308
|
+
} else {
|
|
67309
|
+
break;
|
|
67310
|
+
}
|
|
67311
|
+
}
|
|
67312
|
+
if (arrayItems.length > 0) {
|
|
67313
|
+
raw[key] = arrayItems;
|
|
67314
|
+
i = j2;
|
|
67315
|
+
continue;
|
|
67316
|
+
} else {
|
|
67317
|
+
raw[key] = value;
|
|
67318
|
+
}
|
|
67281
67319
|
} else if (value === "true") {
|
|
67282
67320
|
raw[key] = true;
|
|
67283
67321
|
} else if (value === "false") {
|
|
@@ -67285,6 +67323,7 @@ function parseFrontmatter(content, mapping) {
|
|
|
67285
67323
|
} else {
|
|
67286
67324
|
raw[key] = value;
|
|
67287
67325
|
}
|
|
67326
|
+
i++;
|
|
67288
67327
|
}
|
|
67289
67328
|
const frontmatter = {};
|
|
67290
67329
|
const titleField = mapping?.title || "title";
|
|
@@ -67310,11 +67349,36 @@ function parseFrontmatter(content, mapping) {
|
|
|
67310
67349
|
const tagsField = mapping?.tags || "tags";
|
|
67311
67350
|
frontmatter.tags = raw[tagsField] || raw.tags;
|
|
67312
67351
|
frontmatter.atUri = raw.atUri;
|
|
67313
|
-
return { frontmatter, body };
|
|
67352
|
+
return { frontmatter, body, rawFrontmatter: raw };
|
|
67314
67353
|
}
|
|
67315
67354
|
function getSlugFromFilename(filename) {
|
|
67316
67355
|
return filename.replace(/\.mdx?$/, "").toLowerCase().replace(/\s+/g, "-");
|
|
67317
67356
|
}
|
|
67357
|
+
function getSlugFromOptions(relativePath, rawFrontmatter, options = {}) {
|
|
67358
|
+
const { slugSource = "filename", slugField = "slug", removeIndexFromSlug = false } = options;
|
|
67359
|
+
let slug;
|
|
67360
|
+
switch (slugSource) {
|
|
67361
|
+
case "path":
|
|
67362
|
+
slug = relativePath.replace(/\.mdx?$/, "").toLowerCase().replace(/\s+/g, "-");
|
|
67363
|
+
break;
|
|
67364
|
+
case "frontmatter":
|
|
67365
|
+
const frontmatterValue = rawFrontmatter[slugField] || rawFrontmatter.slug || rawFrontmatter.url;
|
|
67366
|
+
if (frontmatterValue && typeof frontmatterValue === "string") {
|
|
67367
|
+
slug = frontmatterValue.replace(/^\//, "").toLowerCase().replace(/\s+/g, "-");
|
|
67368
|
+
} else {
|
|
67369
|
+
slug = getSlugFromFilename(path3.basename(relativePath));
|
|
67370
|
+
}
|
|
67371
|
+
break;
|
|
67372
|
+
case "filename":
|
|
67373
|
+
default:
|
|
67374
|
+
slug = getSlugFromFilename(path3.basename(relativePath));
|
|
67375
|
+
break;
|
|
67376
|
+
}
|
|
67377
|
+
if (removeIndexFromSlug) {
|
|
67378
|
+
slug = slug.replace(/\/_?index$/, "");
|
|
67379
|
+
}
|
|
67380
|
+
return slug;
|
|
67381
|
+
}
|
|
67318
67382
|
async function getContentHash(content) {
|
|
67319
67383
|
const encoder = new TextEncoder;
|
|
67320
67384
|
const data = encoder.encode(content);
|
|
@@ -67330,7 +67394,23 @@ function shouldIgnore(relativePath, ignorePatterns) {
|
|
|
67330
67394
|
}
|
|
67331
67395
|
return false;
|
|
67332
67396
|
}
|
|
67333
|
-
async function scanContentDirectory(contentDir,
|
|
67397
|
+
async function scanContentDirectory(contentDir, frontmatterMappingOrOptions, ignorePatterns = []) {
|
|
67398
|
+
let options;
|
|
67399
|
+
if (frontmatterMappingOrOptions && (("slugSource" in frontmatterMappingOrOptions) || ("frontmatterMapping" in frontmatterMappingOrOptions) || ("ignorePatterns" in frontmatterMappingOrOptions))) {
|
|
67400
|
+
options = frontmatterMappingOrOptions;
|
|
67401
|
+
} else {
|
|
67402
|
+
options = {
|
|
67403
|
+
frontmatterMapping: frontmatterMappingOrOptions,
|
|
67404
|
+
ignorePatterns
|
|
67405
|
+
};
|
|
67406
|
+
}
|
|
67407
|
+
const {
|
|
67408
|
+
frontmatterMapping,
|
|
67409
|
+
ignorePatterns: ignore = [],
|
|
67410
|
+
slugSource,
|
|
67411
|
+
slugField,
|
|
67412
|
+
removeIndexFromSlug
|
|
67413
|
+
} = options;
|
|
67334
67414
|
const patterns = ["**/*.md", "**/*.mdx"];
|
|
67335
67415
|
const posts = [];
|
|
67336
67416
|
for (const pattern of patterns) {
|
|
@@ -67339,21 +67419,25 @@ async function scanContentDirectory(contentDir, frontmatterMapping, ignorePatter
|
|
|
67339
67419
|
absolute: false
|
|
67340
67420
|
});
|
|
67341
67421
|
for (const relativePath of files) {
|
|
67342
|
-
if (shouldIgnore(relativePath,
|
|
67422
|
+
if (shouldIgnore(relativePath, ignore)) {
|
|
67343
67423
|
continue;
|
|
67344
67424
|
}
|
|
67345
67425
|
const filePath = path3.join(contentDir, relativePath);
|
|
67346
67426
|
const rawContent = await fs2.readFile(filePath, "utf-8");
|
|
67347
67427
|
try {
|
|
67348
|
-
const { frontmatter, body } = parseFrontmatter(rawContent, frontmatterMapping);
|
|
67349
|
-
const
|
|
67350
|
-
|
|
67428
|
+
const { frontmatter, body, rawFrontmatter } = parseFrontmatter(rawContent, frontmatterMapping);
|
|
67429
|
+
const slug = getSlugFromOptions(relativePath, rawFrontmatter, {
|
|
67430
|
+
slugSource,
|
|
67431
|
+
slugField,
|
|
67432
|
+
removeIndexFromSlug
|
|
67433
|
+
});
|
|
67351
67434
|
posts.push({
|
|
67352
67435
|
filePath,
|
|
67353
67436
|
slug,
|
|
67354
67437
|
frontmatter,
|
|
67355
67438
|
content: body,
|
|
67356
|
-
rawContent
|
|
67439
|
+
rawContent,
|
|
67440
|
+
rawFrontmatter
|
|
67357
67441
|
});
|
|
67358
67442
|
} catch (error) {
|
|
67359
67443
|
console.error(`Error parsing ${relativePath}:`, error);
|
|
@@ -67496,8 +67580,13 @@ async function resolveImagePath(ogImage, imagesDir, contentDir) {
|
|
|
67496
67580
|
async function createDocument(agent, post, config, coverImage) {
|
|
67497
67581
|
const pathPrefix = config.pathPrefix || "/posts";
|
|
67498
67582
|
const postPath = `${pathPrefix}/${post.slug}`;
|
|
67499
|
-
const textContent = stripMarkdownForText(post.content);
|
|
67500
67583
|
const publishDate = new Date(post.frontmatter.publishDate);
|
|
67584
|
+
let textContent;
|
|
67585
|
+
if (config.textContentField && post.rawFrontmatter?.[config.textContentField]) {
|
|
67586
|
+
textContent = String(post.rawFrontmatter[config.textContentField]);
|
|
67587
|
+
} else {
|
|
67588
|
+
textContent = stripMarkdownForText(post.content);
|
|
67589
|
+
}
|
|
67501
67590
|
const record = {
|
|
67502
67591
|
$type: "site.standard.document",
|
|
67503
67592
|
title: post.frontmatter.title,
|
|
@@ -67507,6 +67596,9 @@ async function createDocument(agent, post, config, coverImage) {
|
|
|
67507
67596
|
publishedAt: publishDate.toISOString(),
|
|
67508
67597
|
canonicalUrl: `${config.siteUrl}${postPath}`
|
|
67509
67598
|
};
|
|
67599
|
+
if (post.frontmatter.description) {
|
|
67600
|
+
record.description = post.frontmatter.description;
|
|
67601
|
+
}
|
|
67510
67602
|
if (coverImage) {
|
|
67511
67603
|
record.coverImage = coverImage;
|
|
67512
67604
|
}
|
|
@@ -67528,8 +67620,13 @@ async function updateDocument(agent, post, atUri, config, coverImage) {
|
|
|
67528
67620
|
const [, , collection, rkey] = uriMatch;
|
|
67529
67621
|
const pathPrefix = config.pathPrefix || "/posts";
|
|
67530
67622
|
const postPath = `${pathPrefix}/${post.slug}`;
|
|
67531
|
-
const textContent = stripMarkdownForText(post.content);
|
|
67532
67623
|
const publishDate = new Date(post.frontmatter.publishDate);
|
|
67624
|
+
let textContent;
|
|
67625
|
+
if (config.textContentField && post.rawFrontmatter?.[config.textContentField]) {
|
|
67626
|
+
textContent = String(post.rawFrontmatter[config.textContentField]);
|
|
67627
|
+
} else {
|
|
67628
|
+
textContent = stripMarkdownForText(post.content);
|
|
67629
|
+
}
|
|
67533
67630
|
const record = {
|
|
67534
67631
|
$type: "site.standard.document",
|
|
67535
67632
|
title: post.frontmatter.title,
|
|
@@ -67539,6 +67636,9 @@ async function updateDocument(agent, post, atUri, config, coverImage) {
|
|
|
67539
67636
|
publishedAt: publishDate.toISOString(),
|
|
67540
67637
|
canonicalUrl: `${config.siteUrl}${postPath}`
|
|
67541
67638
|
};
|
|
67639
|
+
if (post.frontmatter.description) {
|
|
67640
|
+
record.description = post.frontmatter.description;
|
|
67641
|
+
}
|
|
67542
67642
|
if (coverImage) {
|
|
67543
67643
|
record.coverImage = coverImage;
|
|
67544
67644
|
}
|
|
@@ -67818,6 +67918,18 @@ function generateConfigTemplate(options) {
|
|
|
67818
67918
|
if (options.ignore && options.ignore.length > 0) {
|
|
67819
67919
|
config.ignore = options.ignore;
|
|
67820
67920
|
}
|
|
67921
|
+
if (options.slugSource && options.slugSource !== "filename") {
|
|
67922
|
+
config.slugSource = options.slugSource;
|
|
67923
|
+
}
|
|
67924
|
+
if (options.slugField && options.slugField !== "slug") {
|
|
67925
|
+
config.slugField = options.slugField;
|
|
67926
|
+
}
|
|
67927
|
+
if (options.removeIndexFromSlug) {
|
|
67928
|
+
config.removeIndexFromSlug = options.removeIndexFromSlug;
|
|
67929
|
+
}
|
|
67930
|
+
if (options.textContentField) {
|
|
67931
|
+
config.textContentField = options.textContentField;
|
|
67932
|
+
}
|
|
67821
67933
|
return JSON.stringify(config, null, 2);
|
|
67822
67934
|
}
|
|
67823
67935
|
async function loadState(configDir) {
|
|
@@ -68104,38 +68216,24 @@ var injectCommand = import_cmd_ts3.command({
|
|
|
68104
68216
|
const resolvedOutputDir = path7.isAbsolute(outputDir) ? outputDir : path7.join(configDir, outputDir);
|
|
68105
68217
|
R2.info(`Scanning for HTML files in: ${resolvedOutputDir}`);
|
|
68106
68218
|
const state = await loadState(configDir);
|
|
68107
|
-
const
|
|
68108
|
-
"+page",
|
|
68109
|
-
"index",
|
|
68110
|
-
"_index",
|
|
68111
|
-
"page",
|
|
68112
|
-
"readme"
|
|
68113
|
-
]);
|
|
68114
|
-
const pathToAtUri = new Map;
|
|
68219
|
+
const slugToAtUri = new Map;
|
|
68115
68220
|
for (const [filePath, postState] of Object.entries(state.posts)) {
|
|
68116
|
-
if (postState.atUri) {
|
|
68117
|
-
|
|
68118
|
-
|
|
68119
|
-
|
|
68120
|
-
|
|
68121
|
-
const slug = pathParts[pathParts.length - 2];
|
|
68122
|
-
if (slug && slug !== "." && slug !== "content" && slug !== "routes" && slug !== "src") {
|
|
68123
|
-
basename4 = slug;
|
|
68124
|
-
}
|
|
68125
|
-
}
|
|
68126
|
-
}
|
|
68127
|
-
pathToAtUri.set(basename4, postState.atUri);
|
|
68128
|
-
const dirName = path7.basename(path7.dirname(filePath));
|
|
68129
|
-
if (dirName !== "." && dirName !== "content" && !(dirName.startsWith("(") && dirName.endsWith(")"))) {
|
|
68130
|
-
pathToAtUri.set(`${dirName}/${basename4}`, postState.atUri);
|
|
68221
|
+
if (postState.atUri && postState.slug) {
|
|
68222
|
+
slugToAtUri.set(postState.slug, postState.atUri);
|
|
68223
|
+
const lastSegment = postState.slug.split("/").pop();
|
|
68224
|
+
if (lastSegment && lastSegment !== postState.slug) {
|
|
68225
|
+
slugToAtUri.set(lastSegment, postState.atUri);
|
|
68131
68226
|
}
|
|
68227
|
+
} else if (postState.atUri) {
|
|
68228
|
+
const basename4 = path7.basename(filePath, path7.extname(filePath));
|
|
68229
|
+
slugToAtUri.set(basename4.toLowerCase(), postState.atUri);
|
|
68132
68230
|
}
|
|
68133
68231
|
}
|
|
68134
|
-
if (
|
|
68232
|
+
if (slugToAtUri.size === 0) {
|
|
68135
68233
|
R2.warn("No published posts found in state. Run 'sequoia publish' first.");
|
|
68136
68234
|
return;
|
|
68137
68235
|
}
|
|
68138
|
-
R2.info(`Found ${
|
|
68236
|
+
R2.info(`Found ${slugToAtUri.size} slug mappings from published posts`);
|
|
68139
68237
|
const htmlFiles = await glob("**/*.html", {
|
|
68140
68238
|
cwd: resolvedOutputDir,
|
|
68141
68239
|
absolute: false
|
|
@@ -68154,19 +68252,16 @@ var injectCommand = import_cmd_ts3.command({
|
|
|
68154
68252
|
const htmlDir = path7.dirname(relativePath);
|
|
68155
68253
|
const htmlBasename = path7.basename(relativePath, ".html");
|
|
68156
68254
|
let atUri;
|
|
68157
|
-
atUri =
|
|
68255
|
+
atUri = slugToAtUri.get(htmlBasename);
|
|
68158
68256
|
if (!atUri && htmlBasename === "index" && htmlDir !== ".") {
|
|
68159
|
-
|
|
68160
|
-
atUri = pathToAtUri.get(slug);
|
|
68257
|
+
atUri = slugToAtUri.get(htmlDir);
|
|
68161
68258
|
if (!atUri) {
|
|
68162
|
-
const
|
|
68163
|
-
|
|
68164
|
-
atUri = pathToAtUri.get(`${path7.basename(parentDir)}/${slug}`);
|
|
68165
|
-
}
|
|
68259
|
+
const lastDir = path7.basename(htmlDir);
|
|
68260
|
+
atUri = slugToAtUri.get(lastDir);
|
|
68166
68261
|
}
|
|
68167
68262
|
}
|
|
68168
68263
|
if (!atUri && htmlDir !== ".") {
|
|
68169
|
-
atUri =
|
|
68264
|
+
atUri = slugToAtUri.get(`${htmlDir}/${htmlBasename}`);
|
|
68170
68265
|
}
|
|
68171
68266
|
if (!atUri) {
|
|
68172
68267
|
skippedCount++;
|
|
@@ -68266,7 +68361,13 @@ var publishCommand = import_cmd_ts4.command({
|
|
|
68266
68361
|
const state = await loadState(configDir);
|
|
68267
68362
|
const s = Ie();
|
|
68268
68363
|
s.start("Scanning for posts...");
|
|
68269
|
-
const posts = await scanContentDirectory(contentDir,
|
|
68364
|
+
const posts = await scanContentDirectory(contentDir, {
|
|
68365
|
+
frontmatterMapping: config.frontmatter,
|
|
68366
|
+
ignorePatterns: config.ignore,
|
|
68367
|
+
slugSource: config.slugSource,
|
|
68368
|
+
slugField: config.slugField,
|
|
68369
|
+
removeIndexFromSlug: config.removeIndexFromSlug
|
|
68370
|
+
});
|
|
68270
68371
|
s.stop(`Found ${posts.length} posts`);
|
|
68271
68372
|
const postsToPublish = [];
|
|
68272
68373
|
for (const post of posts) {
|
|
@@ -68360,7 +68461,8 @@ Dry run complete. No changes made.`);
|
|
|
68360
68461
|
state.posts[relativeFilePath] = {
|
|
68361
68462
|
contentHash,
|
|
68362
68463
|
atUri,
|
|
68363
|
-
lastPublished: new Date().toISOString()
|
|
68464
|
+
lastPublished: new Date().toISOString(),
|
|
68465
|
+
slug: post.slug
|
|
68364
68466
|
};
|
|
68365
68467
|
} catch (error) {
|
|
68366
68468
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -68447,11 +68549,18 @@ var syncCommand = import_cmd_ts5.command({
|
|
|
68447
68549
|
}
|
|
68448
68550
|
const contentDir = path9.isAbsolute(config.contentDir) ? config.contentDir : path9.join(configDir, config.contentDir);
|
|
68449
68551
|
s.start("Scanning local content...");
|
|
68450
|
-
const localPosts = await scanContentDirectory(contentDir,
|
|
68552
|
+
const localPosts = await scanContentDirectory(contentDir, {
|
|
68553
|
+
frontmatterMapping: config.frontmatter,
|
|
68554
|
+
ignorePatterns: config.ignore,
|
|
68555
|
+
slugSource: config.slugSource,
|
|
68556
|
+
slugField: config.slugField,
|
|
68557
|
+
removeIndexFromSlug: config.removeIndexFromSlug
|
|
68558
|
+
});
|
|
68451
68559
|
s.stop(`Found ${localPosts.length} local posts`);
|
|
68560
|
+
const pathPrefix = config.pathPrefix || "/posts";
|
|
68452
68561
|
const postsByPath = new Map;
|
|
68453
68562
|
for (const post of localPosts) {
|
|
68454
|
-
const postPath =
|
|
68563
|
+
const postPath = `${pathPrefix}/${post.slug}`;
|
|
68455
68564
|
postsByPath.set(postPath, post);
|
|
68456
68565
|
}
|
|
68457
68566
|
const state = await loadState(configDir);
|
|
@@ -68549,7 +68658,7 @@ Publish evergreen content to the ATmosphere
|
|
|
68549
68658
|
|
|
68550
68659
|
> https://tangled.org/stevedylan.dev/sequoia
|
|
68551
68660
|
`,
|
|
68552
|
-
version: "0.1.
|
|
68661
|
+
version: "0.1.2-beta.0",
|
|
68553
68662
|
cmds: {
|
|
68554
68663
|
auth: authCommand,
|
|
68555
68664
|
init: initCommand,
|