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.
Files changed (2) hide show
  1. package/dist/index.js +158 -49
  2. 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
- for (const line of lines) {
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, frontmatterMapping, ignorePatterns = []) {
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, ignorePatterns)) {
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 filename = path3.basename(relativePath);
67350
- const slug = getSlugFromFilename(filename);
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 genericFilenames = new Set([
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
- let basename4 = path7.basename(filePath, path7.extname(filePath));
68118
- if (genericFilenames.has(basename4.toLowerCase())) {
68119
- const pathParts = filePath.split(/[/\\]/).filter((p) => p && !(p.startsWith("(") && p.endsWith(")")));
68120
- if (pathParts.length >= 2) {
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 (pathToAtUri.size === 0) {
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 ${pathToAtUri.size} published posts in state`);
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 = pathToAtUri.get(htmlBasename);
68255
+ atUri = slugToAtUri.get(htmlBasename);
68158
68256
  if (!atUri && htmlBasename === "index" && htmlDir !== ".") {
68159
- const slug = path7.basename(htmlDir);
68160
- atUri = pathToAtUri.get(slug);
68257
+ atUri = slugToAtUri.get(htmlDir);
68161
68258
  if (!atUri) {
68162
- const parentDir = path7.dirname(htmlDir);
68163
- if (parentDir !== ".") {
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 = pathToAtUri.get(`${htmlDir}/${htmlBasename}`);
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, config.frontmatter, config.ignore);
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, config.frontmatter);
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 = `/posts/${post.slug}`;
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.1",
68661
+ version: "0.1.2-beta.0",
68553
68662
  cmds: {
68554
68663
  auth: authCommand,
68555
68664
  init: initCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sequoia-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2-beta.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sequoia": "dist/index.js"