@barbapapazes/content-creation 0.10.0 → 0.12.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/README.md +36 -0
- package/dist/cli.mjs +3 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -26,6 +26,42 @@ content-creation --path /path/to/content
|
|
|
26
26
|
# Result: /path/to/content/2026/01/01/linkedin.md
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
You can configure the tool using a configuration file. Create one of the following files:
|
|
32
|
+
|
|
33
|
+
- Local config: `content-creation.config.{ts,js,mjs,json}` in your project root
|
|
34
|
+
- Global config: `~/.content-creationrc` (JSON format)
|
|
35
|
+
|
|
36
|
+
### Configuration Options
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export default {
|
|
40
|
+
// Array of thematic areas (currently unused, reserved for future features)
|
|
41
|
+
thematic: ['JavaScript', 'TypeScript', 'Node.js'],
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Example Configuration
|
|
46
|
+
|
|
47
|
+
Create a `content-creation.config.ts` file:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { defineConfig } from '@barbapapazes/content-creation'
|
|
51
|
+
|
|
52
|
+
export default defineConfig({
|
|
53
|
+
thematic: ['JavaScript', 'TypeScript', 'Node.js'],
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or a JSON config file (`~/.content-creationrc`):
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"thematic": ["JavaScript", "TypeScript", "Node.js"]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
29
65
|
## Directory Structure
|
|
30
66
|
|
|
31
67
|
The CLI creates a nested directory structure organized by date:
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import e from"node:process";import{intro as t,isCancel as n,log as r,outro as i,select as a,text as o}from"@clack/prompts";import{cac as s}from"cac";import{addDays as c,format as l,parse as u}from"date-fns";import{existsSync as d,mkdirSync as f,readFileSync as p,readdirSync as m,statSync as h,writeFileSync as g}from"node:fs";import{join as _,resolve as v}from"node:path";import y from"gray-matter";var b=`0.
|
|
2
|
+
import e from"node:process";import{intro as t,isCancel as n,log as r,outro as i,select as a,text as o}from"@clack/prompts";import{cac as s}from"cac";import{addDays as c,format as l,parse as u}from"date-fns";import{existsSync as d,mkdirSync as f,readFileSync as p,readdirSync as m,statSync as h,writeFileSync as g}from"node:fs";import{join as _,resolve as v}from"node:path";import y from"gray-matter";var b=`0.12.0`;function x(t,n,i,a,o=e.cwd()){let s=v(_(o,l(t,`yyyy`),l(t,`MM`),l(t,`dd`))),c=_(s,`linkedin.md`);if(d(c))return r.info(`linkedin.md already exists at: ${c}`),s;f(s,{recursive:!0}),r.success(`Created directory: ${s}`);let u={title:n};if(i&&(u.video=!0),a&&(u.images=[null]),g(c,y.stringify(`
|
|
3
3
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
`,u),`utf-8`),r.success(`Created linkedin.md at: ${c}`),i){let e=_(s,`linkedin-script.md`);g(e,``,`utf-8`),r.success(`Created linkedin-script.md at: ${e}`)}return s}function S(t=e.cwd()){let n=[];try{let e=m(t).filter(e=>h(_(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let r of e){let e=_(t,r),i=m(e).filter(t=>h(_(e,t)).isDirectory()&&/^\d{2}$/.test(t));for(let t of i){let i=_(e,t),a=m(i).filter(e=>h(_(i,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of a){let a=_(i,e);if(d(_(a,`linkedin.md`))){let i=`${r}-${t}-${e}`;n.push({path:a,date:new Date(i),displayPath:`${r}/${t}/${e}`})}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime()).slice(0,8)}function
|
|
6
|
+
Je m'appelle Estéban, et pour toi, je décode l'IA pour t'aider à passer à l'action. Abonne-toi ! 🛠️
|
|
7
|
+
`,u),`utf-8`),r.success(`Created linkedin.md at: ${c}`),i){let e=_(s,`linkedin-script.md`);g(e,``,`utf-8`),r.success(`Created linkedin-script.md at: ${e}`)}return s}function S(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}function C(t,n,i=e.cwd()){let a=S(t),o=_(v(_(i,`resources`)),a),s=`${n}.md`,c=_(o,s);if(d(c))return r.info(`${s} already exists at: ${c}`),o;f(o,{recursive:!0}),r.success(`Created directory: ${o}`);let l={title:t,url:``,date:``};return g(c,y.stringify(``,l),`utf-8`),r.success(`Created ${s} at: ${c}`),o}function w(t=e.cwd()){let n=[];try{let e=m(t).filter(e=>h(_(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let r of e){let e=_(t,r),i=m(e).filter(t=>h(_(e,t)).isDirectory()&&/^\d{2}$/.test(t));for(let t of i){let i=_(e,t),a=m(i).filter(e=>h(_(i,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of a){let a=_(i,e);if(d(_(a,`linkedin.md`))){let i=`${r}-${t}-${e}`;n.push({path:a,date:new Date(i),displayPath:`${r}/${t}/${e}`})}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime()).slice(0,8)}function T(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=m(e);for(let i of r)if(h(_(e,i)).isFile()){let e=i.toLowerCase().substring(i.lastIndexOf(`.`));t.includes(e)&&n.push(i)}}catch(e){r.error(`Error reading directory: ${e}`)}return n.sort()}function E(e,t){try{let{data:n,content:i}=y(p(e,`utf-8`));n.images=t.length>0?t:[null],g(e,y.stringify(i,n),`utf-8`),r.success(`Updated ${e} with ${t.length} image(s)`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function D(t){if(t.length===0)return r.warn(`No dated folders with linkedin.md found`),null;let i=await a({message:`Select a folder to link images:`,options:t.map(e=>({label:e.displayPath,value:e.path,hint:`Link images in ${e.displayPath}`}))});return n(i)&&(r.error(`Operation cancelled.`),e.exit(0)),t.find(e=>e.path===i)||null}async function O(t=e.cwd()){let n=w(t);if(n.length===0){r.warn(`No dated folders with linkedin.md found`);return}let i=await D(n);if(!i)return;let a=T(i.path);if(a.length===0){r.info(`No images found in ${i.displayPath}`),E(_(i.path,`linkedin.md`),[]);return}r.info(`Found ${a.length} image(s): ${a.join(`, `)}`),E(_(i.path,`linkedin.md`),a)}async function k(){let t=new Date,i=[];for(let e=0;e<7;e++){let n=l(c(t,e),`yyyy-MM-dd`),r=n,a=`Create content for ${n}`;e===0?(r=`Today (${n})`,a=`Create content for today`):e===1?(r=`Tomorrow (${n})`,a=`Create content for tomorrow`):(r=`In ${e} days (${n})`,a=`Create content for ${n}`),i.push({label:r,value:`day-${e}`,hint:a})}i.push({label:`Custom date`,value:`custom`,hint:`Enter a specific date`});let s=await a({message:`When do you want to create content?`,options:i});if(n(s)&&(r.error(`Operation cancelled.`),e.exit(0)),s!==`custom`)return c(t,Number.parseInt(s.split(`-`)[1]));let d=await o({message:`Enter date (YYYY-MM-DD):`,placeholder:l(t,`yyyy-MM-dd`),validate:e=>{if(!e)return`Date is required`;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return`Invalid date format. Please use YYYY-MM-DD`;try{let t=u(e,`yyyy-MM-dd`,new Date);if(Number.isNaN(t.getTime()))return`Invalid date`}catch{return`Invalid date`}}});return n(d)&&(r.error(`Operation cancelled.`),e.exit(0)),u(d,`yyyy-MM-dd`,new Date)}async function A(){let t=await o({message:`What is the title of your content?`,placeholder:`Enter content title`,validate:e=>{if(!e||e.trim().length===0)return`Title is required`}});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t.trim()}async function j(){let t=await a({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function M(){let t=await a({message:`Are there images?`,options:[{label:`Yes`,value:!0,hint:`Add images section to frontmatter`},{label:`No`,value:!1,hint:`No images metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function N(){let t=await a({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}const P=s(`content-creation`);P.command(`[path]`,`Create a dated content directory with linkedin.md file`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create dated content directory`);let a=await A(),o=await j(),s=await M(),c=await k();x(c,a,o,s,n||r?.path||e.cwd()),i(`✓ Content directory created: ${l(c,`yyyy/MM/dd`)}/linkedin.md`)}),P.command(`link-images [path]`,`Link images to LinkedIn frontmatter in recent folders`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Link Images to LinkedIn`),await O(n||r?.path||e.cwd()),i(`✓ Images linked successfully`)}),P.command(`resource [path]`,`Create a resource with article/video/audio markdown file`).option(`--path <path>`,`Base path for resource directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Resource`);let a=await A(),o=await N();C(a,o,n||r?.path||e.cwd()),i(`✓ Resource created: resources/${S(a)}/${o}.md`)}),P.help(),P.version(b),P.parse();export{};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barbapapazes/content-creation",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"author": "Estéban Soubiran <esteban@soubiran.dev>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/Barbapapazes",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@clack/prompts": "^0.10.1",
|
|
26
|
+
"c12": "^3.3.3",
|
|
26
27
|
"cac": "^6.7.14",
|
|
27
28
|
"date-fns": "^4.1.0",
|
|
28
29
|
"gray-matter": "^4.0.3"
|