chalknotes 0.0.34 → 1.0.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 (56) hide show
  1. package/README.md +143 -188
  2. package/bin/chalknotes.js +31 -0
  3. package/bin/cli.js +27 -288
  4. package/dist/commands/init.d.ts +6 -0
  5. package/dist/commands/init.d.ts.map +1 -0
  6. package/dist/commands/init.js +196 -0
  7. package/dist/commands/init.js.map +1 -0
  8. package/dist/commands/scaffold.d.ts +6 -0
  9. package/dist/commands/scaffold.d.ts.map +1 -0
  10. package/dist/commands/scaffold.js +196 -0
  11. package/dist/commands/scaffold.js.map +1 -0
  12. package/dist/index.d.ts +53 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +163 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/notion/client.d.ts +14 -0
  17. package/dist/notion/client.d.ts.map +1 -0
  18. package/dist/notion/client.js +191 -0
  19. package/dist/notion/client.js.map +1 -0
  20. package/dist/plugins/parser.d.ts +10 -0
  21. package/dist/plugins/parser.d.ts.map +1 -0
  22. package/dist/plugins/parser.js +307 -0
  23. package/dist/plugins/parser.js.map +1 -0
  24. package/dist/templates/components.d.ts +4 -0
  25. package/dist/templates/components.d.ts.map +1 -0
  26. package/dist/templates/components.js +556 -0
  27. package/dist/templates/components.js.map +1 -0
  28. package/dist/templates/pages.d.ts +4 -0
  29. package/dist/templates/pages.d.ts.map +1 -0
  30. package/dist/templates/pages.js +321 -0
  31. package/dist/templates/pages.js.map +1 -0
  32. package/dist/types/index.d.ts +64 -0
  33. package/dist/types/index.d.ts.map +1 -0
  34. package/dist/types/index.js +3 -0
  35. package/dist/types/index.js.map +1 -0
  36. package/dist/utils/config.d.ts +12 -0
  37. package/dist/utils/config.d.ts.map +1 -0
  38. package/dist/utils/config.js +99 -0
  39. package/dist/utils/config.js.map +1 -0
  40. package/dist/utils/detection.d.ts +19 -0
  41. package/dist/utils/detection.d.ts.map +1 -0
  42. package/dist/utils/detection.js +246 -0
  43. package/dist/utils/detection.js.map +1 -0
  44. package/dist/utils/logger.d.ts +10 -0
  45. package/dist/utils/logger.d.ts.map +1 -0
  46. package/dist/utils/logger.js +62 -0
  47. package/dist/utils/logger.js.map +1 -0
  48. package/package.json +47 -10
  49. package/templates/example-config.js +34 -0
  50. package/templates/example-env +3 -0
  51. package/src/index.js +0 -10
  52. package/src/lib/getAllPosts.js +0 -35
  53. package/src/lib/getPostBySlug.js +0 -380
  54. package/src/lib/nextHelpers.js +0 -36
  55. package/src/lib/notion.js +0 -11
  56. package/src/utils.js +0 -6
@@ -1,380 +0,0 @@
1
- const { notion, dbId } = require("./notion")
2
- const { slugify } = require("../utils")
3
-
4
- const getPostBySlug = async (slug) => {
5
- try {
6
- const response = await notion.databases.query({
7
- database_id: dbId,
8
- filter: {
9
- property: "Published",
10
- checkbox: {
11
- equals: true,
12
- },
13
- },
14
- })
15
-
16
- if (response.results.length === 0) {
17
- throw new Error("No posts found")
18
- }
19
-
20
- for (const page of response.results) {
21
- const titleProperty = page.properties["Name"]
22
- const title = titleProperty?.title?.[0]?.plain_text
23
- const pageSlug = slugify(title)
24
-
25
- if (pageSlug === slug) {
26
- const response = await notion.blocks.children.list({
27
- block_id: page.id,
28
- })
29
-
30
- let content = []
31
- for (const block of response.results) {
32
- content.push(convertBlockToStructuredJSON(block))
33
- }
34
-
35
- return {
36
- title,
37
- slug: pageSlug,
38
- blocks: content,
39
- notionPageId: page.id,
40
- }
41
- }
42
- }
43
-
44
- throw new Error(`No post found with slug "${slug}"`)
45
- } catch (error) {
46
- console.error(error)
47
- throw new Error(`Error fetching posts from Notion: ${error.message}`)
48
- }
49
- }
50
-
51
- function convertBlockToStructuredJSON(block) {
52
- const base = { type: block.type };
53
-
54
- switch (block.type) {
55
- case "paragraph":
56
- return {
57
- ...base,
58
- text: extractPlainText(block.paragraph.rich_text),
59
- richText: block.paragraph.rich_text,
60
- };
61
-
62
- case "heading_1":
63
- case "heading_2":
64
- case "heading_3":
65
- return {
66
- ...base,
67
- text: extractPlainText(block[block.type].rich_text),
68
- richText: block[block.type].rich_text,
69
- };
70
-
71
- case "bulleted_list_item":
72
- case "numbered_list_item":
73
- return {
74
- ...base,
75
- text: extractPlainText(block[block.type].rich_text),
76
- richText: block[block.type].rich_text,
77
- };
78
-
79
- case "image": {
80
- const image = block.image;
81
- const url = image.type === "external" ? image.external.url : image.file.url;
82
- const caption = extractPlainText(image.caption);
83
- return {
84
- ...base,
85
- imageUrl: url,
86
- caption,
87
- alt: caption || "Blog image from Notion",
88
- };
89
- }
90
-
91
- case "quote":
92
- return {
93
- ...base,
94
- text: extractPlainText(block.quote.rich_text),
95
- richText: block.quote.rich_text,
96
- };
97
-
98
- case "code":
99
- return {
100
- ...base,
101
- code: extractPlainText(block.code.rich_text),
102
- language: block.code.language || "text",
103
- };
104
-
105
- case "divider":
106
- return { ...base };
107
-
108
- case "callout":
109
- return { ...base };
110
-
111
- case "toggle":
112
- return { ...base };
113
-
114
- case "table_of_contents":
115
- return { ...base };
116
- case "bookmark":
117
- return { ...base };
118
-
119
- case "equation":
120
- return { ...base };
121
-
122
- case "table":
123
- return { ...base };
124
-
125
- default:
126
- return {
127
- ...base,
128
- unsupported: true,
129
- };
130
- }
131
- }
132
-
133
- function extractPlainText(richText = []) {
134
- return richText.map(t => t.plain_text).join("");
135
- }
136
-
137
-
138
- /**
139
- * Process individual Notion blocks and convert to HTML
140
- * @param {Object} block - Notion block object
141
- * @returns {string} HTML string
142
- */
143
- function processBlock(block) {
144
- switch (block.type) {
145
- case "paragraph":
146
- return processRichText(block.paragraph.rich_text, "p", "mb-6 leading-relaxed text-slate-700 text-base")
147
-
148
- case "heading_1":
149
- return processRichText(
150
- block.heading_1.rich_text,
151
- "h1",
152
- "text-4xl font-extrabold mb-8 mt-12 text-slate-900 border-b border-slate-200 pb-6",
153
- )
154
-
155
- case "heading_2":
156
- return processRichText(block.heading_2.rich_text, "h2", "text-3xl font-bold mb-6 mt-10 text-slate-900")
157
-
158
- case "heading_3":
159
- return processRichText(block.heading_3.rich_text, "h3", "text-2xl font-semibold mb-4 mt-8 text-slate-900")
160
-
161
- case "bulleted_list_item":
162
- return processRichText(
163
- block.bulleted_list_item.rich_text,
164
- "li",
165
- "mb-2 ml-6 leading-relaxed text-slate-700 list-disc",
166
- )
167
-
168
- case "numbered_list_item":
169
- return processRichText(
170
- block.numbered_list_item.rich_text,
171
- "li",
172
- "mb-2 ml-6 leading-relaxed text-slate-700 list-decimal",
173
- )
174
-
175
- case "quote":
176
- return processRichText(
177
- block.quote.rich_text,
178
- "blockquote",
179
- "border-l-4 border-indigo-400 pl-6 italic text-slate-600 mb-8 bg-slate-50 py-6 rounded-r-xl text-lg leading-relaxed font-medium",
180
- )
181
-
182
- case "code":
183
- const codeContent = block.code.rich_text.map((text) => text.plain_text).join("")
184
- const language = block.code.language || "text"
185
- return `<pre class="bg-slate-900 text-slate-100 p-6 rounded-xl overflow-x-auto mb-8 text-sm leading-6 shadow-xl border border-slate-800"><code class="language-${language}">${escapeHtml(codeContent)}</code></pre>`
186
-
187
- case "image":
188
- return processImage(block.image)
189
-
190
- case "divider":
191
- return '<hr class="my-12 border-slate-200" />'
192
-
193
- case "callout":
194
- return processCallout(block.callout)
195
-
196
- case "toggle":
197
- return processToggle(block.toggle)
198
-
199
- case "table_of_contents":
200
- return '<div class="bg-indigo-50 border border-indigo-200 rounded-xl p-6 mb-8 shadow-sm"><p class="text-indigo-900 font-semibold text-lg">📋 Table of Contents</p><p class="text-indigo-600 text-sm mt-1">(Generated automatically)</p></div>'
201
-
202
- case "bookmark":
203
- return processBookmark(block.bookmark)
204
-
205
- case "equation":
206
- return `<div class="bg-slate-50 p-6 rounded-xl mb-8 text-center border border-slate-200 shadow-sm"><p class="text-slate-600 mb-3 font-semibold">📐 Mathematical equation</p><p class="font-mono text-sm bg-white p-4 rounded-lg border shadow-sm">${block.equation.expression}</p></div>`
207
-
208
- default:
209
- // For unsupported blocks, try to extract plain text
210
- if (block[block.type]?.rich_text) {
211
- return processRichText(block[block.type].rich_text, "p", "mb-6 text-slate-500 italic text-base")
212
- }
213
- return ""
214
- }
215
- }
216
-
217
- /**
218
- * Process image block with size, alignment, and alt text
219
- * @param {Object} image - Notion image block
220
- * @returns {string} HTML string
221
- */
222
- function processImage(image) {
223
- const imageUrl = image.type === "external" ? image.external.url : image.file.url;
224
- const caption = image.caption?.map((text) => text.plain_text).join("") || "";
225
- const altText = caption || "Blog image from Notion";
226
-
227
- // Strict size constraints for blog layout
228
- const containerClasses = "max-w-[400px] mx-auto px-4 my-4";
229
- const figureClasses = "relative w-full max-w-[300px] sm:max-w-[400px] h-[300px]";
230
-
231
- // Log image URL for debugging
232
- console.log("Image URL:", imageUrl);
233
-
234
- return `
235
- <div className="${containerClasses}">
236
- <figure className="${figureClasses}">
237
- <Image
238
- src="${imageUrl}"
239
- alt="${escapeHtml(altText)}"
240
- fill
241
- className="rounded-xl object-contain"
242
- sizes="(max-width: 640px) 300px, 400px"
243
- priority={false}
244
- />
245
- ${caption ? `<figcaption className="text-slate-600 mt-2 text-sm text-center font-medium italic">${escapeHtml(caption)}</figcaption>` : ""}
246
- </figure>
247
- </div>
248
- `.trim();
249
- }
250
-
251
-
252
- /**
253
- * Process callout block
254
- * @param {Object} callout - Notion callout block
255
- * @returns {string} HTML string
256
- */
257
- function processCallout(callout) {
258
- const content = processRichText(callout.rich_text, "div", "")
259
- const icon = callout.icon?.emoji || "💡"
260
- const bgColor = callout.color || "blue"
261
-
262
- const colorClasses = {
263
- blue: "bg-blue-50 border-blue-200 text-blue-900",
264
- gray: "bg-slate-50 border-slate-200 text-slate-900",
265
- yellow: "bg-amber-50 border-amber-200 text-amber-900",
266
- red: "bg-red-50 border-red-200 text-red-900",
267
- green: "bg-emerald-50 border-emerald-200 text-emerald-900",
268
- purple: "bg-purple-50 border-purple-200 text-purple-900",
269
- pink: "bg-pink-50 border-pink-200 text-pink-900",
270
- }
271
-
272
- const colorClass = colorClasses[bgColor] || colorClasses.blue
273
-
274
- return `
275
- <div class="${colorClass} border-l-4 p-6 my-8 rounded-r-xl shadow-sm">
276
- <div class="flex items-start">
277
- <span class="mr-4 text-2xl flex-shrink-0">${icon}</span>
278
- <div class="flex-1 leading-relaxed text-base font-medium">${content}</div>
279
- </div>
280
- </div>
281
- `.trim()
282
- }
283
-
284
- /**
285
- * Process bookmark block
286
- * @param {Object} bookmark - Notion bookmark block
287
- * @returns {string} HTML string
288
- */
289
- function processBookmark(bookmark) {
290
- const url = bookmark.url
291
- const title = bookmark.caption?.[0]?.plain_text || "Bookmark"
292
-
293
- return `
294
- <div class="my-8">
295
- <a href="${url}" target="_blank" rel="noopener noreferrer" class="block border border-slate-200 rounded-xl p-6 hover:border-slate-300 hover:shadow-lg transition-all duration-300 bg-white hover:bg-slate-50">
296
- <div class="flex items-center">
297
- <div class="flex-1 min-w-0">
298
- <p class="font-semibold text-slate-900 truncate text-lg">${escapeHtml(title)}</p>
299
- <p class="text-sm text-slate-500 truncate mt-2">${url}</p>
300
- </div>
301
- <svg class="w-6 h-6 text-slate-400 ml-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
302
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
303
- </svg>
304
- </div>
305
- </a>
306
- </div>
307
- `.trim()
308
- }
309
-
310
- /**
311
- * Process toggle block
312
- * @param {Object} toggle - Notion toggle block
313
- * @returns {string} HTML string
314
- */
315
- function processToggle(toggle) {
316
- const content = processRichText(toggle.rich_text, "div", "")
317
- return `
318
- <details class="my-6">
319
- <summary class="cursor-pointer font-semibold text-slate-700 hover:text-slate-900 text-lg leading-relaxed transition-colors duration-200">
320
- ${content}
321
- </summary>
322
- <div class="mt-4 pl-6 border-l-2 border-slate-200">
323
- <!-- Toggle content would go here if Notion API provided it -->
324
- <p class="text-slate-600 text-base italic">Toggle content not available in current API</p>
325
- </div>
326
- </details>
327
- `.trim()
328
- }
329
-
330
- /**
331
- * Process rich text and apply formatting
332
- * @param {Array} richText - Array of rich text objects
333
- * @param {string} tag - HTML tag to wrap content
334
- * @param {string} className - CSS classes
335
- * @returns {string} HTML string
336
- */
337
- function processRichText(richText, tag, className) {
338
- if (!richText || richText.length === 0) return ""
339
-
340
- const content = richText
341
- .map((text) => {
342
- let result = text.plain_text
343
-
344
- // Apply annotations
345
- if (text.annotations.bold) result = `<strong class="font-bold">${result}</strong>`
346
- if (text.annotations.italic) result = `<em class="italic">${result}</em>`
347
- if (text.annotations.strikethrough) result = `<del class="line-through">${result}</del>`
348
- if (text.annotations.code)
349
- result = `<code class="bg-slate-100 px-2 py-1 rounded-md text-sm font-mono text-slate-800 border border-slate-200">${result}</code>`
350
-
351
- // Apply links
352
- if (text.href)
353
- result = `<a href="${text.href}" class="text-indigo-600 hover:text-indigo-800 underline font-medium transition-colors duration-200" target="_blank" rel="noopener noreferrer">${result}</a>`
354
-
355
- return result
356
- })
357
- .join("")
358
-
359
- return `<${tag} class="${className}">${content}</${tag}>`
360
- }
361
-
362
- /**
363
- * Escape HTML special characters
364
- * @param {string} text - Text to escape
365
- * @returns {string} Escaped text
366
- */
367
- function escapeHtml(text) {
368
- const map = {
369
- "&": "&amp;",
370
- "<": "&lt;",
371
- ">": "&gt;",
372
- '"': "&quot;",
373
- "'": "&#039;",
374
- }
375
- return text.replace(/[&<>"']/g, (m) => map[m])
376
- }
377
-
378
- module.exports = {
379
- getPostBySlug,
380
- }
@@ -1,36 +0,0 @@
1
- const { getAllPosts } = require('./getAllPosts');
2
- const { getPostBySlug } = require('./getPostBySlug');
3
-
4
- /**
5
- * Provides static props for a post page using slug
6
- */
7
- const getStaticPropsForPost = async ({ params }) => {
8
- const post = await getPostBySlug(params.slug);
9
-
10
- return {
11
- props: {
12
- post
13
- }
14
- };
15
- };
16
-
17
- /**
18
- * Provides static paths for all blog posts
19
- */
20
- const getStaticPathsForPosts = async () => {
21
- const posts = await getAllPosts();
22
-
23
- const paths = posts.map((post) => ({
24
- params: { slug: post.slug }
25
- }));
26
-
27
- return {
28
- paths,
29
- fallback: false,
30
- };
31
- };
32
-
33
- module.exports = {
34
- getStaticPropsForPost,
35
- getStaticPathsForPosts
36
- };
package/src/lib/notion.js DELETED
@@ -1,11 +0,0 @@
1
- const { Client } = require('@notionhq/client');
2
- const dotenv = require('dotenv');
3
- dotenv.config();
4
-
5
- const notion = new Client({ auth: process.env.NOTION_TOKEN });
6
- const dbId = process.env.NOTION_DATABASE_ID;
7
-
8
- module.exports = {
9
- notion,
10
- dbId
11
- }
package/src/utils.js DELETED
@@ -1,6 +0,0 @@
1
- function slugify(str) {
2
- return str.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
3
- }
4
-
5
- module.exports = { slugify };
6
-