create-markdown2site 0.0.0-stage → 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.
package/index.js ADDED
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { createInterface } from 'node:readline/promises';
6
+ import { stdin, stdout, argv, exit, cwd, env } from 'node:process';
7
+
8
+ const TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), 'template');
9
+ const PRESETS = ['default', 'ocean', 'forest', 'sunset', 'rose', 'midnight', 'mono', 'paper'];
10
+ const RENAME = { _gitignore: '.gitignore' };
11
+
12
+ const color = stdout.isTTY && !env.NO_COLOR;
13
+ const paint = (code) => (s) => (color ? `\x1b[${code}m${s}\x1b[0m` : String(s));
14
+ const bold = paint('1');
15
+ const dim = paint('2');
16
+ const green = paint('32');
17
+ const cyan = paint('36');
18
+ const red = paint('31');
19
+
20
+ const HELP = `
21
+ ${bold('create-markdown2site')} — scaffold a markdown2site website
22
+
23
+ ${bold('Usage')}
24
+ npm create markdown2site@latest [project-name] [-- options]
25
+ npx create-markdown2site [project-name] [options]
26
+
27
+ ${bold('Options')}
28
+ -p, --preset <name> Color preset: ${PRESETS.join(', ')}
29
+ -t, --title <title> Site title (defaults to the project name)
30
+ -f, --force Write into a non-empty folder
31
+ -y, --yes Skip questions and use defaults
32
+ -h, --help Show this help
33
+ -v, --version Show version
34
+ `;
35
+
36
+ function parseArgs(args) {
37
+ const opts = { dir: undefined, preset: undefined, title: undefined, force: false, yes: false };
38
+ for (let i = 0; i < args.length; i += 1) {
39
+ const arg = args[i];
40
+ const next = () => {
41
+ const value = args[i + 1];
42
+ if (!value || value.startsWith('-')) fail(`Missing value for ${arg}`);
43
+ i += 1;
44
+ return value;
45
+ };
46
+ if (arg === '-h' || arg === '--help') opts.help = true;
47
+ else if (arg === '-v' || arg === '--version') opts.version = true;
48
+ else if (arg === '-f' || arg === '--force') opts.force = true;
49
+ else if (arg === '-y' || arg === '--yes') opts.yes = true;
50
+ else if (arg === '-p' || arg === '--preset') opts.preset = next();
51
+ else if (arg.startsWith('--preset=')) opts.preset = arg.slice(9);
52
+ else if (arg === '-t' || arg === '--title') opts.title = next();
53
+ else if (arg.startsWith('--title=')) opts.title = arg.slice(8);
54
+ else if (arg.startsWith('-')) fail(`Unknown option: ${arg}`);
55
+ else if (!opts.dir) opts.dir = arg;
56
+ else fail(`Unexpected argument: ${arg}`);
57
+ }
58
+ return opts;
59
+ }
60
+
61
+ function fail(message) {
62
+ console.error(`\n${red('✖')} ${message}\n`);
63
+ exit(1);
64
+ }
65
+
66
+ function toPackageName(name) {
67
+ return name
68
+ .trim()
69
+ .toLowerCase()
70
+ .replace(/\s+/g, '-')
71
+ .replace(/^[._]/, '')
72
+ .replace(/[^a-z0-9-~]+/g, '-')
73
+ .replace(/-+/g, '-')
74
+ .replace(/^-|-$/g, '') || 'markdown2site-app';
75
+ }
76
+
77
+ function toTitle(name) {
78
+ return name
79
+ .replace(/[-_]+/g, ' ')
80
+ .replace(/\s+/g, ' ')
81
+ .trim()
82
+ .split(' ')
83
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
84
+ .join(' ') || 'My Site';
85
+ }
86
+
87
+ function isEmptyDir(dir) {
88
+ if (!fs.existsSync(dir)) return true;
89
+ const files = fs.readdirSync(dir).filter((f) => f !== '.git' && f !== '.DS_Store');
90
+ return files.length === 0;
91
+ }
92
+
93
+ // Escape the title for the kind of file it's inserted into.
94
+ const escapers = {
95
+ '.js': (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/`/g, '\\`').replace(/\$\{/g, '\\${'),
96
+ '.jsx': (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/`/g, '\\`').replace(/\$\{/g, '\\${'),
97
+ '.html': (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'),
98
+ };
99
+
100
+ function copyTemplate(src, dest, vars) {
101
+ fs.mkdirSync(dest, { recursive: true });
102
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
103
+ const from = path.join(src, entry.name);
104
+ const to = path.join(dest, RENAME[entry.name] || entry.name);
105
+ if (entry.isDirectory()) {
106
+ copyTemplate(from, to, vars);
107
+ continue;
108
+ }
109
+ const ext = path.extname(entry.name);
110
+ if (['.js', '.jsx', '.html', '.md', '.json'].includes(ext) || entry.name === '_gitignore') {
111
+ const escape = escapers[ext] || ((s) => s);
112
+ let text = fs.readFileSync(from, 'utf8');
113
+ for (const [key, value] of Object.entries(vars)) text = text.split(`%${key}%`).join(escape(value));
114
+ fs.writeFileSync(to, text);
115
+ } else {
116
+ fs.copyFileSync(from, to);
117
+ }
118
+ }
119
+ }
120
+
121
+ function packageManager() {
122
+ const agent = env.npm_config_user_agent || '';
123
+ if (agent.startsWith('pnpm')) return 'pnpm';
124
+ if (agent.startsWith('yarn')) return 'yarn';
125
+ if (agent.startsWith('bun')) return 'bun';
126
+ return 'npm';
127
+ }
128
+
129
+ async function main() {
130
+ const opts = parseArgs(argv.slice(2));
131
+ if (opts.help) { console.log(HELP); return; }
132
+ if (opts.version) {
133
+ const pkg = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
134
+ console.log(pkg.version);
135
+ return;
136
+ }
137
+
138
+ const interactive = stdin.isTTY && stdout.isTTY && !opts.yes;
139
+ const rl = interactive ? createInterface({ input: stdin, output: stdout }) : null;
140
+ const ask = async (question, fallback) => {
141
+ if (!rl) return fallback;
142
+ const answer = (await rl.question(`${cyan('?')} ${bold(question)} ${dim(`(${fallback})`)} `)).trim();
143
+ return answer || fallback;
144
+ };
145
+
146
+ try {
147
+ console.log(`\n${bold('markdown2site')} ${dim('— build websites from markdown')}\n`);
148
+
149
+ const dirName = opts.dir || (await ask('Project name:', 'my-site'));
150
+ const targetDir = path.resolve(cwd(), dirName);
151
+ const baseName = path.basename(targetDir);
152
+
153
+ if (!isEmptyDir(targetDir) && !opts.force) {
154
+ const answer = rl
155
+ ? (await ask(`"${dirName}" is not empty. Write into it anyway? (y/N)`, 'n')).toLowerCase()
156
+ : 'n';
157
+ if (answer !== 'y' && answer !== 'yes') fail(`Folder "${dirName}" is not empty. Use --force to write into it.`);
158
+ }
159
+
160
+ let preset = opts.preset;
161
+ if (!preset && rl) {
162
+ console.log(dim(` Presets: ${PRESETS.map((p, i) => `${i + 1}) ${p}`).join(' ')}`));
163
+ const picked = await ask('Color preset:', 'default');
164
+ preset = PRESETS[Number(picked) - 1] || picked;
165
+ }
166
+ preset = preset || 'default';
167
+ if (!PRESETS.includes(preset)) fail(`Unknown preset "${preset}". Choose one of: ${PRESETS.join(', ')}`);
168
+
169
+ const title = opts.title || toTitle(baseName);
170
+
171
+ copyTemplate(TEMPLATE_DIR, targetDir, { SITE_TITLE: title, PRESET: preset });
172
+
173
+ const pkgPath = path.join(targetDir, 'package.json');
174
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
175
+ pkg.name = toPackageName(baseName);
176
+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
177
+
178
+ const pm = packageManager();
179
+ const run = pm === 'npm' ? 'npm run' : pm;
180
+ const relative = path.relative(cwd(), targetDir);
181
+
182
+ console.log(`\n${green('✔')} Created ${bold(title)} in ${cyan(relative || '.')}\n`);
183
+ console.log(' Next steps:\n');
184
+ if (relative) console.log(` cd ${relative.includes(' ') ? `"${relative}"` : relative}`);
185
+ console.log(` ${pm} install`);
186
+ console.log(` ${run} dev\n`);
187
+ console.log(dim(' Edit src/site.config.js to configure your site, and add pages in public/markdown/.\n'));
188
+ } finally {
189
+ if (rl) rl.close();
190
+ }
191
+ }
192
+
193
+ main().catch((err) => fail(err && err.message ? err.message : String(err)));
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "create-markdown2site",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a ready-to-run markdown2site website: npm create markdown2site my-site",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-markdown2site": "index.js"
8
+ },
9
+ "files": [
10
+ "index.js",
11
+ "template"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "keywords": [
17
+ "markdown2site",
18
+ "create",
19
+ "scaffold",
20
+ "markdown",
21
+ "react",
22
+ "vite",
23
+ "website"
24
+ ],
25
+ "author": "imaketech <imaketechofficial@gmail.com>",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/imaketech1/markdown2site.git",
30
+ "directory": "create-markdown2site"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/imaketech1/markdown2site/issues"
34
+ }
35
+ }
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ *.log
4
+ .DS_Store
5
+ .env
6
+ .env.local
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>%SITE_TITLE%</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.jsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "markdown2site-app",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "markdown2site": "^1.1.0",
13
+ "react": "^19.0.0",
14
+ "react-dom": "^19.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-react": "^6.0.0",
18
+ "vite": "^8.0.0"
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ # Welcome to %SITE_TITLE%
2
+
3
+ This website is generated from markdown files with **markdown2site**. Edit
4
+ `public/markdown/index.md` to change this page.
5
+
6
+ ## What's here
7
+
8
+ - **Home** — this page, from `public/markdown/index.md`
9
+ - **Posts** — every file in `public/markdown/posts/` listed in `src/site.config.js`
10
+ - **Themes** — light and dark mode, color presets, fonts and layouts
11
+
12
+ ## Make it yours
13
+
14
+ Click the palette button in the bottom-left corner to try colors, fonts and
15
+ layouts live. When you like it, press **Copy config** and paste the result into
16
+ `src/site.config.js`.
17
+
18
+ ## Next
19
+
20
+ Read [Getting Started](/getting-started) to add your first page.
@@ -0,0 +1,33 @@
1
+ ## Add a page
2
+
3
+ 1. Create a file in `public/markdown/posts/`, for example `my-page.md`.
4
+ 2. Add it to `postsList` in `src/site.config.js`:
5
+
6
+ ```js
7
+ postsList: [
8
+ { title: 'My Page', slug: 'my-page' },
9
+ ],
10
+ ```
11
+
12
+ 3. Open `/my-page` in the browser.
13
+
14
+ ## Change the look
15
+
16
+ Everything lives in `src/site.config.js`:
17
+
18
+ | Option | Values |
19
+ |---|---|
20
+ | `preset` | `default`, `ocean`, `forest`, `sunset`, `rose`, `midnight`, `mono`, `paper` |
21
+ | `mode` | `light`, `dark`, `system` |
22
+ | `layout.style` | `classic`, `card`, `minimal`, `wide` |
23
+ | `sidebar.align` | `left`, `right`, `hidden` |
24
+ | `header.show` | `true`, `false` |
25
+
26
+ ## Deploy
27
+
28
+ ```bash
29
+ npm run build
30
+ ```
31
+
32
+ Upload the `dist/` folder to any static host. For GitHub Pages, set
33
+ `router: 'hash'` in `src/site.config.js`.
@@ -0,0 +1,34 @@
1
+ ## Headings
2
+
3
+ Every `##` and `###` title shows up in the **On this page** sidebar.
4
+
5
+ ### Smaller sections
6
+
7
+ Like this one.
8
+
9
+ ## Text and links
10
+
11
+ Write **bold**, *italic*, `inline code` and [links](/getting-started) as usual.
12
+
13
+ > Blockquotes are great for tips and notes.
14
+
15
+ ## Code
16
+
17
+ ```js
18
+ function greet(name) {
19
+ return `Hello, ${name}!`;
20
+ }
21
+ ```
22
+
23
+ ## Lists and tasks
24
+
25
+ - Plain lists
26
+ - [x] Task lists
27
+ - [ ] Still to do
28
+
29
+ ## Tables
30
+
31
+ | Feature | Supported |
32
+ |---|---|
33
+ | Tables | Yes |
34
+ | Images | Yes |
@@ -0,0 +1,10 @@
1
+ import { SiteConfigProvider, MarkdownSite } from 'markdown2site';
2
+ import { siteConfig } from './site.config.js';
3
+
4
+ export default function App() {
5
+ return (
6
+ <SiteConfigProvider config={siteConfig}>
7
+ <MarkdownSite />
8
+ </SiteConfigProvider>
9
+ );
10
+ }
@@ -0,0 +1,9 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App.jsx';
4
+
5
+ createRoot(document.getElementById('root')).render(
6
+ <StrictMode>
7
+ <App />
8
+ </StrictMode>,
9
+ );
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Your site's configuration. Every option is optional.
3
+ * Turn on `features.customizer`, tweak the look live in the browser, then click "Copy config".
4
+ */
5
+ export const siteConfig = {
6
+ siteTitle: '%SITE_TITLE%',
7
+
8
+ // 'default' | 'ocean' | 'forest' | 'sunset' | 'rose' | 'midnight' | 'mono' | 'paper'
9
+ preset: '%PRESET%',
10
+ mode: 'system', // 'light' | 'dark' | 'system'
11
+
12
+ fonts: {
13
+ body: 'inter',
14
+ heading: 'plus-jakarta',
15
+ code: 'jetbrains-mono',
16
+ },
17
+
18
+ typography: {
19
+ headingStyle: 'accent', // 'plain' | 'underline' | 'accent' | 'hash'
20
+ },
21
+
22
+ layout: {
23
+ style: 'classic', // 'classic' | 'card' | 'minimal' | 'wide'
24
+ },
25
+
26
+ header: {
27
+ logo: '📝 %SITE_TITLE%',
28
+ variant: 'glass', // 'gradient' | 'solid' | 'glass' | 'minimal'
29
+ show: true,
30
+ showNav: true,
31
+ showThemeToggle: true,
32
+ sticky: true,
33
+ },
34
+
35
+ // "On this page" navigation built from your ## titles
36
+ sidebar: {
37
+ align: 'right', // 'left' | 'right' | 'hidden'
38
+ variant: 'line', // 'line' | 'card' | 'minimal'
39
+ },
40
+
41
+ footer: {
42
+ text: `© ${new Date().getFullYear()} %SITE_TITLE%`,
43
+ links: [{ label: 'GitHub', url: 'https://github.com', external: true }],
44
+ },
45
+
46
+ features: {
47
+ readingProgress: true,
48
+ backToTop: true,
49
+ customizer: true, // set to false before you deploy
50
+ },
51
+
52
+ router: 'browser', // use 'hash' for GitHub Pages
53
+
54
+ // Each slug matches a file in public/markdown/posts/<slug>.md
55
+ postsList: [
56
+ { title: 'Getting Started', slug: 'getting-started' },
57
+ { title: 'Writing Pages', slug: 'writing-pages' },
58
+ ],
59
+ };
@@ -0,0 +1,7 @@
1
+ import react from '@vitejs/plugin-react';
2
+ import { defineConfig } from 'vite';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ build: { chunkSizeWarningLimit: 1500 },
7
+ });
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.