@entro314labs/markdownfix 0.0.6

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/.remarkignore ADDED
@@ -0,0 +1,59 @@
1
+ # Dependencies
2
+ node_modules/
3
+ npm-debug.log*
4
+ yarn-debug.log*
5
+ yarn-error.log*
6
+
7
+ # Build outputs
8
+ dist/
9
+ build/
10
+ out/
11
+ .next/
12
+ .nuxt/
13
+ .cache/
14
+
15
+ # Environment files
16
+ .env*
17
+
18
+ # IDE files
19
+ .vscode/
20
+ .idea/
21
+ *.swp
22
+ *.swo
23
+ *~
24
+
25
+ # OS files
26
+ .DS_Store
27
+ Thumbs.db
28
+
29
+ # Logs
30
+ *.log
31
+ logs/
32
+
33
+ # Temporary files
34
+ .tmp/
35
+ temp/
36
+ *.tmp
37
+
38
+ # Package manager files
39
+ package-lock.json
40
+ yarn.lock
41
+ pnpm-lock.yaml
42
+
43
+ # Git files
44
+ .git/
45
+ .gitignore
46
+
47
+ # Backup files
48
+ *.backup
49
+ *.bak
50
+ *~
51
+
52
+ # Coverage reports
53
+ coverage/
54
+ .nyc_output/
55
+
56
+ # Specific files to ignore
57
+ CHANGELOG.md
58
+ LICENSE
59
+ README.md
package/.remarkrc.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Opinionated Remark configuration for markdown processing
3
+ * Supports .md, and .mdx files with consistent formatting
4
+ * Optionally supports .mdd files if mdd package is installed
5
+ */
6
+
7
+ // Try to load MDD plugins if available
8
+ let mddPlugins = [];
9
+ try {
10
+ const mddDocStructure = await import('@entro314labs/mdd/plugins/remark-mdd-document-structure.js');
11
+ const mddTextFormatting = await import('@entro314labs/mdd/plugins/remark-mdd-text-formatting.js');
12
+ const mddMdxConditional = await import('@entro314labs/mdd/plugins/remark-mdx-conditional.js');
13
+
14
+ mddPlugins = [
15
+ mddMdxConditional.default,
16
+ mddDocStructure.default,
17
+ mddTextFormatting.default
18
+ ];
19
+ console.log('✓ MDD support enabled');
20
+ } catch (e) {
21
+ // MDD package not installed - only support .md and .mdx
22
+ mddPlugins = ['remark-mdx'];
23
+ console.log('ℹ MDD support not available (install mdd package for .mdd file support)');
24
+ }
25
+
26
+ export default {
27
+ // Configure remark-stringify output formatting
28
+ settings: {
29
+ bullet: '-', // Use - for unordered lists
30
+ bulletOther: '*', // Use * for nested lists
31
+ bulletOrdered: '.', // Use 1. 2. 3. for ordered lists
32
+ emphasis: '_', // Use _emphasis_ over *emphasis*
33
+ strong: '*', // Use **strong** over __strong__
34
+ fence: '`', // Use ``` for code fences
35
+ fences: true, // Always use fences for code blocks
36
+ incrementListMarker: true, // Increment ordered list markers
37
+ listItemIndent: 'one', // Use one space for list indentation
38
+ quote: '"', // Use double quotes in titles
39
+ rule: '-', // Use --- for horizontal rules
40
+ ruleRepetition: 3, // Use exactly 3 characters for rules
41
+ ruleSpaces: false, // No spaces in horizontal rules
42
+ setext: false, // Use # instead of === underlines
43
+ tightDefinitions: true, // No blank lines between definitions
44
+ },
45
+
46
+ plugins: [
47
+ // Enable support for frontmatter (---, +++)
48
+ 'remark-frontmatter',
49
+
50
+ // Enable GitHub Flavored Markdown (tables, strikethrough, etc.)
51
+ 'remark-gfm',
52
+
53
+ // MDX/MDD plugins (loaded dynamically above)
54
+ ...mddPlugins,
55
+
56
+ // Apply consistent style presets
57
+ 'remark-preset-lint-consistent',
58
+ 'remark-preset-lint-recommended',
59
+ 'remark-preset-lint-markdown-style-guide',
60
+
61
+ // Lint rules
62
+ ['remark-lint-heading-increment', true], // MD001
63
+ ['remark-lint-no-duplicate-headings', true], // MD024
64
+ ['remark-lint-no-emphasis-as-heading', true], // MD036
65
+ ['remark-lint-emphasis-marker', '_'],
66
+ ['remark-lint-strong-marker', '*'],
67
+ ['remark-lint-heading-style', 'atx'],
68
+ ['remark-lint-list-item-indent', 'one'],
69
+ ['remark-lint-ordered-list-marker-style', '.'],
70
+ ['remark-lint-ordered-list-marker-value', 'ordered'],
71
+ ['remark-lint-unordered-list-marker-style', '-'],
72
+ ['remark-lint-table-cell-padding', 'padded'],
73
+ ['remark-lint-table-pipe-alignment', true],
74
+ ['remark-lint-link-title-style', '"'],
75
+ ['remark-lint-no-trailing-spaces', true],
76
+ ['remark-lint-final-newline', true],
77
+ ['remark-lint-hard-break-spaces', true],
78
+ ['remark-lint-no-empty-sections', true],
79
+
80
+ // Must be last - handles the output formatting
81
+ 'remark-stringify'
82
+ ]
83
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Entro314 Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,346 @@
1
+ # Markdown Formatter (markdownfix)
2
+
3
+ **Opinionated markdown formatter and linter for `.md`, `.mdd`, and `.mdx` files**
4
+
5
+ Built on the Remark ecosystem with strict, consistent formatting rules for developer documentation, technical writing, and web content. Perfect for README files, technical docs, blogs, and GitHub wikis.
6
+
7
+ ## Features
8
+
9
+ - ✅ **Opinionated formatting** - Consistent style across all markdown files
10
+ - ✅ **GitHub Flavored Markdown** - Tables, task lists, strikethrough, autolinks
11
+ - ✅ **MDX support** - JSX components in markdown
12
+ - ✅ **Comprehensive linting** - 40+ lint rules for quality and consistency
13
+ - ✅ **Link validation** - Check for broken links
14
+ - ✅ **Auto-fixing** - Automatically fix formatting issues
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Install globally
20
+ npm install -g markdownfix
21
+
22
+ # Or use with npx (no installation)
23
+ npx markdownfix --help
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ # Format all markdown files in current directory
30
+ markdownfix format
31
+
32
+ # Check formatting without writing changes
33
+ markdownfix check
34
+
35
+ # Lint files for issues
36
+ markdownfix lint
37
+
38
+ # Format specific files
39
+ markdownfix format README.md docs/*.md
40
+
41
+ # Use glob patterns
42
+ markdownfix format --glob "src/**/*.md"
43
+
44
+ # Quiet mode (suppress output)
45
+ markdownfix format --quiet
46
+ ```
47
+
48
+ ### Using as a Package Script
49
+
50
+ Add to your `package.json`:
51
+
52
+ ```json
53
+ {
54
+ "scripts": {
55
+ "format:md": "markdownfix format",
56
+ "lint:md": "markdownfix lint"
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### Initialize Project
62
+
63
+ ```bash
64
+ # Create .remarkrc.js configuration
65
+ markdownfix init
66
+
67
+ # Create example content structure
68
+ markdownfix setup
69
+ ```
70
+
71
+ ## What Gets Formatted
72
+
73
+ ### Lists
74
+ - **Unordered**: `-` (never `*` or `+`)
75
+ - **Ordered**: `1.` incremental (never `1)`)
76
+
77
+ ### Emphasis
78
+ - **Italic**: `_text_` (underscore, not asterisk)
79
+ - **Bold**: `**text**` (double asterisk, not underscore)
80
+
81
+ ### Headings
82
+ - **Style**: ATX (`# Title`), never setext (`===`)
83
+ - **Hierarchy**: Must progress sequentially (no skipping levels)
84
+
85
+ ### Code Blocks
86
+ - Always fenced (` ``` `) with language identifiers
87
+ - Never indented code blocks
88
+
89
+ ### Formatting
90
+ - **Line length**: 100 characters max with smart wrapping
91
+ - **Tables**: Padded cells with aligned pipes (auto-formatted)
92
+ - **Final newline**: All files must end with `\n`
93
+ - **No trailing spaces**: Automatically removed
94
+
95
+ ## File Support
96
+
97
+ | Extension | Support | Features | Use Case |
98
+ |-----------|---------|----------|----------|
99
+ | `.md` | ✅ Full | GFM, frontmatter, tables, task lists | Documentation, READMEs, blogs |
100
+ | `.mdx` | ✅ Full | Above + JSX components, imports/exports | React docs, interactive content |
101
+ | `.mdd` | ⚠️ Optional | Business documents (see below) | Invoices, proposals, contracts |
102
+
103
+ ### MDD Support
104
+
105
+ This formatter can **optionally** format `.mdd` files if you install the [MDD package](https://www.npmjs.com/package/@entro314labs/mdd):
106
+
107
+ ```bash
108
+ # Install MDD support
109
+ pnpm add mdd
110
+
111
+ # Now .mdd files will be formatted
112
+ pnpm run format
113
+ ```
114
+
115
+ **Note**: MDD is a separate project for business documents. See the [MDD project](https://github.com/entro314-labs/mdd) for details.
116
+
117
+ ## CLI Commands
118
+
119
+ ### Available Commands
120
+
121
+ ```bash
122
+ markdownfix format [files...] # Format and fix markdown files
123
+ markdownfix check [files...] # Check without writing changes
124
+ markdownfix lint [files...] # Lint for issues only
125
+ markdownfix init # Create .remarkrc.js config
126
+ markdownfix setup # Create example content structure
127
+ ```
128
+
129
+ ### Command Aliases
130
+
131
+ The CLI is available via two commands:
132
+
133
+ - `markdownfix` (full name)
134
+ - `mdfix` (short alias)
135
+
136
+ ```bash
137
+ # These are equivalent
138
+ markdownfix format
139
+ mdfix format
140
+ ```
141
+
142
+ ### Options
143
+
144
+ ```bash
145
+ --help, -h Show help message
146
+ --version, -v Show version number
147
+ --quiet, -q Suppress output except errors
148
+ --glob <pattern> Use glob pattern for file matching
149
+ ```
150
+
151
+ ### Examples
152
+
153
+ ```bash
154
+ # Format all markdown in project
155
+ mdfix format
156
+
157
+ # Check specific files
158
+ mdfix check README.md CHANGELOG.md
159
+
160
+ # Lint with glob pattern
161
+ mdfix lint --glob "docs/**/*.{md,mdx}"
162
+
163
+ # Quiet formatting
164
+ mdfix format --quiet
165
+
166
+ # Get help
167
+ mdfix --help
168
+ ```
169
+
170
+ ## Development Scripts
171
+
172
+ If you're working on the markdownfix project itself:
173
+
174
+ ```bash
175
+ # Formatting
176
+ pnpm run format # Apply fixes using remark-cli
177
+ pnpm run format:check # Preview changes
178
+
179
+ # Linting
180
+ pnpm run lint # Check compliance
181
+
182
+ # Combined
183
+ pnpm run process # Format then lint
184
+ pnpm run process:safe # Dry-run first, then process
185
+ pnpm test # Same as process:safe
186
+
187
+ # Utilities
188
+ pnpm run check-links # Validate all markdown links
189
+ pnpm run clean-cache # Clear remark cache
190
+ ```
191
+
192
+ ## Project Structure
193
+
194
+ ```
195
+ markdownfix/
196
+ ├── content/ # Example content
197
+ │ ├── docs/
198
+ │ ├── blog/
199
+ │ └── guides/
200
+ ├── .remarkrc.js # Formatting & linting rules
201
+ ├── .remarkignore # Files to exclude
202
+ ├── setup.js # Bootstrap script
203
+ ├── package.json
204
+ └── README.md
205
+ ```
206
+
207
+ ## Configuration
208
+
209
+ ### `.remarkrc.js`
210
+
211
+ Central configuration defining:
212
+ - All formatting rules
213
+ - Plugin chain order
214
+ - Lint rule settings
215
+
216
+ **Plugin order is critical:**
217
+ 1. `remark-frontmatter` - Parse YAML/TOML
218
+ 2. `remark-gfm` - GitHub Flavored Markdown
219
+ 3. `remark-mdx` - MDX support (conditional)
220
+ 4. Lint presets + rules
221
+ 5. `remark-stringify` - Must be last
222
+
223
+ ### `.remarkignore`
224
+
225
+ Files excluded from processing:
226
+ - `README.md`
227
+ - `CHANGELOG.md`
228
+ - `LICENSE`
229
+ - `node_modules/`
230
+ - `pnpm-lock.yaml`
231
+
232
+ ## Frontmatter Convention
233
+
234
+ ```yaml
235
+ ---
236
+ title: Required for all content files
237
+ date: YYYY-MM-DD format preferred
238
+ author: Optional but recommended
239
+ ---
240
+ ```
241
+
242
+ ## Common Issues
243
+
244
+ ### "No input" Errors
245
+
246
+ **Cause**: No markdown files found or all files excluded
247
+
248
+ **Solution**:
249
+ - Ensure markdown files exist in `content/` directory
250
+ - Check `.remarkignore` patterns
251
+ - Verify correct file extensions (`.md`, `.mdx`)
252
+
253
+ ### Linting Failures After Formatting
254
+
255
+ **Cause**: Some rules require manual fixes (duplicate headings, broken links)
256
+
257
+ **Solution**:
258
+ - Read console output for specific violations
259
+ - Manually fix reported issues
260
+ - Use `pnpm run format:check` to separate formatting from lint errors
261
+
262
+ ### MDX Parsing Errors
263
+
264
+ **Cause**: Invalid JSX syntax
265
+
266
+ **Solution**:
267
+ - Verify JSX is valid JavaScript
268
+ - Check all tags are properly closed
269
+ - Ensure React components use `<Component />` not `<Component>`
270
+
271
+ ## Bootstrap New Project
272
+
273
+ ```bash
274
+ # Create example content structure
275
+ node setup.js
276
+
277
+ # Install dependencies
278
+ pnpm install
279
+
280
+ # Verify everything works
281
+ pnpm run process:safe
282
+ ```
283
+
284
+ ## Editor Integration
285
+
286
+ **Disable VS Code remark extensions** - they may conflict with this opinionated configuration. Always use command line processing.
287
+
288
+ ## Two Projects, One Ecosystem
289
+
290
+ This repository focuses on **developer documentation** (READMEs, technical docs, blogs).
291
+
292
+ For **business documents** (invoices, proposals, letters, contracts), we have a companion project:
293
+
294
+ ### 📄 [MDD (Markdown Document)](https://github.com/entro314-labs/mdd)
295
+
296
+ MDD extends markdown with business-focused features:
297
+
298
+ - Letterheads and headers/footers
299
+ - Signature blocks
300
+ - Contact information blocks
301
+ - Professional PDF/DOCX output
302
+ - Invoice and proposal templates
303
+
304
+ ```bash
305
+ # Quick example
306
+ cd ../mdd
307
+ pnpm run preview examples/invoice.mdd
308
+ # Opens professional HTML invoice in browser
309
+ ```
310
+
311
+ **When to use which:**
312
+
313
+ | Use Case | Project | File Type |
314
+ |----------|---------|-----------|
315
+ | README files | Markdown Formatter | `.md` |
316
+ | Technical docs | Markdown Formatter | `.md` |
317
+ | Blog posts | Markdown Formatter | `.md` / `.mdx` |
318
+ | React documentation | Markdown Formatter | `.mdx` |
319
+ | **Business letters** | **MDD** | `.mdd` |
320
+ | **Invoices** | **MDD** | `.mdd` |
321
+ | **Proposals** | **MDD** | `.mdd` |
322
+ | **Contracts** | **MDD** | `.mdd` |
323
+
324
+ ## Documentation
325
+
326
+ - **[COMPREHENSIVE-GUIDE.md](COMPREHENSIVE-GUIDE.md)** - Detailed technical guide
327
+ - **[content/guides/style-guide.md](content/guides/style-guide.md)** - Style specifications
328
+
329
+ ## Tech Stack
330
+
331
+ - **Remark** - Markdown processor
332
+ - **Unified** - AST transformation
333
+ - **GFM** - GitHub Flavored Markdown
334
+ - **MDX** - JSX in markdown
335
+
336
+ ## Contributing
337
+
338
+ This is an opinionated formatter with specific style decisions. Contributions should:
339
+ - Maintain existing formatting rules
340
+ - Add value for developer documentation
341
+ - Not break existing lint rules
342
+ - Include test cases
343
+
344
+ ## License
345
+
346
+ MIT
package/cli.js ADDED
@@ -0,0 +1,344 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI tool for markdown formatting and linting
5
+ * Provides commands to format, lint, and check markdown files
6
+ */
7
+
8
+ import { remark } from 'remark';
9
+ import { read } from 'to-vfile';
10
+ import { reporter } from 'vfile-reporter';
11
+ import remarkPresetLintRecommended from 'remark-preset-lint-recommended';
12
+ import remarkPresetLintConsistent from 'remark-preset-lint-consistent';
13
+ import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide';
14
+ import remarkFrontmatter from 'remark-frontmatter';
15
+ import remarkGfm from 'remark-gfm';
16
+ import remarkStringify from 'remark-stringify';
17
+ import fs from 'fs/promises';
18
+ import path from 'path';
19
+ import { glob } from 'glob';
20
+
21
+ const MARKDOWN_EXTENSIONS = ['md', 'mdx', 'mdd'];
22
+
23
+ // Import configuration from .remarkrc.js
24
+ let remarkConfig;
25
+ try {
26
+ const configModule = await import(path.join(process.cwd(), '.remarkrc.js'));
27
+ remarkConfig = configModule.default;
28
+ } catch (e) {
29
+ console.warn('⚠️ No .remarkrc.js found in current directory, using default config');
30
+ remarkConfig = null;
31
+ }
32
+
33
+ /**
34
+ * Show help text
35
+ */
36
+ function showHelp() {
37
+ console.log(`
38
+ markdownfix - Opinionated markdown formatter and linter
39
+
40
+ USAGE:
41
+ markdownfix <command> [options] [files...]
42
+
43
+ COMMANDS:
44
+ format [files] Format markdown files (writes changes)
45
+ check [files] Check formatting without writing changes
46
+ lint [files] Lint markdown files (no formatting)
47
+ init Create .remarkrc.js configuration file
48
+ setup Create example content structure
49
+
50
+ OPTIONS:
51
+ --help, -h Show this help message
52
+ --version, -v Show version number
53
+ --quiet, -q Suppress output except errors
54
+ --glob <pattern> Use glob pattern (e.g., "**/*.md")
55
+
56
+ EXAMPLES:
57
+ # Format all markdown files in current directory
58
+ markdownfix format
59
+
60
+ # Check specific files
61
+ markdownfix check README.md docs/*.md
62
+
63
+ # Lint with glob pattern
64
+ markdownfix lint --glob "src/**/*.md"
65
+
66
+ # Format quietly
67
+ markdownfix format --quiet
68
+
69
+ # Initialize configuration
70
+ markdownfix init
71
+
72
+ For more information, visit: https://github.com/entro314-labs/markdownfix
73
+ `);
74
+ }
75
+
76
+ /**
77
+ * Get files to process
78
+ */
79
+ async function getFiles(args, globPattern) {
80
+ if (globPattern) {
81
+ return glob(globPattern, { ignore: 'node_modules/**' });
82
+ }
83
+
84
+ if (args.length > 0) {
85
+ return args;
86
+ }
87
+
88
+ // Default: find all markdown files in current directory and subdirectories
89
+ const patterns = MARKDOWN_EXTENSIONS.map(ext => `**/*.${ext}`);
90
+ const allFiles = [];
91
+
92
+ for (const pattern of patterns) {
93
+ const files = await glob(pattern, { ignore: ['node_modules/**', '.git/**'] });
94
+ allFiles.push(...files);
95
+ }
96
+
97
+ return allFiles;
98
+ }
99
+
100
+ /**
101
+ * Process files with remark
102
+ */
103
+ async function processFiles(files, options = {}) {
104
+ const { write = false, quiet = false, lintOnly = false } = options;
105
+
106
+ let hasErrors = false;
107
+ let processedCount = 0;
108
+
109
+ for (const filePath of files) {
110
+ try {
111
+ const file = await read(filePath);
112
+
113
+ // Build processor
114
+ let processor = remark()
115
+ .use(remarkFrontmatter, ['yaml'])
116
+ .use(remarkGfm);
117
+
118
+ // Add lint presets
119
+ processor = processor
120
+ .use(remarkPresetLintRecommended)
121
+ .use(remarkPresetLintConsistent)
122
+ .use(remarkPresetLintMarkdownStyleGuide);
123
+
124
+ // Add stringify for formatting (unless lint-only)
125
+ if (!lintOnly) {
126
+ processor = processor.use(remarkStringify, {
127
+ bullet: '-',
128
+ emphasis: '_',
129
+ fences: true,
130
+ listItemIndent: 'one',
131
+ rule: '-',
132
+ strong: '*',
133
+ tightDefinitions: true
134
+ });
135
+ }
136
+
137
+ const result = await processor.process(file);
138
+
139
+ // Check for lint errors/warnings
140
+ if (result.messages.length > 0) {
141
+ if (!quiet) {
142
+ console.error(reporter(result));
143
+ }
144
+ hasErrors = true;
145
+ }
146
+
147
+ // Write changes if requested and not lint-only
148
+ if (write && !lintOnly) {
149
+ await fs.writeFile(filePath, String(result));
150
+ if (!quiet) {
151
+ console.log(`✓ Formatted: ${filePath}`);
152
+ }
153
+ processedCount++;
154
+ } else if (!write && !lintOnly && !quiet) {
155
+ // Preview mode
156
+ console.log(`\n${'='.repeat(60)}`);
157
+ console.log(`File: ${filePath}`);
158
+ console.log('='.repeat(60));
159
+ console.log(String(result));
160
+ }
161
+
162
+ } catch (error) {
163
+ console.error(`✗ Error processing ${filePath}:`, error.message);
164
+ hasErrors = true;
165
+ }
166
+ }
167
+
168
+ return { hasErrors, processedCount, totalFiles: files.length };
169
+ }
170
+
171
+ /**
172
+ * Initialize .remarkrc.js configuration
173
+ */
174
+ async function initConfig() {
175
+ const configPath = path.join(process.cwd(), '.remarkrc.js');
176
+
177
+ try {
178
+ await fs.access(configPath);
179
+ console.log('⚠️ .remarkrc.js already exists');
180
+ return;
181
+ } catch {
182
+ // File doesn't exist, create it
183
+ }
184
+
185
+ const configContent = `/**
186
+ * Remark configuration for markdown formatting and linting
187
+ * @see https://github.com/remarkjs/remark
188
+ */
189
+
190
+ export default {
191
+ plugins: [
192
+ // Frontmatter support
193
+ ['remark-frontmatter', ['yaml']],
194
+
195
+ // GitHub Flavored Markdown
196
+ 'remark-gfm',
197
+
198
+ // Presets
199
+ 'remark-preset-lint-recommended',
200
+ 'remark-preset-lint-consistent',
201
+ 'remark-preset-lint-markdown-style-guide',
202
+
203
+ // Formatting rules
204
+ ['remark-lint-emphasis-marker', '_'],
205
+ ['remark-lint-strong-marker', '*'],
206
+ ['remark-lint-unordered-list-marker-style', '-'],
207
+ ['remark-lint-ordered-list-marker-style', '.'],
208
+ ['remark-lint-maximum-line-length', 100],
209
+
210
+ // Stringify options for formatting
211
+ ['remark-stringify', {
212
+ bullet: '-',
213
+ emphasis: '_',
214
+ fences: true,
215
+ listItemIndent: 'one',
216
+ rule: '-',
217
+ strong: '*',
218
+ tightDefinitions: true
219
+ }]
220
+ ]
221
+ };
222
+ `;
223
+
224
+ await fs.writeFile(configPath, configContent);
225
+ console.log('✓ Created .remarkrc.js configuration file');
226
+ }
227
+
228
+ /**
229
+ * Main CLI entry point
230
+ */
231
+ async function main() {
232
+ const args = process.argv.slice(2);
233
+
234
+ // Handle version
235
+ if (args.includes('--version') || args.includes('-v')) {
236
+ const pkg = JSON.parse(await fs.readFile(new URL('./package.json', import.meta.url), 'utf-8'));
237
+ console.log(pkg.version);
238
+ return;
239
+ }
240
+
241
+ // Handle help
242
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
243
+ showHelp();
244
+ return;
245
+ }
246
+
247
+ const command = args[0];
248
+ const commandArgs = args.slice(1);
249
+
250
+ // Parse options
251
+ const options = {
252
+ quiet: commandArgs.includes('--quiet') || commandArgs.includes('-q'),
253
+ glob: null
254
+ };
255
+
256
+ // Extract glob pattern
257
+ const globIndex = commandArgs.findIndex(arg => arg === '--glob');
258
+ if (globIndex !== -1 && commandArgs[globIndex + 1]) {
259
+ options.glob = commandArgs[globIndex + 1];
260
+ }
261
+
262
+ // Filter out option flags to get file arguments
263
+ const fileArgs = commandArgs.filter(arg =>
264
+ !arg.startsWith('--') &&
265
+ !arg.startsWith('-') &&
266
+ arg !== options.glob
267
+ );
268
+
269
+ // Execute command
270
+ switch (command) {
271
+ case 'format': {
272
+ const files = await getFiles(fileArgs, options.glob);
273
+ if (files.length === 0) {
274
+ console.log('No markdown files found');
275
+ return;
276
+ }
277
+ if (!options.quiet) {
278
+ console.log(`Formatting ${files.length} file(s)...`);
279
+ }
280
+ const result = await processFiles(files, { write: true, quiet: options.quiet });
281
+ if (!options.quiet) {
282
+ console.log(`\n✓ Formatted ${result.processedCount} of ${result.totalFiles} files`);
283
+ }
284
+ process.exit(result.hasErrors ? 1 : 0);
285
+ break;
286
+ }
287
+
288
+ case 'check': {
289
+ const files = await getFiles(fileArgs, options.glob);
290
+ if (files.length === 0) {
291
+ console.log('No markdown files found');
292
+ return;
293
+ }
294
+ if (!options.quiet) {
295
+ console.log(`Checking ${files.length} file(s)...`);
296
+ }
297
+ const result = await processFiles(files, { write: false, quiet: options.quiet });
298
+ if (!options.quiet) {
299
+ console.log(`\n${result.hasErrors ? '✗' : '✓'} Checked ${result.totalFiles} files`);
300
+ }
301
+ process.exit(result.hasErrors ? 1 : 0);
302
+ break;
303
+ }
304
+
305
+ case 'lint': {
306
+ const files = await getFiles(fileArgs, options.glob);
307
+ if (files.length === 0) {
308
+ console.log('No markdown files found');
309
+ return;
310
+ }
311
+ if (!options.quiet) {
312
+ console.log(`Linting ${files.length} file(s)...`);
313
+ }
314
+ const result = await processFiles(files, { lintOnly: true, quiet: options.quiet });
315
+ if (!options.quiet) {
316
+ console.log(`\n${result.hasErrors ? '✗' : '✓'} Linted ${result.totalFiles} files`);
317
+ }
318
+ process.exit(result.hasErrors ? 1 : 0);
319
+ break;
320
+ }
321
+
322
+ case 'init': {
323
+ await initConfig();
324
+ break;
325
+ }
326
+
327
+ case 'setup': {
328
+ // Import and run setup.js
329
+ const setupPath = new URL('./setup.js', import.meta.url);
330
+ await import(setupPath);
331
+ break;
332
+ }
333
+
334
+ default:
335
+ console.error(`Unknown command: ${command}`);
336
+ console.error('Run "markdownfix --help" for usage information');
337
+ process.exit(1);
338
+ }
339
+ }
340
+
341
+ main().catch(error => {
342
+ console.error('Fatal error:', error);
343
+ process.exit(1);
344
+ });
package/package.json ADDED
@@ -0,0 +1,129 @@
1
+ {
2
+ "name": "@entro314labs/markdownfix",
3
+ "version": "0.0.6",
4
+ "description": "Opinionated markdown formatter and linter for MD, MDD and MDX files using Remark/Unified ecosystem",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/entro314-labs/markdownfix.git"
9
+ },
10
+ "homepage": "https://github.com/entro314-labs/markdownfix#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/entro314-labs/markdownfix/issues"
13
+ },
14
+ "files": [
15
+ ".remarkrc.js",
16
+ ".remarkignore",
17
+ "setup.js",
18
+ "cli.js",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "bin": {
23
+ "markdownfix": "./cli.js",
24
+ "mdfix": "./cli.js",
25
+ "markdownfix-setup": "./setup.js"
26
+ },
27
+ "keywords": [
28
+ "markdown",
29
+ "mdx",
30
+ "mdd",
31
+ "linter",
32
+ "formatter",
33
+ "remark",
34
+ "gfm",
35
+ "unified",
36
+ "markdownfix",
37
+ "markdown-linter",
38
+ "opinionated"
39
+ ],
40
+ "author": "Dominikos Pritis",
41
+ "license": "MIT",
42
+ "devDependencies": {
43
+ "markdown-link-check": "^3.14.1",
44
+ "remark-cli": "^12.0.1"
45
+ },
46
+ "engines": {
47
+ "node": ">=22.0.0"
48
+ },
49
+ "dependencies": {
50
+ "@adobe/remark-gridtables": "^3.0.15",
51
+ "@code-dot-org/remark-plugins": "^2.0.0",
52
+ "@entro314labs/mdd": "^0.0.6",
53
+ "@theguild/remark-mermaid": "^0.3.0",
54
+ "fumadocs-docgen": "^3.0.2",
55
+ "glob": "^11.0.3",
56
+ "js-yaml": "^4.1.0",
57
+ "rehype-remark": "^10.0.1",
58
+ "remark": "^15.0.1",
59
+ "remark-directive": "^4.0.0",
60
+ "remark-docx": "^0.1.8",
61
+ "remark-emoji": "^5.0.2",
62
+ "remark-frontmatter": "^5.0.0",
63
+ "remark-gfm": "^4.0.1",
64
+ "remark-html": "^16.0.1",
65
+ "remark-linkify": "^1.1.0",
66
+ "remark-lint": "^10.0.1",
67
+ "remark-lint-alphabetize-lists": "^3.0.0",
68
+ "remark-lint-definition-case": "^4.0.1",
69
+ "remark-lint-emphasis-marker": "^4.0.1",
70
+ "remark-lint-final-newline": "^3.0.1",
71
+ "remark-lint-frontmatter-schema": "^3.15.4",
72
+ "remark-lint-hard-break-spaces": "^4.1.1",
73
+ "remark-lint-heading-capitalization": "^1.3.0",
74
+ "remark-lint-heading-increment": "^4.0.1",
75
+ "remark-lint-heading-style": "^4.0.1",
76
+ "remark-lint-link-title-style": "^4.0.1",
77
+ "remark-lint-list-item-bullet-indent": "^5.0.1",
78
+ "remark-lint-list-item-indent": "^4.0.1",
79
+ "remark-lint-list-item-style": "^3.0.1",
80
+ "remark-lint-match-punctuation": "^0.2.1",
81
+ "remark-lint-maximum-line-length": "^4.1.1",
82
+ "remark-lint-mdash-style": "^1.1.1",
83
+ "remark-lint-mdx-jsx-attribute-sort": "^1.0.1",
84
+ "remark-lint-mdx-jsx-no-void-children": "^1.0.1",
85
+ "remark-lint-mdx-jsx-quote-style": "^1.0.1",
86
+ "remark-lint-mdx-jsx-self-close": "^1.0.1",
87
+ "remark-lint-no-dead-urls": "^2.0.1",
88
+ "remark-lint-no-duplicate-headings": "^4.0.1",
89
+ "remark-lint-no-duplicate-headings-in-section": "^4.0.1",
90
+ "remark-lint-no-emphasis-as-heading": "^4.0.1",
91
+ "remark-lint-no-empty-sections": "^4.0.0",
92
+ "remark-lint-no-heading-indent": "^5.0.1",
93
+ "remark-lint-no-heading-punctuation": "^4.0.1",
94
+ "remark-lint-no-trailing-spaces": "^3.0.2",
95
+ "remark-lint-ordered-list-marker-style": "^4.0.1",
96
+ "remark-lint-ordered-list-marker-value": "^4.0.1",
97
+ "remark-lint-strong-marker": "^4.0.1",
98
+ "remark-lint-table-cell-padding": "^5.1.1",
99
+ "remark-lint-table-pipe-alignment": "^4.1.1",
100
+ "remark-lint-table-pipes": "^5.0.1",
101
+ "remark-lint-unordered-list-marker-style": "^4.0.1",
102
+ "remark-mdc": "^3.7.0",
103
+ "remark-mdx": "^3.1.1",
104
+ "remark-mdx-frontmatter": "^5.2.0",
105
+ "remark-preset-lint-consistent": "^6.0.1",
106
+ "remark-preset-lint-markdown-style-guide": "^6.0.1",
107
+ "remark-preset-lint-recommended": "^7.0.1",
108
+ "remark-preset-remcohaszing": "^3.1.1",
109
+ "remark-rehype": "^11.1.2",
110
+ "remark-stringify": "^11.0.0",
111
+ "remark-toc": "^9.0.0",
112
+ "remark-typography": "^0.6.36",
113
+ "remark-validate-links": "^13.1.0",
114
+ "to-vfile": "^8.0.0",
115
+ "unified-lint-rule": "^3.0.1",
116
+ "unist-util-visit": "^5.0.0",
117
+ "vfile-reporter": "^8.1.1"
118
+ },
119
+ "scripts": {
120
+ "lint": "remark . --quiet --frail",
121
+ "format": "remark . --output --quiet",
122
+ "format:check": "remark . --quiet",
123
+ "check-links": "markdown-link-check **/*.{md,mdx,mdd}",
124
+ "process": "pnpm run format && pnpm run lint",
125
+ "process:safe": "pnpm run format:check && pnpm run lint",
126
+ "clean-cache": "rm -rf .remark-cache",
127
+ "test": "pnpm run process:safe"
128
+ }
129
+ }
package/setup.js ADDED
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Setup script for markdown processor project
5
+ * Creates necessary directories and example files
6
+ */
7
+
8
+ import fs from 'fs/promises'
9
+ import path from 'path'
10
+
11
+ const EXAMPLE_FILES = {
12
+ 'content/docs/getting-started.md': `---
13
+ title: Getting Started Guide
14
+ date: 2024-01-15
15
+ author: Team
16
+ ---
17
+
18
+ # Getting Started Guide
19
+
20
+ This is an example markdown file that will be processed by our formatter.
21
+
22
+ ## Installation
23
+
24
+ 1. Clone the repository
25
+ 2. Run \`npm install\`
26
+ 3. Start formatting your files
27
+
28
+ ### Prerequisites
29
+
30
+ Before you begin, make sure you have:
31
+
32
+ * Node.js 16 or higher
33
+ * npm or yarn package manager
34
+
35
+ ## Usage
36
+
37
+ The formatter supports various commands:
38
+
39
+ - \`npm run lint\` - Check for issues
40
+ - \`npm run format\` - Fix formatting
41
+ - \`npm run process\` - Complete processing
42
+
43
+ **Note**: Always backup your files before processing.
44
+
45
+ > **Warning**: Some changes cannot be automatically fixed and require manual review.
46
+
47
+ For more details, see the [API Reference](./api-reference.md).
48
+ `,
49
+
50
+ 'content/docs/api-reference.md': `---
51
+ title: API Reference
52
+ date: 2024-01-20
53
+ ---
54
+
55
+ # API Reference
56
+
57
+ ## Configuration Options
58
+
59
+ | Option | Type | Default | Description |
60
+ |--------|------|---------|-------------|
61
+ | \`bullet\` | string | \`-\` | Unordered list marker |
62
+ | \`emphasis\` | string | \`_\` | Emphasis marker |
63
+ | \`strong\` | string | \`*\` | Strong marker |
64
+
65
+ ## Available Scripts
66
+
67
+ ### npm run lint
68
+
69
+ Checks all markdown files for style violations.
70
+
71
+ \`\`\`bash
72
+ npm run lint
73
+ \`\`\`
74
+
75
+ ### npm run format
76
+
77
+ Automatically fixes formatting issues.
78
+
79
+ \`\`\`bash
80
+ npm run format
81
+ \`\`\`
82
+
83
+ ## Custom Rules
84
+
85
+ You can add custom rules in \`.remarkrc.js\`:
86
+
87
+ \`\`\`javascript
88
+ ['remark-lint-maximum-line-length', 80]
89
+ \`\`\`
90
+ `,
91
+
92
+ 'content/blog/index.md': `# Blog Posts
93
+
94
+ Welcome to our blog! Here are our latest posts:
95
+
96
+ ## Recent Posts
97
+
98
+ - [Markdown Tips and Tricks](./2024-01-15-markdown-tips.md)
99
+ - [MDX Features Overview](./2024-02-01-mdx-features.mdx)
100
+
101
+ ## Archive
102
+
103
+ Browse all posts by date or category.
104
+ `,
105
+
106
+ 'content/guides/style-guide.md': `---
107
+ title: Markdown Style Guide
108
+ ---
109
+
110
+ # Markdown Style Guide
111
+
112
+ This document defines the formatting standards enforced by this project.
113
+
114
+ ## Headings
115
+
116
+ - Use ATX-style headings (\`#\` syntax)
117
+ - Don't skip heading levels
118
+ - Add blank lines before and after headings
119
+
120
+ ### Good Example
121
+
122
+ \`\`\`markdown
123
+ # Main Heading
124
+
125
+ ## Section Heading
126
+
127
+ ### Subsection Heading
128
+ \`\`\`
129
+
130
+ ### Bad Example
131
+
132
+ \`\`\`markdown
133
+ #Main Heading
134
+ ####Skipped Levels
135
+ \`\`\`
136
+
137
+ ## Lists
138
+
139
+ ### Unordered Lists
140
+
141
+ Use hyphens (\`-\`) for unordered lists:
142
+
143
+ \`\`\`markdown
144
+ - First item
145
+ - Second item
146
+ - Nested item
147
+ - Another nested item
148
+ \`\`\`
149
+
150
+ ### Ordered Lists
151
+
152
+ Use periods (\`.\`) for ordered lists:
153
+
154
+ \`\`\`markdown
155
+ 1. First step
156
+ 2. Second step
157
+ 3. Third step
158
+ \`\`\`
159
+
160
+ ## Emphasis
161
+
162
+ - Use \`_underscores_\` for _emphasis_
163
+ - Use \`**asterisks**\` for **strong emphasis**
164
+
165
+ ## Code
166
+
167
+ ### Inline Code
168
+
169
+ Use \`backticks\` for inline code.
170
+
171
+ ### Code Blocks
172
+
173
+ Always use fenced code blocks with language identifiers:
174
+
175
+ \`\`\`javascript
176
+ function example() {
177
+ return "Hello World"
178
+ }
179
+ \`\`\`
180
+
181
+ ## Links
182
+
183
+ - Use descriptive link text
184
+ - Include link titles when helpful: [Example](https://example.com "Example Website")
185
+
186
+ ## Images
187
+
188
+ Always include descriptive alt text:
189
+
190
+ \`\`\`markdown
191
+ ![Screenshot of the main dashboard](./images/dashboard.png)
192
+ \`\`\`
193
+ `
194
+ }
195
+
196
+ async function createDirectory(dirPath) {
197
+ try {
198
+ await fs.mkdir(dirPath, { recursive: true })
199
+ console.log(`✓ Created directory: ${dirPath}`)
200
+ } catch (error) {
201
+ console.error(`✗ Failed to create directory ${dirPath}:`, error.message)
202
+ }
203
+ }
204
+
205
+ async function createFile(filePath, content) {
206
+ try {
207
+ // Ensure the directory exists
208
+ const dir = path.dirname(filePath)
209
+ await createDirectory(dir)
210
+
211
+ // Write the file
212
+ await fs.writeFile(filePath, content, 'utf8')
213
+ console.log(`✓ Created file: ${filePath}`)
214
+ } catch (error) {
215
+ console.error(`✗ Failed to create file ${filePath}:`, error.message)
216
+ }
217
+ }
218
+
219
+ async function setup() {
220
+ console.log('🚀 Setting up markdown processor project...\n')
221
+
222
+ // Create directories
223
+ const directories = [
224
+ 'content',
225
+ 'content/docs',
226
+ 'content/blog',
227
+ 'content/guides'
228
+ ]
229
+
230
+ console.log('📁 Creating directories...')
231
+ for (const dir of directories) {
232
+ await createDirectory(dir)
233
+ }
234
+
235
+ console.log('\n📝 Creating example files...')
236
+ for (const [filePath, content] of Object.entries(EXAMPLE_FILES)) {
237
+ await createFile(filePath, content)
238
+ }
239
+
240
+ console.log('\n✅ Setup complete!')
241
+ console.log('\nNext steps:')
242
+ console.log('1. Run `npm install` to install dependencies')
243
+ console.log('2. Run `npm run process` to format the example files')
244
+ console.log('3. Check the `content/` directory for formatted examples')
245
+ console.log('4. Add your own markdown files to the `content/` directory')
246
+ console.log('\nAvailable commands:')
247
+ console.log('- `npm run lint` - Check for formatting issues')
248
+ console.log('- `npm run format` - Fix formatting issues')
249
+ console.log('- `npm run process` - Format and lint files')
250
+ console.log('- `npm run check-links` - Check for broken links')
251
+ }
252
+
253
+ // Run setup if this script is executed directly
254
+ if (import.meta.url === `file://${process.argv[1]}`) {
255
+ setup().catch(console.error)
256
+ }
257
+
258
+ export { setup }