eleventy-generate-posts 0.0.4 → 0.0.7

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/changelog.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 20260320 (v0.0.7)
4
+
5
+ * Added support for timestamps (something I use in my sites instead of a simple article date).
6
+ * Pressing escape or ctrl-C during prompting now fails gracefully.
7
+ * Show post count in output.
8
+
9
+ ## 20260318 (v0.0.6)
10
+
11
+ Replaced the word generator API with a different one (https://random-word-api.herokuapp.com/home) since the original was no longer available.
12
+
13
+ ## 20240109 (v0.0.5)
14
+
15
+ Added `-g` parameter used to append generator content to the end of generated posts:
16
+
17
+ > Post content generated by [Eleventy Generate Posts](https://www.npmjs.com/package/eleventy-generate-posts).
18
+
3
19
  ## 20230709 - version 0.0.4
4
20
 
5
21
  Made number of posts the first prompt, it seemed better this way.
@@ -1,167 +1,194 @@
1
- #!/usr/bin/env node
2
- import fs from 'fs-extra';
3
- import path from 'path';
4
- import boxen from 'boxen';
5
- import chalk from 'chalk';
6
- import prompts from 'prompts';
7
- import YAML from 'yaml';
8
- import logger from 'cli-logger';
9
- var log = logger();
10
- var HighlightType;
11
- (function (HighlightType) {
12
- HighlightType[HighlightType["Red"] = 0] = "Red";
13
- HighlightType[HighlightType["Yellow"] = 1] = "Yellow";
14
- HighlightType[HighlightType["Green"] = 2] = "Green";
15
- })(HighlightType || (HighlightType = {}));
16
- const APP_NAME = '11ty Generate Posts';
17
- const APP_AUTHOR = 'by John M. Wargo (https://johnwargo.com)\n';
18
- const ELEVENTY_FILES = ['.eleventy.js', 'eleventy.config.js'];
19
- const NEW_LINE = "\n";
20
- const spaces40 = '-'.repeat(40);
21
- const red = HighlightType.Red;
22
- const yellow = HighlightType.Yellow;
23
- const green = HighlightType.Green;
24
- var numPosts;
25
- var startYear;
26
- var tag;
27
- var targetFolder;
28
- var yearMode;
29
- function zeroPad(tmpVal, numChars = 2) {
30
- return tmpVal.toString().padStart(numChars, '0');
31
- }
32
- function directoryExists(filePath) {
33
- if (fs.existsSync(filePath)) {
34
- try {
35
- return fs.lstatSync(filePath).isDirectory();
36
- }
37
- catch (err) {
38
- log.error(`checkDirectory error: ${err}`);
39
- return false;
40
- }
41
- }
42
- return false;
43
- }
44
- function writeConsole(color, highlightText, msg) {
45
- if (color == HighlightType.Red)
46
- console.log(NEW_LINE + chalk.red(`${highlightText}: `) + msg + NEW_LINE);
47
- if (color == HighlightType.Yellow)
48
- console.log(chalk.yellow(`${highlightText}: `) + msg);
49
- if (color == HighlightType.Green)
50
- console.log(chalk.green(`${highlightText}: `) + msg);
51
- }
52
- function getRandomInt(max) {
53
- return Math.floor(Math.random() * max) + 1;
54
- }
55
- function checkEleventyProject() {
56
- log.debug('Validating project folder');
57
- let result = false;
58
- ELEVENTY_FILES.forEach((file) => {
59
- let tmpFile = path.join(process.cwd(), file);
60
- if (fs.existsSync(tmpFile)) {
61
- result = true;
62
- }
63
- });
64
- return result;
65
- }
66
- if (!checkEleventyProject()) {
67
- log.error('Current folder is not an Eleventy project folder.');
68
- process.exit(1);
69
- }
70
- console.log(boxen(APP_NAME, { padding: 1 }));
71
- console.log(APP_AUTHOR);
72
- const debugMode = process.argv.includes('-d');
73
- if (debugMode) {
74
- writeConsole(green, 'Debug mode', 'enabled\n');
75
- }
76
- log.level(debugMode ? log.DEBUG : log.INFO);
77
- const questions = [
78
- {
79
- type: 'number',
80
- name: 'numPosts',
81
- initial: 10,
82
- message: 'Number of posts to generate?'
83
- }, {
84
- type: 'text',
85
- name: 'targetFolder',
86
- initial: 'src/posts',
87
- message: 'Target folder for generated posts?'
88
- }, {
89
- type: 'text',
90
- name: 'tag',
91
- message: 'Post tag?',
92
- initial: 'post'
93
- }, {
94
- type: 'number',
95
- name: 'startYear',
96
- initial: new Date().getFullYear(),
97
- message: 'Start year for generated posts?'
98
- }, {
99
- type: 'confirm',
100
- name: 'yearMode',
101
- initial: true,
102
- message: 'Use year folder for posts?'
103
- },
104
- ];
105
- const response = await prompts(questions);
106
- targetFolder = response.targetFolder;
107
- numPosts = response.numPosts;
108
- startYear = response.startYear;
109
- tag = response.tag;
110
- yearMode = response.yearMode;
111
- console.log('\nSettings Summary:');
112
- console.log(spaces40);
113
- writeConsole(yellow, 'Number of posts', numPosts.toString());
114
- writeConsole(yellow, 'Target Folder', targetFolder);
115
- writeConsole(yellow, 'Start Year', startYear.toString());
116
- writeConsole(yellow, 'Tag', tag);
117
- writeConsole(yellow, 'Year mode', yearMode ? 'enabled' : 'disabled');
118
- if (!(numPosts > 0 && numPosts < 101)) {
119
- writeConsole(red, 'Error', 'Number of posts must be between 1 and 100');
120
- process.exit(1);
121
- }
122
- var outputFilePath = path.join(process.cwd(), targetFolder);
123
- writeConsole(yellow, 'Output folder', outputFilePath);
124
- if (!directoryExists(outputFilePath)) {
125
- writeConsole(red, 'Error', 'Output folder does not exist');
126
- process.exit(1);
127
- }
128
- console.log('\nGenerating posts...');
129
- console.log(spaces40);
130
- var currentDate = new Date();
131
- if (startYear)
132
- currentDate.setFullYear(startYear);
133
- numPosts++;
134
- for (let i = 1; i < numPosts; i++) {
135
- log.debug('\nGetting random words (this may take a few seconds)');
136
- let wordCount = getRandomInt(4) + 3;
137
- let letTitleRes = await fetch(`https://random-word-api.vercel.app/api?words=${wordCount}`);
138
- let titleWords = await letTitleRes.json();
139
- titleWords = titleWords.map((a) => a.charAt(0).toUpperCase() + a.substr(1));
140
- let postTitle = titleWords.join(' ');
141
- log.debug(`Post title: ${postTitle}`);
142
- currentDate.setDate(currentDate.getDate() - getRandomInt(20));
143
- let postDate = `${currentDate.getFullYear()}-${zeroPad(currentDate.getMonth() + 1)}-${zeroPad(currentDate.getDate())}`;
144
- log.debug(`Post date: ${postDate}`);
145
- var postFm = {
146
- title: postTitle,
147
- date: postDate,
148
- tags: tag
149
- };
150
- log.debug('Getting bacon ipsum text (this may take a few seconds)...');
151
- let response = await fetch(`https://baconipsum.com/api/?type=all-meat&paras=${getRandomInt(10)}&start-with-lorem=1`);
152
- let postContent = await response.json();
153
- log.debug(`Post content: ${postContent}`);
154
- var thePost = '---\n';
155
- thePost += YAML.stringify(postFm, { logLevel: 'silent' });
156
- thePost += '---\n\n';
157
- thePost += postContent.join('\n\n');
158
- var outputFilePath = path.join(process.cwd(), targetFolder);
159
- if (yearMode) {
160
- outputFilePath = path.join(outputFilePath, currentDate.getFullYear().toString());
161
- if (!fs.existsSync(outputFilePath))
162
- fs.mkdirSync(outputFilePath, { recursive: true });
163
- }
164
- var outputFilePath = path.join(outputFilePath, postTitle.toLowerCase().replaceAll(' ', '-') + '.md');
165
- writeConsole(green, 'Writing', outputFilePath);
166
- fs.writeFileSync(outputFilePath, thePost, 'utf8');
167
- }
1
+ #!/usr/bin/env node
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import boxen from 'boxen';
5
+ import chalk from 'chalk';
6
+ import prompts from 'prompts';
7
+ import YAML from 'yaml';
8
+ import logger from 'cli-logger';
9
+ var log = logger();
10
+ var HighlightType;
11
+ (function (HighlightType) {
12
+ HighlightType[HighlightType["Red"] = 0] = "Red";
13
+ HighlightType[HighlightType["Yellow"] = 1] = "Yellow";
14
+ HighlightType[HighlightType["Green"] = 2] = "Green";
15
+ })(HighlightType || (HighlightType = {}));
16
+ const APP_NAME = '11ty Generate Posts';
17
+ const APP_AUTHOR = 'by John M. Wargo (https://johnwargo.com)\n';
18
+ const ELEVENTY_FILES = ['.eleventy.js', 'eleventy.config.js'];
19
+ const GENERATOR_CONTENT = '***\n\nPost content generated by [Eleventy Generate Posts](https://www.npmjs.com/package/eleventy-generate-posts)';
20
+ const NEW_LINE = "\n";
21
+ const spaces40 = '-'.repeat(40);
22
+ const red = HighlightType.Red;
23
+ const yellow = HighlightType.Yellow;
24
+ const green = HighlightType.Green;
25
+ var counterLen;
26
+ var counterStr;
27
+ var numPosts;
28
+ var startYear;
29
+ var tag;
30
+ var targetFolder;
31
+ var timestampMode;
32
+ var yearMode;
33
+ const onCancelPrompt = () => {
34
+ log.info('\nOperation cancelled by user!');
35
+ process.exit(0);
36
+ };
37
+ function zeroPad(tmpVal, numChars = 2) {
38
+ return tmpVal.toString().padStart(numChars, '0');
39
+ }
40
+ function directoryExists(filePath) {
41
+ if (fs.existsSync(filePath)) {
42
+ try {
43
+ return fs.lstatSync(filePath).isDirectory();
44
+ }
45
+ catch (err) {
46
+ log.error(`checkDirectory error: ${err}`);
47
+ return false;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+ function writeConsole(color, highlightText, msg) {
53
+ if (color == HighlightType.Red)
54
+ console.log(NEW_LINE + chalk.red(`${highlightText}: `) + msg + NEW_LINE);
55
+ if (color == HighlightType.Yellow)
56
+ console.log(chalk.yellow(`${highlightText}: `) + msg);
57
+ if (color == HighlightType.Green)
58
+ console.log(chalk.green(`${highlightText}: `) + msg);
59
+ }
60
+ function getRandomInt(max) {
61
+ return Math.floor(Math.random() * max) + 1;
62
+ }
63
+ function checkEleventyProject() {
64
+ log.debug('Validating project folder');
65
+ let result = false;
66
+ ELEVENTY_FILES.forEach((file) => {
67
+ let tmpFile = path.join(process.cwd(), file);
68
+ if (fs.existsSync(tmpFile)) {
69
+ result = true;
70
+ }
71
+ });
72
+ return result;
73
+ }
74
+ if (!checkEleventyProject()) {
75
+ log.error('Current folder is not an Eleventy project folder.');
76
+ process.exit(1);
77
+ }
78
+ console.log(boxen(APP_NAME, { padding: 1 }));
79
+ console.log(APP_AUTHOR);
80
+ console.log('Add -d to command to enable debug mode, -g to append generator info to post files.\n');
81
+ const debugMode = process.argv.includes('-d');
82
+ if (debugMode) {
83
+ writeConsole(green, 'Debug mode', 'enabled\n');
84
+ }
85
+ log.level(debugMode ? log.DEBUG : log.INFO);
86
+ const generatorInfo = process.argv.includes('-g');
87
+ if (generatorInfo) {
88
+ writeConsole(green, 'Append generator info', 'enabled\n');
89
+ }
90
+ const questions = [
91
+ {
92
+ type: 'number',
93
+ name: 'numPosts',
94
+ initial: 10,
95
+ message: 'Number of posts to generate (1-100)?'
96
+ }, {
97
+ type: 'text',
98
+ name: 'targetFolder',
99
+ initial: 'src/posts',
100
+ message: 'Target folder for generated posts?'
101
+ }, {
102
+ type: 'text',
103
+ name: 'tag',
104
+ message: 'Post tag?',
105
+ initial: 'post'
106
+ }, {
107
+ type: 'number',
108
+ name: 'startYear',
109
+ initial: new Date().getFullYear(),
110
+ message: 'Start year for generated posts?'
111
+ }, {
112
+ type: 'confirm',
113
+ name: 'yearMode',
114
+ initial: true,
115
+ message: 'Use year folder for posts?'
116
+ }, {
117
+ type: 'confirm',
118
+ name: 'timestampMode',
119
+ initial: true,
120
+ message: 'Include timestamp in post metadata?'
121
+ }
122
+ ];
123
+ const response = await prompts(questions, { onCancel: onCancelPrompt });
124
+ targetFolder = response.targetFolder;
125
+ numPosts = response.numPosts;
126
+ counterLen = numPosts.toString().length;
127
+ startYear = response.startYear;
128
+ tag = response.tag;
129
+ yearMode = response.yearMode;
130
+ timestampMode = response.timestampMode;
131
+ console.log('\nSettings Summary:');
132
+ console.log(spaces40);
133
+ writeConsole(yellow, 'Number of posts', numPosts.toString());
134
+ writeConsole(yellow, 'Target Folder', targetFolder);
135
+ writeConsole(yellow, 'Start Year', startYear.toString());
136
+ writeConsole(yellow, 'Tag', tag);
137
+ writeConsole(yellow, 'Year mode', yearMode ? 'enabled' : 'disabled');
138
+ writeConsole(yellow, 'Timestamp mode', timestampMode ? 'enabled' : 'disabled');
139
+ if (!(numPosts > 0 && numPosts < 101)) {
140
+ writeConsole(red, 'Error', 'Number of posts must be between 1 and 100');
141
+ process.exit(1);
142
+ }
143
+ var outputFilePath = path.join(process.cwd(), targetFolder);
144
+ writeConsole(yellow, 'Output folder', outputFilePath);
145
+ if (!directoryExists(outputFilePath)) {
146
+ writeConsole(red, 'Error', 'Output folder does not exist');
147
+ process.exit(1);
148
+ }
149
+ console.log('\nGenerating posts...');
150
+ console.log(spaces40);
151
+ var currentDate = new Date();
152
+ if (startYear)
153
+ currentDate.setFullYear(startYear);
154
+ numPosts++;
155
+ for (let i = 1; i < numPosts; i++) {
156
+ log.debug('\nGetting random words (this may take a few seconds)');
157
+ let wordCount = getRandomInt(4) + 3;
158
+ let letTitleRes = await fetch(`https://random-word-api.herokuapp.com/word?number=${wordCount}`);
159
+ let titleWords = await letTitleRes.json();
160
+ titleWords = titleWords.map((a) => a.charAt(0).toUpperCase() + a.substr(1));
161
+ let postTitle = titleWords.join(' ');
162
+ log.debug(`Post title: ${postTitle}`);
163
+ currentDate.setDate(currentDate.getDate() - getRandomInt(20));
164
+ let postDate = `${currentDate.getFullYear()}-${zeroPad(currentDate.getMonth() + 1)}-${zeroPad(currentDate.getDate())}`;
165
+ log.debug(`Post date: ${postDate}`);
166
+ var postFm = {
167
+ title: postTitle,
168
+ date: postDate,
169
+ tags: tag
170
+ };
171
+ if (timestampMode) {
172
+ postFm.timestamp = currentDate.toISOString();
173
+ }
174
+ log.debug('Getting bacon ipsum text (this may take a few seconds)...');
175
+ let response = await fetch(`https://baconipsum.com/api/?type=all-meat&paras=${getRandomInt(10)}&start-with-lorem=1`);
176
+ let postContent = await response.json();
177
+ log.debug(`Post content: ${postContent}`);
178
+ if (generatorInfo)
179
+ postContent.push(GENERATOR_CONTENT);
180
+ var thePost = '---\n';
181
+ thePost += YAML.stringify(postFm, { logLevel: 'silent' });
182
+ thePost += '---\n\n';
183
+ thePost += postContent.join('\n\n');
184
+ var outputFilePath = path.join(process.cwd(), targetFolder);
185
+ if (yearMode) {
186
+ outputFilePath = path.join(outputFilePath, currentDate.getFullYear().toString());
187
+ if (!fs.existsSync(outputFilePath))
188
+ fs.mkdirSync(outputFilePath, { recursive: true });
189
+ }
190
+ var outputFilePath = path.join(outputFilePath, postTitle.toLowerCase().replaceAll(' ', '-') + '.md');
191
+ counterStr = i.toString().padStart(counterLen);
192
+ writeConsole(green, `[${counterStr}] Writing`, outputFilePath);
193
+ fs.writeFileSync(outputFilePath, thePost, 'utf8');
194
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "eleventy-generate-posts",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "description": "Generates batches of Eleventy posts",
5
5
  "author": "John M. Wargo",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/johnwargo/eleventy-generate-posts"
9
+ "url": "git+https://github.com/johnwargo/eleventy-generate-posts.git"
10
10
  },
11
11
  "type": "module",
12
12
  "main": "eleventy-generate-posts.js",
@@ -19,7 +19,8 @@
19
19
  ],
20
20
  "scripts": {
21
21
  "test": "echo \"Error: no test specified\" && exit 1",
22
- "start": "tsc && node eleventy-generate-posts.js"
22
+ "start": "tsc && node eleventy-generate-posts.js",
23
+ "startg": "tsc && node eleventy-generate-posts.js -g"
23
24
  },
24
25
  "dependencies": {
25
26
  "boxen": "^7.0.2",
package/readme.md CHANGED
@@ -43,6 +43,7 @@ by John M. Wargo (https://johnwargo.com)
43
43
  √ Post tag? ... post
44
44
  √ Start year for generated posts? ... 2023
45
45
  √ Use year folder for posts? ... yes
46
+ √ Include timestamp in post metadata? ... yes
46
47
 
47
48
  Settings Summary:
48
49
  ----------------------------------------
@@ -55,16 +56,16 @@ Output folder: D:\dev\node\11ty-generate-posts\posts
55
56
 
56
57
  Generating posts...
57
58
  ----------------------------------------
58
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\navigator-washbasin-dramatize-landside-sensation.md
59
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\amusing-peculiar-surgical-borough-impotency-surround-tubular.md
60
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\maturely-convene-squishier-verify.md
61
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\matchless-overlaid-expend-oxidation-tribesman.md
62
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\basis-brunt-swaddling-ladylike-support-epidemic-graded.md
63
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\certify-unsheathe-undress-obstacle-tweak-tray-ridden.md
64
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\matching-enjoying-contact-atlas.md
65
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\android-gecko-penalize-possum.md
66
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\contempt-acquire-filtrate-defense-ergonomic-acts.md
67
- Writing: D:\dev\node\11ty-generate-posts\posts\2023\splinter-ecology-computer-nearby-shorts-feminize.md
59
+ [ 1] Writing: D:\dev\node\11ty-generate-posts\posts\2023\navigator-washbasin-dramatize-landside-sensation.md
60
+ [ 2] Writing: D:\dev\node\11ty-generate-posts\posts\2023\amusing-peculiar-surgical-borough-impotency-surround-tubular.md
61
+ [ 3] Writing: D:\dev\node\11ty-generate-posts\posts\2023\maturely-convene-squishier-verify.md
62
+ [ 4] Writing: D:\dev\node\11ty-generate-posts\posts\2023\matchless-overlaid-expend-oxidation-tribesman.md
63
+ [ 5] Writing: D:\dev\node\11ty-generate-posts\posts\2023\basis-brunt-swaddling-ladylike-support-epidemic-graded.md
64
+ [ 6] Writing: D:\dev\node\11ty-generate-posts\posts\2023\certify-unsheathe-undress-obstacle-tweak-tray-ridden.md
65
+ [ 7] Writing: D:\dev\node\11ty-generate-posts\posts\2023\matching-enjoying-contact-atlas.md
66
+ [ 8] Writing: D:\dev\node\11ty-generate-posts\posts\2023\android-gecko-penalize-possum.md
67
+ [ 9] Writing: D:\dev\node\11ty-generate-posts\posts\2023\contempt-acquire-filtrate-defense-ergonomic-acts.md
68
+ [10] Writing: D:\dev\node\11ty-generate-posts\posts\2023\splinter-ecology-computer-nearby-shorts-feminize.md
68
69
  ```
69
70
 
70
71
  Configuration options are:
@@ -74,11 +75,17 @@ Configuration options are:
74
75
  * **Post Tag:** The front matter `tag` property applied to the generated posts
75
76
  * **Start Year:** The starting year used for post date in the generated posts. The command uses the current date or the current date with the specified year (when provided) to for the post date for the first generated post. For subsequent post dates, the command randomly decrements the day.
76
77
  * **Use Year Folder:** specifies whether the module writes generated posts to the Posts folder (`N`) or into a separate folder for each year (`Y`).
78
+ * **Timestamp** Adds a `timestamp` property (`timestamp: 2026-01-13T12:13:16.979Z`) to generated post frontmatter. On many of my sites, I display the `timestamp` for posts instead of the `date` property; this allows me to sort posts by creation time as well as date.
77
79
 
78
80
  Obviously if you generate enough posts to push into the previous year, the posts will save into a folder for the previous year.
79
81
 
80
82
  To enable debug mode, pass a `-d` flag on the command-line; in this mode, the module writes additional information to the console as it executes.
81
83
 
84
+ Add a reference to this module at the bottom of all generated posts using the `-g` command-line parameter. I added this feature primarily for my personal use so I can advertise the module when I use it. Here's an example of the added content:
85
+
86
+ > ***
87
+ > Post content generated by [Eleventy Generate Posts](https://www.npmjs.com/package/eleventy-generate-posts)
88
+
82
89
  ## Example Post
83
90
 
84
91
  A sample generated post looks like the following:
@@ -86,8 +93,9 @@ A sample generated post looks like the following:
86
93
  ```markdown
87
94
  ---
88
95
  title: Boaster Halogen Jokingly Evident Decode Steadfast
89
- date: 2023-03-19
96
+ date: 2026-01-13
90
97
  tags: post
98
+ timestamp: 2026-01-13T12:13:16.979Z
91
99
  ---
92
100
 
93
101
  Bacon ipsum dolor amet brisket picanha swine beef ribs pork. Pig short ribs andouille ham ribeye hamburger